2021-03-28 23:39:18 +00:00
|
|
|
|
|
|
|
import { TapeData } from '../types';
|
|
|
|
import Apple2IO from '../apple2io';
|
|
|
|
import { debug } from '../util';
|
|
|
|
|
|
|
|
export const TAPE_TYPES = ['wav', 'aiff', 'aif', 'mp3', 'm4a'] as const;
|
|
|
|
|
|
|
|
export default class Tape {
|
|
|
|
constructor(private readonly io: Apple2IO) {}
|
|
|
|
|
2021-03-31 00:27:44 +00:00
|
|
|
public doLoadLocalTape(file: File, done?: () => void) {
|
2021-03-28 23:39:18 +00:00
|
|
|
const kHz = this.io.getKHz();
|
|
|
|
|
|
|
|
// Audio Buffer Source
|
|
|
|
let context: AudioContext;
|
|
|
|
if (AudioContext) {
|
|
|
|
context = new AudioContext();
|
|
|
|
} else {
|
|
|
|
window.alert('Not supported by your browser');
|
2021-03-31 00:27:44 +00:00
|
|
|
done && done();
|
2021-03-28 23:39:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const fileReader = new FileReader();
|
|
|
|
fileReader.onload = (ev: ProgressEvent) => {
|
|
|
|
const target: FileReader = ev.target as FileReader;
|
|
|
|
const result: ArrayBuffer = target.result as ArrayBuffer;
|
2021-06-14 00:06:16 +00:00
|
|
|
context.decodeAudioData(result).then((buffer) => {
|
2021-03-28 23:39:18 +00:00
|
|
|
const buf: TapeData = [];
|
|
|
|
const data = buffer.getChannelData(0);
|
|
|
|
let datum = data[0];
|
|
|
|
let old = (datum > 0.0), current;
|
|
|
|
let last = 0;
|
|
|
|
let delta: number;
|
2022-05-31 15:38:40 +00:00
|
|
|
debug(`Sample Count: ${data.length}`);
|
|
|
|
debug(`Sample rate: ${buffer.sampleRate}`);
|
2021-03-28 23:39:18 +00:00
|
|
|
for (let idx = 1; idx < data.length; idx++) {
|
|
|
|
datum = data[idx];
|
|
|
|
if ((datum > 0.1) || (datum < -0.1)) {
|
|
|
|
current = (datum > 0.0);
|
2022-05-18 15:19:45 +00:00
|
|
|
if (current !== old) {
|
2021-03-28 23:39:18 +00:00
|
|
|
delta = idx - last;
|
|
|
|
if (delta > 2000000) {
|
|
|
|
delta = 2000000;
|
|
|
|
}
|
|
|
|
let ival = delta / buffer.sampleRate * 1000;
|
|
|
|
if (ival >= 0.550 && ival < 0.750) {
|
|
|
|
ival = 0.650; // Header
|
|
|
|
} else if (ival >= 0.175 && ival < 0.225) {
|
|
|
|
ival = 0.200; // sync 1
|
|
|
|
} else if (ival >= 0.225 && ival < 0.275) {
|
|
|
|
ival = 0.250; // 0 / sync 2
|
|
|
|
} else if (ival >= 0.450 && ival < 0.550) {
|
|
|
|
ival = 0.500; // 1
|
|
|
|
} else {
|
|
|
|
// debug(idx + ' ' + buf.length + ' ' + ival);
|
|
|
|
}
|
|
|
|
buf.push([ival * kHz, current]);
|
|
|
|
old = current;
|
|
|
|
last = idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.io.setTape(buf);
|
|
|
|
if (done) {
|
|
|
|
done();
|
|
|
|
}
|
2022-05-31 15:38:40 +00:00
|
|
|
}, (error: Error) => {
|
2021-03-28 23:39:18 +00:00
|
|
|
window.alert(error.message);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
fileReader.readAsArrayBuffer(file);
|
|
|
|
}
|
|
|
|
}
|