├── songs ├── testsong.txt ├── upbeat.xmz ├── feelgoodplace1.xmz ├── recording_take1.json ├── base2.json ├── fgtake1.json ├── recbase.json ├── upbeatchorus.json ├── lead.json ├── upbeatintrolead.json ├── yoshimibrowsertest.js ├── upbeatintrolead.js ├── recorded.js ├── rec1.json └── recorded2.js ├── 4klang ├── 4klang.inc.js ├── songs │ ├── .gitignore │ └── new.inc.js ├── tools │ ├── .gitignore │ ├── BA_DarkChorus.4ki │ ├── pOWL_BAS_Dubstep03.4ki │ ├── pOWL_BAS_Dubstep07.4ki │ └── compile.sh ├── .gitignore ├── buildwindowsexe.sh ├── createnotes.js ├── instruments │ ├── test.inc │ ├── hihat.inc │ ├── clap.inc │ ├── piano.inc │ ├── KY_Rhodes.inc │ ├── basedrum.inc │ ├── basedrum2.inc │ ├── basedrum3.inc │ ├── KY_Lullaby.inc │ ├── KY_Lullaby2.inc │ ├── basedrum4.inc │ ├── guitar.inc │ ├── guitar2.inc │ ├── KY_GarageOrgan.inc │ ├── hihat2.inc │ ├── piano2.inc │ ├── snare.inc │ ├── string.inc │ ├── KY_GarageOrganChorus.inc │ ├── PA_LoFiChoir.inc │ ├── synth.inc │ ├── synthFlanger.inc │ ├── snare5.inc │ ├── bass2.inc │ ├── BA_DarkChorus.inc │ ├── BA_DirectPunchMS.inc │ ├── BA_Dark.inc │ ├── snare2.inc │ ├── snare6.inc │ ├── pOWL_BAS_Dubstep03.inc │ ├── BA_SawBassFlanger.inc │ ├── SY_RandomArp.inc │ ├── PA_Fairies.inc │ ├── snare4.inc │ ├── BA_Mighty.inc │ ├── SY_RandomArpFlanger.inc │ ├── BA_SawBass.inc │ ├── pad2.inc │ ├── LD_More&MoreMS.inc │ ├── snare3.inc │ ├── BA_Mighty_Feedback.inc │ ├── bass.inc │ ├── pad.inc │ ├── rimshot.inc │ ├── pOWL_BAS_Dubstep07.inc │ ├── airy.inc │ ├── PA_Jarresque.inc │ ├── BA_Deepness.inc │ ├── PA_JarresqueChorus.inc │ ├── LD_AlphaOmegaMS.inc │ ├── PA_LongPad.inc │ ├── PA_Strangeland.inc │ ├── PA_StrangelandChorus.inc │ ├── BA_NotFromThisWorld2.inc │ ├── BA_NotFromThisWorld.inc │ ├── LD_Morpher.inc │ ├── LD_Farscape.inc │ ├── PA_SynastasiaMS.inc │ ├── LD_Short&PunchyMS.inc │ ├── LD_RestInPeaceMS.inc │ ├── PA_Minorium.inc │ └── GA_RestInPeaceMS.inc ├── runlinux.sh ├── run.sh ├── computefunctionhash.js ├── LICENSE_4klang ├── livereload.js └── README.md ├── wasmaudioworklet ├── midisequencer │ ├── ui │ │ └── pianorolldemo │ │ │ ├── .gitignore │ │ │ └── rollup-config.js │ ├── embeddablesongcompiler.js │ ├── sequenceconstants.js │ └── editorfunctions.js ├── synth1 │ ├── modformat │ │ ├── package.json │ │ └── README.md │ ├── build │ │ └── .gitignore │ ├── assembly │ │ ├── __tests__ │ │ │ ├── as-pect.d.ts │ │ │ ├── midi │ │ │ │ └── instruments │ │ │ │ │ └── audioplayer.spec.ts │ │ │ ├── synth │ │ │ │ ├── triangleoscillator.spec.ts │ │ │ │ ├── pan.spec.ts │ │ │ │ └── ifftoscillator.spec.ts │ │ │ └── fx │ │ │ │ ├── limiter.spec.ts │ │ │ │ └── multibandeq.spec.ts │ │ ├── environment.ts │ │ ├── synth │ │ │ ├── bpm.ts │ │ │ ├── decibel.ts │ │ │ ├── sineoscillator.class.ts │ │ │ ├── noise.class.ts │ │ │ ├── note.ts │ │ │ ├── sawoscillator.class.ts │ │ │ ├── squareoscillator.class.ts │ │ │ ├── clip.ts │ │ │ ├── shaper.ts │ │ │ ├── triangleoscillator.class.ts │ │ │ ├── pan.class.ts │ │ │ ├── stereosignal.class.ts │ │ │ └── envelope.class.ts │ │ ├── common │ │ │ └── mixcommon.ts │ │ ├── instruments │ │ │ ├── instrument.class.ts │ │ │ ├── drums │ │ │ │ └── kick2.class.ts │ │ │ ├── hihat.class.ts │ │ │ ├── testinstrument.class.ts │ │ │ ├── lead │ │ │ │ └── sinelead.ts │ │ │ ├── kick.class.ts │ │ │ └── sawbass.class.ts │ │ ├── midi │ │ │ ├── sequencer │ │ │ │ ├── midiparts.ts │ │ │ │ └── midisequencer.ts │ │ │ └── instruments │ │ │ │ └── defaultinstrument.ts │ │ ├── math │ │ │ └── sin.ts │ │ ├── fx │ │ │ ├── midsideprocessor.ts │ │ │ ├── tribandeq.ts │ │ │ ├── multibandeq.ts │ │ │ ├── stereocompressor.ts │ │ │ ├── limiter.ts │ │ │ ├── bandpass.ts │ │ │ ├── eqband.ts │ │ │ ├── comb.ts │ │ │ └── allpass.ts │ │ └── mixes │ │ │ ├── empty.mix.ts │ │ │ └── midi.mix.ts │ ├── .gitignore │ ├── songs │ │ ├── kickbeat.json │ │ ├── recordedsong1.json │ │ ├── kickbeat.js │ │ ├── shufflebeat.js │ │ └── infiniteenergytest.js │ ├── run.sh │ ├── asconfig.json │ ├── as-pect.config.js │ ├── moduleworkerloader.js │ ├── browsersynthcompiler.js │ ├── as-pect.asconfig.json │ └── createbrowsertsbundle.js ├── player │ ├── musicandshadervideoplayer │ │ ├── .gitignore │ │ ├── index.html │ │ ├── renderworker.js │ │ └── rollup.config.js │ └── infinitemusic.html ├── .gitignore ├── common │ ├── filedownload.js │ ├── audioworkletmodules.js │ ├── workermessagehandler.js │ ├── scriptloader.js │ ├── ui │ │ ├── modal.html.js │ │ ├── modal.js │ │ ├── progress.spec.js │ │ ├── progress-spinner.js │ │ └── progress-bar.js │ └── png.spec.js ├── rollup-config-songcompiler.js ├── emptysong.js ├── index.html ├── overview.plantuml ├── webaudiomodules │ ├── yoshimi-xml-parser.js │ └── preseteditor.html ├── analyser │ ├── levelanalysernode.js │ ├── levelanalyserprocessor.js │ └── levelanalyser.spec.js ├── app.spec.js ├── wasmgit │ ├── wasmgitui.html │ └── commitmessagemodal.html ├── visualizer │ └── videoscheduler.js ├── web-test-runner.config.js ├── package.json └── screenrecorder │ └── cameraviewer.js ├── runosx.sh ├── test.xmz ├── dawplugin ├── .gitignore └── CMakeLists.txt ├── .github ├── FUNDING.yml └── workflows │ └── release.yml ├── .vscode ├── settings.json └── launch.json ├── startjack.sh ├── .gitignore ├── .devcontainer ├── post-create.sh └── devcontainer.json ├── testmidi.js ├── playback.js ├── record.js ├── pattern ├── recordedpattern.class.js └── playable │ ├── basepattern.js │ └── arpeggiato1.js ├── LICENSE-WITHOUT-YOSHIMI ├── midi ├── output.js ├── recorder.js └── recorder.class.js ├── tools └── livecodingscheduler.js ├── drumtrack.js ├── testpattern4.js └── README.md /songs/testsong.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /4klang/4klang.inc.js: -------------------------------------------------------------------------------- 1 | songs/shuffle.inc.js -------------------------------------------------------------------------------- /4klang/songs/.gitignore: -------------------------------------------------------------------------------- 1 | virgill 2 | powl -------------------------------------------------------------------------------- /4klang/tools/.gitignore: -------------------------------------------------------------------------------- 1 | instrumentdisassembler -------------------------------------------------------------------------------- /wasmaudioworklet/midisequencer/ui/pianorolldemo/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/modformat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } -------------------------------------------------------------------------------- /runosx.sh: -------------------------------------------------------------------------------- 1 | /Applications/ZynAddSubFx.app/Contents/MacOS/zynaddsubfx-bin -l test.xmz -------------------------------------------------------------------------------- /test.xmz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petersalomonsen/javascriptmusic/HEAD/test.xmz -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/build/.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | *.wasm.map 3 | *.asm.js 4 | *.wat -------------------------------------------------------------------------------- /dawplugin/.gitignore: -------------------------------------------------------------------------------- 1 | *.tar.gz* 2 | build 3 | WasmEdge-* 4 | JUCE 5 | juce* 6 | wasmedge 7 | -------------------------------------------------------------------------------- /wasmaudioworklet/player/musicandshadervideoplayer/.gitignore: -------------------------------------------------------------------------------- 1 | ufo.wasm.png 2 | index.bundle.html -------------------------------------------------------------------------------- /songs/upbeat.xmz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petersalomonsen/javascriptmusic/HEAD/songs/upbeat.xmz -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [petersalomonsen] 4 | -------------------------------------------------------------------------------- /wasmaudioworklet/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.wav 3 | *.flac 4 | .DS_Store 5 | wavfiles 6 | *.jpeg 7 | *.png 8 | -------------------------------------------------------------------------------- /songs/feelgoodplace1.xmz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petersalomonsen/javascriptmusic/HEAD/songs/feelgoodplace1.xmz -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/__tests__/as-pect.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /4klang/.gitignore: -------------------------------------------------------------------------------- 1 | 4klang.o 2 | 4klang.inc 3 | 4klang.h 4 | 4klangrender 5 | *.raw 6 | *.wav 7 | *.exe 8 | _pie 9 | yasm.exe -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.inc": "cpp", 4 | "*.mm": "cpp" 5 | } 6 | } -------------------------------------------------------------------------------- /4klang/tools/BA_DarkChorus.4ki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petersalomonsen/javascriptmusic/HEAD/4klang/tools/BA_DarkChorus.4ki -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/.gitignore: -------------------------------------------------------------------------------- 1 | out.flac 2 | # ignore generated Amiga Protracker modules 3 | *.mod 4 | .DS_Store 5 | 6 | -------------------------------------------------------------------------------- /wasmaudioworklet/midisequencer/embeddablesongcompiler.js: -------------------------------------------------------------------------------- 1 | export { compileSong, generateSong, songargkeys } from './songcompiler.js'; -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/environment.ts: -------------------------------------------------------------------------------- 1 | // earlier this used to externally declared 2 | export const SAMPLERATE: f32 = 44100; -------------------------------------------------------------------------------- /4klang/tools/pOWL_BAS_Dubstep03.4ki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petersalomonsen/javascriptmusic/HEAD/4klang/tools/pOWL_BAS_Dubstep03.4ki -------------------------------------------------------------------------------- /4klang/tools/pOWL_BAS_Dubstep07.4ki: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petersalomonsen/javascriptmusic/HEAD/4klang/tools/pOWL_BAS_Dubstep07.4ki -------------------------------------------------------------------------------- /startjack.sh: -------------------------------------------------------------------------------- 1 | /usr/local/bin/jackd -X coremidi 2 | /Applications/ZynAddSubFx.app/Contents/MacOS/zynaddsubfx-bin -a -l songs/feelgoodplace1.xmz & -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | recordings/* 3 | *.wav 4 | *.flac 5 | .DS_Store 6 | out 7 | *.mp4 8 | *.webm 9 | dist 10 | mediacontent 11 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/songs/kickbeat.json: -------------------------------------------------------------------------------- 1 | {"patterns":[[64,0,0,0,64,0,0,0,64,0,0,0,64,0,0,0]],"instrumentPatternLists":[[0],[0],[0],[0],[0],[1],[0],[0],[0]]} -------------------------------------------------------------------------------- /wasmaudioworklet/midisequencer/sequenceconstants.js: -------------------------------------------------------------------------------- 1 | export const SEQ_MSG_LOOP = -1; 2 | export const SEQ_MSG_START_RECORDING = -2; 3 | export const SEQ_MSG_STOP_RECORDING = -3; -------------------------------------------------------------------------------- /4klang/buildwindowsexe.sh: -------------------------------------------------------------------------------- 1 | node 4klang.inc.js 2 | ./yasm.exe -f elf32 4klang.asm 3 | gcc -s -O3 -m32 4klang.o 4klangrender.c -o 4klangrender 4 | ./yasm.exe -f bin tiny.asm -o tiny.exe 5 | -------------------------------------------------------------------------------- /4klang/tools/compile.sh: -------------------------------------------------------------------------------- 1 | g++ -m32 instrumentdisassembler.cpp -o instrumentdisassembler 2 | ./instrumentdisassembler BA_DarkChorus.4ki 3 | #./instrumentdisassembler pOWL_BAS_Dubstep07.4ki -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/bpm.ts: -------------------------------------------------------------------------------- 1 | import { SAMPLERATE } from "../environment"; 2 | 3 | export function beatToFrame(beat: f64, bpm: f32): usize { 4 | return (SAMPLERATE * beat * 60 / bpm) as usize; 5 | } 6 | -------------------------------------------------------------------------------- /4klang/createnotes.js: -------------------------------------------------------------------------------- 1 | new Array(128).fill(null).map((v, ndx) => 2 | (['c','cs','d','ds','e','f','fs','g','gs','a','as','b'])[ndx%12]+''+Math.floor(ndx/12) 3 | ).forEach((note, ndx) => console.log(`%define ${note} ${ndx}`)); -------------------------------------------------------------------------------- /.devcontainer/post-create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo apt-get install -y pulseaudio 3 | pulseaudio -D --exit-idle-time=-1 4 | cd wasmaudioworklet 5 | yarn install 6 | yarn playwright install-deps 7 | yarn playwright install 8 | 9 | -------------------------------------------------------------------------------- /4klang/instruments/test.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(64),DECAY(64),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 3 | GO4K_FST AMOUNT(64),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 4 | -------------------------------------------------------------------------------- /songs/recording_take1.json: -------------------------------------------------------------------------------- 1 | [[2.702,[148,62,87]],[0.144,[148,62,0]],[0.165,[148,67,84]],[0.174,[148,69,84]],[0.003,[148,67,0]],[0.131,[148,69,0]],[0.06,[148,71,59]],[0.122,[148,71,0]],[0.1,[148,72,74]],[0.012,[148,71,49]],[0.026,[148,71,0]],[0.312,[148,72,0]]] -------------------------------------------------------------------------------- /4klang/runlinux.sh: -------------------------------------------------------------------------------- 1 | node 4klang.inc.js 2 | yasm -f elf32 4klang.asm 3 | gcc -m32 4klang.o 4klangrender.c -o 4klangrender 4 | node livereload.js | sox -S -t raw -b 32 -e float -r 44100 -c 2 - -d 5 | #./4klangrender | sox -S -t raw -b 32 -e float -r 44100 -c 2 - out.wav 6 | 7 | -------------------------------------------------------------------------------- /wasmaudioworklet/common/filedownload.js: -------------------------------------------------------------------------------- 1 | export function triggerDownload(url, filename) { 2 | const a = document.createElement('a'); 3 | a.href = url; 4 | a.download = filename; 5 | document.documentElement.appendChild(a); 6 | a.click(); 7 | a.remove(); 8 | } -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "postCreateCommand": "./.devcontainer/post-create.sh", 3 | "customizations": { 4 | "vscode": { 5 | "extensions": [ 6 | "github.vscode-github-actions" 7 | ] 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /wasmaudioworklet/rollup-config-songcompiler.js: -------------------------------------------------------------------------------- 1 | import terser from '@rollup/plugin-terser'; 2 | 3 | export default { 4 | input: './midisequencer/embeddablesongcompiler.js', 5 | output: { file: '../dist/songcompiler.bundle.js', format: 'esm'}, 6 | plugins: [terser()], 7 | }; -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/run.sh: -------------------------------------------------------------------------------- 1 | npm run fastbuild 2 | #./node_modules/.bin/asc assembly/index.ts -b build/index.wasm --sourceMap -t build/index.wat 3 | #node index.js | sox -S -t raw -b 32 -e float -r 44100 -c 2 - -d 4 | node index.js | sox -S -t raw -b 32 -e float -r 44100 -c 2 - out.wav 5 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/common/mixcommon.ts: -------------------------------------------------------------------------------- 1 | export function createInstrumentArray(length: i32, factoryFunc: () => T): T[] { 2 | const arr = new Array(length); 3 | for(let n = 0; n < length;n++) { 4 | arr[n] = factoryFunc(); 5 | } 6 | return arr; 7 | } 8 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/instruments/instrument.class.ts: -------------------------------------------------------------------------------- 1 | import { StereoSignal } from "../synth/stereosignal.class"; 2 | 3 | export abstract class Instrument { 4 | abstract set note(note: f32); 5 | readonly signal: StereoSignal = new StereoSignal(); 6 | abstract next(): void; 7 | } -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/midi/sequencer/midiparts.ts: -------------------------------------------------------------------------------- 1 | import { MidiSequencerPart, MidiSequencerPartSchedule } from "./midisequencerpart"; 2 | 3 | export const midiparts: MidiSequencerPart[] = new Array(); 4 | export const midipartschedule: MidiSequencerPartSchedule[] = new Array(); 5 | -------------------------------------------------------------------------------- /4klang/instruments/hihat.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(0),RELEASE(0),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(NOISE) 3 | GO4K_FOP OP(FOP_MULP) 4 | GO4K_VCF FREQUENCY(128),RESONANCE(128),VCFTYPE(HIGHPASS) 5 | GO4K_PAN PANNING(64) 6 | GO4K_OUT GAIN(64), AUXSEND(0) 7 | -------------------------------------------------------------------------------- /4klang/instruments/clap.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(76),SUSTAIN(0),RELEASE(0),GAIN(32) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(NOISE) 4 | GO4K_FOP OP(FOP_MULP) 5 | GO4K_VCF FREQUENCY(80),RESONANCE(128),VCFTYPE(LOWPASS) 6 | GO4K_PAN PANNING(64) 7 | GO4K_OUT GAIN(64), AUXSEND(0) 8 | -------------------------------------------------------------------------------- /4klang/instruments/piano.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(74),SUSTAIN(0),RELEASE(74),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(96),GAIN(128),FLAGS(SINE) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(96),GAIN(128),FLAGS(SINE) 4 | GO4K_FOP OP(FOP_MULP) 5 | GO4K_FOP OP(FOP_MULP) 6 | GO4K_PAN PANNING(64) 7 | GO4K_OUT GAIN(64), AUXSEND(93) 8 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/decibel.ts: -------------------------------------------------------------------------------- 1 | export function decibelToGain(dB: f32): f32 { 2 | return NativeMathf.pow(10, dB/20.0); 3 | } 4 | 5 | export function midiLevelToDecibel(midiLevel: u8): f32 { 6 | return 40 * NativeMathf.log10(midiLevel as f32/127); 7 | } 8 | 9 | export function midiLevelToGain(midiLevel: u8): f32 { 10 | return decibelToGain(midiLevelToDecibel(midiLevel)); 11 | } -------------------------------------------------------------------------------- /wasmaudioworklet/emptysong.js: -------------------------------------------------------------------------------- 1 | global.bpm = 120; 2 | global.pattern_size_shift = 4; 3 | 4 | addInstrument('kick', {type: 'number'}); 5 | addInstrument('sinelead', {type: 'note'}); 6 | 7 | playPatterns({ 8 | kick: pp(4, [ 9 | 64, , , , 10 | 64, , , , 11 | 64, , ,10, 12 | 64, , 30, ,]), 13 | sinelead: pp(4, [ 14 | d5,,,a4, 15 | ,a4,,, 16 | c5,d5,,f5, 17 | ,f5,d5 18 | ]) 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/songs/recordedsong1.json: -------------------------------------------------------------------------------- 1 | {"patterns":[[64,0,0,0,64,0,0,0,64,0,0,0,64,0,0,0],[0,0,0,0,77,0,67,0,76,65,0,0,72,0,74,0],[24,0,48,0,24,0,48,0,24,0,48,0,24,0,48,0],[26,0,0,26,38,0,0,26,0,26,38,0,29,1,41,0],[0,0,0,0,52,0,0,0,0,0,0,0,12,0,0,0],[0,0,69,0,65,0,69,0,0,0,65,0,0,0,0,0],[0,0,62,0,69,0,62,0,0,0,62,0,0,0,0,0],[0,0,65,0,0,0,65,0,0,0,74,0,0,0,0,0],[0,0,62,0,74,0,72,0,69,0,67,0,0,0,65,62]],"instrumentPatternLists":[[9],[4],[6],[7],[8],[1],[5],[2,3],[3]]} -------------------------------------------------------------------------------- /4klang/songs/new.inc.js: -------------------------------------------------------------------------------- 1 | const incMake4k = require('../4klang_inc/4klang_inc.make.js'); 2 | const fs = require('fs'); 3 | 4 | global.bpm = 120; 5 | global.pattern_size_shift = 4; 6 | calculatePatternSize(); 7 | global.looptimes = 2; 8 | 9 | addInstrument('instr1', fs.readFileSync('./instruments/LD_AlphaOmegaMS.inc').toString()); 10 | 11 | playPatterns({instr1: pp(4,[ 12 | c5(2),,,, 13 | e5(2),,,, 14 | g5(2),,,, 15 | ])}) 16 | 17 | incMake4k.makeVierKlangInc(); -------------------------------------------------------------------------------- /testmidi.js: -------------------------------------------------------------------------------- 1 | const midi = require('midi'); 2 | 3 | // Set up a new output. 4 | const output = new midi.output(); 5 | let outputIndex; 6 | for(var n=0;n 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/modformat/README.md: -------------------------------------------------------------------------------- 1 | Generate Amiga Protracker Modules 2 | ================================= 3 | 4 | A Proof Of Concept rendering Amiga Protracker Modules from Javascript with samples generated from AssemblyScript. 5 | 6 | Full build pipeline (from the parent directory, and remember to run `npm install` first): 7 | 8 | `npm run fastbuild && node --experimental-modules modformat/song1.js` 9 | 10 | This will output a module with filename `trackerscripting.mod` -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/math/sin.ts: -------------------------------------------------------------------------------- 1 | // By Max Graey ( https://github.com/petersalomonsen/javascriptmusic/issues/2#issuecomment-469419609 ) 2 | 3 | export const PI: f32 = 3.141592653589793; 4 | export function sin(x: f32): f32 { 5 | var y: f32, z: f32; 6 | x *= 1 / PI; 7 | y = floor(x); 8 | z = x - y; 9 | z *= 1.0 - z; 10 | z *= 3.6 * z + 3.1; 11 | return select(-z, z, y & 1); 12 | } 13 | 14 | export function cos(x: f32): f32 { 15 | return sin(x + PI * .5); 16 | } -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/__tests__/midi/instruments/audioplayer.spec.ts: -------------------------------------------------------------------------------- 1 | import { allocateAudioBuffer, MonoAudioPlayer } from "../../../mixes/globalimports"; 2 | 3 | describe("audioplayer", () => { 4 | it("should allocate audio buffer and reference it in the audioplayer", () => { 5 | const audioBufPtr = allocateAudioBuffer(1024); 6 | const audioPlayer = new MonoAudioPlayer(0); 7 | expect(changetype(audioPlayer.audioBuffer)).toBe(audioBufPtr); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /4klang/instruments/KY_Rhodes.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(96),SUSTAIN(0),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(73),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 3 | GO4K_FST AMOUNT(96),DEST(4*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 6 | GO4K_FOP OP(FOP_MULP) 7 | GO4K_VCF FREQUENCY(48),RESONANCE(64),VCFTYPE(LOWPASS) 8 | GO4K_PAN PANNING(64) 9 | GO4K_OUT GAIN(96), AUXSEND(4) 10 | -------------------------------------------------------------------------------- /4klang/instruments/basedrum.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(32),SUSTAIN(96),RELEASE(64),GAIN(128) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(70),SUSTAIN(0),RELEASE(0),GAIN(128) 4 | GO4K_DST DRIVE(32), SNHFREQ(128), FLAGS(0) 5 | GO4K_FST AMOUNT(80),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(45),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(66),GAIN(128),FLAGS(SINE) 8 | GO4K_FOP OP(FOP_MULP) 9 | GO4K_PAN PANNING(64) 10 | GO4K_OUT GAIN(128), AUXSEND(0) 11 | -------------------------------------------------------------------------------- /4klang/instruments/basedrum2.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(96),RELEASE(64),GAIN(128) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(70),SUSTAIN(0),RELEASE(0),GAIN(128) 4 | GO4K_DST DRIVE(32), SNHFREQ(128), FLAGS(0) 5 | GO4K_FST AMOUNT(80),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(46),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_FOP OP(FOP_MULP) 9 | GO4K_PAN PANNING(64) 10 | GO4K_OUT GAIN(128), AUXSEND(0) 11 | -------------------------------------------------------------------------------- /4klang/instruments/basedrum3.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(32),SUSTAIN(96),RELEASE(76),GAIN(128) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(70),SUSTAIN(30),RELEASE(64),GAIN(128) 4 | GO4K_DST DRIVE(32), SNHFREQ(128), FLAGS(0) 5 | GO4K_FST AMOUNT(80),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(45),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(66),GAIN(128),FLAGS(SINE) 8 | GO4K_FOP OP(FOP_MULP) 9 | GO4K_PAN PANNING(64) 10 | GO4K_OUT GAIN(128), AUXSEND(0) 11 | -------------------------------------------------------------------------------- /4klang/instruments/KY_Lullaby.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(32),DECAY(80),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(88),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 3 | GO4K_FOP OP(FOP_MULP) 4 | GO4K_PAN PANNING(64) 5 | GO4K_DLL PREGAIN(64),DRY(64),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 6 | GO4K_FOP OP(FOP_XCH) 7 | GO4K_DLL PREGAIN(64),DRY(64),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 8 | GO4K_FOP OP(FOP_XCH) 9 | GO4K_OUT GAIN(64), AUXSEND(64) 10 | -------------------------------------------------------------------------------- /4klang/instruments/KY_Lullaby2.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(32),DECAY(80),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(88),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 3 | GO4K_FOP OP(FOP_MULP) 4 | GO4K_PAN PANNING(64) 5 | GO4K_DLL PREGAIN(96),DRY(64),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 6 | GO4K_FOP OP(FOP_XCH) 7 | GO4K_DLL PREGAIN(96),DRY(64),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 8 | GO4K_FOP OP(FOP_XCH) 9 | GO4K_OUT GAIN(96), AUXSEND(96) 10 | -------------------------------------------------------------------------------- /4klang/instruments/basedrum4.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(32),SUSTAIN(96),RELEASE(82),GAIN(128) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(41),RELEASE(106),GAIN(128) 4 | GO4K_DST DRIVE(29), SNHFREQ(128), FLAGS(0) 5 | GO4K_FST AMOUNT(77),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(45),DETUNE(64),PHASE(32),GATES(85),COLOR(64),SHAPE(66),GAIN(128),FLAGS(SINE) 8 | GO4K_FOP OP(FOP_MULP) 9 | GO4K_PAN PANNING(64) 10 | GO4K_OUT GAIN(128), AUXSEND(0) 11 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/sineoscillator.class.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SAMPLERATE } from '../environment'; 3 | import { sin, PI } from '../math/sin'; 4 | 5 | export class SineOscillator { 6 | position: u32 = 0; 7 | frequency: f32 = 0; 8 | 9 | next(): f32 { 10 | let ret = sin(PI * 2 * (this.position as f32) / (1 << 16 as f32)); 11 | this.position = (((this.position as f32) + (this.frequency/SAMPLERATE) 12 | * 0x10000 as f32) as u32) & 0xffff; 13 | 14 | return ret as f32; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /wasmaudioworklet/midisequencer/ui/pianorolldemo/rollup-config.js: -------------------------------------------------------------------------------- 1 | import { rollupPluginHTML as html } from '@web/rollup-plugin-html'; 2 | import terser from '@rollup/plugin-terser'; 3 | import { readFileSync} from 'fs'; 4 | 5 | export default { 6 | input: './index.html', 7 | output: { dir: 'dist' }, 8 | plugins: [html({ 9 | minify: true, 10 | extractAssets: false, 11 | transformHtml: [html => 12 | html.replace('', ``) 13 | ] 14 | }), terser()], 15 | }; -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/fx/midsideprocessor.ts: -------------------------------------------------------------------------------- 1 | import { StereoSignal } from "../synth/stereosignal.class"; 2 | 3 | export class MidSideProcessor { 4 | signal: StereoSignal = new StereoSignal(); 5 | 6 | constructor(private side_level: f32) { 7 | 8 | } 9 | 10 | process(left: f32, right: f32): void { 11 | // Mid-side processing 12 | let mid: f32 = (left + right) / 2.0; 13 | let side: f32 = (left - right) / 2.0; 14 | 15 | side *= this.side_level; 16 | this.signal.left = mid + side; 17 | this.signal.right = mid - side; 18 | } 19 | } -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/noise.class.ts: -------------------------------------------------------------------------------- 1 | let x: i32 = 123456789; 2 | let y: i32=234567891; 3 | let z: i32=345678912; 4 | let w: i32=456789123; 5 | let c: i32=0; 6 | 7 | export class Noise { 8 | 9 | next(): f32 { 10 | y ^= (y<<5); y ^= (y>>7); y ^= (y<<22); 11 | 12 | let t = z+w+c; z = w; 13 | c = t < 0 ? 1 : 0; 14 | w = t & 2147483647; 15 | 16 | x += 1411392427; 17 | 18 | let rnd: f32 = ((x + y + w) & 0xffff) as f32; 19 | return ((rnd / (1 << 16 as f32))) - 0.5; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /4klang/instruments/guitar.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(0),SUSTAIN(128),RELEASE(64),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(58),SUSTAIN(0),RELEASE(0),GAIN(128) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(127),GAIN(128),FLAGS(NOISE) 4 | GO4K_FOP OP(FOP_MULP) 5 | GO4K_VCF FREQUENCY(48),RESONANCE(128),VCFTYPE(ALLPASS) 6 | GO4K_DLL PREGAIN(128),DRY(128),FEEDBACK(126),DAMP(48),FREQUENCY(0),DEPTH(0),DELAY(0),COUNT(1) ; ERROR 7 | GO4K_FOP OP(FOP_MULP) 8 | GO4K_VCF FREQUENCY(96),RESONANCE(128),VCFTYPE(LOWPASS) 9 | GO4K_PAN PANNING(64) 10 | GO4K_OUT GAIN(64), AUXSEND(16) 11 | -------------------------------------------------------------------------------- /4klang/instruments/guitar2.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(0),SUSTAIN(128),RELEASE(72),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(58),SUSTAIN(0),RELEASE(0),GAIN(128) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(127),GAIN(128),FLAGS(NOISE) 4 | GO4K_FOP OP(FOP_MULP) 5 | GO4K_VCF FREQUENCY(32),RESONANCE(128),VCFTYPE(ALLPASS) 6 | GO4K_DLL PREGAIN(128),DRY(128),FEEDBACK(128),DAMP(16),FREQUENCY(0),DEPTH(0),DELAY(0),COUNT(1) ; ERROR 7 | GO4K_VCF FREQUENCY(24),RESONANCE(128),VCFTYPE(ALLPASS) 8 | GO4K_FOP OP(FOP_MULP) 9 | GO4K_PAN PANNING(64) 10 | GO4K_OUT GAIN(64), AUXSEND(24) 11 | -------------------------------------------------------------------------------- /4klang/instruments/KY_GarageOrgan.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(96),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(16),SHAPE(64),GAIN(128),FLAGS(TRISAW) 3 | GO4K_VCO TRANSPOSE(112),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(16),GAIN(16),FLAGS(SINE) 4 | GO4K_VCO TRANSPOSE(88),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(32),GAIN(32),FLAGS(SINE) 5 | GO4K_VCF FREQUENCY(100),RESONANCE(128),VCFTYPE(LOWPASS) 6 | GO4K_FOP OP(FOP_ADDP) 7 | GO4K_FOP OP(FOP_ADDP) 8 | GO4K_FOP OP(FOP_MULP) 9 | GO4K_PAN PANNING(64) 10 | GO4K_OUT GAIN(0), AUXSEND(128) 11 | -------------------------------------------------------------------------------- /4klang/instruments/hihat2.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(0),RELEASE(0),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(NOISE) 3 | GO4K_FOP OP(FOP_MULP) 4 | GO4K_VCF FREQUENCY(128),RESONANCE(128),VCFTYPE(BANDPASS) 5 | GO4K_PAN PANNING(64) 6 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 7 | GO4K_FOP OP(FOP_XCH) 8 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 9 | GO4K_FOP OP(FOP_XCH) 10 | GO4K_OUT GAIN(64), AUXSEND(0) 11 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/note.ts: -------------------------------------------------------------------------------- 1 | const pitchstep: f64 = 1.0004513695322617; // Math.pow(2, (1/128) / 12); 2 | const c0: f64 = 8.175798915643707; // 440 * Math.pow(2, -69 / 12); 3 | let pitchtable = __new(128 * 128 * 4, idof>()); 4 | let pitch: f64 = c0; 5 | 6 | for (let n: usize = 0; n < (128 * 128); n++) { 7 | store(pitchtable + (n << 2), pitch as f32); 8 | pitch *= pitchstep; 9 | } 10 | 11 | export function notefreq(note: f32): f32 { 12 | let pitchtableIndex: usize = (note * 128.0) as usize; 13 | return load(pitchtable + (pitchtableIndex << 2)); 14 | } -------------------------------------------------------------------------------- /4klang/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "USAGE: run.sh [mysong.inc.js]" 3 | echo "NOTE:If gcc is not able to compile on OSX, try installing headers from /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg" 4 | 5 | if [ $# -eq 1 ] 6 | then 7 | rm 4klang.inc.js 8 | ln -s $1 ./4klang.inc.js 9 | fi 10 | 11 | node 4klang.inc.js 12 | yasm -f macho 4klang.asm 13 | gcc -Wl,-no_pie -m32 4klang.o 4klangrender.c -o 4klangrender 14 | node livereload.js | sox -S -t raw -b 32 -e float -r 44100 -c 2 - -d 15 | #./4klangrender | sox -S -t raw -b 32 -e float -r 44100 -c 2 - out.wav 16 | -------------------------------------------------------------------------------- /wasmaudioworklet/overview.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | node "Song editor" { 4 | [Song Javascript source] as songsource 5 | [Song compiler] as songcompiler 6 | } 7 | node "Synth / sequencer" { 8 | [AudioWorklet processor] as audioworkletprocessor 9 | [WebAssembly synth] as wasmsynth 10 | } 11 | 12 | cloud "Audio output" as audiooutput { 13 | 14 | } 15 | 16 | songsource -> songcompiler 17 | songcompiler -down-> audioworkletprocessor : "Pattern data" 18 | audioworkletprocessor -> wasmsynth 19 | wasmsynth -> audioworkletprocessor 20 | 21 | audioworkletprocessor -down-> audiooutput 22 | 23 | @enduml -------------------------------------------------------------------------------- /songs/base2.json: -------------------------------------------------------------------------------- 1 | [[0.01,[151,45,79]],[0.525,[151,45,0]],[0.655,[151,48,84]],[0.226,[151,48,0]],[0.091,[151,48,90]],[0.155,[151,48,0]],[0.722,[151,50,87]],[0.297,[151,50,0]],[0.621,[151,50,87]],[0.127,[151,50,0]],[0.176,[151,52,64]],[0.383,[151,52,0]],[0.227,[151,55,63]],[0.242,[151,55,0]],[0.207,[151,57,84]],[0.089,[151,57,0]],[0.018,[151,45,75]],[0.718,[151,45,0]],[0.377,[151,48,79]],[0.276,[151,48,0]],[0.137,[151,48,77]],[0.203,[151,48,0]],[0.422,[151,48,82]],[0.131,[151,48,0]],[0.186,[151,50,79]],[0.434,[151,50,0]],[0.763,[151,52,70]],[0.404,[151,52,0]],[0.177,[151,55,88]],[0.226,[151,55,0]],[0.305,[151,57,82]],[0.063,[151,57,0]]] -------------------------------------------------------------------------------- /wasmaudioworklet/common/audioworkletmodules.js: -------------------------------------------------------------------------------- 1 | const urlMap = {}; 2 | 3 | export function getAudioWorkletModuleUrl(audioWorkletModuleFunction) { 4 | if (!urlMap[audioWorkletModuleFunction.name]) { 5 | const functionSource = audioWorkletModuleFunction.toString(); 6 | const functionSourceUnwrapped = functionSource.substring(functionSource.indexOf('{') + 1, functionSource.lastIndexOf('}')); 7 | urlMap[audioWorkletModuleFunction.name] = URL.createObjectURL(new Blob([functionSourceUnwrapped], { type: 'text/javascript' })); 8 | } 9 | return urlMap[audioWorkletModuleFunction.name]; 10 | } 11 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/sawoscillator.class.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SAMPLERATE } from '../environment'; 3 | 4 | export class SawOscillator { 5 | position: u32 = 0; 6 | frequency: f32 = 0; 7 | 8 | next(): f32 { 9 | if(this.frequency > 0) { 10 | let ret = (this.position as f32 / 0x10000 as f32) - 0.5; 11 | this.position = (((this.position as f32) + (this.frequency/SAMPLERATE) 12 | * 0x10000 as f32) as u32) & 0xffff; 13 | 14 | 15 | return ret as f32; 16 | } else { 17 | return 0; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/squareoscillator.class.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SAMPLERATE } from '../environment'; 3 | 4 | export class SquareOscillator { 5 | position: u32 = 0; 6 | frequency: f32 = 0; 7 | 8 | next(): f32 { 9 | if(this.frequency > 0) { 10 | let ret: f32 = this.position as f32 > 0x8000 ? 0.5 : -0.5; 11 | this.position = (((this.position as f32) + (this.frequency/SAMPLERATE) 12 | * 0x10000 as f32) as u32) & 0xffff; 13 | 14 | 15 | return ret; 16 | } else { 17 | return 0; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/asconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "targets": { 3 | "debug": { 4 | "outFile": "build/debug.wasm", 5 | "textFile": "build/debug.wat", 6 | "sourceMap": true, 7 | "debug": true 8 | }, 9 | "release": { 10 | "outFile": "build/release.wasm", 11 | "textFile": "build/release.wat", 12 | "sourceMap": true, 13 | "optimizeLevel": 3, 14 | "shrinkLevel": 0, 15 | "converge": false, 16 | "noAssert": false 17 | } 18 | }, 19 | "options": { 20 | "bindings": "esm" 21 | } 22 | } -------------------------------------------------------------------------------- /wasmaudioworklet/webaudiomodules/yoshimi-xml-parser.js: -------------------------------------------------------------------------------- 1 | export function getInstrumentNamesFromYoshimiXML(xml) { 2 | const parser = new DOMParser(); 3 | const doc = parser.parseFromString(xml, 'application/xml'); 4 | const instruments = doc.querySelectorAll('INSTRUMENT INFO string[name="name"]'); 5 | 6 | const instrumentNames = []; 7 | for (let n=0; n; 4 | 5 | constructor(freqs: f32[]) { 6 | this.bands = new StaticArray(freqs.length - 1); 7 | for (let n = 1; n < freqs.length; n++) { 8 | this.bands[n - 1] = new EQBand(freqs[n - 1], freqs[n]); 9 | } 10 | } 11 | 12 | 13 | process(signal: f32, levels: f32[]): f32 { 14 | let ret: f32 = 0; 15 | const numbands = this.bands.length; 16 | for (let n = 0; n < numbands; n++) { 17 | ret += this.bands[n].process(signal) * levels[n]; 18 | } 19 | return ret; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/clip.ts: -------------------------------------------------------------------------------- 1 | // From https://ccrma.stanford.edu/~jos/pasp/Soft_Clipping.html#29695 2 | 3 | /** 4 | * soft clip signal with 1/3 headroom as result 5 | * @param signal signal to be clipped 6 | */ 7 | export function softclip(signal: f32): f32 { 8 | if(signal > 1.0) { 9 | return 2.0/3.0; 10 | } else if(signal < -1.0) { 11 | return -2.0/3.0; 12 | } else { 13 | return (signal - ((signal * signal * signal) / 3.0)); 14 | } 15 | } 16 | 17 | export function hardclip(signal: f32): f32 { 18 | if(signal > 1.0) { 19 | return 1.0; 20 | } else if(signal < -1.0) { 21 | return -1.0; 22 | } else { 23 | return signal; 24 | } 25 | } -------------------------------------------------------------------------------- /4klang/instruments/PA_LoFiChoir.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(70),DECAY(64),SUSTAIN(64),RELEASE(80),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(44),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(74),PHASE(0),GATES(85),COLOR(32),SHAPE(64),GAIN(128),FLAGS(TRISAW) 4 | GO4K_FOP OP(FOP_ADDP) 5 | GO4K_DST DRIVE(112), SNHFREQ(128), FLAGS(0) 6 | GO4K_VCF FREQUENCY(53),RESONANCE(31),VCFTYPE(LOWPASS) 7 | GO4K_VCF FREQUENCY(50),RESONANCE(32),VCFTYPE(HIGHPASS) 8 | GO4K_DST DRIVE(64), SNHFREQ(66), FLAGS(0) 9 | GO4K_FOP OP(FOP_MULP) 10 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 11 | GO4K_PAN PANNING(64) 12 | GO4K_OUT GAIN(64), AUXSEND(31) 13 | -------------------------------------------------------------------------------- /4klang/computefunctionhash.js: -------------------------------------------------------------------------------- 1 | 2 | var orighash = 0xEACA71C2; 3 | var hash = orighash; 4 | var str = "GetStdHandle"; 5 | 6 | for(var n=0;n>> 0) >>> 0; 9 | hash = ((hash << 7 >>>0 ) | (hash >> 25 & 0x7f)) >>> 0 ; 10 | 11 | } 12 | 13 | console.log(hash.toString(16)); 14 | 15 | hash = 0; 16 | for(var n=str.length-1;n>=0;n--) { 17 | 18 | hash = ((hash >> 7 & 0x1ffffff ) | (hash << 25)) >>> 0; 19 | 20 | hash = (str.charCodeAt(n) ^ hash >>> 0) >>> 0; 21 | 22 | console.log(str.charCodeAt(n).toString(16), hash.toString(16) ); 23 | 24 | } 25 | 26 | console.log(hash.toString(16), orighash.toString(16)); -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch via NPM", 11 | "cwd": "${workspaceFolder}/wasmaudioworklet", 12 | "runtimeExecutable": "npm", 13 | "runtimeArgs": [ 14 | "test", 15 | "debug" 16 | ], 17 | "port": 9229, 18 | "skipFiles": [ 19 | "/**" 20 | ] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /4klang/instruments/synth.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(64),DECAY(64),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW) 3 | GO4K_FST AMOUNT(96),DEST(4*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(48),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) 6 | GO4K_FST AMOUNT(81),DEST(7*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(8),GAIN(128),FLAGS(SINE|LFO) 9 | GO4K_VCF FREQUENCY(32),RESONANCE(32),VCFTYPE(ALLPASS) 10 | GO4K_FOP OP(FOP_MULP) 11 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 12 | GO4K_PAN PANNING(64) 13 | GO4K_OUT GAIN(64), AUXSEND(64) 14 | -------------------------------------------------------------------------------- /4klang/instruments/synthFlanger.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(64),DECAY(64),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW) 3 | GO4K_FST AMOUNT(96),DEST(4*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(48),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) 6 | GO4K_FST AMOUNT(81),DEST(7*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(8),GAIN(128),FLAGS(SINE|LFO) 9 | GO4K_VCF FREQUENCY(32),RESONANCE(32),VCFTYPE(ALLPASS) 10 | GO4K_FOP OP(FOP_MULP) 11 | GO4K_DLL PREGAIN(112),DRY(16),FEEDBACK(112),DAMP(16),FREQUENCY(32),DEPTH(48),DELAY(17),COUNT(1) ; ERROR 12 | GO4K_PAN PANNING(64) 13 | GO4K_OUT GAIN(32), AUXSEND(32) 14 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/moduleworkerloader.js: -------------------------------------------------------------------------------- 1 | importScripts('https://cdn.jsdelivr.net/npm/es-module-shims@1/dist/es-module-shims.wasm.min.js'); 2 | 3 | 4 | const importMap = { 5 | imports: { 6 | "assemblyscript": "https://cdn.jsdelivr.net/npm/assemblyscript@0.27.14/dist/assemblyscript.js", 7 | "assemblyscript/asc": "https://cdn.jsdelivr.net/npm/assemblyscript@0.27.14/dist/asc.js", 8 | "binaryen": "https://cdn.jsdelivr.net/npm/binaryen@112.0.0-nightly.20230411/index.js", 9 | "long": "https://cdn.jsdelivr.net/npm/long@5.2.1/index.js" 10 | } 11 | }; 12 | 13 | 14 | importShim.addImportMap(importMap); 15 | importShim('./browsercompilerwebworker.js').then((res) => { 16 | console.log("module has been loaded"); 17 | postMessage('ready'); 18 | }).catch(e => setTimeout(() => { throw e; })); -------------------------------------------------------------------------------- /4klang/instruments/snare5.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(0),RELEASE(64),GAIN(128) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(52),SUSTAIN(0),RELEASE(0),GAIN(128) 4 | GO4K_FST AMOUNT(80),DEST(5*MAX_UNIT_SLOTS+1+FST_SET) 5 | GO4K_FOP OP(FOP_POP) 6 | GO4K_VCO TRANSPOSE(48),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(74),GAIN(64),FLAGS(SINE) 7 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(64),FLAGS(NOISE) 8 | GO4K_VCF FREQUENCY(112),RESONANCE(128),VCFTYPE(LOWPASS) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_FOP OP(FOP_MULP) 11 | GO4K_VCF FREQUENCY(22),RESONANCE(22),VCFTYPE(HIGHPASS) 12 | GO4K_DLL PREGAIN(32),DRY(64),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(9),COUNT(8) ; ERROR 13 | GO4K_PAN PANNING(64) 14 | GO4K_OUT GAIN(64), AUXSEND(0) 15 | -------------------------------------------------------------------------------- /wasmaudioworklet/midisequencer/editorfunctions.js: -------------------------------------------------------------------------------- 1 | import { getRecordedData as getWAMRecordedData } from '../webaudiomodules/wammanager.js'; 2 | import { getRecordedData as getMidiSynthRecordedData } from '../synth1/audioworklet/midisynthaudioworklet.js'; 3 | import { RecordConverter } from './recording.js'; 4 | import { recordingStartTimeMillis } from './songcompiler.js'; 5 | import { bpm } from './pattern.js'; 6 | 7 | export async function insertMidiRecording(insertStringIntoEditor) { 8 | let recordedData; 9 | if (window.audioworkletnode) { 10 | recordedData = await getMidiSynthRecordedData(); 11 | } else { 12 | recordedData = await getWAMRecordedData(); 13 | } 14 | insertStringIntoEditor(new RecordConverter(recordedData, bpm, 15 | recordingStartTimeMillis / 1000).trackerPatternData); 16 | } -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/shaper.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Taken from https://github.com/hzdgopher/64klang/blob/master/Player/Player/SynthNode.cpp#L2050 3 | */ 4 | 5 | const NOISEGEN_B0: f32 = 0.99765014648437500; 6 | 7 | export class WaveShaper { 8 | drive: f32 = 0.5; 9 | 10 | process(input: f32): f32 { 11 | // clamp to -1..1 (a little less) 12 | let f: f32 = this.drive < NOISEGEN_B0 ? this.drive > -NOISEGEN_B0 ? 13 | this.drive : -NOISEGEN_B0 : 14 | NOISEGEN_B0; 15 | 16 | // k = 2*amount/(1-amount); 17 | let v = (f+f)/(1.0-f); 18 | 19 | // process f(x) = (1+k)*x/(1+k*abs(x)) 20 | return (1.0 + v) * 21 | input/ 22 | (1.0 + v * min(abs(input), 1.0)); 23 | } 24 | } -------------------------------------------------------------------------------- /4klang/instruments/bass2.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(32),DECAY(76),SUSTAIN(0),RELEASE(32),GAIN(81) 2 | GO4K_FST AMOUNT(106),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_FST AMOUNT(84),DEST(11*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_ENV ATTAC(0),DECAY(60),SUSTAIN(0),RELEASE(0),GAIN(128) 5 | GO4K_FST AMOUNT(50),DEST(7*MAX_UNIT_SLOTS+6+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(64),DETUNE(70),PHASE(40),GATES(85),COLOR(64),SHAPE(80),GAIN(126),FLAGS(SINE) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(48),PHASE(48),GATES(85),COLOR(64),SHAPE(96),GAIN(128),FLAGS(SINE) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_VCF FREQUENCY(30),RESONANCE(128),VCFTYPE(LOWPASS) 11 | GO4K_DST DRIVE(107), SNHFREQ(128), FLAGS(0) 12 | GO4K_VCF FREQUENCY(35),RESONANCE(128),VCFTYPE(ALLPASS) 13 | GO4K_FOP OP(FOP_MULP) 14 | GO4K_PAN PANNING(64) 15 | GO4K_OUT GAIN(64), AUXSEND(0) 16 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/triangleoscillator.class.ts: -------------------------------------------------------------------------------- 1 | import { SAMPLERATE } from '../environment'; 2 | 3 | export class TriangleOscillator { 4 | position: i32 = 0; 5 | frequency: f32 = 0; 6 | 7 | next(): f32 { 8 | if (this.frequency > 0) { 9 | const pos: i32 = this.position; 10 | let ret: f32; 11 | if (pos < 0x8000) { 12 | ret = (pos as f32 / 0x8000 as f32); 13 | } else { 14 | ret = (- (pos - 0x8000) as f32 / 0x8000 as f32) + 1.0; 15 | } 16 | 17 | this.position = (((this.position as f32) + (this.frequency / SAMPLERATE) 18 | * 0x10000 as f32) as i32) & 0xffff; 19 | 20 | return ret * 2 - 1 as f32; 21 | } else { 22 | return 0; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/fx/stereocompressor.ts: -------------------------------------------------------------------------------- 1 | import { StereoSignal } from "../synth/stereosignal.class"; 2 | import { MonoCompressor } from "./monocompressor"; 3 | 4 | export class StereoCompressor { 5 | leftCompressor: MonoCompressor; 6 | rightCompressor: MonoCompressor; 7 | 8 | resultSignal: StereoSignal = new StereoSignal(); 9 | 10 | constructor(numsamples: usize) { 11 | this.leftCompressor = new MonoCompressor(numsamples); 12 | this.rightCompressor = new MonoCompressor(numsamples); 13 | } 14 | 15 | process(left: f32, right: f32, threshold: f32, makeupgain: f32): void { 16 | this.resultSignal.left = this.leftCompressor.process(left, threshold, makeupgain); 17 | this.resultSignal.right = this.rightCompressor.process(right, threshold, makeupgain); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/__tests__/synth/triangleoscillator.spec.ts: -------------------------------------------------------------------------------- 1 | import { TriangleOscillator } from '../../synth/triangleoscillator.class'; 2 | 3 | describe('triangleoscillator', () => { 4 | it('should output a triangle wave on 440 hz', () => { 5 | const osc = new TriangleOscillator(); 6 | osc.frequency = 440; 7 | 8 | const framesPerCycle: f64 = 44100 / 440; 9 | for (let n: f64 = 0; n < framesPerCycle * 1; n++) { 10 | const sample = osc.next(); 11 | 12 | const wavepos = (n % framesPerCycle) / framesPerCycle; 13 | const expectedSample: f64 = wavepos < 0.5 ? 14 | (wavepos * 4.0) - 1.0 : 15 | (-(wavepos - 0.5) * 4.0) + 1.0; 16 | 17 | expect(sample).toBeCloseTo(expectedSample as f32, 1); 18 | } 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /songs/fgtake1.json: -------------------------------------------------------------------------------- 1 | [[0.17,[148,67,0]],[1.33,[148,62,84]],[0.143,[148,62,0]],[0.148,[148,67,90]],[0.15,[148,67,0]],[0.015,[148,69,75]],[0.131,[148,69,0]],[0.004,[148,71,68]],[0.109,[148,71,0]],[0.074,[148,71,84]],[0.05,[148,72,70]],[0.049,[148,71,0]],[0.312,[148,72,0]],[0.008,[148,71,82]],[0.28,[148,71,0]],[0.008,[148,69,89]],[0.288,[148,67,64]],[0.001,[148,69,0]],[0.138,[148,67,0]],[0.03,[148,74,88]],[0.145,[148,74,0]],[0.122,[148,74,75]],[0.148,[148,74,0]],[0.031,[148,69,87]],[0.157,[148,69,0]],[0.162,[148,67,84]],[0.279,[148,67,0]],[0.027,[148,62,72]],[0.254,[148,62,0]],[0.044,[148,67,83]],[0.625,[148,67,0]],[1.744,[148,65,93]],[0.605,[148,65,0]],[0.009,[148,64,72]],[0.247,[148,64,0]],[0.201,[148,64,55]],[0.06,[148,65,84]],[0.023,[148,64,0]],[0.089,[148,64,77]],[0.004,[148,65,0]],[0.289,[148,64,0]],[0.008,[148,62,87]],[0.293,[148,60,87]],[0.047,[148,62,0]],[0.319,[148,60,0]]] -------------------------------------------------------------------------------- /songs/recbase.json: -------------------------------------------------------------------------------- 1 | [[1.085,[151,48,0]],[0.005,[151,55,82]],[0.111,[151,55,0]],[0.212,[151,55,69]],[0.105,[151,58,73]],[0.006,[151,55,0]],[0.258,[151,58,0]],[0.029,[151,55,70]],[0.287,[151,55,0]],[0.003,[151,58,96]],[0.295,[151,58,0]],[0.024,[151,48,84]],[1.047,[151,55,93]],[0.011,[151,48,0]],[0.123,[151,55,0]],[0.189,[151,55,62]],[0.097,[151,58,87]],[0.001,[151,55,0]],[0.3,[151,58,0]],[0.019,[151,55,74]],[0.286,[151,58,93]],[0.001,[151,55,0]],[0.307,[151,58,0]],[0.006,[151,46,81]],[1.171,[151,49,71]],[0.006,[151,46,0]],[0.031,[151,50,84]],[0.038,[151,49,0]],[1.136,[151,50,0]],[0.017,[151,48,87]],[0.9,[151,55,57]],[0.015,[151,48,0]],[0.018,[151,55,0]],[0.051,[151,55,2]],[0.033,[151,55,0]],[0.033,[151,60,89]],[0.137,[151,60,0]],[0.148,[151,60,69]],[0.135,[151,55,74]],[0.002,[151,60,0]],[0.266,[151,55,0]],[0.011,[151,60,83]],[0.307,[151,60,0]],[0.015,[151,55,87]],[0.288,[151,55,0]]] -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/fx/limiter.ts: -------------------------------------------------------------------------------- 1 | import { SAMPLERATE } from '../environment'; 2 | 3 | export class Limiter { 4 | attack: f32 = 0; 5 | release: f32 = 0; 6 | envelope: f32 = 0; 7 | 8 | constructor(attackMs: f32, releaseMs: f32) { 9 | this.attack = Mathf.pow(0.01, 1.0 / (attackMs * SAMPLERATE * 0.001)); 10 | this.release = Mathf.pow(0.01, 1.0 / (releaseMs * SAMPLERATE * 0.001)); 11 | } 12 | 13 | process(signal: f32): f32 { 14 | const v = Mathf.abs(signal); 15 | if (v > this.envelope) { 16 | this.envelope = this.attack * (this.envelope - v) + v; 17 | } else { 18 | this.envelope = this.release * (this.envelope - v) + v; 19 | } 20 | 21 | if (this.envelope > 1) { 22 | signal /= this.envelope; 23 | } 24 | return signal; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/pan.class.ts: -------------------------------------------------------------------------------- 1 | import { StereoSignal } from "./stereosignal.class"; 2 | 3 | const HALF_OF_SQRT_2 = NativeMathf.sqrt(2) / 2; 4 | 5 | export class Pan { 6 | leftLevel: f32; 7 | rightLevel: f32; 8 | 9 | constructor() { 10 | this.setPan(0.5); 11 | } 12 | 13 | /** 14 | * Don't use as part of the rendering process. Use to calculate pan levels on controller changes 15 | * @param pan from 0.0 (left) to 1.0 (right). 0.5 is center 16 | */ 17 | setPan(pan: f32): void { 18 | const angle = (pan - 0.5) * NativeMathf.PI / 2; 19 | // left channel 20 | this.leftLevel = HALF_OF_SQRT_2 * (NativeMathf.cos(angle) - NativeMathf.sin(angle)); 21 | // right channel 22 | this.rightLevel = HALF_OF_SQRT_2 * (NativeMathf.cos(angle) + NativeMathf.sin(angle)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /4klang/instruments/BA_DarkChorus.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(PULSE) 3 | GO4K_VCO TRANSPOSE(52),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 4 | GO4K_FST AMOUNT(96),DEST(5*MAX_UNIT_SLOTS+5+FST_SET) 5 | GO4K_FOP OP(FOP_POP) 6 | GO4K_VCO TRANSPOSE(40),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(127),GAIN(128),FLAGS(TRISAW|LFO) 7 | GO4K_FOP OP(FOP_MULP) 8 | GO4K_VCF FREQUENCY(48),RESONANCE(48),VCFTYPE(LOWPASS) 9 | GO4K_FOP OP(FOP_MULP) 10 | GO4K_PAN PANNING(64) 11 | GO4K_DLL PREGAIN(112),DRY(64),FEEDBACK(64),DAMP(64),FREQUENCY(48),DEPTH(64),DELAY(17),COUNT(1) ; ERROR 12 | GO4K_FOP OP(FOP_XCH) 13 | GO4K_DLL PREGAIN(112),DRY(64),FEEDBACK(64),DAMP(64),FREQUENCY(46),DEPTH(64),DELAY(18),COUNT(1) ; ERROR 14 | GO4K_OUT GAIN(32), AUXSEND(0) 15 | -------------------------------------------------------------------------------- /wasmaudioworklet/common/workermessagehandler.js: -------------------------------------------------------------------------------- 1 | export class WorkerMessageHandler { 2 | 3 | constructor(messagePort) { 4 | this.workerMessageListeners = []; 5 | this.messagePort = messagePort; 6 | messagePort.onmessage = (msg) => { 7 | this.workerMessageListeners = this.workerMessageListeners.filter(listener => 8 | listener(msg) === true); 9 | } 10 | messagePort.onmessageerror = (err) => { 11 | console.error(err); 12 | }; 13 | } 14 | 15 | async callAndGetResult(message, responseFilter) { 16 | const result = await new Promise((resolve) => { 17 | this.workerMessageListeners.push((msg) => 18 | responseFilter(msg.data) ? resolve(msg.data) : true); 19 | this.messagePort.postMessage(message); 20 | }); 21 | return result; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /4klang/instruments/BA_DirectPunchMS.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(64),RELEASE(76),GAIN(128) 2 | GO4K_FST AMOUNT(112),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_VCO TRANSPOSE(52),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(PULSE) 4 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 5 | GO4K_FOP OP(FOP_ADDP) 6 | GO4K_ENV ATTAC(0),DECAY(66),SUSTAIN(16),RELEASE(64),GAIN(128) 7 | GO4K_FST AMOUNT(96),DEST(9*MAX_UNIT_SLOTS+4+FST_SET) 8 | GO4K_FST AMOUNT(96),DEST(10*MAX_UNIT_SLOTS+4+FST_SET) 9 | GO4K_FOP OP(FOP_POP) 10 | GO4K_VCF FREQUENCY(12),RESONANCE(128),VCFTYPE(LOWPASS) 11 | GO4K_VCF FREQUENCY(12),RESONANCE(128),VCFTYPE(LOWPASS) 12 | GO4K_FOP OP(FOP_MULP) 13 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 14 | GO4K_PAN PANNING(64) 15 | GO4K_OUT GAIN(128), AUXSEND(4) 16 | -------------------------------------------------------------------------------- /4klang/instruments/BA_Dark.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(PULSE) 3 | GO4K_VCO TRANSPOSE(52),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 4 | GO4K_FST AMOUNT(96),DEST(5*MAX_UNIT_SLOTS+5+FST_SET) 5 | GO4K_FOP OP(FOP_POP) 6 | GO4K_VCO TRANSPOSE(40),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(127),GAIN(128),FLAGS(TRISAW|LFO) 7 | GO4K_FOP OP(FOP_MULP) 8 | GO4K_VCF FREQUENCY(48),RESONANCE(48),VCFTYPE(LOWPASS) 9 | GO4K_FOP OP(FOP_MULP) 10 | GO4K_PAN PANNING(64) 11 | GO4K_DLL PREGAIN(0),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 12 | GO4K_FOP OP(FOP_XCH) 13 | GO4K_DLL PREGAIN(0),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 14 | GO4K_FOP OP(FOP_XCH) 15 | GO4K_OUT GAIN(64), AUXSEND(64) 16 | -------------------------------------------------------------------------------- /4klang/instruments/snare2.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(0),RELEASE(72),GAIN(128) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(56),SUSTAIN(0),RELEASE(0),GAIN(128) 4 | GO4K_FST AMOUNT(108),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 5 | GO4K_FST AMOUNT(72),DEST(7*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(32),GAIN(64),FLAGS(SINE) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(80),GAIN(64),FLAGS(SINE) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(64),FLAGS(NOISE) 10 | GO4K_VCF FREQUENCY(104),RESONANCE(128),VCFTYPE(LOWPASS) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_MULP) 14 | GO4K_VCF FREQUENCY(22),RESONANCE(32),VCFTYPE(HIGHPASS) 15 | GO4K_PAN PANNING(64) 16 | GO4K_OUT GAIN(64), AUXSEND(0) 17 | -------------------------------------------------------------------------------- /4klang/instruments/snare6.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(0),RELEASE(72),GAIN(128) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(0),RELEASE(0),GAIN(128) 4 | GO4K_FST AMOUNT(108),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 5 | GO4K_FST AMOUNT(72),DEST(7*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(63),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(32),GAIN(64),FLAGS(NOISE) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(96),GAIN(64),FLAGS(TRISAW) 9 | GO4K_VCO TRANSPOSE(76),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(96),GAIN(64),FLAGS(TRISAW) 10 | GO4K_VCF FREQUENCY(96),RESONANCE(128),VCFTYPE(LOWPASS) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_MULP) 14 | GO4K_VCF FREQUENCY(28),RESONANCE(32),VCFTYPE(HIGHPASS) 15 | GO4K_PAN PANNING(64) 16 | GO4K_OUT GAIN(79), AUXSEND(0) 17 | -------------------------------------------------------------------------------- /4klang/instruments/pOWL_BAS_Dubstep03.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(72),DECAY(72),SUSTAIN(0),RELEASE(32),GAIN(128) 2 | GO4K_DST DRIVE(123), SNHFREQ(128), FLAGS(0) 3 | GO4K_FST AMOUNT(76),DEST(4*MAX_UNIT_SLOTS+1+FST_SET) 4 | GO4K_FST AMOUNT(120),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 5 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(32),GATES(0),COLOR(128),SHAPE(96),GAIN(128),FLAGS(TRISAW) 6 | GO4K_VCO TRANSPOSE(78),DETUNE(64),PHASE(32),GATES(0),COLOR(128),SHAPE(96),GAIN(128),FLAGS(TRISAW) 7 | GO4K_FOP OP(FOP_MULP) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(128),SHAPE(64),GAIN(128),FLAGS(TRISAW) 9 | GO4K_FOP OP(FOP_MULP) 10 | GO4K_FOP OP(FOP_MULP) 11 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(128),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 12 | GO4K_FST AMOUNT(128),DEST(12*MAX_UNIT_SLOTS+4+FST_SET+FST_POP) 13 | GO4K_VCF FREQUENCY(0),RESONANCE(128),VCFTYPE(LOWPASS) 14 | GO4K_PAN PANNING(64) 15 | GO4K_OUT GAIN(80), AUXSEND(2) 16 | -------------------------------------------------------------------------------- /4klang/instruments/BA_SawBassFlanger.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(96),RELEASE(64),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(64),RELEASE(64),GAIN(128) 3 | GO4K_FST AMOUNT(104),DEST(9*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(64),DETUNE(68),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 6 | GO4K_VCO TRANSPOSE(64),DETUNE(60),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 7 | GO4K_VCO TRANSPOSE(76),DETUNE(64),PHASE(0),GATES(85),COLOR(6),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_FOP OP(FOP_ADDP) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_VCF FREQUENCY(32),RESONANCE(128),VCFTYPE(LOWPASS) 11 | GO4K_FOP OP(FOP_MULP) 12 | GO4K_DLL PREGAIN(96),DRY(16),FEEDBACK(96),DAMP(16),FREQUENCY(32),DEPTH(75),DELAY(17),COUNT(1) ; ERROR 13 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 14 | GO4K_PAN PANNING(64) 15 | GO4K_OUT GAIN(128), AUXSEND(32) 16 | -------------------------------------------------------------------------------- /4klang/instruments/SY_RandomArp.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(0),RELEASE(16),GAIN(128) 2 | GO4K_FST AMOUNT(88),DEST(10*MAX_UNIT_SLOTS+4+FST_SET) 3 | GO4K_DST DRIVE(16), SNHFREQ(128), FLAGS(0) 4 | GO4K_VCO TRANSPOSE(8),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(NOISE) 5 | GO4K_DST DRIVE(64), SNHFREQ(0), FLAGS(0) 6 | GO4K_FST AMOUNT(74),DEST(11*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(62),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(PULSE) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(74),PHASE(0),GATES(85),COLOR(16),SHAPE(64),GAIN(128),FLAGS(PULSE) 10 | GO4K_FOP OP(FOP_ADDP) 11 | GO4K_VCF FREQUENCY(48),RESONANCE(128),VCFTYPE(LOWPASS) 12 | GO4K_VCF FREQUENCY(48),RESONANCE(64),VCFTYPE(LOWPASS) 13 | GO4K_FOP OP(FOP_MULP) 14 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 15 | GO4K_PAN PANNING(64) 16 | GO4K_OUT GAIN(128), AUXSEND(16) 17 | -------------------------------------------------------------------------------- /4klang/instruments/PA_Fairies.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(80),DECAY(96),SUSTAIN(80),RELEASE(88),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(96),SUSTAIN(40),RELEASE(88),GAIN(128) 3 | GO4K_DST DRIVE(32), SNHFREQ(128), FLAGS(0) 4 | GO4K_FST AMOUNT(96),DEST(9*MAX_UNIT_SLOTS+4+FST_SET) 5 | GO4K_FOP OP(FOP_POP) 6 | GO4K_VCO TRANSPOSE(64),DETUNE(56),PHASE(3),GATES(85),COLOR(3),SHAPE(64),GAIN(64),FLAGS(TRISAW) 7 | GO4K_VCO TRANSPOSE(64),DETUNE(72),PHASE(3),GATES(85),COLOR(3),SHAPE(64),GAIN(64),FLAGS(TRISAW) 8 | GO4K_FOP OP(FOP_ADDP) 9 | GO4K_DST DRIVE(96), SNHFREQ(128), FLAGS(0) 10 | GO4K_VCF FREQUENCY(16),RESONANCE(24),VCFTYPE(HIGHPASS) 11 | GO4K_FOP OP(FOP_MULP) 12 | GO4K_PAN PANNING(64) 13 | GO4K_DLL PREGAIN(96),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 14 | GO4K_FOP OP(FOP_XCH) 15 | GO4K_DLL PREGAIN(96),DRY(128),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 16 | GO4K_FOP OP(FOP_XCH) 17 | GO4K_OUT GAIN(64), AUXSEND(64) 18 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/fx/bandpass.ts: -------------------------------------------------------------------------------- 1 | import { BiQuadFilter, FilterType, Q_BUTTERWORTH } from "../synth/biquad"; 2 | import { SAMPLERATE } from "../environment"; 3 | 4 | export class BandPass { 5 | lpfilter: BiQuadFilter = new BiQuadFilter(); 6 | hpfilter: BiQuadFilter = new BiQuadFilter(); 7 | 8 | constructor(lowfreq: f32, hifreq: f32) { 9 | this.update_frequencies(lowfreq, hifreq); 10 | } 11 | 12 | clearBuffers(): void { 13 | this.hpfilter.clearBuffers(); 14 | this.lpfilter.clearBuffers(); 15 | } 16 | 17 | update_frequencies(lowfreq: f32, hifreq: f32): void { 18 | this.lpfilter.update_coeffecients(FilterType.LowPass, SAMPLERATE, hifreq, Q_BUTTERWORTH); 19 | this.hpfilter.update_coeffecients(FilterType.HighPass, SAMPLERATE, lowfreq, Q_BUTTERWORTH); 20 | } 21 | 22 | process(sample: f32): f32 { 23 | return this.lpfilter.process(this.hpfilter.process(sample)); 24 | } 25 | } -------------------------------------------------------------------------------- /playback.js: -------------------------------------------------------------------------------- 1 | const midi = require('midi'); 2 | 3 | // Set up a new output. 4 | const output = new midi.output(); 5 | let outputIndex; 6 | for(var n=0;n { 18 | if(msg) { 19 | output.sendMessage(msg); 20 | } 21 | 22 | if(midimessages.length>0) { 23 | const evt = midimessages.shift(); 24 | const deltatime = evt[0]; 25 | const nextmessage = evt[1]; 26 | 27 | accumulatedDeltaTime += deltatime * 1000; 28 | setTimeout(() => playEvent(nextmessage), accumulatedDeltaTime - Date.now()); 29 | } 30 | 31 | 32 | }; 33 | 34 | playEvent(); 35 | -------------------------------------------------------------------------------- /wasmaudioworklet/analyser/levelanalysernode.js: -------------------------------------------------------------------------------- 1 | 2 | export async function connectLevelAnalyser(audioNode) { 3 | await audioNode.context.audioWorklet.addModule(new URL('levelanalyserprocessor.js', import.meta.url)); 4 | const levelAnalyserNode = new AudioWorkletNode(audioNode.context, 'levelanalyserprocessor'); 5 | levelAnalyserNode.port.start(); 6 | audioNode.connect(levelAnalyserNode); 7 | 8 | return () => new Promise(resolve => { 9 | levelAnalyserNode.port.onmessage = (msg) => { 10 | resolve(msg.data); 11 | }; 12 | levelAnalyserNode.port.postMessage({ stats: true }); 13 | }); 14 | } 15 | 16 | export function skipClipsWithinCentiSeconds(cliparray) { 17 | return cliparray.reduce((newarr, curr) => { 18 | if (newarr.length == 0 || 19 | Math.round(curr.time * 100) != Math.round(newarr[newarr.length-1].time * 100) ) { 20 | newarr.push(curr); 21 | } 22 | return newarr; 23 | }, []); 24 | } -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/midi/instruments/defaultinstrument.ts: -------------------------------------------------------------------------------- 1 | import { Envelope } from "../../synth/envelope.class"; 2 | import { notefreq } from "../../synth/note"; 3 | import { SineOscillator } from "../../synth/sineoscillator.class"; 4 | import { MidiVoice } from "../midisynth"; 5 | 6 | export class DefaultInstrument extends MidiVoice { 7 | osc: SineOscillator = new SineOscillator(); 8 | env: Envelope = new Envelope(0.01, 0.0, 1.0, 0.01); 9 | 10 | noteon(note: u8, velocity: u8): void { 11 | super.noteon(note, velocity); 12 | this.osc.frequency = notefreq(note); 13 | this.env.attack(); 14 | } 15 | 16 | noteoff(): void { 17 | this.env.release(); 18 | } 19 | 20 | isDone(): boolean { 21 | return this.env.isDone(); 22 | } 23 | 24 | nextframe(): void { 25 | const signal = this.osc.next() * this.env.next() * this.velocity / 256; 26 | this.channel.signal.addMonoSignal(signal, 0.2, 0.5); 27 | } 28 | } -------------------------------------------------------------------------------- /songs/upbeatchorus.json: -------------------------------------------------------------------------------- 1 | [[0.185,[139,64,0]],[0.179,[155,64,127]],[0.184,[139,64,0]],[0.183,[155,71,127]],[0.048,[155,72,127]],[0.028,[139,71,0]],[0.2,[139,72,0]],[0,[155,71,127]],[0.477,[155,67,127]],[0.02,[139,71,0]],[0.224,[139,67,0]],[0.248,[155,71,127]],[0.243,[139,71,0]],[0.151,[155,71,127]],[0.182,[139,71,0]],[0.166,[155,71,127]],[0.068,[155,72,127]],[0.023,[139,71,0]],[0.177,[139,72,0]],[0.016,[155,71,127]],[0.448,[155,67,127]],[0.018,[139,71,0]],[0.286,[139,67,0]],[0.192,[155,64,127]],[0.258,[139,64,0]],[0.128,[155,64,127]],[0.26,[139,64,0]],[0.144,[155,71,127]],[0.051,[155,72,127]],[0.051,[139,71,0]],[0.178,[155,71,127]],[0.014,[139,72,0]],[0.43,[155,67,127]],[0.024,[139,71,0]],[0.269,[139,67,0]],[0.17,[155,74,127]],[0.036,[155,76,127]],[0.035,[139,74,0]],[0.214,[155,74,127]],[0.01,[139,76,0]],[0.266,[155,71,127]],[0.014,[139,74,0]],[0.18,[139,71,0]],[0.062,[155,69,127]],[0.086,[155,71,127]],[0.027,[139,69,0]],[0.064,[155,69,127]],[0.035,[139,71,0]],[0.03,[155,67,127]],[0.026,[139,69,0]],[0.268,[139,67,0]]] -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/browsersynthcompiler.js: -------------------------------------------------------------------------------- 1 | const synthcompilerworker = new Promise(resolve => { 2 | const worker = new Worker(new URL('moduleworkerloader.js', import.meta.url)); 3 | worker.onmessage = () => resolve(worker); 4 | }); 5 | 6 | export async function compileWebAssemblySynth(synthsource, song, samplerate, exportmode) { 7 | const worker = await synthcompilerworker; 8 | worker.postMessage({ 9 | synthsource: synthsource, 10 | samplerate: samplerate, 11 | song: song, 12 | exportmode: exportmode 13 | }); 14 | 15 | const result = await new Promise((resolve) => worker.onmessage = (msg) => resolve(msg)); 16 | if (result.data.binary) { 17 | console.log('successfully compiled webassembly synth'); 18 | return result.data.binary; 19 | } else if (result.data.error) { 20 | throw new Error(result.data.error); 21 | } else { 22 | console.log('no changes for webassembly synth'); 23 | } 24 | return null; 25 | } -------------------------------------------------------------------------------- /4klang/instruments/snare4.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(0),RELEASE(72),GAIN(126) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(56),SUSTAIN(0),RELEASE(0),GAIN(128) 4 | GO4K_FST AMOUNT(108),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 5 | GO4K_FST AMOUNT(72),DEST(7*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(32),GAIN(64),FLAGS(SINE) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(80),GAIN(64),FLAGS(SINE) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(10),GAIN(64),FLAGS(NOISE) 10 | GO4K_VCF FREQUENCY(96),RESONANCE(128),VCFTYPE(LOWPASS) 11 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(16),FLAGS(NOISE) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_ADDP) 14 | GO4K_FOP OP(FOP_ADDP) 15 | GO4K_FOP OP(FOP_MULP) 16 | GO4K_VCF FREQUENCY(22),RESONANCE(32),VCFTYPE(HIGHPASS) 17 | GO4K_PAN PANNING(64) 18 | GO4K_OUT GAIN(64), AUXSEND(0) 19 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/as-pect.asconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "targets": { 3 | "coverage": { 4 | "lib": [ 5 | "../node_modules/@as-covers/assembly/index.ts" 6 | ], 7 | "transform": [ 8 | "@as-covers/transform", 9 | "@as-pect/transform" 10 | ] 11 | }, 12 | "noCoverage": { 13 | "transform": [ 14 | "@as-pect/transform" 15 | ] 16 | } 17 | }, 18 | "options": { 19 | "initialMemory": 1024, 20 | "exportMemory": true, 21 | "outFile": "output.wasm", 22 | "textFile": "output.wat", 23 | "bindings": "raw", 24 | "exportStart": "_start", 25 | "exportRuntime": true, 26 | "use": [ 27 | "RTRACE=1" 28 | ], 29 | "debug": true, 30 | "exportTable": true 31 | }, 32 | "extends": "./asconfig.json", 33 | "entries": [ 34 | "../node_modules/@as-pect/assembly/assembly/index.ts" 35 | ] 36 | } -------------------------------------------------------------------------------- /wasmaudioworklet/app.spec.js: -------------------------------------------------------------------------------- 1 | import { waitForAppReady } from './app.js'; 2 | 3 | describe('app', function() { 4 | this.timeout(10000); 5 | this.afterAll(async () => { 6 | document.documentElement.removeChild(document.querySelector('app-javascriptmusic')); 7 | }); 8 | it('should start app, and show spinner while starting', async () => { 9 | document.documentElement.appendChild(document.createElement('app-javascriptmusic')); 10 | while (document.getElementsByTagName('progress-spinner').length === 0) { 11 | await new Promise(r => setTimeout(() => r(), 0)); 12 | } 13 | assert.equal(document.getElementsByTagName('progress-spinner').length, 1); 14 | await waitForAppReady(); 15 | const appElement = document.getElementsByTagName('app-javascriptmusic')[0].shadowRoot; 16 | assert.equal(appElement.querySelector('#startaudiobutton').disabled, false); 17 | assert.equal(document.getElementsByTagName('progress-spinner').length, 0); 18 | }); 19 | 20 | }); -------------------------------------------------------------------------------- /4klang/instruments/BA_Mighty.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(88),SUSTAIN(0),RELEASE(0),GAIN(128) 3 | GO4K_FST AMOUNT(96),DEST(6*MAX_UNIT_SLOTS+7+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(64),DETUNE(68),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW) 6 | GO4K_VCO TRANSPOSE(52),DETUNE(58),PHASE(32),GATES(85),COLOR(32),SHAPE(64),GAIN(128),FLAGS(TRISAW) 7 | GO4K_VCO TRANSPOSE(88),DETUNE(62),PHASE(48),GATES(85),COLOR(48),SHAPE(32),GAIN(32),FLAGS(SINE) 8 | GO4K_FOP OP(FOP_ADDP) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_VCF FREQUENCY(64),RESONANCE(64),VCFTYPE(LOWPASS) 11 | GO4K_DST DRIVE(120), SNHFREQ(128), FLAGS(0) 12 | GO4K_FOP OP(FOP_MULP) 13 | GO4K_PAN PANNING(64) 14 | GO4K_DLL PREGAIN(96),DRY(96),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 15 | GO4K_FOP OP(FOP_XCH) 16 | GO4K_DLL PREGAIN(96),DRY(96),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 17 | GO4K_FOP OP(FOP_XCH) 18 | GO4K_OUT GAIN(64), AUXSEND(64) 19 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/__tests__/fx/limiter.spec.ts: -------------------------------------------------------------------------------- 1 | import { Limiter } from '../../fx/limiter'; 2 | import { SineOscillator } from '../../synth/sineoscillator.class'; 3 | 4 | describe('limiter', () => { 5 | it('should limit signal', () => { 6 | const limiter = new Limiter(10, 10); 7 | const osc = new SineOscillator(); 8 | osc.frequency = 440; 9 | 10 | let peak1: f32 = 0; 11 | for (let frame = 0; frame < 500; frame++) { 12 | const val = limiter.process(osc.next() * 2); 13 | const abs = Mathf.abs(val); 14 | if (abs > peak1) { 15 | peak1 = abs; 16 | } 17 | } 18 | 19 | let peak2: f32 = 0; 20 | for (let frame = 0; frame < 100; frame++) { 21 | const val = limiter.process(osc.next() * 2); 22 | const abs = Mathf.abs(val); 23 | if (abs > peak2) { 24 | peak2 = abs; 25 | } 26 | } 27 | expect(peak1 / peak2).toBeCloseTo(1.27 as f32); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /4klang/instruments/SY_RandomArpFlanger.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(69),RELEASE(16),GAIN(128) 2 | GO4K_FST AMOUNT(88),DEST(10*MAX_UNIT_SLOTS+4+FST_SET) 3 | GO4K_DST DRIVE(16), SNHFREQ(128), FLAGS(0) 4 | GO4K_VCO TRANSPOSE(8),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(NOISE) 5 | GO4K_DST DRIVE(64), SNHFREQ(0), FLAGS(0) 6 | GO4K_FST AMOUNT(74),DEST(11*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(62),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(PULSE) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(74),PHASE(0),GATES(85),COLOR(16),SHAPE(64),GAIN(128),FLAGS(PULSE) 10 | GO4K_FOP OP(FOP_ADDP) 11 | GO4K_VCF FREQUENCY(48),RESONANCE(128),VCFTYPE(LOWPASS) 12 | GO4K_VCF FREQUENCY(48),RESONANCE(64),VCFTYPE(LOWPASS) 13 | GO4K_FOP OP(FOP_MULP) 14 | GO4K_DLL PREGAIN(112),DRY(16),FEEDBACK(112),DAMP(16),FREQUENCY(32),DEPTH(64),DELAY(17),COUNT(1) ; ERROR 15 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 16 | GO4K_PAN PANNING(64) 17 | GO4K_OUT GAIN(128), AUXSEND(16) 18 | -------------------------------------------------------------------------------- /4klang/instruments/BA_SawBass.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(96),RELEASE(64),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(64),RELEASE(64),GAIN(128) 3 | GO4K_FST AMOUNT(104),DEST(9*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(64),DETUNE(68),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 6 | GO4K_VCO TRANSPOSE(64),DETUNE(60),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 7 | GO4K_VCO TRANSPOSE(76),DETUNE(64),PHASE(0),GATES(85),COLOR(6),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_FOP OP(FOP_ADDP) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_VCF FREQUENCY(32),RESONANCE(128),VCFTYPE(LOWPASS) 11 | GO4K_FOP OP(FOP_MULP) 12 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(9),COUNT(8) ; ERROR 13 | GO4K_PAN PANNING(64) 14 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(32),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 15 | GO4K_FOP OP(FOP_XCH) 16 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(32),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 17 | GO4K_OUT GAIN(128), AUXSEND(0) 18 | -------------------------------------------------------------------------------- /4klang/instruments/pad2.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(72),DECAY(96),SUSTAIN(96),RELEASE(88),GAIN(128) 2 | GO4K_FST AMOUNT(64),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(60),PHASE(32),GATES(85),COLOR(80),SHAPE(64),GAIN(128),FLAGS(TRISAW) 4 | GO4K_VCO TRANSPOSE(64),DETUNE(72),PHASE(32),GATES(85),COLOR(96),SHAPE(112),GAIN(64),FLAGS(SINE) 5 | GO4K_VCO TRANSPOSE(80),DETUNE(112),PHASE(0),GATES(85),COLOR(64),SHAPE(16),GAIN(128),FLAGS(PULSE|LFO) 6 | GO4K_FST AMOUNT(68),DEST(2*MAX_UNIT_SLOTS+2+FST_SET) 7 | GO4K_FST AMOUNT(60),DEST(3*MAX_UNIT_SLOTS+2+FST_SET) 8 | GO4K_FOP OP(FOP_POP) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_FOP OP(FOP_MULP) 11 | GO4K_VCF FREQUENCY(80),RESONANCE(24),VCFTYPE(LOWPASS) 12 | GO4K_VCF FREQUENCY(48),RESONANCE(24),VCFTYPE(HIGHPASS) 13 | GO4K_PAN PANNING(64) 14 | GO4K_DLL PREGAIN(96),DRY(128),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 15 | GO4K_FOP OP(FOP_XCH) 16 | GO4K_DLL PREGAIN(96),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 17 | GO4K_FOP OP(FOP_XCH) 18 | GO4K_OUT GAIN(0), AUXSEND(32) 19 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/fx/eqband.ts: -------------------------------------------------------------------------------- 1 | import { BiQuadFilter, FilterType, Q_BUTTERWORTH } from "../synth/biquad"; 2 | import { SAMPLERATE } from "../environment"; 3 | 4 | export class EQBand { 5 | lpfilter: BiQuadFilter = new BiQuadFilter(); 6 | hpfilter: BiQuadFilter = new BiQuadFilter(); 7 | lpfilter2: BiQuadFilter = new BiQuadFilter(); 8 | hpfilter2: BiQuadFilter = new BiQuadFilter(); 9 | 10 | 11 | constructor(lowfreq: f32, hifreq: f32) { 12 | this.lpfilter.update_coeffecients(FilterType.LowPass, SAMPLERATE, hifreq, Q_BUTTERWORTH); 13 | this.lpfilter2.update_coeffecients(FilterType.LowPass, SAMPLERATE, hifreq, Q_BUTTERWORTH); 14 | this.hpfilter.update_coeffecients(FilterType.HighPass, SAMPLERATE, lowfreq, Q_BUTTERWORTH); 15 | this.hpfilter2.update_coeffecients(FilterType.HighPass, SAMPLERATE, lowfreq, Q_BUTTERWORTH); 16 | } 17 | 18 | process(left: f32): f32 { 19 | return this.lpfilter2.process(this.lpfilter.process(this.hpfilter2.process(this.hpfilter.process(left)))); 20 | } 21 | } -------------------------------------------------------------------------------- /4klang/instruments/LD_More&MoreMS.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(80),SUSTAIN(0),RELEASE(32),GAIN(128) 2 | GO4K_VCO TRANSPOSE(52),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(56),DEST(5*MAX_UNIT_SLOTS+2+FST_SET) 4 | GO4K_FST AMOUNT(72),DEST(6*MAX_UNIT_SLOTS+2+FST_SET) 5 | GO4K_FOP OP(FOP_POP) 6 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 7 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_FOP OP(FOP_ADDP) 9 | GO4K_ENV ATTAC(0),DECAY(76),SUSTAIN(0),RELEASE(32),GAIN(128) 10 | GO4K_FST AMOUNT(88),DEST(12*MAX_UNIT_SLOTS+4+FST_SET) 11 | GO4K_FST AMOUNT(88),DEST(13*MAX_UNIT_SLOTS+4+FST_SET) 12 | GO4K_FOP OP(FOP_POP) 13 | GO4K_VCF FREQUENCY(80),RESONANCE(128),VCFTYPE(BANDSTOP) 14 | GO4K_VCF FREQUENCY(80),RESONANCE(128),VCFTYPE(LOWPASS) 15 | GO4K_FOP OP(FOP_MULP) 16 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 17 | GO4K_PAN PANNING(64) 18 | GO4K_OUT GAIN(128), AUXSEND(32) 19 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/stereosignal.class.ts: -------------------------------------------------------------------------------- 1 | import { Pan } from './pan.class'; 2 | 3 | export class StereoSignal { 4 | left: f32 = 0; 5 | right: f32 = 0; 6 | 7 | clear(): void { 8 | this.left = 0; 9 | this.right = 0; 10 | } 11 | 12 | /** 13 | * Add left and right values directly to the signal 14 | * @param left 15 | * @param right 16 | */ 17 | @inline 18 | add(left: f32, right:f32): void { 19 | this.left += left; 20 | this.right += right; 21 | } 22 | 23 | /** 24 | * Add stereo signal with simple (not proper) panning 25 | * @param signal 26 | * @param level 27 | * @param pan 0.0 - 1.0 28 | */ 29 | addStereoSignal(signal: StereoSignal, level: f32, pan: f32): void { 30 | this.left += signal.left * pan * level; 31 | this.right += signal.right * (1 - pan) * level; 32 | } 33 | 34 | addMonoSignal(signal: f32, level: f32, pan: f32): void { 35 | this.left += signal * pan * level; 36 | this.right += signal * (1 - pan) * level; 37 | } 38 | } -------------------------------------------------------------------------------- /4klang/instruments/snare3.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(74),SUSTAIN(0),RELEASE(64),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(72),SUSTAIN(0),RELEASE(72),GAIN(128) 3 | GO4K_FST AMOUNT(128),DEST(1*MAX_UNIT_SLOTS+2+FST_SET) 4 | GO4K_ENV ATTAC(0),DECAY(56),SUSTAIN(0),RELEASE(0),GAIN(128) 5 | GO4K_FST AMOUNT(108),DEST(7*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FST AMOUNT(72),DEST(8*MAX_UNIT_SLOTS+1+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(32),GAIN(64),FLAGS(SINE) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(80),GAIN(64),FLAGS(SINE) 10 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(64),FLAGS(NOISE) 11 | GO4K_VCF FREQUENCY(104),RESONANCE(128),VCFTYPE(LOWPASS) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_ADDP) 14 | GO4K_FOP OP(FOP_MULP) 15 | GO4K_VCF FREQUENCY(22),RESONANCE(32),VCFTYPE(HIGHPASS) 16 | GO4K_DLL PREGAIN(32),DRY(128),FEEDBACK(115),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(1),COUNT(8) ; ERROR 17 | GO4K_FOP OP(FOP_MULP) 18 | GO4K_PAN PANNING(64) 19 | GO4K_OUT GAIN(64), AUXSEND(0) 20 | -------------------------------------------------------------------------------- /4klang/instruments/BA_Mighty_Feedback.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(64),RELEASE(74),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(88),SUSTAIN(16),RELEASE(64),GAIN(128) 3 | GO4K_FST AMOUNT(96),DEST(7*MAX_UNIT_SLOTS+7+FST_SET) 4 | GO4K_FST AMOUNT(32),DEST(10*MAX_UNIT_SLOTS+4+FST_SET) 5 | GO4K_FOP OP(FOP_POP) 6 | GO4K_VCO TRANSPOSE(64),DETUNE(68),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW) 7 | GO4K_VCO TRANSPOSE(52),DETUNE(58),PHASE(32),GATES(85),COLOR(32),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_VCO TRANSPOSE(88),DETUNE(62),PHASE(48),GATES(85),COLOR(48),SHAPE(32),GAIN(32),FLAGS(SINE) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_FOP OP(FOP_ADDP) 11 | GO4K_VCF FREQUENCY(64),RESONANCE(64),VCFTYPE(HIGHPASS) 12 | GO4K_DST DRIVE(120), SNHFREQ(128), FLAGS(0) 13 | GO4K_FOP OP(FOP_MULP) 14 | GO4K_PAN PANNING(64) 15 | GO4K_DLL PREGAIN(96),DRY(96),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 16 | GO4K_FOP OP(FOP_XCH) 17 | GO4K_DLL PREGAIN(96),DRY(96),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 18 | GO4K_FOP OP(FOP_XCH) 19 | GO4K_OUT GAIN(64), AUXSEND(64) 20 | -------------------------------------------------------------------------------- /4klang/instruments/bass.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(32),DECAY(64),SUSTAIN(64),RELEASE(64),GAIN(64) 2 | GO4K_FST AMOUNT(120),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(32),GATES(85),COLOR(80),SHAPE(64),GAIN(128),FLAGS(PULSE) 4 | GO4K_VCO TRANSPOSE(64),DETUNE(72),PHASE(32),GATES(85),COLOR(96),SHAPE(64),GAIN(128),FLAGS(TRISAW) 5 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(90),GAIN(128),FLAGS(SINE|LFO) 6 | GO4K_FST AMOUNT(68),DEST(2*MAX_UNIT_SLOTS+2+FST_SET) 7 | GO4K_FST AMOUNT(60),DEST(3*MAX_UNIT_SLOTS+2+FST_SET) 8 | GO4K_FOP OP(FOP_POP) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_FOP OP(FOP_MULP) 11 | GO4K_VCF FREQUENCY(18),RESONANCE(64),VCFTYPE(PEAK) 12 | GO4K_VCF FREQUENCY(32),RESONANCE(48),VCFTYPE(LOWPASS) 13 | GO4K_DST DRIVE(88), SNHFREQ(128), FLAGS(0) 14 | GO4K_PAN PANNING(64) 15 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 16 | GO4K_FOP OP(FOP_XCH) 17 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 18 | GO4K_FOP OP(FOP_XCH) 19 | GO4K_OUT GAIN(64), AUXSEND(64) 20 | -------------------------------------------------------------------------------- /4klang/instruments/pad.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(72),DECAY(96),SUSTAIN(96),RELEASE(88),GAIN(128) 2 | GO4K_FST AMOUNT(64),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(60),PHASE(32),GATES(85),COLOR(80),SHAPE(64),GAIN(128),FLAGS(PULSE) 4 | GO4K_VCO TRANSPOSE(64),DETUNE(72),PHASE(32),GATES(85),COLOR(96),SHAPE(64),GAIN(128),FLAGS(TRISAW) 5 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(90),GAIN(128),FLAGS(SINE|LFO) 6 | GO4K_FST AMOUNT(68),DEST(2*MAX_UNIT_SLOTS+2+FST_SET) 7 | GO4K_FST AMOUNT(61),DEST(3*MAX_UNIT_SLOTS+2+FST_SET) 8 | GO4K_FOP OP(FOP_POP) 9 | GO4K_FOP OP(FOP_ADDP) 10 | GO4K_FOP OP(FOP_MULP) 11 | GO4K_VCF FREQUENCY(26),RESONANCE(128),VCFTYPE(PEAK) 12 | GO4K_VCF FREQUENCY(64),RESONANCE(64),VCFTYPE(LOWPASS) 13 | GO4K_DST DRIVE(104), SNHFREQ(128), FLAGS(0) 14 | GO4K_PAN PANNING(64) 15 | GO4K_DLL PREGAIN(96),DRY(128),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 16 | GO4K_FOP OP(FOP_XCH) 17 | GO4K_DLL PREGAIN(96),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 18 | GO4K_FOP OP(FOP_XCH) 19 | GO4K_OUT GAIN(0), AUXSEND(32) 20 | -------------------------------------------------------------------------------- /4klang/instruments/rimshot.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(85),SUSTAIN(0),RELEASE(72),GAIN(128) 2 | GO4K_FST AMOUNT(128),DEST(0*MAX_UNIT_SLOTS+2+FST_SET) 3 | GO4K_ENV ATTAC(0),DECAY(56),SUSTAIN(0),RELEASE(0),GAIN(128) 4 | GO4K_FST AMOUNT(108),DEST(6*MAX_UNIT_SLOTS+1+FST_SET) 5 | GO4K_FST AMOUNT(72),DEST(7*MAX_UNIT_SLOTS+1+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(32),GAIN(64),FLAGS(SINE) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(80),GAIN(64),FLAGS(SINE) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(64),FLAGS(NOISE) 10 | GO4K_VCF FREQUENCY(104),RESONANCE(128),VCFTYPE(LOWPASS) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_MULP) 14 | GO4K_VCF FREQUENCY(23),RESONANCE(32),VCFTYPE(HIGHPASS) 15 | GO4K_VCF FREQUENCY(80),RESONANCE(54),VCFTYPE(HIGHPASS) 16 | GO4K_VCF FREQUENCY(56),RESONANCE(3),VCFTYPE(ALLPASS) 17 | GO4K_DLL PREGAIN(63),DRY(128),FEEDBACK(60),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 18 | GO4K_PAN PANNING(64) 19 | GO4K_OUT GAIN(3), AUXSEND(0) 20 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/fx/comb.ts: -------------------------------------------------------------------------------- 1 | import { DelayLine } from "./delayline"; 2 | 3 | export class Comb { 4 | readonly delay_line: DelayLine; 5 | feedback: f32; 6 | filter_state: f32; 7 | dampening: f32; 8 | dampening_inverse: f32; 9 | 10 | constructor(delay_length: usize) { 11 | this.delay_line = new DelayLine(delay_length); 12 | this.feedback= 0.5; 13 | this.filter_state = 0.0; 14 | this.dampening = 0.5; 15 | this.dampening_inverse = 0.5; 16 | 17 | } 18 | 19 | set_dampening(value: f32): void{ 20 | this.dampening = value; 21 | this.dampening_inverse = 1.0 - value; 22 | } 23 | 24 | set_feedback(value: f32): void { 25 | this.feedback = value; 26 | } 27 | 28 | tick(input: f32): f32 { 29 | let output = this.delay_line.read(); 30 | 31 | this.filter_state = 32 | output * this.dampening_inverse + this.filter_state * this.dampening; 33 | 34 | this.delay_line 35 | .write_and_advance(input + this.filter_state * this.feedback); 36 | 37 | return output; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /4klang/LICENSE_4klang: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dominik Ries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wasmaudioworklet/wasmgit/wasmgitui.html: -------------------------------------------------------------------------------- 1 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
-------------------------------------------------------------------------------- /4klang/instruments/pOWL_BAS_Dubstep07.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(0),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(128),SHAPE(127),GAIN(128),FLAGS(TRISAW) 3 | GO4K_VCO TRANSPOSE(64),DETUNE(80),PHASE(32),GATES(0),COLOR(128),SHAPE(127),GAIN(128),FLAGS(TRISAW) 4 | GO4K_FOP OP(FOP_ADDP) 5 | GO4K_FOP OP(FOP_MULP) 6 | GO4K_VCF FREQUENCY(48),RESONANCE(4),VCFTYPE(LOWPASS) 7 | GO4K_DST DRIVE(127), SNHFREQ(128), FLAGS(0) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(0),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 9 | GO4K_FST AMOUNT(68),DEST(1*MAX_UNIT_SLOTS+1+FST_SET) 10 | GO4K_FOP OP(FOP_POP) 11 | GO4K_ENV ATTAC(0),DECAY(0),SUSTAIN(128),RELEASE(128),GAIN(128) 12 | GO4K_FST AMOUNT(70),DEST(1*MAX_UNIT_SLOTS+1+FST_SET) 13 | GO4K_FST AMOUNT(58),DEST(2*MAX_UNIT_SLOTS+1+FST_SET) 14 | GO4K_FST AMOUNT(48),DEST(5*MAX_UNIT_SLOTS+4+FST_SET) 15 | GO4K_FOP OP(FOP_MULP) 16 | GO4K_DLL PREGAIN(72),DRY(0),FEEDBACK(32),DAMP(109),FREQUENCY(0),DEPTH(0),DELAY(9),COUNT(8) ; ERROR 17 | GO4K_DLL PREGAIN(16),DRY(128),FEEDBACK(32),DAMP(109),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 18 | GO4K_PAN PANNING(64) 19 | GO4K_OUT GAIN(48), AUXSEND(8) 20 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/mixes/empty.mix.ts: -------------------------------------------------------------------------------- 1 | import { Kick2, SawBass, Instrument, 2 | StereoSignal, 3 | Freeverb } from "./globalimports"; 4 | 5 | export const PATTERN_SIZE_SHIFT: usize = 4; 6 | export const BEATS_PER_PATTERN_SHIFT: usize = 2; 7 | 8 | const kick = new Kick2(); 9 | const sawbass = new SawBass(); 10 | 11 | export function setChannelValue(channel: usize, value: f32): void { 12 | ([kick, sawbass] as Instrument[])[channel].note = value; 13 | } 14 | 15 | const mainline = new StereoSignal(); 16 | const reverbline = new StereoSignal(); 17 | const freeverb = new Freeverb(); 18 | 19 | @inline 20 | export function mixernext(leftSampleBufferPtr: usize, rightSampleBufferPtr: usize): void { 21 | mainline.clear(); 22 | reverbline.clear(); 23 | 24 | kick.next(); 25 | mainline.addStereoSignal(kick.signal, 0.5, 0.5); 26 | 27 | sawbass.next(); 28 | mainline.addStereoSignal(sawbass.signal, 0.5, 0.5); 29 | reverbline.addStereoSignal(sawbass.signal, 0.1, 0.5); 30 | 31 | freeverb.tick(reverbline); 32 | 33 | store(leftSampleBufferPtr, mainline.left + reverbline.left); 34 | store(rightSampleBufferPtr, mainline.right + reverbline.right); 35 | } -------------------------------------------------------------------------------- /4klang/instruments/airy.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(48),DECAY(80),SUSTAIN(64),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(64),FLAGS(NOISE) 3 | GO4K_DST DRIVE(64), SNHFREQ(0), FLAGS(0) 4 | GO4K_VCO TRANSPOSE(16),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) 5 | GO4K_FOP OP(FOP_ADDP) 6 | GO4K_FST AMOUNT(40),DEST(10*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FST AMOUNT(58),DEST(10*MAX_UNIT_SLOTS+5+FST_SET) 8 | GO4K_FOP OP(FOP_POP) 9 | GO4K_VCO TRANSPOSE(88),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 10 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(5),FLAGS(NOISE) 11 | GO4K_VCF FREQUENCY(77),RESONANCE(24),VCFTYPE(BANDPASS) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_PUSH) 14 | GO4K_VCF FREQUENCY(32),RESONANCE(128),VCFTYPE(BANDPASS) 15 | GO4K_FOP OP(FOP_XCH) 16 | GO4K_VCF FREQUENCY(96),RESONANCE(128),VCFTYPE(BANDPASS) 17 | GO4K_FOP OP(FOP_ADDP) 18 | GO4K_FOP OP(FOP_MULP) 19 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 20 | GO4K_PAN PANNING(64) 21 | GO4K_OUT GAIN(64), AUXSEND(64) 22 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/songs/kickbeat.js: -------------------------------------------------------------------------------- 1 | const vierklang = require('../../../4klang/4klang_inc/pattern_tools.js'); 2 | 3 | global.bpm = 120; 4 | global.pattern_size_shift = 4; 5 | // global.looptimes = 100; 6 | 7 | /*soloInstrument('pad1'); 8 | soloInstrument('pad2'); 9 | soloInstrument('pad3');*/ 10 | // soloInstrument('lead1'); 11 | 12 | addInstrument('lead1', ''); 13 | addInstrument('bass', ''); 14 | addInstrument('pad1', ''); 15 | addInstrument('pad2', ''); 16 | addInstrument('pad3', ''); 17 | addInstrumentGroup('pads', ['pad1', 'pad2', 'pad3']); 18 | addInstrument('kick', ''); 19 | addInstrument('snare', ''); 20 | addInstrumentGroup('drums', ['kick', 'snare']); 21 | addInstrument('drivelead', ''); 22 | addInstrument('hihat', ''); 23 | 24 | playPatterns({ 25 | kick: pp(4, [ 26 | 64, , , , 27 | 64, , , , 28 | 64, , , , 29 | 64, , , ,]) 30 | }); 31 | const patterns = vierklang.generatePatterns(); 32 | const instrumentPatternLists = vierklang.generateInstrumentPatternLists(); 33 | 34 | module.exports = { 35 | patterns: patterns, 36 | instrumentPatternLists: instrumentPatternLists 37 | } 38 | 39 | require('fs').writeFileSync('kickbeat.json', JSON.stringify(module.exports)); -------------------------------------------------------------------------------- /wasmaudioworklet/common/scriptloader.js: -------------------------------------------------------------------------------- 1 | export async function loadScript(src) { 2 | return new Promise((resolve) => { 3 | const scriptelement = document.createElement('script'); 4 | scriptelement.onload = () => resolve(); 5 | scriptelement.src = src; 6 | document.documentElement.appendChild(scriptelement); 7 | }); 8 | } 9 | 10 | export async function loadScriptModuleFromText(text) { 11 | return new Promise((resolve) => { 12 | const scriptelement = document.createElement('script'); 13 | scriptelement.onload = () => resolve(); 14 | scriptelement.src = URL.createObjectURL(new Blob([text], {type: "application/javascript"})); 15 | scriptelement.type = "module"; 16 | document.documentElement.appendChild(scriptelement); 17 | }); 18 | } 19 | 20 | export async function loadCSS(href) { 21 | return new Promise((resolve) => { 22 | const head = document.getElementsByTagName('head')[0]; 23 | const link = document.createElement('link'); 24 | link.rel = 'stylesheet'; 25 | link.type = 'text/css'; 26 | link.href = href; 27 | link.media = 'all'; 28 | head.appendChild(link); 29 | link.onload = () => resolve(); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /record.js: -------------------------------------------------------------------------------- 1 | const midi = require('midi'); 2 | 3 | // create a readable stream 4 | var stream1 = midi.createReadStream(); 5 | var input = new midi.input(); 6 | 7 | let inputIndex; 8 | 9 | for(var n=0;n { 25 | 26 | const now = new Date().getTime(); 27 | midimessages.push([ 28 | now - previousTime 29 | , msg]); 30 | previousTime = now; 31 | }); 32 | 33 | process.on('SIGINT', function() { 34 | console.log("Caught interrupt signal"); 35 | for(let n=0;n$NEWVERSION, branch is $BRANCH, not publishing, only dry-run" 29 | npm publish --dry-run 30 | else 31 | npm publish 32 | fi 33 | env: 34 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 35 | -------------------------------------------------------------------------------- /4klang/instruments/PA_Jarresque.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(80),DECAY(128),SUSTAIN(128),RELEASE(80),GAIN(128) 2 | GO4K_VCO TRANSPOSE(16),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) 3 | GO4K_FST AMOUNT(80),DEST(13*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FST AMOUNT(48),DEST(9*MAX_UNIT_SLOTS+4+FST_SET) 5 | GO4K_FST AMOUNT(58),DEST(9*MAX_UNIT_SLOTS+5+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(64),DETUNE(56),PHASE(0),GATES(85),COLOR(4),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_VCO TRANSPOSE(76),DETUNE(72),PHASE(0),GATES(85),COLOR(8),SHAPE(64),GAIN(128),FLAGS(TRISAW) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(16),FLAGS(NOISE) 10 | GO4K_VCF FREQUENCY(105),RESONANCE(18),VCFTYPE(BANDPASS) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_PUSH) 14 | GO4K_VCF FREQUENCY(26),RESONANCE(128),VCFTYPE(BANDPASS) 15 | GO4K_FOP OP(FOP_XCH) 16 | GO4K_VCF FREQUENCY(96),RESONANCE(128),VCFTYPE(BANDPASS) 17 | GO4K_FOP OP(FOP_ADDP) 18 | GO4K_FOP OP(FOP_MULP) 19 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 20 | GO4K_PAN PANNING(64) 21 | GO4K_OUT GAIN(64), AUXSEND(64) 22 | -------------------------------------------------------------------------------- /4klang/instruments/BA_Deepness.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(32),SUSTAIN(112),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(117),FLAGS(SINE) 3 | GO4K_FST AMOUNT(112),DEST(6*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FST AMOUNT(112),DEST(7*MAX_UNIT_SLOTS+5+FST_SET) 5 | GO4K_FST AMOUNT(40),DEST(8*MAX_UNIT_SLOTS+5+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(80),PHASE(80),GATES(85),COLOR(80),SHAPE(64),GAIN(128),FLAGS(TRISAW) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(48),PHASE(48),GATES(85),COLOR(48),SHAPE(64),GAIN(128),FLAGS(TRISAW) 10 | GO4K_FOP OP(FOP_ADDP) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_VCF FREQUENCY(48),RESONANCE(88),VCFTYPE(LOWPASS) 13 | GO4K_VCF FREQUENCY(48),RESONANCE(88),VCFTYPE(LOWPASS) 14 | GO4K_FOP OP(FOP_MULP) 15 | GO4K_PAN PANNING(64) 16 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 17 | GO4K_FOP OP(FOP_XCH) 18 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 19 | GO4K_FOP OP(FOP_XCH) 20 | GO4K_OUT GAIN(0), AUXSEND(96) 21 | -------------------------------------------------------------------------------- /4klang/instruments/PA_JarresqueChorus.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(80),DECAY(128),SUSTAIN(128),RELEASE(80),GAIN(128) 2 | GO4K_VCO TRANSPOSE(16),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) 3 | GO4K_FST AMOUNT(80),DEST(13*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FST AMOUNT(48),DEST(9*MAX_UNIT_SLOTS+4+FST_SET) 5 | GO4K_FST AMOUNT(58),DEST(9*MAX_UNIT_SLOTS+5+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(64),DETUNE(56),PHASE(0),GATES(85),COLOR(4),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_VCO TRANSPOSE(76),DETUNE(72),PHASE(0),GATES(85),COLOR(8),SHAPE(64),GAIN(128),FLAGS(TRISAW) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(16),FLAGS(NOISE) 10 | GO4K_VCF FREQUENCY(105),RESONANCE(18),VCFTYPE(BANDPASS) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_PUSH) 14 | GO4K_VCF FREQUENCY(26),RESONANCE(128),VCFTYPE(BANDPASS) 15 | GO4K_FOP OP(FOP_XCH) 16 | GO4K_VCF FREQUENCY(96),RESONANCE(128),VCFTYPE(BANDPASS) 17 | GO4K_FOP OP(FOP_ADDP) 18 | GO4K_FOP OP(FOP_MULP) 19 | GO4K_DLL PREGAIN(112),DRY(64),FEEDBACK(64),DAMP(64),FREQUENCY(56),DEPTH(48),DELAY(17),COUNT(1) ; ERROR 20 | GO4K_PAN PANNING(64) 21 | GO4K_OUT GAIN(64), AUXSEND(64) 22 | -------------------------------------------------------------------------------- /4klang/instruments/LD_AlphaOmegaMS.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(32),RELEASE(76),GAIN(128) 2 | GO4K_VCO TRANSPOSE(52),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(48),DEST(6*MAX_UNIT_SLOTS+2+FST_SET) 4 | GO4K_FST AMOUNT(80),DEST(7*MAX_UNIT_SLOTS+2+FST_SET) 5 | GO4K_FST AMOUNT(96),DEST(8*MAX_UNIT_SLOTS+5+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(76),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 8 | GO4K_VCO TRANSPOSE(76),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW) 10 | GO4K_FOP OP(FOP_ADDP) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_ENV ATTAC(0),DECAY(80),SUSTAIN(30),RELEASE(64),GAIN(128) 13 | GO4K_FST AMOUNT(96),DEST(14*MAX_UNIT_SLOTS+4+FST_SET) 14 | GO4K_FOP OP(FOP_POP) 15 | GO4K_VCF FREQUENCY(64),RESONANCE(128),VCFTYPE(LOWPASS) 16 | GO4K_FOP OP(FOP_MULP) 17 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 18 | GO4K_PAN PANNING(64) 19 | GO4K_DLL PREGAIN(128),DRY(0),FEEDBACK(0),DAMP(0),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 20 | GO4K_OUT GAIN(64), AUXSEND(32) 21 | -------------------------------------------------------------------------------- /4klang/livereload.js: -------------------------------------------------------------------------------- 1 | const cp = require('child_process'); 2 | const fs = require('fs'); 3 | 4 | let livereloadchild = null; 5 | let childfinishedpromise = null; 6 | 7 | const startChild = () => { 8 | livereloadchild = cp.spawn('./4klangrender', [], { 9 | stdio: ['pipe', 'inherit', 'inherit','ipc'] 10 | }); 11 | childfinishedpromise = new Promise(resolve => 12 | livereloadchild.on('exit', () => resolve()) 13 | ) 14 | }; 15 | 16 | let reloadinprogress = false; 17 | fs.watchFile('./4klang.inc.js', async (curr, prev) => { 18 | if(reloadinprogress) { 19 | return; 20 | } 21 | reloadinprogress = true; 22 | 23 | const previousLiveReloadChild = livereloadchild; 24 | try { 25 | cp.execSync('node 4klang.inc.js'); 26 | cp.execSync('yasm -f macho 4klang.asm'); 27 | cp.execSync('gcc -Wl,-no_pie -m32 4klang.o 4klangrender.c -o 4klangrender'); 28 | previousLiveReloadChild.kill('SIGUSR1'); 29 | await childfinishedpromise; 30 | 31 | startChild(); 32 | console.error('RELOAD'); 33 | } catch(e) { 34 | console.error('Failed to reload'); 35 | } 36 | reloadinprogress = false; 37 | }); 38 | 39 | startChild(); 40 | -------------------------------------------------------------------------------- /pattern/recordedpattern.class.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | module.exports = class RecordedPattern { 3 | constructor(output, midimessages) { 4 | this.output = output; 5 | 6 | if(typeof midimessages === 'string') { 7 | try { 8 | midimessages = JSON.parse(fs.readFileSync(midimessages)); 9 | } catch(e) { 10 | console.log('Unable to open',midimessages,e); 11 | midimessages = []; 12 | } 13 | } 14 | this.midimessages = midimessages; 15 | this.playIndex = 0; 16 | } 17 | 18 | play(msg) { 19 | if(msg) { 20 | this.output.sendMessage(msg); 21 | } else { 22 | this.accumulatedDeltaTime = Date.now(); 23 | this.playIndex = 0; 24 | } 25 | 26 | if(this.playIndex < this.midimessages.length) { 27 | const evt = this.midimessages[this.playIndex++]; 28 | const deltatime = evt[0]; 29 | const nextmessage = evt[1]; 30 | 31 | this.accumulatedDeltaTime += deltatime * 1000; 32 | setTimeout(() => this.play(nextmessage), this.accumulatedDeltaTime - Date.now()); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /4klang/instruments/PA_LongPad.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(74),DECAY(96),SUSTAIN(96),RELEASE(96),GAIN(128) 2 | GO4K_ENV ATTAC(96),DECAY(104),SUSTAIN(104),RELEASE(96),GAIN(64) 3 | GO4K_FST AMOUNT(128),DEST(13*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(28),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) 6 | GO4K_FST AMOUNT(96),DEST(12*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(80),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(56),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW) 10 | GO4K_VCO TRANSPOSE(52),DETUNE(64),PHASE(0),GATES(85),COLOR(32),SHAPE(64),GAIN(128),FLAGS(TRISAW) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_VCF FREQUENCY(53),RESONANCE(128),VCFTYPE(BANDSTOP) 14 | GO4K_VCF FREQUENCY(16),RESONANCE(48),VCFTYPE(LOWPASS) 15 | GO4K_FOP OP(FOP_MULP) 16 | GO4K_PAN PANNING(64) 17 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 18 | GO4K_FOP OP(FOP_XCH) 19 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 20 | GO4K_FOP OP(FOP_XCH) 21 | GO4K_OUT GAIN(64), AUXSEND(64) 22 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/mixes/midi.mix.ts: -------------------------------------------------------------------------------- 1 | import { midichannels, MidiChannel, MidiVoice, SineOscillator, Envelope, notefreq } from './globalimports'; 2 | import { outputline } from '../midi/midisynth'; 3 | import { hardclip } from '../synth/clip'; 4 | 5 | class SimpleSine extends MidiVoice { 6 | osc: SineOscillator = new SineOscillator(); 7 | env: Envelope = new Envelope(0.1, 0.0, 1.0, 0.1); 8 | 9 | noteon(note: u8, velocity: u8): void { 10 | super.noteon(note, velocity); 11 | this.osc.frequency = notefreq(note); 12 | this.env.attack(); 13 | } 14 | 15 | noteoff(): void { 16 | this.env.release(); 17 | } 18 | 19 | isDone(): boolean { 20 | return this.env.isDone(); 21 | } 22 | 23 | nextframe(): void { 24 | const signal = this.osc.next() * this.env.next() * this.velocity / 256; 25 | this.channel.signal.addMonoSignal(signal, 0.2, 0.7); 26 | } 27 | } 28 | 29 | export function initializeMidiSynth(): void { 30 | midichannels[0] = new MidiChannel(6, (channel: MidiChannel) => new SimpleSine(channel)); 31 | } 32 | 33 | export function postprocess(): void { 34 | outputline.left = hardclip(outputline.left); 35 | outputline.right = hardclip(outputline.right); 36 | } -------------------------------------------------------------------------------- /4klang/instruments/PA_Strangeland.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(32),DECAY(88),SUSTAIN(64),RELEASE(92),GAIN(128) 2 | GO4K_VCO TRANSPOSE(38),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(72),DEST(13*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 6 | GO4K_FST AMOUNT(72),DEST(15*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(2),SHAPE(64),GAIN(128),FLAGS(PULSE) 9 | GO4K_VCO TRANSPOSE(76),DETUNE(69),PHASE(0),GATES(85),COLOR(123),SHAPE(64),GAIN(128),FLAGS(PULSE) 10 | GO4K_VCO TRANSPOSE(76),DETUNE(55),PHASE(0),GATES(85),COLOR(24),SHAPE(64),GAIN(128),FLAGS(PULSE) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_PUSH) 14 | GO4K_VCF FREQUENCY(96),RESONANCE(96),VCFTYPE(HIGHPASS) 15 | GO4K_FOP OP(FOP_XCH) 16 | GO4K_VCF FREQUENCY(52),RESONANCE(112),VCFTYPE(BANDPASS) 17 | GO4K_FOP OP(FOP_ADDP) 18 | GO4K_VCF FREQUENCY(48),RESONANCE(128),VCFTYPE(LOWPASS) 19 | GO4K_FOP OP(FOP_MULP) 20 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 21 | GO4K_PAN PANNING(64) 22 | GO4K_OUT GAIN(32), AUXSEND(32) 23 | -------------------------------------------------------------------------------- /4klang/instruments/PA_StrangelandChorus.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(32),DECAY(88),SUSTAIN(64),RELEASE(92),GAIN(128) 2 | GO4K_VCO TRANSPOSE(38),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(72),DEST(13*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 6 | GO4K_FST AMOUNT(72),DEST(15*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(2),SHAPE(64),GAIN(128),FLAGS(PULSE) 9 | GO4K_VCO TRANSPOSE(76),DETUNE(69),PHASE(0),GATES(85),COLOR(123),SHAPE(64),GAIN(128),FLAGS(PULSE) 10 | GO4K_VCO TRANSPOSE(76),DETUNE(55),PHASE(0),GATES(85),COLOR(24),SHAPE(64),GAIN(128),FLAGS(PULSE) 11 | GO4K_FOP OP(FOP_ADDP) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_PUSH) 14 | GO4K_VCF FREQUENCY(96),RESONANCE(96),VCFTYPE(HIGHPASS) 15 | GO4K_FOP OP(FOP_XCH) 16 | GO4K_VCF FREQUENCY(52),RESONANCE(112),VCFTYPE(BANDPASS) 17 | GO4K_FOP OP(FOP_ADDP) 18 | GO4K_VCF FREQUENCY(48),RESONANCE(128),VCFTYPE(LOWPASS) 19 | GO4K_FOP OP(FOP_MULP) 20 | GO4K_DLL PREGAIN(112),DRY(32),FEEDBACK(32),DAMP(32),FREQUENCY(48),DEPTH(64),DELAY(17),COUNT(1) ; ERROR 21 | GO4K_PAN PANNING(64) 22 | GO4K_OUT GAIN(32), AUXSEND(32) 23 | -------------------------------------------------------------------------------- /pattern/playable/basepattern.js: -------------------------------------------------------------------------------- 1 | const Pattern = require('../pattern.class.js'); 2 | const patterns = { 3 | 'basic': [ 4 | [['c3', 100]], 5 | [['c3', 100]], 6 | [['c3', 100]], 7 | [['c3', 100]], 8 | [], 9 | [['c3', 100]], 10 | [], 11 | [['c3', 100]] 12 | ] 13 | }; 14 | 15 | class BasePattern extends Pattern { 16 | constructor(output) { 17 | super(output); 18 | this.stepsperbeat = 4; 19 | this.offset = 0; 20 | this.channel = 3; 21 | } 22 | 23 | async play(patternName, transpose) { 24 | this.offset = Math.round(global.currentBeat()); 25 | 26 | const rows = patterns[patternName]; 27 | 28 | for(let ndx=0;ndx { 33 | this.velocity = note[1]; 34 | this.note(this.toNoteNumber(note[0]) + transpose, 0.5 / this.stepsperbeat); 35 | }); 36 | } 37 | } 38 | } 39 | 40 | module.exports = BasePattern; -------------------------------------------------------------------------------- /wasmaudioworklet/common/ui/modal.html.js: -------------------------------------------------------------------------------- 1 | export const modalTemplate = (modalcontent) => ` 45 |
46 | ${modalcontent} 47 |
`; -------------------------------------------------------------------------------- /4klang/instruments/BA_NotFromThisWorld2.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(88),SUSTAIN(128),RELEASE(6),GAIN(128) 2 | GO4K_VCO TRANSPOSE(16),DETUNE(64),PHASE(88),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(68),DEST(14*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(17),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 6 | GO4K_FST AMOUNT(48),DEST(16*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FOP OP(FOP_POP) 8 | GO4K_VCO TRANSPOSE(28),DETUNE(68),PHASE(0),GATES(85),COLOR(56),SHAPE(32),GAIN(128),FLAGS(SINE) 9 | GO4K_FST AMOUNT(112),DEST(9*MAX_UNIT_SLOTS+4+FST_SET) 10 | GO4K_VCO TRANSPOSE(56),DETUNE(77),PHASE(0),GATES(85),COLOR(23),SHAPE(64),GAIN(128),FLAGS(PULSE) 11 | GO4K_VCO TRANSPOSE(52),DETUNE(72),PHASE(0),GATES(85),COLOR(52),SHAPE(64),GAIN(128),FLAGS(PULSE) 12 | GO4K_FOP OP(FOP_MULP) 13 | GO4K_FOP OP(FOP_MULP) 14 | GO4K_FOP OP(FOP_PUSH) 15 | GO4K_VCF FREQUENCY(28),RESONANCE(64),VCFTYPE(BANDPASS) 16 | GO4K_FOP OP(FOP_XCH) 17 | GO4K_VCF FREQUENCY(64),RESONANCE(64),VCFTYPE(BANDPASS) 18 | GO4K_FOP OP(FOP_ADDP) 19 | GO4K_DST DRIVE(64), SNHFREQ(64), FLAGS(0) 20 | GO4K_FOP OP(FOP_MULP) 21 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(96),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(1),COUNT(8) ; ERROR 22 | GO4K_PAN PANNING(64) 23 | GO4K_OUT GAIN(64), AUXSEND(96) 24 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/fx/allpass.ts: -------------------------------------------------------------------------------- 1 | import { DelayLine } from "./delayline"; 2 | 3 | export class AllPass { 4 | readonly delay_line: DelayLine; 5 | 6 | constructor(delay_length: usize) { 7 | this.delay_line = new DelayLine(delay_length); 8 | } 9 | 10 | tick(input: f32): f32 { 11 | let delayed: f32 = this.delay_line.read(); 12 | let output: f32 = -input + delayed; 13 | 14 | // in the original version of freeverb this is a member which is never modified 15 | const feedback: f32 = 0.5; 16 | 17 | this.delay_line 18 | .write_and_advance(input + delayed * feedback); 19 | 20 | return output; 21 | } 22 | } 23 | 24 | export class AllPassFloat { 25 | coeff: f32; 26 | previousinput: f32; 27 | previousoutput: f32; 28 | 29 | setDelta(delta: f32): void { 30 | this.coeff = (1 - delta) / (1 + delta); 31 | } 32 | 33 | clearBuffers(): void { 34 | this.previousinput = 0; 35 | this.previousoutput = 0; 36 | } 37 | 38 | process(input: f32): f32 { 39 | const output = this.coeff * (input 40 | - this.previousoutput) 41 | + this.previousinput; 42 | this.previousoutput = output; 43 | this.previousinput = input; 44 | return output; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /4klang/instruments/BA_NotFromThisWorld.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(88),SUSTAIN(128),RELEASE(6),GAIN(128) 2 | GO4K_VCO TRANSPOSE(17),DETUNE(64),PHASE(88),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(72),DEST(12*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FST AMOUNT(48),DEST(14*MAX_UNIT_SLOTS+4+FST_SET) 5 | GO4K_FOP OP(FOP_POP) 6 | GO4K_VCO TRANSPOSE(40),DETUNE(67),PHASE(0),GATES(85),COLOR(56),SHAPE(64),GAIN(128),FLAGS(TRISAW) 7 | GO4K_FST AMOUNT(112),DEST(7*MAX_UNIT_SLOTS+4+FST_SET) 8 | GO4K_VCO TRANSPOSE(68),DETUNE(77),PHASE(0),GATES(85),COLOR(23),SHAPE(124),GAIN(128),FLAGS(SINE) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(72),PHASE(0),GATES(85),COLOR(52),SHAPE(124),GAIN(128),FLAGS(SINE) 10 | GO4K_FOP OP(FOP_MULP) 11 | GO4K_FOP OP(FOP_MULP) 12 | GO4K_FOP OP(FOP_PUSH) 13 | GO4K_VCF FREQUENCY(32),RESONANCE(64),VCFTYPE(BANDPASS) 14 | GO4K_FOP OP(FOP_XCH) 15 | GO4K_VCF FREQUENCY(96),RESONANCE(79),VCFTYPE(BANDPASS) 16 | GO4K_FOP OP(FOP_ADDP) 17 | GO4K_VCF FREQUENCY(19),RESONANCE(37),VCFTYPE(HIGHPASS) 18 | GO4K_FOP OP(FOP_MULP) 19 | GO4K_PAN PANNING(64) 20 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 21 | GO4K_FOP OP(FOP_XCH) 22 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 23 | GO4K_FOP OP(FOP_XCH) 24 | GO4K_OUT GAIN(128), AUXSEND(128) 25 | -------------------------------------------------------------------------------- /LICENSE-WITHOUT-YOSHIMI: -------------------------------------------------------------------------------- 1 | NOTE: 2 | You may use this license only if you remove all references to 3 | Yoshimi which requires the GPL license. 4 | 5 | ============================================================== 6 | 7 | MIT License 8 | 9 | Copyright (c) 2018-2021 Peter Johan Salomonsen 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | -------------------------------------------------------------------------------- /midi/output.js: -------------------------------------------------------------------------------- 1 | const midi = require('midi'); 2 | 3 | // Set up a new output. 4 | const output = new midi.output(); 5 | let outputIndex; 6 | for(var n=0;n { 13 | output.muted = channels; 14 | }; 15 | output.solo = (channels) => { 16 | output.soloed = channels; 17 | }; 18 | output.sendMessage = (msg) => { 19 | if(output.muted && output.muted.findIndex((channel) => (msg[0] & 0xf) === channel) > -1) { 20 | 21 | // Muted - do nothing 22 | } else if(!output.soloed || output.soloed.findIndex((channel) => (msg[0] & 0xf) === channel) > -1){ 23 | output._sendMessage(msg); 24 | } 25 | }; 26 | 27 | output.openPort(outputIndex); 28 | 29 | global.shutdownhooks = [ 30 | () => { 31 | console.log("All notes off"); 32 | for(let n=0;n<16;n++) { 33 | output.sendMessage([0xb0 + n, 123, 0]); 34 | } 35 | } 36 | ]; 37 | process.on('SIGINT', function() { 38 | global.shutdownhooks.forEach(shutdownhook => { 39 | shutdownhook(); 40 | }); 41 | process.exit(); 42 | 43 | }); 44 | 45 | module.exports = output; -------------------------------------------------------------------------------- /4klang/instruments/LD_Morpher.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(0),SUSTAIN(128),RELEASE(64),GAIN(128) 2 | GO4K_VCO TRANSPOSE(40),DETUNE(64),PHASE(96),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(72),DEST(14*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(32),DETUNE(64),PHASE(96),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 6 | GO4K_FST AMOUNT(80),DEST(16*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FST AMOUNT(80),DEST(8*MAX_UNIT_SLOTS+5+FST_SET) 8 | GO4K_FOP OP(FOP_POP) 9 | GO4K_VCO TRANSPOSE(64),DETUNE(56),PHASE(0),GATES(85),COLOR(44),SHAPE(64),GAIN(128),FLAGS(PULSE) 10 | GO4K_VCO TRANSPOSE(64),DETUNE(76),PHASE(0),GATES(85),COLOR(24),SHAPE(64),GAIN(64),FLAGS(TRISAW) 11 | GO4K_VCO TRANSPOSE(64),DETUNE(56),PHASE(0),GATES(85),COLOR(95),SHAPE(64),GAIN(128),FLAGS(TRISAW) 12 | GO4K_FOP OP(FOP_ADDP) 13 | GO4K_FOP OP(FOP_ADDP) 14 | GO4K_FOP OP(FOP_PUSH) 15 | GO4K_VCF FREQUENCY(16),RESONANCE(32),VCFTYPE(BANDPASS) 16 | GO4K_FOP OP(FOP_XCH) 17 | GO4K_VCF FREQUENCY(80),RESONANCE(64),VCFTYPE(BANDPASS) 18 | GO4K_FOP OP(FOP_ADDP) 19 | GO4K_VCF FREQUENCY(20),RESONANCE(48),VCFTYPE(PEAK) 20 | GO4K_VCF FREQUENCY(80),RESONANCE(80),VCFTYPE(PEAK) 21 | GO4K_FOP OP(FOP_MULP) 22 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 23 | GO4K_PAN PANNING(64) 24 | GO4K_OUT GAIN(64), AUXSEND(16) 25 | -------------------------------------------------------------------------------- /songs/lead.json: -------------------------------------------------------------------------------- 1 | [[0.02,[149,69,60]],[0.027,[149,68,0]],[0.18,[149,69,0]],[0.384,[149,69,81]],[0.329,[149,69,0]],[0.269,[149,67,80]],[0.447,[149,67,0]],[0.105,[149,64,60]],[0.297,[149,64,0]],[0.319,[149,63,84]],[0.04,[149,64,61]],[0.03,[149,63,0]],[0.194,[149,64,0]],[0.036,[149,62,60]],[0.126,[149,62,0]],[0.056,[149,60,46]],[0.091,[149,60,0]],[0.034,[149,62,74]],[0.168,[149,62,0]],[0.037,[149,60,64]],[0.089,[149,60,0]],[0.022,[149,57,71]],[0.129,[149,57,0]],[0.069,[149,55,61]],[0.069,[149,57,62]],[0.016,[149,55,0]],[0.169,[149,57,0]],[0.027,[149,60,75]],[0.087,[149,60,0]],[0.213,[149,62,71]],[0.089,[149,62,0]],[0.199,[149,60,55]],[0.096,[149,60,0]],[0.009,[149,62,74]],[0.12,[149,62,0]],[0.076,[149,60,57]],[0.095,[149,60,0]],[0.025,[149,63,69]],[0.001,[149,62,44]],[0.014,[149,63,0]],[0.008,[149,62,0]],[0.01,[149,64,74]],[0.28,[149,64,0]],[0.275,[149,67,79]],[0.289,[149,67,0]],[0.28,[149,67,39]],[0.014,[149,68,65]],[0.012,[149,67,0]],[0.027,[149,69,69]],[0.005,[149,68,0]],[0.319,[149,69,0]],[1.169,[149,75,81]],[0.041,[149,76,59]],[0.037,[149,75,0]],[0.089,[149,76,0]],[0.135,[149,74,75]],[0.154,[149,74,0]],[0.052,[149,72,52]],[0.104,[149,72,0]],[0.179,[149,74,82]],[0.106,[149,74,0]],[0.187,[149,74,41]],[0.101,[149,74,0]],[0.034,[149,72,51]],[0.103,[149,72,0]],[0.056,[149,69,57]],[0.109,[149,69,0]],[0.035,[149,67,76]],[0.144,[149,67,0]],[0.004,[149,68,78]],[0.057,[149,69,56]],[0.005,[149,68,0]],[0.16,[149,69,0]]] -------------------------------------------------------------------------------- /songs/upbeatintrolead.json: -------------------------------------------------------------------------------- 1 | [[0.757,[154,62,127]],[0.087,[138,62,0]],[0.177,[154,67,127]],[0.137,[138,67,0]],[0.205,[154,62,127]],[0.113,[138,62,0]],[0.243,[154,67,127]],[0.132,[138,67,0]],[0.754,[154,62,127]],[0.086,[138,62,0]],[0.032,[154,67,127]],[0.119,[138,67,0]],[0.018,[154,62,127]],[0.105,[138,62,0]],[0.02,[154,69,127]],[0.29,[138,69,0]],[0.05,[154,67,127]],[0.338,[138,67,0]],[0.053,[154,65,127]],[0.54,[138,65,0]],[0.45,[154,64,127]],[0.104,[138,64,0]],[0.154,[154,64,127]],[0.064,[154,65,127]],[0.013,[138,64,0]],[0.153,[138,65,0]],[0.13,[154,64,127]],[0.19,[138,64,0]],[0.22,[154,62,127]],[1.219,[138,62,0]],[1.812,[154,62,127]],[0.089,[138,62,0]],[0.149,[154,67,127]],[0.158,[138,67,0]],[0.206,[154,69,127]],[0.124,[138,69,0]],[0.198,[154,69,127]],[0,[154,71,127]],[0.072,[138,69,0]],[0.534,[138,71,0]],[0.43,[154,71,127]],[0.052,[154,72,127]],[0.025,[138,71,0]],[0.101,[138,72,0]],[0.094,[154,71,127]],[0.28,[138,71,0]],[0.112,[154,69,127]],[0.198,[138,69,0]],[0.163,[154,67,127]],[0.147,[138,67,0]],[0.103,[154,74,127]],[0.362,[138,74,0]],[0.274,[154,72,127]],[0.169,[138,72,0]],[0.181,[154,71,127]],[0.166,[138,71,0]],[0.201,[154,72,127]],[0.171,[138,72,0]],[0.151,[154,71,127]],[0.221,[138,71,0]],[0.09,[154,69,127]],[0.585,[138,69,0]],[0.391,[154,71,127]],[0.038,[154,72,127]],[0.026,[138,71,0]],[0.16,[138,72,0]],[0.096,[154,71,127]],[0.127,[138,71,0]],[0.131,[154,69,127]],[0.168,[138,69,0]],[0.036,[154,67,127]],[0.086,[138,67,0]]] -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/songs/shufflebeat.js: -------------------------------------------------------------------------------- 1 | global.bpm = 80; 2 | global.pattern_size_shift = 3; 3 | global.beats_per_pattern_shift = 1; 4 | calculatePatternSize(); 5 | // global.looptimes = 100; 6 | 7 | 8 | 9 | // soloInstrument('sinelead'); 10 | // global.WASM_SYNTH_LOCATION = 'https://gist.githubusercontent.com/petersalomonsen/8ed949e2cfada00b82845828e415a8b8/raw/15404228b2c4bebf79f5f2a18dcecc50d0fb8721/synth.wasm'; 11 | 12 | var pianoVoices = []; 13 | for(let n=1;n<=8;n++) { 14 | pianoVoices.push('piano'+n); 15 | addInstrument('piano'+n, {type: 'note'}); 16 | } 17 | addInstrumentGroup('piano', pianoVoices); 18 | addInstrument('kick', {type: 'number'}); 19 | addInstrument('snare', {type: 'number'}); 20 | addInstrument('hihat', {type: 'number'}); 21 | addInstrument('bass', {type: 'note'}); 22 | addInstrument('eftang', {type: 'note'}); 23 | var padVoices = []; 24 | for(let n=1;n<=8;n++) { 25 | padVoices.push('pad'+n); 26 | addInstrument('pad'+n, {type: 'note'}); 27 | } 28 | addInstrumentGroup('pads', padVoices); 29 | addInstrument('sinelead', {type: 'note'}); 30 | 31 | playPatterns({ 32 | hihat: pp(4, [ 33 | 60,,30,60,,30, 34 | 60,,30,60,,30, 35 | 60,,30,60,,30, 36 | 60,,30,60,,30 37 | ]), 38 | kick: pp(4, [ 39 | 60,,,,,, 40 | ,,,,,, 41 | ,,,60,,20, 42 | ]), 43 | snare: pp(4, [ 44 | ,,,,,,60 45 | ,,,,,40, 46 | ,,10,,,,60 47 | ,,,,,20 48 | ]) 49 | }); -------------------------------------------------------------------------------- /wasmaudioworklet/visualizer/videoscheduler.js: -------------------------------------------------------------------------------- 1 | let videoSchedule = []; 2 | 3 | export function setVideoSchedule(schedule) { 4 | videoSchedule = schedule; 5 | } 6 | 7 | export function exportVideoSchedule() { 8 | const exportObj = videoSchedule.map(sch => ({ 9 | startTime: sch.startTime, 10 | stopTime: sch.stopTime, 11 | clipStartTime: sch.clipStartTime, 12 | videoUrl: sch.video.videoElement?.src ?? undefined, 13 | imageUrl: sch.video.imageElement?.src ?? undefined, 14 | })); 15 | return JSON.stringify(exportObj, null, 1); 16 | } 17 | 18 | export function getActiveVideo(milliseconds) { 19 | const activeSchedule = videoSchedule.find(sch => { 20 | if (sch.startTime <= milliseconds && (!sch.stopTime || sch.stopTime > milliseconds)) { 21 | return true; 22 | } else { 23 | return false; 24 | } 25 | }); 26 | if (activeSchedule) { 27 | if (activeSchedule.video.AsyncFunctionvideoElement) { 28 | activeSchedule.video.videoElement.currentTime = ((milliseconds - activeSchedule.startTime + activeSchedule.clipStartTime) / 1000).toFixed(2); 29 | return activeSchedule.video.videoElement; 30 | } else if (activeSchedule.video.imageElement && activeSchedule.video.imageElement.complete) { 31 | return activeSchedule.video.imageElement; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /4klang/instruments/LD_Farscape.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(80),DECAY(64),SUSTAIN(96),RELEASE(80),GAIN(128) 2 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(NOISE) 3 | GO4K_DST DRIVE(64), SNHFREQ(0), FLAGS(0) 4 | GO4K_FST AMOUNT(128),DEST(8*MAX_UNIT_SLOTS+4+FST_SET) 5 | GO4K_FOP OP(FOP_POP) 6 | GO4K_VCO TRANSPOSE(30),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) 7 | GO4K_FST AMOUNT(95),DEST(18*MAX_UNIT_SLOTS+2+FST_SET) 8 | GO4K_FOP OP(FOP_POP) 9 | GO4K_VCO TRANSPOSE(10),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(TRISAW|LFO) 10 | GO4K_FST AMOUNT(88),DEST(12*MAX_UNIT_SLOTS+2+FST_SET) 11 | GO4K_FOP OP(FOP_POP) 12 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE) 13 | GO4K_VCO TRANSPOSE(64),DETUNE(65),PHASE(64),GATES(85),COLOR(64),SHAPE(32),GAIN(128),FLAGS(SINE) 14 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(64),GATES(85),COLOR(64),SHAPE(64),GAIN(6),FLAGS(NOISE) 15 | GO4K_VCF FREQUENCY(64),RESONANCE(64),VCFTYPE(LOWPASS) 16 | GO4K_FOP OP(FOP_ADDP) 17 | GO4K_FOP OP(FOP_ADDP) 18 | GO4K_VCF FREQUENCY(54),RESONANCE(4),VCFTYPE(LOWPASS) 19 | GO4K_DST DRIVE(64), SNHFREQ(128), FLAGS(0) 20 | GO4K_FOP OP(FOP_MULP) 21 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 22 | GO4K_PAN PANNING(64) 23 | GO4K_OUT GAIN(64), AUXSEND(32) 24 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/__tests__/synth/pan.spec.ts: -------------------------------------------------------------------------------- 1 | import { Pan } from "../../synth/pan.class"; 2 | 3 | describe('pan', () => { 4 | it('should center pan', () => { 5 | const pan = new Pan(); 6 | pan.setPan(0.5); 7 | expect(pan.leftLevel).toBeCloseTo(NativeMathf.sqrt(1/2)); 8 | expect(pan.rightLevel).toBeCloseTo(NativeMathf.sqrt(1/2)); 9 | }); 10 | it('should pan left', () => { 11 | const pan = new Pan(); 12 | pan.setPan(0.0); 13 | expect(pan.leftLevel).toBeCloseTo(1.0); 14 | expect(pan.rightLevel).toBeCloseTo(0); 15 | }); 16 | it('should pan right', () => { 17 | const pan = new Pan(); 18 | pan.setPan(1.0); 19 | expect(pan.leftLevel).toBeCloseTo(0); 20 | expect(pan.rightLevel).toBeCloseTo(1.0); 21 | }); 22 | it('should pan medium left', () => { 23 | const pan = new Pan(); 24 | pan.setPan(0.25); 25 | expect(pan.leftLevel).toBeGreaterThan(NativeMathf.sqrt(1/2)); 26 | expect(pan.rightLevel).toBeLessThan(NativeMathf.sqrt(1/2)); 27 | }); 28 | it('should pan medium right', () => { 29 | const pan = new Pan(); 30 | pan.setPan(0.75); 31 | expect(pan.rightLevel).toBeGreaterThan(NativeMathf.sqrt(1/2)); 32 | expect(pan.leftLevel).toBeLessThan(NativeMathf.sqrt(1/2)); 33 | }); 34 | }); -------------------------------------------------------------------------------- /4klang/instruments/PA_SynastasiaMS.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(64),DECAY(70),SUSTAIN(70),RELEASE(88),GAIN(128) 2 | GO4K_VCO TRANSPOSE(69),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(72),DEST(11*MAX_UNIT_SLOTS+2+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(73),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 6 | GO4K_FST AMOUNT(56),DEST(12*MAX_UNIT_SLOTS+2+FST_SET) 7 | GO4K_FST AMOUNT(56),DEST(12*MAX_UNIT_SLOTS+5+FST_SET) 8 | GO4K_FOP OP(FOP_POP) 9 | GO4K_VCO TRANSPOSE(77),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 10 | GO4K_FST AMOUNT(72),DEST(13*MAX_UNIT_SLOTS+2+FST_SET) 11 | GO4K_FOP OP(FOP_POP) 12 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 13 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 14 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(0),SHAPE(64),GAIN(128),FLAGS(TRISAW) 15 | GO4K_FOP OP(FOP_ADDP) 16 | GO4K_FOP OP(FOP_ADDP) 17 | GO4K_VCF FREQUENCY(104),RESONANCE(96),VCFTYPE(LOWPASS) 18 | GO4K_FOP OP(FOP_MULP) 19 | GO4K_DLL PREGAIN(64),DRY(64),FEEDBACK(64),DAMP(0),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 20 | GO4K_PAN PANNING(64) 21 | GO4K_DLL PREGAIN(128),DRY(0),FEEDBACK(0),DAMP(0),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 22 | GO4K_OUT GAIN(16), AUXSEND(64) 23 | -------------------------------------------------------------------------------- /songs/yoshimibrowsertest.js: -------------------------------------------------------------------------------- 1 | // SONGMODE=YOSHIMI 2 | setBPM(120); 3 | const drums = new TrackerPattern(output, 1, 4); 4 | const base = new TrackerPattern(output, 7, 4); 5 | const pad = new TrackerPattern(output, 0, 4); 6 | 7 | const drumtrack = async () => drums.steps(4,[ 8 | [c3,gs3(1,40)],,gs3(1,20),, 9 | [d3(1,127),gs3(1,40)],,gs3(1,20),, 10 | [c3,gs3(1,40)],,[c3,gs3(1,40)],, 11 | [d3(1,127),gs3(1,40)],,[c3,gs3(1,40)],, 12 | ]); 13 | 14 | for(let n=0;n<2;n++) { 15 | pad.steps(2, [ 16 | ,a5,d6,f6, 17 | e6,,c6,d6 18 | ]); 19 | 20 | base.steps(4, [ 21 | d3,,d4,,f3,,a4,, 22 | a2,,a3,,c3,,c4 23 | ]); 24 | 25 | await drumtrack() 26 | 27 | base.steps(4, [ 28 | d3,,d4,,f3,,a4,, 29 | a2,,a3,,c3,,c4 30 | ]); 31 | 32 | await drumtrack(); 33 | } 34 | 35 | 36 | pad.steps(2, [ 37 | ,g5,c6,g5, 38 | f6,,e6,c6 39 | ]); 40 | 41 | base.steps(4, [ 42 | c3,,c4,,e3,,g4,, 43 | g2,,g3,,as3,,as4 44 | ]); 45 | 46 | await drumtrack(); 47 | base.steps(4, [ 48 | c3,,c4,,e3,,g4,, 49 | g2,,g3,,as3,,as4 50 | ]); 51 | 52 | await drumtrack(); 53 | 54 | pad.steps(2, [ 55 | ,a5,d6,f6, 56 | e6,,c6,d6 57 | ]); 58 | 59 | base.steps(4, [ 60 | d3,,d4,,f3,,a4,, 61 | a2,,a3,,c3,,c4 62 | ]); 63 | 64 | await drumtrack(); 65 | 66 | base.steps(4, [ 67 | d3,,d4,,f3,,a4,, 68 | a2,,a3,,c3,,c4,d4(1/8) 69 | ]); 70 | 71 | await drumtrack(); 72 | 73 | loopHere(); 74 | -------------------------------------------------------------------------------- /wasmaudioworklet/analyser/levelanalyserprocessor.js: -------------------------------------------------------------------------------- 1 | class LevelAnalyserProcessor extends AudioWorkletProcessor { 2 | stats = { 3 | clips: [] 4 | }; 5 | 6 | position = 0; 7 | 8 | constructor() { 9 | super(); 10 | 11 | this.port.onmessage = async (msg) => { 12 | if (msg.data.stats) { 13 | this.port.postMessage(this.stats); 14 | this.stats.clips = []; 15 | } 16 | }; 17 | } 18 | 19 | process(inputs, outputs, parameters) { 20 | const numChannels = inputs[0].length; 21 | const threshold = 1.0; 22 | 23 | for (let ch = 0; ch < numChannels; ch++) { 24 | const channeldata = inputs[0][ch]; 25 | for (let n = 0; n < channeldata.length; n++) { 26 | if (Math.abs(channeldata[n]) > threshold) { 27 | this.stats.clips.push({ 28 | channel: ch, 29 | position: this.position + n, 30 | time: ((this.position + n) / sampleRate), 31 | currentTime: currentTime, 32 | value: channeldata[n] 33 | }); 34 | } 35 | 36 | } 37 | } 38 | this.position += inputs[0][0].length; 39 | return true; 40 | } 41 | } 42 | 43 | registerProcessor('levelanalyserprocessor', LevelAnalyserProcessor); 44 | -------------------------------------------------------------------------------- /pattern/playable/arpeggiato1.js: -------------------------------------------------------------------------------- 1 | const Pattern = require('../pattern.class.js'); 2 | 3 | class Arpeggiator extends Pattern { 4 | constructor(output) { 5 | super(output); 6 | this.stepsperbeat = 4; 7 | this.offset = 0; 8 | this.channel = 4; 9 | } 10 | async play(variation, basenote) { 11 | 12 | const variations = [ 13 | [0, 2, 3, 7, 12, 7, 3, 2], 14 | [0, 2, 4, 7, 12, 7, 4, 2], 15 | [4, 5, 7, 12, 7, 5, 4, 0], 16 | [3, 5, 7, 12, 7, 5, 3, 0], 17 | [-5, 0, 2, 4, 7, 4, 2, 0], 18 | [-5, 0, 2, 3, 7, 3, 2, 0], 19 | [-5, 0, 5, 7, 5, 0, -5, 0] 20 | ]; 21 | 22 | const notes = variations[variation]; 23 | 24 | for(let ndx=0;ndx { 30 | await this.waitForStep(ndx + 2); 31 | this.velocity = Math.floor(this.velocity * (1/3)); 32 | this.note(note+12, 1 / this.stepsperbeat * 2); 33 | })(); 34 | } 35 | this.offset += notes.length / this.stepsperbeat; 36 | } 37 | } 38 | 39 | module.exports = Arpeggiator; -------------------------------------------------------------------------------- /wasmaudioworklet/common/ui/modal.js: -------------------------------------------------------------------------------- 1 | import { modalTemplate } from './modal.html.js'; 2 | 3 | customElements.define('common-modal', 4 | class extends HTMLElement { 5 | constructor() { 6 | super(); 7 | this.attachShadow({mode: 'open'}); 8 | this.resultPromise = new Promise(resolve => this.shadowRoot.result = resolve); 9 | } 10 | }); 11 | 12 | export async function modal(modalContent) { 13 | const modalElement = document.createElement('common-modal'); 14 | modalElement.shadowRoot.innerHTML = modalTemplate(modalContent); 15 | document.documentElement.appendChild(modalElement); 16 | const result = await modalElement.resultPromise; 17 | document.documentElement.removeChild(modalElement); 18 | return result; 19 | } 20 | 21 | export async function modalYesNo(title, question) { 22 | return modal(` 23 |

${title}

24 |

${question}

25 |

26 | 27 | 28 |

`); 29 | } 30 | 31 | export async function modalOkCancel(title, info, cancelText, okText) { 32 | return modal(` 33 |

${title}

34 |

${info}

35 |

36 | 37 | 38 |

`); 39 | } -------------------------------------------------------------------------------- /wasmaudioworklet/analyser/levelanalyser.spec.js: -------------------------------------------------------------------------------- 1 | import { connectLevelAnalyser, skipClipsWithinCentiSeconds } from './levelanalysernode.js'; 2 | 3 | describe('levelanalyser', async function () { 4 | this.timeout(10000); 5 | let stats; 6 | it('should analyse levels of audio input', async () => { 7 | const renderSampleRate = 44100; 8 | const duration = 5; 9 | const offlineCtx = new OfflineAudioContext(2, 10 | duration * renderSampleRate, 11 | renderSampleRate); 12 | 13 | const oscNode = new OscillatorNode(offlineCtx, { 14 | frequency: 440, channelCount: 2, 15 | type: "sine" 16 | }); 17 | oscNode.start(0); 18 | 19 | const gainNode = offlineCtx.createGain(); 20 | gainNode.gain.setValueAtTime(1.1, 0); 21 | oscNode.connect(gainNode); 22 | 23 | const getStats = await connectLevelAnalyser(gainNode); 24 | 25 | await offlineCtx.startRendering(); 26 | 27 | stats = await getStats(); 28 | console.log('found', stats.clips.length, 'clips. latest timestamp:', 29 | stats.clips[stats.clips.length - 1].currentTime, stats.clips[stats.clips.length - 1].time); 30 | 31 | assert(stats.clips.length > 0); 32 | }); 33 | it('display clips at least 10 milliseconds apart', async () => { 34 | expect(skipClipsWithinCentiSeconds(stats.clips).length).lessThan(stats.clips.length / 100); 35 | }); 36 | }); -------------------------------------------------------------------------------- /midi/recorder.js: -------------------------------------------------------------------------------- 1 | module.exports = function(recordingFileName) { 2 | const midi = require('midi'); 3 | 4 | // create a readable stream 5 | var stream1 = midi.createReadStream(); 6 | var input = new midi.input(); 7 | 8 | let inputIndex; 9 | 10 | for(var n=0;n { 26 | 27 | const now = new Date().getTime(); 28 | midimessages.push([ 29 | now - previousTime 30 | , msg]); 31 | previousTime = now; 32 | console.log(msg); 33 | }); 34 | 35 | global.shutdownhooks.push(() => { 36 | if(midimessages.length > 0) { 37 | console.log("Store recording"); 38 | 39 | for(let n=0;n { 7 | const binaryData = new Uint8Array(16 * 1024 + Math.random() * 32 * 1024); 8 | binaryData.forEach((v, n, arr) => arr[n] = Math.random() * 255); 9 | const encodedDataUrl = encodeBufferAsPNG(binaryData); 10 | 11 | const afterDecoded = await decodeBufferFromPNG(encodedDataUrl); 12 | assert.equal(afterDecoded.length, binaryData.length); 13 | 14 | for (let n = 0; n < binaryData.length; n++) { 15 | assert.equal(afterDecoded[n], binaryData[n], `data not matching at index ${n}`); 16 | } 17 | }); 18 | it("should encode larger binary content as png, and be able to decode it back again", async () => { 19 | const binaryData = new Uint8Array(128 * 1024 + Math.random() * 128 * 1024); 20 | binaryData.forEach((v, n, arr) => arr[n] = Math.random() * 255); 21 | const encodedDataUrl = encodeBufferAsPNG(binaryData); 22 | 23 | const afterDecoded = await decodeBufferFromPNG(encodedDataUrl); 24 | assert.equal(afterDecoded.length, binaryData.length); 25 | 26 | for (let n = 0; n < binaryData.length; n++) { 27 | assert.equal(afterDecoded[n], binaryData[n], `data not matching at index ${n}`); 28 | } 29 | }); 30 | }); -------------------------------------------------------------------------------- /4klang/README.md: -------------------------------------------------------------------------------- 1 | Programming music for 4klang using Javascript 2 | ============================================= 3 | 4 | [4klang](https://github.com/hzdgopher/4klang/) is a tiny but powerful synthesizer written for making music to fit in 4KB demos. When exporting a song using the VSTi the result is a `4klang.inc` and `4klang.asm` file to be assembled with [yasm](https://yasm.tortall.net/), and this project export the very same files, but instead they are generated by writing your song in Javascript. 5 | 6 | The Javascript api contains simple functions for creating patterns and playing notes, and you can use your programming skills to create loops, generate fancy patterns or whatever. 7 | 8 | You'll find example songs in [songs](songs) folder. So if you e.g. want to test the song [groove is in the code](songs/grooveisinthecode.inc.js), you just symlink it to `4klang.inc.js` in this directory. 9 | 10 | The [run.sh](run.sh) script is currently tuned for Mac OSX, and there's another [runlinux.sh](runlinux.sh) that works for Linux. It will use nodejs to generate `4klang.inc` which then will be assembled with [4klang.asm](4klang.asm) to create an object file to be linked with [4klangrender.c](4klangrender.c) which will create an executable that creates raw audio data. By piping it into [SoX](http://sox.sourceforge.net/) you'll get audio output or you can generate a wav file. 11 | 12 | Here's the generated audio output from the example song [Groove is in the code](https://soundcloud.com/psalomo/groove-is-in-the-code-4klang-mix) -------------------------------------------------------------------------------- /4klang/instruments/PA_Minorium.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(0),DECAY(64),SUSTAIN(88),RELEASE(48),GAIN(128) 2 | GO4K_VCO TRANSPOSE(16),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 3 | GO4K_FST AMOUNT(48),DEST(10*MAX_UNIT_SLOTS+4+FST_SET) 4 | GO4K_FOP OP(FOP_POP) 5 | GO4K_VCO TRANSPOSE(8),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 6 | GO4K_FST AMOUNT(32),DEST(12*MAX_UNIT_SLOTS+4+FST_SET) 7 | GO4K_FST AMOUNT(68),DEST(11*MAX_UNIT_SLOTS+2+FST_SET) 8 | GO4K_FST AMOUNT(88),DEST(14*MAX_UNIT_SLOTS+4+FST_SET) 9 | GO4K_FOP OP(FOP_POP) 10 | GO4K_VCO TRANSPOSE(67),DETUNE(64),PHASE(0),GATES(85),COLOR(16),SHAPE(64),GAIN(72),FLAGS(TRISAW) 11 | GO4K_VCF FREQUENCY(64),RESONANCE(128),VCFTYPE(LOWPASS) 12 | GO4K_VCO TRANSPOSE(71),DETUNE(64),PHASE(0),GATES(85),COLOR(16),SHAPE(64),GAIN(64),FLAGS(TRISAW) 13 | GO4K_VCF FREQUENCY(64),RESONANCE(128),VCFTYPE(LOWPASS) 14 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(16),SHAPE(64),GAIN(96),FLAGS(TRISAW) 15 | GO4K_VCF FREQUENCY(64),RESONANCE(128),VCFTYPE(LOWPASS) 16 | GO4K_FOP OP(FOP_ADDP) 17 | GO4K_FOP OP(FOP_ADDP) 18 | GO4K_ENV ATTAC(88),DECAY(88),SUSTAIN(96),RELEASE(96),GAIN(128) 19 | GO4K_FST AMOUNT(88),DEST(20*MAX_UNIT_SLOTS+4+FST_SET) 20 | GO4K_FOP OP(FOP_POP) 21 | GO4K_VCF FREQUENCY(32),RESONANCE(16),VCFTYPE(BANDSTOP) 22 | GO4K_FOP OP(FOP_MULP) 23 | GO4K_DLL PREGAIN(48),DRY(96),FEEDBACK(48),DAMP(128),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 24 | GO4K_PAN PANNING(64) 25 | GO4K_OUT GAIN(128), AUXSEND(96) 26 | -------------------------------------------------------------------------------- /wasmaudioworklet/wasmgit/commitmessagemodal.html: -------------------------------------------------------------------------------- 1 | 46 |
47 | Enter commit message
48 | 49 |
50 |
51 |

52 |     
53 | 54 | 55 |
-------------------------------------------------------------------------------- /tools/livecodingscheduler.js: -------------------------------------------------------------------------------- 1 | const cp = require('child_process'); 2 | const readline = require('readline'); 3 | 4 | const rl = readline.createInterface({ 5 | input: process.stdin, 6 | output: process.stdout 7 | }); 8 | 9 | let livereloadchild = null; 10 | 11 | function enableLiveRestart(beatInterval = 4) { 12 | if(process.argv.findIndex(arg => arg === '--livereloadchild') > -1) { 13 | return false; 14 | } else { 15 | rl.question('Press enter to restart at next beat', () => { 16 | const nextBeat = (Math.floor(global.currentBeat() / beatInterval) * beatInterval) + beatInterval; 17 | const nextBeatStartTime = global.beatToTime(nextBeat); 18 | 19 | if(livereloadchild) { 20 | const previousLiveReloadChild = livereloadchild; 21 | setTimeout(() => { 22 | previousLiveReloadChild.kill('SIGINT'); 23 | console.log('\nReload'); 24 | }, nextBeatStartTime - new Date().getTime()); 25 | } 26 | livereloadchild = cp.fork(process.argv[1], ['--livereloadchild', '--starttime', nextBeatStartTime], 27 | { 28 | stdio: ['pipe', 'inherit', 'inherit','ipc'] 29 | } 30 | ); 31 | setTimeout(() => enableLiveRestart(), 0); 32 | }); 33 | return true; 34 | } 35 | } 36 | 37 | module.exports = { 38 | enableLiveRestart: enableLiveRestart 39 | } -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/__tests__/fx/multibandeq.spec.ts: -------------------------------------------------------------------------------- 1 | import { MultiBandEQ } from '../../fx/multibandeq'; 2 | import { SineOscillator } from '../../synth/sineoscillator.class'; 3 | import { TriBandEQ } from '../../fx/tribandeq'; 4 | 5 | describe('multibandeq', () => { 6 | it('should create eq with 3 bands', () => { 7 | const eq = new MultiBandEQ([20, 200, 400, 600]); 8 | const eq2 = new TriBandEQ(20, 200, 400, 600); 9 | const osc = new SineOscillator(); 10 | osc.frequency = 440; 11 | 12 | let peak1: f32 = 0; 13 | for (let frame = 0; frame < 1000; frame++) { 14 | const nextosc = osc.next(); 15 | const val = eq.process(nextosc, [1.0, 0.0, 0.0]); 16 | const val2 = eq2.process(nextosc, 1.0, 0.0, 0.0); 17 | 18 | expect(val).toBe(val2); 19 | 20 | const abs = Mathf.abs(val); 21 | if (abs > peak1) { 22 | peak1 = abs; 23 | } 24 | } 25 | expect(peak1).toBeLessThan(0.2); 26 | 27 | let peak2: f32 = 0; 28 | for (let frame = 0; frame < 1000; frame++) { 29 | const nextosc = osc.next(); 30 | const val = eq.process(nextosc, [0.0, 0.0, 1.0]); 31 | const val2 = eq2.process(nextosc, 0.0, 0.0, 1.0); 32 | expect(val).toBe(val2); 33 | 34 | const abs = Mathf.abs(val); 35 | if (abs > peak2) { 36 | peak2 = abs; 37 | } 38 | } 39 | expect(peak2).toBeGreaterThan(0.4); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /drumtrack.js: -------------------------------------------------------------------------------- 1 | const midi = require('midi'); 2 | 3 | // Set up a new output. 4 | const output = new midi.output(); 5 | let outputIndex; 6 | for(var n=0;n 15 | (['c','c#','d','d#','e','f','f#','g','g#','a','a#','b'])[ndx%12]+''+Math.floor(ndx/12) 16 | ).reduce((prev, curr, ndx) => { 17 | prev[curr] = ndx; 18 | return prev; 19 | }, {}); 20 | 21 | const midimessages = [ 22 | [1, 'c3'], 23 | [0, 'g#3'], 24 | [4, 'e3'], 25 | [0, 'g#3'], 26 | [1, 'c3'], 27 | [3, 'g#3'], 28 | [2, 'c3'], 29 | [2, 'e3'], 30 | [0, 'g#3'], 31 | [3, 'c3'] 32 | ].map(v => [v[0]*0.17, [0x91, noteStringToNoteNumberMap[v[1]], 127]]); 33 | 34 | let accumulatedDeltaTime = Date.now(); 35 | 36 | let msgindex = 0; 37 | 38 | const playEvent = (msg) => { 39 | if(msg) { 40 | output.sendMessage(msg); 41 | } 42 | 43 | if(msgindex playEvent(nextmessage), accumulatedDeltaTime - Date.now()); 52 | } 53 | }; 54 | 55 | playEvent(); -------------------------------------------------------------------------------- /wasmaudioworklet/common/ui/progress.spec.js: -------------------------------------------------------------------------------- 1 | import { setProgressbarValue } from './progress-bar.js'; 2 | import { toggleSpinner } from './progress-spinner.js' 3 | 4 | describe('progress', function() { 5 | it('should toggle spinner', async () => { 6 | toggleSpinner(true); 7 | assert.equal(document.getElementsByTagName('progress-spinner').length, 1); 8 | await new Promise(resolve => setTimeout(() => resolve(), 100)); 9 | toggleSpinner(false); 10 | assert.equal(document.getElementsByTagName('progress-spinner').length, 0); 11 | }); 12 | it('should set progressbar value', async () => { 13 | assert.equal(document.getElementsByTagName('progress-bar').length, 0); 14 | setProgressbarValue(0.25); 15 | assert.equal(document.getElementsByTagName('progress-bar').length, 1); 16 | assert.equal(document.querySelector('progress-bar').shadowRoot.querySelector('.progress-fill').style.width, '25%'); 17 | assert.equal(document.querySelector('progress-bar').shadowRoot.querySelector('.progress-text').innerHTML, '25%'); 18 | setProgressbarValue(0.75); 19 | assert.equal(document.getElementsByTagName('progress-bar').length, 1); 20 | assert.equal(document.querySelector('progress-bar').shadowRoot.querySelector('.progress-fill').style.width, '75%'); 21 | assert.equal(document.querySelector('progress-bar').shadowRoot.querySelector('.progress-text').innerHTML, '75%'); 22 | setProgressbarValue(null); 23 | assert.equal(document.getElementsByTagName('progress-bar').length, 0); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /songs/upbeatintrolead.js: -------------------------------------------------------------------------------- 1 | const playpattern = (instrument) => instrument.play([[ 1.5, d5(0.17399999999999993, 127) ], 2 | [ 2, g5(0.274, 127) ], 3 | [ 2.75, d5(0.22599999999999998, 127) ], 4 | [ 3.5, g5(0.26400000000000023, 127) ], 5 | [ 5.25, d5(0.1719999999999997, 127) ], 6 | [ 5.5, g5(0.23799999999999955, 127) ], 7 | [ 5.75, d5(0.20999999999999996, 127) ], 8 | [ 6, a5(0.5800000000000001, 127) ], 9 | [ 6 + 2/3, g5(0.6760000000000002, 127) ], 10 | [ 7.5, f5(1.0800000000000003, 127) ], 11 | [ 9.5, e5(1 / 16, 110) ], 12 | [ 9 + 7/8, e5(1/16, 127) ], 13 | [ 10, f5(3 / 8, 127) ], 14 | [ 10 + 2/3, e5(2 / 3, 127) ], 15 | [ 11.5, d5(2.5, 127) ], 16 | [ 17.5, d5(0.17800000000000082, 127) ], 17 | [ 18, g5(0.31599999999999895, 127) ], 18 | [ 18 + 2/3, a5(0.2480000000000011, 127) ], 19 | [ 19 + 3/8, a5(0.14399999999999835, 127) ], 20 | [ 19 + 4/8, b5(1.2119999999999997, 127) ], 21 | [ 21 + 3/8, b5(0.15399999999999991, 127) ], 22 | [ 21.5, c6(0.25200000000000244, 127) ], 23 | [ 22, b5(0.5599999999999987, 127) ], 24 | [ 22 + 2/3, a5(0.3960000000000008, 127) ], 25 | [ 23.5, g5(0.2940000000000005, 127) ], 26 | [ 24, d6(0.7240000000000002, 127) ], 27 | [ 25.272, c6(0.33800000000000097, 127) ], 28 | [ 26, b5(0.33200000000000074, 127) ], 29 | [ 26 + 2/3, c6(0.34199999999999875, 127) ], 30 | [ 27 + 1/3, b5(0.44200000000000017, 127) ], 31 | [ 28, a5(1.1699999999999982, 127) ], 32 | [ 29 + 7/8, b5(0.1280000000000001, 127) ], 33 | [ 30, c6(0.3719999999999999, 127) ], 34 | [ 30.5, b5(0.25400000000000134, 127) ], 35 | [ 31, a5(0.3359999999999985, 127) ], 36 | [ 31.5, g5(0.1720000000000006, 127) ]]); 37 | 38 | module.exports = playpattern; -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/instruments/drums/kick2.class.ts: -------------------------------------------------------------------------------- 1 | 2 | import { StereoSignal } from '../../synth/stereosignal.class'; 3 | import { Envelope } from '../../synth/envelope.class'; 4 | import { Noise } from '../../synth/noise.class'; 5 | import { BandPass } from '../../fx/bandpass'; 6 | import { Instrument } from '../instrument.class'; 7 | 8 | export class Kick2 extends Instrument { 9 | private velocity: f32; 10 | 11 | readonly noise: Noise = new Noise(); 12 | 13 | readonly env2: Envelope = new Envelope(0.001, 0.01, 0.0, 1); 14 | readonly bp2: BandPass = new BandPass(4000, 5000); 15 | 16 | readonly env3: Envelope = new Envelope(0.001, 0.1, 0.05, 0.1); 17 | readonly bp3: BandPass = new BandPass(10, 100); 18 | 19 | set note(note: f32) { 20 | if(note > 1) { 21 | this.velocity = note / 16; 22 | this.env2.attack(); 23 | this.env3.attack(); 24 | } else { 25 | 26 | this.env2.release(); 27 | this.env3.release(); 28 | } 29 | } 30 | 31 | next(): void { 32 | let env2: f32 = this.env2.next(); 33 | let env3: f32 = this.env3.next(); 34 | 35 | let osc: f32 = this.noise.next(); 36 | 37 | 38 | let sig2 = this.bp2.process(osc) * env2 ; 39 | let sig3 = this.bp3.process(osc) * env3 * 8; 40 | 41 | this.signal.left = this.velocity * (-sig2 + sig3); 42 | this.signal.right = this.velocity * ( + sig2 - sig3); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/__tests__/synth/ifftoscillator.spec.ts: -------------------------------------------------------------------------------- 1 | import { IFFTOscillator } from "../../synth/ifftoscillator.class"; 2 | 3 | describe('ifftoscillator', () => { 4 | it('should output a sine wave on 440 hz', () => { 5 | const osc = new IFFTOscillator(11); 6 | osc.frequency = 440; 7 | osc.createWave([0.0], [1.0]); 8 | expect(osc.fft.buffer[1].im).toBeCloseTo(-1023); 9 | expect(osc.fft.buffer[2047].im).toBeCloseTo(1023); 10 | osc.fft.calculateInverse(); 11 | const framesPerCycle = 44100 / 440; 12 | for (let n = 0;n < framesPerCycle * 5; n++) { 13 | const sample = osc.next(); 14 | expect(sample).toBeCloseTo( 15 | NativeMathf.sin((n as f32) * 2 * NativeMathf.PI / 16 | (framesPerCycle as f32)), 1); 17 | } 18 | }); 19 | it('should output an approximate (interpolated) sine wave on 440 hz', () => { 20 | const osc = new IFFTOscillator(5); 21 | osc.frequency = 440; 22 | osc.createWave([0.0], [1.0]); 23 | expect(osc.fft.buffer[1].im).toBeCloseTo(-15); 24 | expect(osc.fft.buffer[31].im).toBeCloseTo(15); 25 | osc.fft.calculateInverse(); 26 | const framesPerCycle = 44100 / 440; 27 | for (let n = 0;n < framesPerCycle; n++) { 28 | const sample = osc.next(); 29 | expect(sample).toBeCloseTo( 30 | NativeMathf.sin((n as f32) * 2 * NativeMathf.PI / 31 | (framesPerCycle as f32)), 1); 32 | } 33 | }); 34 | }); -------------------------------------------------------------------------------- /wasmaudioworklet/webaudiomodules/preseteditor.html: -------------------------------------------------------------------------------- 1 | 31 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
channelinstrumentselect preset
-------------------------------------------------------------------------------- /4klang/instruments/GA_RestInPeaceMS.inc: -------------------------------------------------------------------------------- 1 | GO4K_ENV ATTAC(48),DECAY(64),SUSTAIN(80),RELEASE(80),GAIN(128) 2 | GO4K_ENV ATTAC(0),DECAY(88),SUSTAIN(64),RELEASE(88),GAIN(128) 3 | GO4K_FST AMOUNT(96),DEST(13*MAX_UNIT_SLOTS+5+FST_SET) 4 | GO4K_FST AMOUNT(96),DEST(14*MAX_UNIT_SLOTS+5+FST_SET) 5 | GO4K_FST AMOUNT(96),DEST(15*MAX_UNIT_SLOTS+5+FST_SET) 6 | GO4K_FOP OP(FOP_POP) 7 | GO4K_VCO TRANSPOSE(72),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 8 | GO4K_FST AMOUNT(56),DEST(13*MAX_UNIT_SLOTS+2+FST_SET) 9 | GO4K_FST AMOUNT(77),DEST(15*MAX_UNIT_SLOTS+2+FST_SET) 10 | GO4K_FOP OP(FOP_POP) 11 | GO4K_VCO TRANSPOSE(48),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(SINE|LFO) 12 | GO4K_FST AMOUNT(72),DEST(14*MAX_UNIT_SLOTS+2+FST_SET) 13 | GO4K_FOP OP(FOP_POP) 14 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(128),FLAGS(PULSE) 15 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(64),FLAGS(PULSE) 16 | GO4K_VCO TRANSPOSE(64),DETUNE(64),PHASE(0),GATES(85),COLOR(64),SHAPE(64),GAIN(96),FLAGS(PULSE) 17 | GO4K_FOP OP(FOP_ADDP) 18 | GO4K_FOP OP(FOP_ADDP) 19 | GO4K_VCF FREQUENCY(88),RESONANCE(128),VCFTYPE(LOWPASS) 20 | GO4K_FOP OP(FOP_MULP) 21 | GO4K_VCO TRANSPOSE(52),DETUNE(64),PHASE(0),GATES(93),COLOR(87),SHAPE(64),GAIN(128),FLAGS(GATE|LFO) 22 | GO4K_FOP OP(FOP_MULP) 23 | GO4K_DLL PREGAIN(64),DRY(128),FEEDBACK(64),DAMP(64),FREQUENCY(0),DEPTH(0),DELAY(17),COUNT(1) ; ERROR 24 | GO4K_PAN PANNING(64) 25 | GO4K_DLL PREGAIN(128),DRY(0),FEEDBACK(0),DAMP(0),FREQUENCY(0),DEPTH(0),DELAY(18),COUNT(1) ; ERROR 26 | GO4K_OUT GAIN(64), AUXSEND(32) 27 | -------------------------------------------------------------------------------- /wasmaudioworklet/web-test-runner.config.js: -------------------------------------------------------------------------------- 1 | import { playwrightLauncher } from '@web/test-runner-playwright'; 2 | 3 | export default { 4 | files: [ 5 | '**/*.spec.js', // include `.spec.ts` files 6 | '!./node_modules/**/*', // exclude any node modules 7 | ], 8 | concurrency: 1, 9 | watch: false, 10 | testsFinishTimeout: 60000, 11 | testRunnerHtml: testRunnerImport => 12 | ` 13 | 14 | 19 | 20 | 21 | `, 22 | browsers: [ 23 | playwrightLauncher({ product: 'chromium', launchOptions: { args: ['--autoplay-policy=no-user-gesture-required'] } }), 24 | playwrightLauncher({ 25 | product: 'firefox', launchOptions: { 26 | headless: true, 27 | firefoxUserPrefs: { 28 | 'media.autoplay.block-webaudio': false, // Allow Web Audio autoplay 29 | 'media.autoplay.default': 0, // Allow autoplay for all media 30 | 'media.autoplay.allow-extension-background-pages': true, 31 | 'media.autoplay.blocking_policy': 0, 32 | 'dom.require_user_interaction_for_audio': false, // Remove user gesture requirement 33 | 'dom.audiochannel.mutedByDefault': false 34 | } 35 | } 36 | }), 37 | /*playwrightLauncher({ 38 | product: 'webkit',launchOptions: { 39 | headless: false 40 | } 41 | })*/ 42 | ], 43 | }; -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/instruments/hihat.class.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SAMPLERATE } from '../environment'; 3 | import { StereoSignal } from '../synth/stereosignal.class'; 4 | import { Envelope } from '../synth/envelope.class'; 5 | import { BiQuadFilter, FilterType, Q_BUTTERWORTH } from '../synth/biquad'; 6 | import { Noise } from '../synth/noise.class'; 7 | 8 | export class Hihat { 9 | private _note: f32; 10 | private velocity: f32; 11 | 12 | readonly envelope: Envelope = new Envelope(0.0, 0.08, 0, 0.1); 13 | readonly noise: Noise = new Noise(); 14 | 15 | readonly filter: BiQuadFilter = new BiQuadFilter(); 16 | readonly signal: StereoSignal = new StereoSignal(); 17 | 18 | set note(note: f32) { 19 | if(note > 1) { 20 | this.velocity = note / 32; 21 | this.envelope.attack(); 22 | } else { 23 | this.envelope.release(); 24 | } 25 | this._note = note; 26 | } 27 | 28 | get note(): f32 { 29 | return this._note; 30 | } 31 | 32 | next(): void { 33 | let env: f32 = this.envelope.next(); 34 | if(env === 0) { 35 | this.signal.clear(); 36 | return; 37 | } 38 | let osc: f32 = this.noise.next(); 39 | let signal = this.velocity * 2 * env * osc; 40 | 41 | this.filter.update_coeffecients(FilterType.HighPass, SAMPLERATE, 42 | 10000 + 2000 * env, Q_BUTTERWORTH); 43 | 44 | signal = this.filter.process(signal); 45 | 46 | this.signal.left = signal; 47 | this.signal.right = signal; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /songs/recorded.js: -------------------------------------------------------------------------------- 1 | const output = require('../midi/output.js'); 2 | const TrackerPattern = require('../pattern/trackerpattern.class.js'); 3 | const recorder = require('../midi/recorder.js'); 4 | const RecordConverter = require('../pattern/recordconverter.js'); 5 | global.bpm = 100; 6 | 7 | const ch2 = new TrackerPattern(output, 1, 4); 8 | ch2.play([ 9 | controlchange(0, 7, 90, 90) 10 | ]); 11 | 12 | const take1 = new RecordConverter(require('../recordings/take1.json')); 13 | 14 | const ch1 = new TrackerPattern(output, 0, 4); 15 | 16 | (async function() { 17 | 18 | const drumpattern = () => ch2.play([ 19 | [0, c3(1, 100), gs3()], 20 | [3/6, fs3()], 21 | [5 / 6, gs3()], 22 | [6/6, c3(), gs3(), d3()], 23 | [9/6, fs3()], 24 | [12 / 6, c3(), cs3()], 25 | [15 / 6, fs3()], 26 | [17 / 6, c3(0.5, 50)], 27 | [18 / 6, c3(1, 100), d3(), gs3()], 28 | [21 / 6, c3(1, 80), fs3()], 29 | ]); 30 | 31 | const drumpattern2 = () => ch2.play([ 32 | [0, c3(1, 100), gs3()], 33 | [3/6, fs3()], 34 | [5 / 6, gs3()], 35 | [6/6, c3(), gs3(), e3(1, 127)], 36 | [9/6, fs3(1/6, 100)], 37 | [12 / 6, c3(), cs3()], 38 | [15 / 6, fs3()], 39 | [17 / 6, c3(0.5, 50)], 40 | [18 / 6, c3(1, 100), e3(1, 127), gs3()], 41 | [21 / 6, c3(1, 80), fs3()], 42 | ]); 43 | 44 | // recorder('./recordings/take1.json'); 45 | take1.quantize(4); 46 | ch1.play(take1.trackerPatternData); 47 | while(true) { 48 | await new TrackerPattern() 49 | .play([ 50 | [4, drumpattern] 51 | ], 1); 52 | } 53 | })(); -------------------------------------------------------------------------------- /wasmaudioworklet/common/ui/progress-spinner.js: -------------------------------------------------------------------------------- 1 | const spinnerHtml = ` 41 |
42 |
43 |
44 |
`; 45 | 46 | customElements.define('progress-spinner', class Spinner extends HTMLElement { 47 | constructor() { 48 | super(); 49 | this.attachShadow({ mode: 'open' }); 50 | this.shadowRoot.innerHTML = spinnerHtml; 51 | } 52 | }); 53 | 54 | export function toggleSpinner(state) { 55 | const spinnerElement = document.getElementsByTagName('progress-spinner')[0]; 56 | if (state && !spinnerElement) { 57 | document.documentElement.appendChild(document.createElement('progress-spinner')); 58 | } else if (!state && spinnerElement) { 59 | spinnerElement.remove(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /dawplugin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(WebAssemblyMusicSynth VERSION 0.0.1) 4 | 5 | add_subdirectory(JUCE) 6 | 7 | juce_add_plugin(WebAssemblyMusicSynth 8 | COMPANY_NAME "WebAssembly Music" 9 | BUNDLE_ID com.petersalomonsen.WebAssemblyMusic 10 | IS_SYNTH TRUE 11 | NEEDS_MIDI_INPUT TRUE 12 | NEEDS_MIDI_OUTPUT FALSE 13 | IS_MIDI_EFFECT FALSE 14 | COPY_PLUGIN_AFTER_BUILD TRUE 15 | PLUGIN_MANUFACTURER_CODE WaMu 16 | PLUGIN_CODE Wasm 17 | FORMATS AU) 18 | 19 | target_sources(WebAssemblyMusicSynth 20 | PRIVATE 21 | WebAssemblyMusicSynth.cpp) 22 | 23 | target_compile_definitions(WebAssemblyMusicSynth 24 | PRIVATE 25 | JUCE_VST3_CAN_REPLACE_VST2=0) 26 | 27 | target_include_directories(WebAssemblyMusicSynth 28 | PRIVATE 29 | ${CMAKE_CURRENT_SOURCE_DIR}/wasmedge/build/include/api 30 | ) 31 | 32 | target_link_libraries(WebAssemblyMusicSynth 33 | PRIVATE 34 | juce::juce_audio_utils 35 | ${CMAKE_CURRENT_SOURCE_DIR}/wasmedge/build/_deps/fmt-build/libfmt.a 36 | ${CMAKE_CURRENT_SOURCE_DIR}/wasmedge/build/lib/api/libwasmedge.a 37 | z 38 | ncurses 39 | pthread # For -pthread, commonly needed for threading support 40 | m # For -lm, math library 41 | xar 42 | PUBLIC 43 | juce::juce_audio_plugin_client 44 | juce::juce_dsp 45 | ) 46 | 47 | if(UNIX AND NOT APPLE) 48 | target_link_libraries(WebAssemblyMusicSynth 49 | PRIVATE 50 | rt # For -lrt, time-related functions, not needed on macOS. 51 | dl # For -ldl, dynamic loading of shared libraries 52 | ) 53 | endif() 54 | 55 | juce_generate_juce_header(WebAssemblyMusicSynth) 56 | -------------------------------------------------------------------------------- /wasmaudioworklet/player/infinitemusic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 41 | 42 | 43 |
44 | 45 | 46 |

47 | choose set
48 | 49 |

50 | 51 | 52 |
53 | 
54 |             
55 |
56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/instruments/testinstrument.class.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SAMPLERATE } from '../environment'; 3 | import { SineOscillator } from '../synth/sineoscillator.class'; 4 | import { StereoSignal } from '../synth/stereosignal.class'; 5 | import { Envelope } from '../synth/envelope.class'; 6 | import { DelayLine } from '../fx/delayline'; 7 | import { notefreq } from '../synth/note'; 8 | 9 | export class TestInstrument { 10 | private _note: f32; 11 | readonly envelope: Envelope = new Envelope(0.02, 0.2, 0.2, 0.2); 12 | readonly sineoscillator: SineOscillator = new SineOscillator(); 13 | readonly sineoscillator2: SineOscillator = new SineOscillator(); 14 | readonly delayline: DelayLine = new DelayLine(SAMPLERATE * 0.5 as usize); 15 | readonly delayline2: DelayLine = new DelayLine(SAMPLERATE * 0.5 as usize); 16 | readonly signal: StereoSignal = new StereoSignal(); 17 | 18 | set note(note: f32) { 19 | if(note!==this.note && note > 1) { 20 | this.envelope.attack(); 21 | this.sineoscillator.frequency = notefreq(note); 22 | this.sineoscillator2.frequency = notefreq(note + 12); 23 | } else if(note===0) { 24 | this.envelope.release(); 25 | } 26 | this._note = note; 27 | } 28 | 29 | get note(): f32 { 30 | return this._note; 31 | } 32 | 33 | next(): void { 34 | let env: f32 = this.envelope.next(); 35 | if(env === 0) { 36 | this.signal.clear(); 37 | } else { 38 | let osc1 = env * this.sineoscillator.next(); 39 | let osc2 = env * this.sineoscillator2.next(); 40 | this.signal.left = osc1 * 0.8 + osc2 * 0.2; 41 | this.signal.right = osc1 * 0.2 + osc2 * 0.8; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /songs/rec1.json: -------------------------------------------------------------------------------- 1 | [[0.003,[144,60,88]],[0.001,[144,55,87]],[0.006,[144,64,70]],[0.001,[144,36,75]],[1.315,[144,55,0]],[0.083,[144,60,0]],[0.028,[144,64,0]],[0.078,[144,60,88]],[0.014,[144,57,95]],[0.018,[144,65,79]],[0.004,[144,62,78]],[0.037,[144,60,0]],[0.107,[144,57,0]],[0.011,[144,65,0]],[0.028,[144,62,0]],[0.235,[144,57,70]],[0.007,[144,60,67]],[0.005,[144,65,79]],[0.024,[144,62,83]],[0.029,[144,60,0]],[0.079,[144,57,0]],[0.169,[144,65,0]],[0.117,[144,62,0]],[0.03,[144,60,77]],[0.007,[144,55,78]],[0.001,[144,64,62]],[1.348,[144,55,0]],[0.052,[144,60,0]],[0.038,[144,64,0]],[0.051,[144,60,88]],[0.012,[144,57,94]],[0.015,[144,65,72]],[0.005,[144,62,75]],[0.057,[144,60,0]],[0.105,[144,57,0]],[0.019,[144,65,0]],[0.021,[144,62,0]],[0.258,[144,60,78]],[0.007,[144,65,79]],[0.001,[144,57,84]],[0.029,[144,36,0]],[0.023,[144,62,65]],[0.034,[144,60,0]],[0.063,[144,57,0]],[0.042,[144,62,0]],[0.083,[144,65,0]],[0.102,[144,62,59]],[0.01,[144,34,69]],[0.003,[144,58,94]],[0.008,[144,53,84]],[0.899,[144,34,0]],[0.289,[144,53,0]],[0.019,[144,38,77]],[0.045,[144,58,0]],[0.053,[144,62,0]],[0.231,[144,60,87]],[0.007,[144,57,89]],[0.013,[144,65,84]],[0.021,[144,62,79]],[0.043,[144,60,0]],[0.063,[144,57,0]],[0.048,[144,65,0]],[0.002,[144,62,0]],[0.301,[144,60,65]],[0.004,[144,57,75]],[0.004,[144,65,65]],[0.029,[144,62,75]],[0.028,[144,60,0]],[0.008,[144,38,0]],[0.07,[144,57,0]],[0.125,[144,65,0]],[0.067,[144,64,60]],[0.005,[144,62,0]],[0.001,[144,55,70]],[0.009,[144,60,69]],[0.001,[144,36,67]],[0.302,[144,55,0]],[0.121,[144,55,9]],[0.385,[144,60,0]],[0.018,[144,55,0]],[0.103,[144,64,0]],[0.233,[144,63,81]],[0.008,[144,67,79]],[0.018,[144,64,83]],[0.047,[144,63,0]],[0.081,[144,67,0]],[0.024,[144,64,0]],[0.151,[144,62,95]],[0.01,[144,67,93]],[0.133,[144,62,0]],[0.001,[144,67,0]],[0.113,[144,60,78]],[0.124,[144,60,0]],[0.039,[144,57,89]],[0.118,[144,57,0]],[0.024,[144,55,89]],[0.067,[144,55,0]],[0.09,[144,36,0]]] -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/createbrowsertsbundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create JSON bundle with sources of the webassembly synth to be compiled directly in the browser 3 | */ 4 | import fs from 'fs'; 5 | 6 | function walkSync(dir, filelist) { 7 | if (dir.indexOf('__tests__') > -1) { 8 | return []; 9 | } 10 | const files = fs.readdirSync(dir); 11 | 12 | filelist = filelist || []; 13 | files.forEach(function(file) { 14 | 15 | if (fs.statSync(dir + file).isDirectory()) { 16 | filelist = walkSync(dir + file + '/', filelist); 17 | } 18 | else { 19 | filelist.push(dir + file); 20 | } 21 | }); 22 | return filelist; 23 | }; 24 | 25 | const rootdir = './assembly/'; 26 | const fileList = walkSync(rootdir); 27 | const fileMap = {}; 28 | const globalimports = []; 29 | fileList.forEach(filename => { 30 | const filecontent = fs.readFileSync(filename).toString(); 31 | const adjustedFileName = filename.substr(rootdir.length); 32 | 33 | switch(adjustedFileName.split('/')[0]) { 34 | case 'instruments': 35 | case 'math': 36 | case 'fx': 37 | case 'common': 38 | case 'midi': 39 | if (adjustedFileName.split('/')[1] === 'sequencer') { 40 | break; 41 | } 42 | case 'synth': 43 | filecontent.split(/\n/) 44 | .filter(line => line.startsWith('export ')) 45 | .map(line => line.replace(' abstract ', ' ').split(' ')[2]) 46 | .map(name => name.split(/[^A-Za-z_0-9]/)[0]) 47 | .forEach(name => globalimports.push(`export { ${name} } from '../${adjustedFileName.replace(/\.ts$/,'')}';`)); 48 | default: 49 | } 50 | fileMap[adjustedFileName] = filecontent; 51 | }); 52 | fileMap['mixes/globalimports.ts'] = globalimports.join('\n'); 53 | fs.writeFileSync('assembly/mixes/globalimports.ts', fileMap['mixes/globalimports.ts']); 54 | fs.writeFileSync('wasmsynthassemblyscriptsources.json', JSON.stringify(fileMap)); -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/instruments/lead/sinelead.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SAMPLERATE } from '../../environment'; 3 | import { StereoSignal } from '../../synth/stereosignal.class'; 4 | import { Envelope } from '../../synth/envelope.class'; 5 | 6 | import { BiQuadFilter, FilterType, Q_BUTTERWORTH } from '../../synth/biquad'; 7 | import { Noise } from '../../synth/noise.class'; 8 | import { BandPass } from '../../fx/bandpass'; 9 | import { SawOscillator } from '../../synth/sawoscillator.class'; 10 | import { notefreq } from '../../synth/note'; 11 | import { SineOscillator } from '../../synth/sineoscillator.class'; 12 | 13 | export class SineLead { 14 | private _note: f32; 15 | 16 | 17 | readonly osc: SineOscillator = new SineOscillator(); 18 | readonly osc2: SineOscillator = new SineOscillator(); 19 | 20 | readonly env1: Envelope = new Envelope(0.02, 0.15, 0.05, 0.3); 21 | 22 | readonly signal: StereoSignal = new StereoSignal(); 23 | 24 | constructor() { 25 | 26 | } 27 | 28 | set note(note: f32) { 29 | if(note > 1) { 30 | this.osc.frequency = notefreq(note) * 2; 31 | this.osc2.frequency = notefreq(note); 32 | this._note = note; 33 | this.env1.attack(); 34 | } else { 35 | this.env1.release(); 36 | } 37 | 38 | } 39 | 40 | get note(): f32 { 41 | return this._note; 42 | } 43 | 44 | next(): void { 45 | const env1: f32 = this.env1.next(); 46 | 47 | let osc: f32 = this.osc.next(); 48 | let osc2: f32 = this.osc2.next() * 0.2 * env1; 49 | osc *= env1; 50 | 51 | const pan = this._note / 127; 52 | 53 | this.signal.left = osc * pan + osc2 * (1-pan); 54 | this.signal.right = osc * (1 - pan) + osc2 * pan; 55 | 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /wasmaudioworklet/player/musicandshadervideoplayer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 58 | 59 | 60 | 61 |
62 | 63 |
64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/midi/sequencer/midisequencer.ts: -------------------------------------------------------------------------------- 1 | import { SAMPLERATE } from "../../environment"; 2 | import { midiparts, midipartschedule } from "./midiparts"; 3 | import { fillSampleBuffer, sampleBufferFrames } from "../midisynth"; 4 | 5 | const PLAY_EVENT_INTERVAL = ((sampleBufferFrames * 1000) as f64 / SAMPLERATE); 6 | 7 | export let currentTimeMillis: f64 = 0; 8 | 9 | export function seek(time: i32): void { 10 | currentTimeMillis = time as f64; 11 | 12 | for (let ndx = 0; 13 | ndx < midipartschedule.length; 14 | ndx++) { 15 | const scheduleEntry = midipartschedule[ndx]; 16 | const midiSequencerPart = midiparts[scheduleEntry.midipartindex]; 17 | if (scheduleEntry.endTime >= currentTimeMillis && scheduleEntry.startTime <= currentTimeMillis) { 18 | midiSequencerPart.seek(Math.round(currentTimeMillis) as i32 - scheduleEntry.startTime); 19 | } else { 20 | midiSequencerPart.seek(0); 21 | } 22 | } 23 | } 24 | 25 | export function playMidiPartEvents(): void { 26 | for (let ndx = 0; 27 | ndx < midipartschedule.length; 28 | ndx++) { 29 | const scheduleEntry = midipartschedule[ndx]; 30 | if (scheduleEntry.startTime > currentTimeMillis) { 31 | break; 32 | } 33 | const midiSequencerPart = midiparts[scheduleEntry.midipartindex]; 34 | if (currentTimeMillis <= (scheduleEntry.endTime + PLAY_EVENT_INTERVAL)) { 35 | midiSequencerPart.playEvents(Math.round(currentTimeMillis) as i32 - scheduleEntry.startTime); 36 | } 37 | } 38 | } 39 | 40 | export function playEventsAndFillSampleBuffer(): void { 41 | playMidiPartEvents(); 42 | fillSampleBuffer(); 43 | currentTimeMillis += PLAY_EVENT_INTERVAL; 44 | } 45 | 46 | export function setMidiPartSchedule(ndx: i32, midipartindex: i32, startTime: i32): void { 47 | midipartschedule[ndx].updateEndTime(midipartindex, startTime); 48 | } 49 | -------------------------------------------------------------------------------- /testpattern4.js: -------------------------------------------------------------------------------- 1 | const Pattern = require('./pattern/pattern.class.js'); 2 | const output = require('./midi/output.js'); 3 | const recorder = require('./midi/recorder.js'); 4 | const RecordedPattern = require('./pattern/recordedpattern.class.js'); 5 | 6 | const fs = require('fs'); 7 | 8 | global.startTime = Date.now(); 9 | global.bpm = 110; 10 | global.currentBeat = () => 11 | ((Date.now() - 12 | global.startTime)/ 13 | (60*1000) 14 | ) * global.bpm; 15 | 16 | 17 | const midi = require('midi'); 18 | 19 | const chord = (new class extends Pattern { 20 | constructor() { 21 | super(output); 22 | this.channel = 2; 23 | this.velocity = 127; 24 | } 25 | async play(notes, duration) { 26 | await this.waitForBeat(Math.round(global.currentBeat())); 27 | notes.forEach(note => this.playNote(note, duration)); 28 | }; 29 | }); 30 | 31 | const Arpeggiator = new require('./pattern/playable/arpeggiato1.js'); 32 | const pattern = new Arpeggiator(output); 33 | 34 | 35 | 36 | 37 | const DrumPattern = require('./pattern/playable/drumpattern.js'); 38 | const drums = new DrumPattern(output); 39 | const BasePattern = require('./pattern/playable/basepattern.js'); 40 | const base = new BasePattern(output); 41 | 42 | // recorder('./recordings/recording4.json'); 43 | 44 | (async function() { 45 | try { 46 | new RecordedPattern(output, JSON.parse(fs.readFileSync('./recordings/recording2.json')) 47 | .map(e => { 48 | e[1][2] /= 3; 49 | return e; 50 | })).play(); 51 | new RecordedPattern(output, JSON.parse(fs.readFileSync('./recordings/recording3.json'))).play(); 52 | new RecordedPattern(output, JSON.parse(fs.readFileSync('./recordings/recording4.json'))).play(); 53 | } catch(e) {} 54 | 55 | while(true) { 56 | await drums.play('baseandhihats'); 57 | } 58 | 59 | })(); -------------------------------------------------------------------------------- /midi/recorder.class.js: -------------------------------------------------------------------------------- 1 | const midi = require('midi'); 2 | const fs = require('fs'); 3 | 4 | const input = new midi.input(); 5 | 6 | let inputIndex; 7 | 8 | for(let n=0;n= 0) { 15 | console.log("Opening",input.getPortName(inputIndex)); 16 | input.openPort(inputIndex); 17 | } 18 | 19 | 20 | class Recorder { 21 | 22 | 23 | constructor(channel = -1, output) { 24 | this.folder = 'recordings'; 25 | this.takeNo = 1; 26 | this.midimessages = []; 27 | 28 | 29 | input.on('message', (deltatime, msg) => { 30 | const now = new Date().getTime(); 31 | if(channel > -1) { 32 | msg[0] = (msg[0] & 0xf0) + channel; 33 | } 34 | this.midimessages.push([ 35 | (now - this.previousTime) / 1000 36 | , msg]); 37 | // console.log(msg); 38 | this.previousTime = now; 39 | if(output) { 40 | output.sendMessage(msg); 41 | } 42 | }); 43 | } 44 | 45 | start() { 46 | console.log("recording start"); 47 | this.midimessages = []; 48 | this.previousTime = new Date().getTime(); 49 | } 50 | 51 | save() { 52 | if(this.midimessages.length > 0) { 53 | const filename = `${this.folder}/recording_take${this.takeNo}.json`; 54 | fs.writeFile(filename, JSON.stringify(this.midimessages), 55 | (filename) => console.log('saved', filename)); 56 | console.log('Recording ended take', this.takeNo); 57 | this.takeNo ++; 58 | } 59 | } 60 | } 61 | module.exports = Recorder; 62 | -------------------------------------------------------------------------------- /wasmaudioworklet/common/ui/progress-bar.js: -------------------------------------------------------------------------------- 1 | const progressbarhtml = ` 2 | 43 |
44 |
50%
45 |
46 |
47 | `; 48 | 49 | let progressbar; 50 | 51 | customElements.define('progress-bar', class ProgressBar extends HTMLElement { 52 | constructor() { 53 | super(); 54 | this.attachShadow({ mode: 'open' }); 55 | this.shadowRoot.innerHTML = progressbarhtml; 56 | } 57 | 58 | setValue(val) { 59 | this.shadowRoot.querySelector('.progress-text').innerHTML = `${this.customText ?? (val * 100).toFixed(0) + '%'}`; 60 | this.shadowRoot.querySelector('.progress-fill').style.width = `${(val * 100).toFixed(2)}%`; 61 | } 62 | }); 63 | 64 | export function setProgressbarValue(val, customText) { 65 | if (val !== null) { 66 | if (!progressbar) { 67 | progressbar = document.createElement('progress-bar'); 68 | document.documentElement.appendChild(progressbar); 69 | } 70 | progressbar.customText = customText; 71 | progressbar.setValue(val); 72 | } else if (progressbar) { 73 | progressbar.remove(); 74 | progressbar = null; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/songs/infiniteenergytest.js: -------------------------------------------------------------------------------- 1 | global.bpm = 90; 2 | global.pattern_size_shift = 4; 3 | // global.looptimes = 100; 4 | 5 | /*soloInstrument('pad1'); 6 | soloInstrument('pad2'); 7 | soloInstrument('pad3');*/ 8 | // soloInstrument('lead1'); 9 | 10 | addInstrument('bell', {type: 'note'}); 11 | addInstrument('bass', {type: 'note'}); 12 | addInstrument('pad1', {type: 'note'}); 13 | addInstrument('pad2', {type: 'note'}); 14 | addInstrument('pad3', {type: 'note'}); 15 | addInstrumentGroup('pads', ['pad1', 'pad2', 'pad3']); 16 | addInstrument('kick', {type: 'number'}); 17 | addInstrument('snare', {type: 'number'}); 18 | addInstrumentGroup('drums', ['kick', 'snare']); 19 | addInstrument('drivelead', {type: 'note'}); 20 | addInstrument('hihat', {type: 'number'}); 21 | addInstrument('squarelead', {type: 'note'}); 22 | 23 | playPatterns({ 24 | drivelead: pp(4, [,,,e5, 25 | a5,b5,c6,, 26 | b5,,a5,e5, 27 | ,,,, 28 | ,,,d5, 29 | g5,a5,b5,, 30 | a5,,g5,d5, 31 | ,,,, 32 | ,,,c5, 33 | f5,g5,a5,, 34 | g5,,d5,e5 35 | ,,f5,, 36 | e5,,,, 37 | ,,,, 38 | ,,,, 39 | ,,,, 40 | ]) 41 | },4); 42 | 43 | playPatterns({ 44 | drivelead: pp(4, [,,,e5, 45 | a5,b5,c6,, 46 | b5,,a5,e5, 47 | ,,,, 48 | ,,,d5, 49 | g5,a5,b5,, 50 | a5,,g5,d5, 51 | ,,,, 52 | ,,,c6, 53 | f6,g6,a6,, 54 | g6,,d6,e6 55 | ,,f6,, 56 | e6(3),,,, 57 | ,,,, 58 | ,,,, 59 | ,,,, 60 | ]), 61 | squarelead: pp(4, [ 62 | a5,e5,e5,a5, 63 | f5,e5,c5,a5, 64 | a5,e6,,a5,e6 65 | ,,,, 66 | g5,d5,d5,g5, 67 | d5,c5,b5,d5, 68 | g5,d6,,g5,d6 69 | ,,,, 70 | f4,c5,f5,f4, 71 | c5,f5,c5,f5, 72 | g4,d5,g5,g4, 73 | d5,g5,d5,g5, 74 | a4,e5,a5,a4, 75 | e5,a5,a4,e5, 76 | a5,e6 77 | ]) 78 | },4); 79 | 80 | playPatterns({ 81 | bell: pp(2,[ 82 | e5,a5,c6,e5,c6,b5,g5,a5, 83 | d5,g5,b5,g5,c6,b5,g5,d5, 84 | c5,f5,a5,c5,d5,g5,b5,d5, 85 | e5,a5,c6,e5,a5 86 | ]) 87 | },200); 88 | 89 | -------------------------------------------------------------------------------- /wasmaudioworklet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wasm-music", 3 | "description": "Javascript/WebAssembly live coding environment for music and synthesis", 4 | "version": "0.0.43", 5 | "repository": { 6 | "url": "https://github.com/petersalomonsen/javascriptmusic" 7 | }, 8 | "license": "GPL-3.0", 9 | "author": { 10 | "name": "Peter Salomonsen", 11 | "url": "https://petersalomonsen.com" 12 | }, 13 | "files": [ 14 | "*", 15 | "midisequencer/ui/pianorolldemo/dist" 16 | ], 17 | "type": "module", 18 | "scripts": { 19 | "test-wtr": "wtr", 20 | "asbuild": "(cd synth1 && asc --runtime stub assembly/index.ts -o build/index.wasm -Oz)", 21 | "fastbuild": "(cd synth1 && asc --runtime stub assembly/index.ts -o build/index.wasm -t build/index.wat)", 22 | "createbrowsersourcebundle": "(cd synth1 && node createbrowsertsbundle.js)", 23 | "test-asc-synth": "(cd synth1 && asp --verbose)", 24 | "test-asc-synth:ci": "(cd synth1 && asp --summary)", 25 | "bundle-pianorolldemo": "(cd midisequencer/ui/pianorolldemo && rm -Rf dist && rollup -c rollup-config.js)", 26 | "bundle-songcompiler": "rollup -c rollup-config-songcompiler.js", 27 | "bundle-musicandshadervideoplayer": "(cd player/musicandshadervideoplayer && rollup -c rollup.config.js)", 28 | "patch-version": "npm --no-git-tag-version version patch", 29 | "serve": "http-server -p 8080 ." 30 | }, 31 | "devDependencies": { 32 | "@as-pect/cli": "^8.1.0", 33 | "@esm-bundle/chai": "^4.3.4-fix.0", 34 | "@playwright/test": "^1.50.1", 35 | "@rollup/plugin-terser": "^0.4.4", 36 | "@web/rollup-plugin-html": "^2.0.1", 37 | "@web/test-runner": "^0.20.0", 38 | "@web/test-runner-playwright": "^0.11.0", 39 | "assemblyscript": "^0.27.14", 40 | "chai": "^4.3.7", 41 | "http-server": "^14.1.1", 42 | "mocha": "^10.2.0", 43 | "rollup": "^4.1.5", 44 | "rollup-plugin-terser": "^7.0.2" 45 | }, 46 | "dependencies": {} 47 | } 48 | -------------------------------------------------------------------------------- /wasmaudioworklet/screenrecorder/cameraviewer.js: -------------------------------------------------------------------------------- 1 | let videoElement; 2 | 3 | function setSmallVideoElementSize() { 4 | videoElement.style.right = '5px'; 5 | videoElement.style.bottom = '5px'; 6 | videoElement.style.width = '250px'; 7 | videoElement.style.height = '250px'; 8 | } 9 | 10 | export function isCameraActive() { 11 | return videoElement ? true : false; 12 | } 13 | 14 | export function toggleCamViewer() { 15 | if (videoElement) { 16 | videoElement.remove(); 17 | videoElement = null; 18 | } else { 19 | videoElement = document.createElement('video'); 20 | videoElement.autoplay = true; 21 | videoElement.style.position = 'fixed'; 22 | videoElement.style.right = '5px'; 23 | videoElement.style.bottom = '5px'; 24 | videoElement.style.width = '250px'; 25 | videoElement.style.height = '250px'; 26 | videoElement.style.zIndex = 9999999; 27 | videoElement.style.transition = 'all 0.5s ease-in-out'; 28 | document.documentElement.appendChild(videoElement); 29 | 30 | if (navigator.mediaDevices.getUserMedia) { 31 | navigator.mediaDevices.getUserMedia({ video: true }) 32 | .then(function (stream) { 33 | videoElement.srcObject = stream; 34 | }) 35 | .catch(function (err) { 36 | console.error(err); 37 | }); 38 | } 39 | } 40 | } 41 | document.addEventListener('keydown', (event) => { 42 | if (event.code === 'Escape') { 43 | if (event.ctrlKey) { 44 | toggleCamViewer(); 45 | } else if (videoElement) { 46 | if (videoElement.style.width === '100%' ) { 47 | setSmallVideoElementSize(); 48 | } else { 49 | videoElement.style.position = 'fixed'; 50 | videoElement.style.right = '0px'; 51 | videoElement.style.bottom = '0px'; 52 | videoElement.style.width = '100%'; 53 | videoElement.style.height = '100%'; 54 | } 55 | } 56 | } 57 | }); 58 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/instruments/kick.class.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SAMPLERATE } from '../environment'; 3 | import { StereoSignal } from '../synth/stereosignal.class'; 4 | import { Envelope } from '../synth/envelope.class'; 5 | import { SawOscillator } from '../synth/sawoscillator.class'; 6 | import { BiQuadFilter, FilterType, Q_BUTTERWORTH } from '../synth/biquad'; 7 | import { Noise } from '../synth/noise.class'; 8 | 9 | 10 | 11 | export class Kick { 12 | private _note: f32; 13 | private velocity: f32; 14 | readonly envelope: Envelope = new Envelope(0.0, 0.2, 0, 0.2); 15 | readonly filterenvelope: Envelope = new Envelope(0.0, 0.05, 0.05, 0.1); 16 | readonly sawoscillator: SawOscillator = new SawOscillator(); 17 | readonly noise: Noise = new Noise(); 18 | 19 | readonly filter: BiQuadFilter = new BiQuadFilter(); 20 | readonly signal: StereoSignal = new StereoSignal(); 21 | 22 | set note(note: f32) { 23 | if(note > 1) { 24 | this.sawoscillator.frequency = 150; 25 | this.velocity = note / 16; 26 | this.envelope.attack(); 27 | this.filterenvelope.attack(); 28 | } else { 29 | this.envelope.release(); 30 | this.filterenvelope.release(); 31 | } 32 | this._note = note; 33 | } 34 | 35 | get note(): f32 { 36 | return this._note; 37 | } 38 | 39 | next(): void { 40 | let env: f32 = this.envelope.next(); 41 | if(env === 0) { 42 | this.signal.clear(); 43 | return; 44 | } 45 | this.sawoscillator.frequency = 20.0 + (env * 150.0); 46 | 47 | this.filter.update_coeffecients(FilterType.LowPass, SAMPLERATE, 48 | 40 + (this.filterenvelope.next() * 2000), 0.2); 49 | 50 | let osc1: f32 = this.envelope.next() * this.velocity * this.sawoscillator.next() * 0.8 + this.noise.next(); 51 | 52 | osc1 = this.filter.process(osc1); 53 | 54 | this.signal.left = env * osc1; 55 | this.signal.right = env * osc1; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/petersalomonsen/javascriptmusic/actions/workflows/main.yml/badge.svg) 2 | 3 | Music written in Javascript 4 | =========================== 5 | 6 | This project demonstrates writing music in Javascript. In the beginning I used NodeJS with MIDI to control a synth, and then got into [4klang](https://github.com/hzdgopher/4klang/) which is a very compact but extremely powerful synth, which finally inspired me to atttempt writing a synth in WebAssembly running entirely in the browser. 7 | 8 | # Webassembly music in the browser 9 | 10 | This is a live-coding environment for music running entirely in the browser, synthesizing music in webassembly using [AssemblyScript](https://docs.assemblyscript.org/). 11 | 12 | You can test it yourself here (remove the `gist` url parameter if you want a clean project ): 13 | https://petersalomonsen.com/webassemblymusic/livecodev2/?gist=5b795090ead4f192e7f5ee5dcdd17392 14 | 15 | Demo videos: 16 | https://www.youtube.com/watch?v=C8j_ieOm4vE&list=PLv5wm4YuO4IxRDu1k8fSBVuUlULA8CRa7 17 | 18 | Sources are in the [wasmaudioworklet](wasmaudioworklet) folder. 19 | 20 | There are also more resources like articles and earlier versions at https://petersalomonsen.com 21 | 22 | # 4klang 23 | 24 | My experiments with the [4klang](https://github.com/hzdgopher/4klang/) synth can be found int the [4klang](4klang) folder. 25 | 26 | If you just want to listen to the songs: 27 | https://soundcloud.com/psalomo/4klang-lazy-grooves 28 | https://soundcloud.com/psalomo/4klang-first-attempt 29 | 30 | You may check out the windows exe of the "first attempt" song (link to zip with exe found in video description): 31 | 32 | https://www.youtube.com/watch?v=zHrbLSjKmxQ 33 | 34 | # NodeJS MIDI 35 | 36 | My first attempt on writing music with Javascript used NodeJS with MIDI routed to [ZynAddSubFX](http://zynaddsubfx.sourceforge.net/) for the sounds 37 | 38 | https://youtu.be/oPfOeEbM4M0 39 | 40 | And you can listen to the song [songs/upbeat.js](songs/upbeat.js) here: 41 | https://soundcloud.com/psalomo/80s-nostalgica 42 | 43 | Run directly from nodejs, only dependes on the node midi package, and connect to a midi device. 44 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/instruments/sawbass.class.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SAMPLERATE } from '../environment'; 3 | import { StereoSignal } from '../synth/stereosignal.class'; 4 | import { Envelope } from '../synth/envelope.class'; 5 | import { SawOscillator } from '../synth/sawoscillator.class'; 6 | import { BiQuadFilter, FilterType, Q_BUTTERWORTH } from '../synth/biquad'; 7 | import { notefreq } from '../synth/note'; 8 | import { Instrument } from './instrument.class'; 9 | 10 | export class SawBass extends Instrument { 11 | readonly envelope: Envelope = new Envelope(0.01, 0.2, 0.8, 0.2); 12 | readonly sawoscillator: SawOscillator = new SawOscillator(); 13 | readonly sawoscillator2: SawOscillator = new SawOscillator(); 14 | readonly filter: BiQuadFilter = new BiQuadFilter(); 15 | readonly hpfilterl: BiQuadFilter = new BiQuadFilter(); 16 | readonly hpfilterr: BiQuadFilter = new BiQuadFilter(); 17 | 18 | constructor() { 19 | super(); 20 | this.hpfilterl.update_coeffecients(FilterType.HighPass, SAMPLERATE, 35, Q_BUTTERWORTH); 21 | this.hpfilterr.update_coeffecients(FilterType.HighPass, SAMPLERATE, 35, Q_BUTTERWORTH); 22 | } 23 | 24 | set note(note: f32) { 25 | if(note > 1) { 26 | this.sawoscillator.frequency = notefreq(note + 0.1); 27 | this.sawoscillator2.frequency = notefreq(note - 0.1); 28 | this.envelope.attack(); 29 | } else { 30 | this.envelope.release(); 31 | } 32 | } 33 | 34 | next(): void { 35 | let env: f32 = this.envelope.next(); 36 | if(env === 0) { 37 | this.signal.clear(); 38 | return; 39 | } 40 | // this.signal.clear(); 41 | this.signal.left /= 1.03; // feedback 42 | this.signal.right /= 1.03; // feedback 43 | this.signal.addMonoSignal( 44 | this.hpfilterl.process(this.sawoscillator.next() * env), 0.3, 0.3 45 | ); 46 | this.signal.addMonoSignal( 47 | this.hpfilterr.process(this.sawoscillator2.next() * env), 0.3, 0.7 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /wasmaudioworklet/player/musicandshadervideoplayer/renderworker.js: -------------------------------------------------------------------------------- 1 | const SAMPLE_FRAMES = 128; 2 | 3 | let wasmInstance; 4 | let wasmleftbuffer; 5 | let wasmrightbuffer; 6 | let durationFrames; 7 | 8 | onmessage = async (msg) => { 9 | if (msg.data.wasm) { 10 | const sampleRate = msg.data.samplerate; 11 | const wasmInstancePromise = WebAssembly.instantiate(msg.data.wasm, 12 | { 13 | environment: { 14 | SAMPLERATE: sampleRate 15 | } 16 | }); 17 | wasmInstance = (await wasmInstancePromise).instance.exports; 18 | 19 | const durationMillis = msg.data.songduration; 20 | 21 | durationFrames = durationMillis * sampleRate / 1000; 22 | 23 | const samplebuffer = wasmInstance.allocateSampleBuffer ? wasmInstance.allocateSampleBuffer(SAMPLE_FRAMES) : wasmInstance.samplebuffer; 24 | wasmleftbuffer = new Float32Array(wasmInstance.memory.buffer, 25 | samplebuffer, 26 | SAMPLE_FRAMES); 27 | wasmrightbuffer = new Float32Array(wasmInstance.memory.buffer, 28 | samplebuffer + (SAMPLE_FRAMES * 4), 29 | SAMPLE_FRAMES); 30 | } 31 | 32 | const leftbuffer = new ArrayBuffer(durationFrames * 4); 33 | const leftview = new DataView(leftbuffer); 34 | const rightbuffer = new ArrayBuffer(durationFrames * 4); 35 | const rightview = new DataView(rightbuffer); 36 | 37 | const isLittleEndian = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1; 38 | let framepos = 0; 39 | 40 | while (framepos < durationFrames) { 41 | wasmInstance.playEventsAndFillSampleBuffer != undefined ? 42 | wasmInstance.playEventsAndFillSampleBuffer() : 43 | wasmInstance.fillSampleBuffer(); 44 | 45 | for (let n = 0; n < SAMPLE_FRAMES && framepos < durationFrames; n++) { 46 | leftview.setFloat32(framepos * 4, wasmleftbuffer[n], isLittleEndian); 47 | rightview.setFloat32(framepos * 4, wasmrightbuffer[n], isLittleEndian); 48 | framepos++; 49 | } 50 | } 51 | postMessage({ leftbuffer, rightbuffer }, [leftbuffer, rightbuffer]); 52 | }; 53 | -------------------------------------------------------------------------------- /wasmaudioworklet/player/musicandshadervideoplayer/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { terser } from 'rollup-plugin-terser'; 2 | import { readFileSync, unlinkSync, writeFileSync, existsSync } from 'fs'; 3 | 4 | export default [ 5 | { 6 | input: 'renderworker.js', 7 | output: { file: 'renderworker.bundle.js', format: 'esm'}, 8 | plugins: [ 9 | terser() 10 | ] 11 | }, 12 | { 13 | input: 'main.js', 14 | output: { file: 'main.bundle.js', format: 'esm' }, 15 | plugins: [ 16 | (() => ({ 17 | transform(code, id) { 18 | const urlMatches = code.match(/(new URL\([^),]+\,\s*import.meta.url\s*\))/g); 19 | 20 | if (urlMatches) { 21 | for (let urlMatch of urlMatches) { 22 | const urlWithAbsolutePath = urlMatch.replace('import.meta.url', `'file://${id}'`); 23 | 24 | const func = new Function('return ' + urlWithAbsolutePath); 25 | const resolvedUrl = func(); 26 | const pathname = resolvedUrl.pathname.replace(/.js$/,'.bundle.js'); 27 | 28 | if (pathname.endsWith('.js') && existsSync(pathname)) { 29 | const jsfilecontent = readFileSync(pathname).toString(); 30 | 31 | code = code.replace(urlMatch, '"data:application/javascript,' + encodeURIComponent(jsfilecontent)+'"'); 32 | } 33 | } 34 | } 35 | return { code }; 36 | } 37 | }))(), 38 | terser(), 39 | { 40 | name: 'inline-js', 41 | closeBundle: () => { 42 | const js = readFileSync('main.bundle.js').toString(); 43 | const html = readFileSync('index.html').toString() 44 | .replace(``, 45 | ``); 46 | 47 | writeFileSync(`index.bundle.html`, html); 48 | unlinkSync('main.bundle.js'); 49 | unlinkSync('renderworker.bundle.js'); 50 | } 51 | } 52 | ] 53 | }]; 54 | -------------------------------------------------------------------------------- /wasmaudioworklet/synth1/assembly/synth/envelope.class.ts: -------------------------------------------------------------------------------- 1 | import { SAMPLERATE } from "../environment"; 2 | 3 | export enum EnvelopeState { 4 | ATTACK = 0, 5 | DECAY = 1, 6 | SUSTAIN = 2, 7 | RELEASE = 3, 8 | DONE = 4 9 | } 10 | 11 | export class Envelope { 12 | attackStep: f32; 13 | decayStep: f32; 14 | sustainLevel: f32; 15 | releaseStep: f32; 16 | 17 | val: f32 = 0; 18 | state: EnvelopeState = EnvelopeState.DONE; 19 | 20 | constructor(attackTime: f32, decayTime: f32, sustainLevel: f32, releaseTime: f32) { 21 | this.attackStep = 1.0 / (attackTime * SAMPLERATE); 22 | this.decayStep = 1.0 / (decayTime * SAMPLERATE); 23 | this.releaseStep = 1.0 / (releaseTime * SAMPLERATE); 24 | this.sustainLevel = sustainLevel; 25 | } 26 | 27 | next(): f32 { 28 | switch(this.state) { 29 | case EnvelopeState.ATTACK: 30 | this.val += this.attackStep; 31 | if(this.val >= 1.0) { 32 | this.val = 1.0; 33 | this.state = EnvelopeState.DECAY; 34 | } 35 | break; 36 | case EnvelopeState.DECAY: 37 | this.val -= this.decayStep; 38 | if(this.val <= this.sustainLevel) { 39 | this.val = this.sustainLevel; 40 | this.state = EnvelopeState.SUSTAIN; 41 | } 42 | break; 43 | case EnvelopeState.SUSTAIN: 44 | break; 45 | case EnvelopeState.RELEASE: 46 | this.val -= this.releaseStep; 47 | if(this.val <= 0) { 48 | this.val = 0; 49 | this.state = EnvelopeState.DONE; 50 | } 51 | break; 52 | case EnvelopeState.DONE: 53 | break; 54 | } 55 | return this.val; 56 | } 57 | 58 | attack(): void { 59 | this.state = EnvelopeState.ATTACK; 60 | } 61 | 62 | release(): void { 63 | this.state = EnvelopeState.RELEASE; 64 | } 65 | 66 | isDone(): boolean { 67 | return this.state === EnvelopeState.DONE; 68 | } 69 | } -------------------------------------------------------------------------------- /songs/recorded2.js: -------------------------------------------------------------------------------- 1 | const output = require('../midi/output.js'); 2 | const TrackerPattern = require('../pattern/trackerpattern.class.js'); 3 | const Recorder = require('../midi/recorder.class.js'); 4 | const recorder = new Recorder(7, output); 5 | const RecordedPattern = require('../pattern/recordedpattern.class'); 6 | 7 | global.bpm = 100; 8 | 9 | const recording = new RecordedPattern(output, 'songs/rec1.json'); 10 | const recbase = new RecordedPattern(output, 'songs/recbase.json'); 11 | 12 | const ch1 = new TrackerPattern(output, 0, 4); 13 | ch1.play([ 14 | controlchange(7, 90, 90) 15 | ]); 16 | 17 | const ch2 = new TrackerPattern(output, 1, 4); 18 | ch2.play([ 19 | controlchange(7, 90, 90) 20 | ]); 21 | 22 | const ch8 = new TrackerPattern(output, 7, 4); 23 | ch8.play([ 24 | controlchange(7, 120, 120) 25 | ]); 26 | 27 | 28 | (async function() { 29 | 30 | const drumpattern = () => ch2.play([ 31 | [0, c3(1, 100), gs3()], 32 | [3/6, fs3()], 33 | [5 / 6, gs3()], 34 | [6/6, c3(), gs3(), d3()], 35 | [9/6, fs3()], 36 | [12 / 6, c3(), cs3()], 37 | [15 / 6, fs3()], 38 | [17 / 6, c3(0.5, 50)], 39 | [18 / 6, c3(1, 100), d3(), gs3()], 40 | [21 / 6, c3(1, 80), fs3()], 41 | ]); 42 | 43 | const drumpattern2 = () => ch2.play([ 44 | [0, c3(1, 100), gs3()], 45 | [3/6, fs3()], 46 | [5 / 6, gs3()], 47 | [6/6, c3(), gs3(), e3(1, 127)], 48 | [9/6, fs3(1/6, 100)], 49 | [12 / 6, c3(), cs3()], 50 | [15 / 6, fs3()], 51 | [17 / 6, c3(0.5, 50)], 52 | [18 / 6, c3(1, 100), e3(1, 127), gs3()], 53 | [21 / 6, c3(1, 80), fs3()], 54 | ]); 55 | 56 | global.startTime = Date.now(); 57 | while(true) { 58 | recording.play(); 59 | recbase.play(); 60 | recorder.start(); 61 | const trackerpattern = new TrackerPattern(); 62 | 63 | await trackerpattern.play([ 64 | [4, drumpattern], 65 | [4, drumpattern], 66 | [4, drumpattern], 67 | [4, drumpattern, () => countdown(4)] 68 | ], 1); 69 | recorder.save(); 70 | } 71 | })(); --------------------------------------------------------------------------------