Skip to content

Commit

Permalink
Merge pull request #48 from stefanschramm/annotations
Browse files Browse the repository at this point in the history
Annotate sections for encoded audio
  • Loading branch information
stefanschramm committed Mar 27, 2024
2 parents 3da37d3 + ff239b4 commit 76f1b01
Show file tree
Hide file tree
Showing 20 changed files with 495 additions and 295 deletions.
482 changes: 202 additions & 280 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion retroload-lib/src/common/Positioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export type Position = {
};

export function formatPosition(p: Position): string {
return `${secondsToTimestamp(p.seconds)} sample ${p.samples}`;
return `${secondsToTimestamp(p.seconds)} sample ${p.samples.toString().padStart(9, '0')}`;
}

function secondsToTimestamp(totalSeconds: number): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {type OptionContainer} from '../../Options.js';
import {type RecorderInterface} from '../../recorder/RecorderInterface.js';
import {unidentifiable, type FormatIdentification} from '../AdapterDefinition.js';
import {type AdapterDefinition} from '../AdapterDefinition.js';
import {hex8} from '../../../common/Utils.js';

const definition: AdapterDefinition = {
name: 'Atari (Generic data)',
Expand Down Expand Up @@ -32,6 +33,7 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, _options: OptionC
e.setDefaultBaudrate();
const chunks = ba.chunks(dataBytesPerBlock);
for (let blockId = 0; blockId < chunks.length; blockId++) {
recorder.beginAnnotation(`Block ${hex8(blockId)}`);
const chunkBa = chunks[blockId];
const partialBlock = chunkBa.length() !== dataBytesPerBlock;
const blockType = partialBlock ? blockTypePartial : blockTypeFull;
Expand All @@ -48,16 +50,19 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, _options: OptionC
blockBa.setUint8(131, calculateChecksum(blockBa));
e.recordIrg((blockId === 0) ? pilotIrgLength : defaultIrgLength); // TODO: create option (longer values are required for "ENTER-loading")
e.recordBytes(blockBa);
recorder.endAnnotation();
}

// End of file block
recorder.beginAnnotation('Block EOF');
const eofBlockBa = BufferAccess.create(132);
eofBlockBa.writeUint8(markerByte);
eofBlockBa.writeUint8(markerByte);
eofBlockBa.writeUint8(blockTypeEndOfFile);
eofBlockBa.setUint8(131, calculateChecksum(eofBlockBa));
e.recordIrg(defaultIrgLength); // TODO: create option (longer values are required for "ENTER-loading")
e.recordBytes(eofBlockBa);
recorder.endAnnotation();
}

function calculateChecksum(ba: BufferAccess) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export class BasicodeEncoder extends AbstractEncoder {

public recordBasicData(ba: BufferAccess) {
for (const chunkBa of ba.chunksPadded(1024, sth)) {
this.recorder.beginAnnotation('Chunk');
this.record(chunkBa, sth);
this.recorder.endAnnotation();
}
}

Expand Down
18 changes: 14 additions & 4 deletions retroload-lib/src/encoding/adapter/c64/C64Encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,33 +171,43 @@ export class C64Encoder extends AbstractEncoder {
headerBa.writeAsciiString(filename); // 16 bytes: filename
headerBa.writeAsciiString(' '.repeat(171)); // 171 bytes: padding with spaces

this.recorder.beginAnnotation('File');

Logger.debug('C64Encoder - recordBasicOrPrg - header:');
Logger.debug(headerBa.asHexDump());

// header
this.recorder.beginAnnotation('Header');
this.recordPilot(this.shortpilot ? 0x1a00 : 0x6a00);
this.recordSyncChain();
this.recordDataWithCheckByte(headerBa);
this.recordEndOfDataMarker();
// header repeated
this.recorder.endAnnotation();

this.recorder.beginAnnotation('Header repeated');
this.recordPilot(0x4f);
this.recordSyncChainRepeated();
this.recordDataWithCheckByte(headerBa);
this.recordEndOfDataMarker();
this.recorder.endAnnotation();

Logger.debug('C64Encoder - recordBasicOrPrg - data:');
Logger.debug(dataBa.asHexDump());

// data
this.recorder.beginAnnotation('Data');
this.recordPilot(0x1a00);
this.recordSyncChain();
this.recordDataWithCheckByte(dataBa); // include end of data marker
this.recordEndOfDataMarker();
// data repeated
this.recorder.endAnnotation();

this.recorder.beginAnnotation('Data repeated');
this.recordPilot(0x4f);
this.recordSyncChainRepeated();
this.recordDataWithCheckByte(dataBa); // include end of data marker
this.recordEndOfDataMarker();
this.recordPilot(0x4e);
this.recorder.endAnnotation();

this.recorder.endAnnotation();
}
}
14 changes: 13 additions & 1 deletion retroload-lib/src/encoding/adapter/cpc/CpcGenericAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {BufferAccess} from '../../../common/BufferAccess.js';
import {type RecorderInterface} from '../../recorder/RecorderInterface.js';
import {unidentifiable, type FormatIdentification} from '../AdapterDefinition.js';
import {type AdapterDefinition} from '../AdapterDefinition.js';
import {calculateCrc16Ccitt} from '../../../common/Utils.js';
import {calculateCrc16Ccitt, hex8} from '../../../common/Utils.js';

/**
* https://www.cpcwiki.eu/imgs/5/5d/S968se08.pdf
Expand Down Expand Up @@ -51,13 +51,17 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, options: OptionCo

e.begin();
for (let b = 0; b < chunks.length; b++) {
recorder.beginAnnotation(`Block ${hex8(b)}`);

const chunk = chunks[b];
const isFirstBlock = b === 0;
const isLastBlock = b === (chunks.length - 1);
const blockDataLocation = load + b * dataBytesPerDataBlock;

// header block

recorder.beginAnnotation('Header');

const headerBa = BufferAccess.create(0x100);
headerBa.writeAsciiString(filename, maxFileNameLength, 0);
headerBa.writeUint8(b + 1); // block number
Expand All @@ -75,10 +79,18 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, options: OptionCo
const headerRecordBa = createHeaderRecord(headerBa);
e.recordDataBlock(headerRecordBa, {...e.getStandardSpeedRecordOptions(), pauseLengthMs: 0x000a});

recorder.endAnnotation(); // end of header

// data block

recorder.beginAnnotation('Data');

const dataRecordBa = createDataRecord(chunk);
e.recordDataBlock(dataRecordBa, {...e.getStandardSpeedRecordOptions(), pauseLengthMs: 0x09c4});

recorder.endAnnotation(); // end of data

recorder.endAnnotation(); // end of block
}
e.end();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, options: OptionCo
e.begin();

for (let block = 0; block < chunks.length; block++) {
recorder.beginAnnotation(`Block ${block}`);

const isFirstBlock = block === 0;
const isLastBlock = block === chunks.length - 1;
const blockDataBa = chunks[block];
Expand Down Expand Up @@ -72,6 +74,8 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, options: OptionCo

e.recordPilot(isFirstBlock ? (shortpilot ? 1.5 : 5.1) : 0.9);
e.recordBytes(blockBa);

recorder.endAnnotation();
}

e.recordPilot(shortpilot ? 1.5 : 5.3);
Expand Down
4 changes: 4 additions & 0 deletions retroload-lib/src/encoding/adapter/kc/KcEncoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export class KcEncoder extends AbstractEncoder {
}

recordBlock(blockNumber: number, blockDataBa: BufferAccess) {
this.recorder.beginAnnotation(`Block ${hex8(blockNumber)}`);

if (blockDataBa.length() > blockSize) {
throw new InputDataError('Block data exceeds length of 128 bytes');
}
Expand All @@ -44,6 +46,8 @@ export class KcEncoder extends AbstractEncoder {
blockBa.setUint8(blockSize + 1, checksum);

this.recordBytes(blockBa);

this.recorder.endAnnotation();
}

recordIntro() {
Expand Down
6 changes: 6 additions & 0 deletions retroload-lib/src/encoding/adapter/mo5/Mo5Encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,27 @@ const fOne = 1200;
export class Mo5Encoder extends AbstractEncoder {
recordStartBlock(ba: BufferAccess) {
Logger.debug(ba.asHexDump());
this.recorder.beginAnnotation('Start block');
this.recordPilot(1);
this.recordBytes(ba);
this.recordPilot(2);
this.recorder.endAnnotation();
}

recordDataBlock(ba: BufferAccess) {
Logger.debug(ba.asHexDump());
this.recorder.beginAnnotation('Data block');
this.recordPilot(0.2);
this.recordBytes(ba);
this.recorder.endAnnotation();
}

recordEndBlock(ba: BufferAccess) {
Logger.debug(ba.asHexDump());
this.recorder.beginAnnotation('End block');
this.recordBytes(ba);
this.recordPilot(1.5);
this.recorder.endAnnotation();
}

override recordByte(byte: number) {
Expand Down
4 changes: 4 additions & 0 deletions retroload-lib/src/encoding/adapter/ta/TaEncoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ export class TaEncoder extends AbstractEncoder {
headerBa.writeAsciiString('', 10, 0xd3);
headerBa.writeAsciiString(filename, maxFileNameLength, 0);

this.recorder.beginAnnotation('Header');
this.recordOscillations(fOne, 500);
this.recordBytes(headerBa);
this.recorder.endAnnotation();
this.recorder.beginAnnotation('Data');
this.recordOscillations(fOne, 500);
this.recordBytes(dataBa);
this.recordOscillations(fOne, 500);
this.recorder.endAnnotation();
}

override recordByte(byte: number) {
Expand Down
4 changes: 4 additions & 0 deletions retroload-lib/src/encoding/adapter/ti/TiEncoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ const fOne = 1379;
*/
export class TiEncoder extends AbstractEncoder {
recordHeader(numberOfRecords: number) {
this.recorder.beginAnnotation('Header');
for (let i = 0; i < 768; i++) {
this.recordByte(0x00);
}
this.recordByte(0xff); // data mark
this.recordByte(numberOfRecords);
this.recordByte(numberOfRecords); // repeated
this.recorder.endAnnotation();
}

recordBlock(blockDataBa: BufferAccess) {
Expand All @@ -31,12 +33,14 @@ export class TiEncoder extends AbstractEncoder {
const checksum = calculateChecksum8(blockDataBa);
// every block is written twice
for (let i = 0; i < 2; i++) {
this.recorder.beginAnnotation(i === 0 ? 'Data' : 'Data repeated');
for (let j = 0; j < 8; j++) {
this.recordByte(0x00);
}
this.recordByte(0xff); // data mark
this.recordBytes(blockDataBa);
this.recordByte(checksum);
this.recorder.endAnnotation();
}
}

Expand Down
7 changes: 7 additions & 0 deletions retroload-lib/src/encoding/adapter/z1013/Z1013Encoder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {AbstractEncoder} from '../AbstractEncoder.js';
import {BufferAccess} from '../../../common/BufferAccess.js';
import {Logger} from '../../../common/logging/Logger.js';
import {hex16} from '../../../common/Utils.js';

const blockDataSize = 32;
const fOne = 1280;
Expand Down Expand Up @@ -38,6 +39,8 @@ export class Z1013Encoder extends AbstractEncoder {
}

recordBlock(blockNumber: number, blockDataBa: BufferAccess) {
this.recorder.beginAnnotation(`Block ${hex16(blockNumber)}`);

const blockBa = BufferAccess.create(2 + blockDataBa.length() + 2);
blockBa.writeUint16Le(blockNumber);
blockBa.writeBa(blockDataBa);
Expand All @@ -48,10 +51,14 @@ export class Z1013Encoder extends AbstractEncoder {
this.recordIntro();
this.recordDelimiter();
this.recordBytes(blockBa);

this.recorder.endAnnotation();
}

recordFirstIntro() {
this.recorder.beginAnnotation('Sync');
this.recordOscillations(fSync, 2000);
this.recorder.endAnnotation();
}

recordIntro() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,18 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, options: OptionCo
headerBlockBa.writeUint16Le(param2);
headerBlockBa.writeUint8(calculateChecksum8Xor(headerBlockBa.slice(0, 18)));

recorder.beginAnnotation('Header');
e.recordStandardSpeedDataBlock(headerBlockBa);
recorder.endAnnotation();

const dataBlockBa = BufferAccess.create(ba.length() + 2);
dataBlockBa.writeUint8(0xff); // marker byte
dataBlockBa.writeBa(ba);
dataBlockBa.writeUint8(calculateChecksum8Xor(dataBlockBa));

recorder.beginAnnotation('Data');
e.recordStandardSpeedDataBlock(dataBlockBa);
recorder.endAnnotation();

e.end();
}
86 changes: 86 additions & 0 deletions retroload-lib/src/encoding/recorder/Annotations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {InternalError} from '../../common/Exceptions.js';
import {AnnotationCollector} from './Annotations.js';

test('Collects annotations', () => {
// Example annotations:
// 0 s 1 s 2s
// -------------------------------------------
// |label 0 |
// ---------------------|---------------------
// |label 0.1 |label 0.2 |
// ---------------------|---------------------

const collector = new AnnotationCollector();
collector.beginAnnotation('label 0', {samples: 0, seconds: 0});
collector.beginAnnotation('label 0.0', {samples: 0, seconds: 0});
collector.endAnnotation({samples: 44100, seconds: 1});
collector.beginAnnotation('label 0.1', {samples: 44100, seconds: 1});
collector.endAnnotation({samples: 88200, seconds: 2});
collector.endAnnotation({samples: 88200, seconds: 2});

const annotations = collector.getAnnotations();

expect(annotations.length).toBe(1);
expect(annotations[0].label).toBe('label 0');
expect(annotations[0].begin).toStrictEqual({samples: 0, seconds: 0});
expect(annotations[0].end).toStrictEqual({samples: 88200, seconds: 2});
expect(annotations[0].annotations.length).toBe(2);

expect(annotations[0].annotations[0].label).toBe('label 0.0');
expect(annotations[0].annotations[0].begin).toStrictEqual({samples: 0, seconds: 0});
expect(annotations[0].annotations[0].end).toStrictEqual({samples: 44100, seconds: 1});
expect(annotations[0].annotations[0].annotations.length).toBe(0);

expect(annotations[0].annotations[1].label).toBe('label 0.1');
expect(annotations[0].annotations[1].begin).toStrictEqual({samples: 44100, seconds: 1});
expect(annotations[0].annotations[1].end).toStrictEqual({samples: 88200, seconds: 2});
expect(annotations[0].annotations[1].annotations.length).toBe(0);
});

test('Throws exception on invalid encapsulation', () => {
let exception: any;
try {
const collector = new AnnotationCollector();
collector.beginAnnotation('label 0', {samples: 1, seconds: 44100});
collector.beginAnnotation('label 0.0', {samples: 0, seconds: 0}); // begin before parent annotation
} catch (e) {
exception = e;
}
expect(exception).toBeInstanceOf(InternalError);
});

test('Throws exception when end is before begin', () => {
let exception: any;
try {
const collector = new AnnotationCollector();
collector.beginAnnotation('label 0', {samples: 88200, seconds: 2});
collector.endAnnotation({samples: 1, seconds: 44100});
} catch (e) {
exception = e;
}
expect(exception).toBeInstanceOf(InternalError);
});

test('Throws exception when begin is before end of previous annotation in same level', () => {
let exception: any;
try {
const collector = new AnnotationCollector();
collector.beginAnnotation('label 0', {samples: 0, seconds: 0});
collector.endAnnotation({samples: 2, seconds: 88200});
collector.beginAnnotation('label 1', {samples: 44100, seconds: 1});
} catch (e) {
exception = e;
}
expect(exception).toBeInstanceOf(InternalError);
});

test('Throws exception when trying to close unopened annotation', () => {
let exception: any;
try {
const collector = new AnnotationCollector();
collector.endAnnotation({samples: 1, seconds: 44100});
} catch (e) {
exception = e;
}
expect(exception).toBeInstanceOf(InternalError);
});
Loading

0 comments on commit 76f1b01

Please sign in to comment.