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) {} public doLoadLocalTape(file: File, done?: () => void) { const kHz = this.io.getKHz(); // Audio Buffer Source let context: AudioContext; if (AudioContext) { context = new AudioContext(); } else { window.alert('Not supported by your browser'); done && done(); return; } const fileReader = new FileReader(); fileReader.onload = (ev: ProgressEvent) => { const target: FileReader = ev.target as FileReader; const result: ArrayBuffer = target.result as ArrayBuffer; context.decodeAudioData(result).then( (buffer) => { const buf: TapeData = []; const data = buffer.getChannelData(0); let datum = data[0]; let old = datum > 0.0, current; let last = 0; let delta: number; debug(`Sample Count: ${data.length}`); debug(`Sample rate: ${buffer.sampleRate}`); for (let idx = 1; idx < data.length; idx++) { datum = data[idx]; if (datum > 0.1 || datum < -0.1) { current = datum > 0.0; if (current !== old) { delta = idx - last; if (delta > 2000000) { delta = 2000000; } let ival = (delta / buffer.sampleRate) * 1000; if (ival >= 0.55 && ival < 0.75) { ival = 0.65; // Header } else if (ival >= 0.175 && ival < 0.225) { ival = 0.2; // sync 1 } else if (ival >= 0.225 && ival < 0.275) { ival = 0.25; // 0 / sync 2 } else if (ival >= 0.45 && ival < 0.55) { ival = 0.5; // 1 } else { // debug(idx + ' ' + buf.length + ' ' + ival); } buf.push([ival * kHz, current]); old = current; last = idx; } } } this.io.setTape(buf); if (done) { done(); } }, (error: Error) => { window.alert(error.message); } ); }; fileReader.readAsArrayBuffer(file); } }