Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | import { TimeInSeconds } from "../midi/types"; import { SoundOff, SoundOn } from "../digital/types"; import { DURATION_CLICK_PAD, Sound, SoundSystem } from "../andy"; import { NoteTable, PAL_CLOCK, Song } from "./types"; import { MOD } from "./song"; export const samplePlayer = (system: SoundSystem, mod: Song, sample: number): SoundOn => { return (note: string, time: TimeInSeconds, track: AudioNode): SoundOff => { // ew. // @ts-ignore const period = [...NoteTable.entries()] .filter(({ 1: v }) => v === note) .map(([k]) => k); const sampBuffer = mod.sampleBuffers[sample]; let tsample: Sound; if (sampBuffer.length) { // Firefox only does: 8,000 Hz to 96,000 Hz // https://www.lim.di.unimi.it/IEEE/VROS/MOD.HTM const buff = system.context().createBuffer( 1, sampBuffer.length, // This is a bit mad (and cool) // adjust the sample rate using on the note value // to adjust the pitch :-o Sadly, firefox wont // let this work below 8000Hz so we can do anything lower // than C-4 (C-3 is like 4143) Math.floor(PAL_CLOCK / period[0]) ); const channel = buff.getChannelData(0); for (let i = 0; i < buff.length; i++) { // Basically adjust the volume - the intensity channel[i] = sampBuffer[i] / 128; } const meta = mod.samples[sample]; tsample = new Sound(buff); const samplen = buff.duration; tsample.loop = false; tsample.loopStart = 0; tsample.loopEnd = samplen; if (meta.repeatPoint > 0) { console.log("loop", meta.repeatLength, meta.repeatPoint); // TODO: should be set in the MOD file (well in xm files it is) tsample.loop = true; // Calculate the time needed for the loop repeat const repeatStart = (meta.repeatPoint / sampBuffer.length) * samplen; const repeatEnd = ((meta.repeatPoint + meta.repeatLength) / sampBuffer.length) * samplen; tsample.loopStart = repeatStart; // TODO: end doesn't have to be at the end of the sample tsample.loopEnd = samplen; } tsample.setContext(system.context()); tsample.volume?.gain.setValueAtTime(0.8, time); // let volume = 0.8; // if (effect?.command === 0xC) { // const volume = ((effect.argX ?? 0) * 16 + (effect.argY ?? 0)) / 64; // tsample.volume?.gain.setValueAtTime(volume, time); // } // if (effect?.command === 0xA) { // // const volume = ((effect.argX ?? 0) * 16 + (effect.argY ?? 0)) / 64; // let end = time; // let volume = 0;; // if (effect.argX > 0) { // // slide volume up // end = time + effect.argX / 64 / 8; // volume = 1; // } // if (effect.argY > 0) { // // slide volume down // end = time + effect.argY / 64 / 8; // volume = 0; // } // console.log(time, end); // tsample.volume?.gain.exponentialRampToValueAtTime(volume, end); // } tsample.panner?.positionX.setValueAtTime(0, time); tsample.panner?.positionY.setValueAtTime(0, time); tsample.panner?.positionZ.setValueAtTime(0, time); // tsample.panner?.connect(system.masterGainNode()); tsample.panner?.connect(track); tsample.play(); ///////////////////////////////////////////// } return () => { if (tsample) { if (tsample.volume) tsample.volume.gain.exponentialRampToValueAtTime(0.0001, (system.currentTime() + DURATION_CLICK_PAD)); if (tsample.volume) tsample.volume.gain.value = 0; tsample.panner?.disconnect(track); tsample.destroy(); } }; } }; /** * Load a mod from the server, and pass it through the * sound system to try to setup the file for play */ export const loadMod = (url: string): Promise<Song> => { return new Promise((resolve, reject) => { const headers = new Headers(); headers.append('User-Agent', 'Andy/1.0'); // if (mimeType) { headers.append('Accept', 'audio/mod'); // headers.append('Content-Type', mimeType); // } const body = undefined; fetch(url, { method: 'GET', mode: 'cors', cache: 'reload', headers: headers, credentials: 'same-origin', redirect: 'follow', referrerPolicy: 'no-referrer', body: (body) ? JSON.stringify(body) : body, }) .then(response => response.arrayBuffer()) .then(response => { let mod = new MOD(response); resolve(mod.song()); }).catch(e => console.error(e)); }); }; |