Skip to content

Commit 5d6b344

Browse files
committed
feat: useAMY composable ready to be improved and used more
1 parent bd23bba commit 5d6b344

File tree

5 files changed

+580
-515
lines changed

5 files changed

+580
-515
lines changed

content/practice/synth/amy/SynthAmy.vue

Lines changed: 29 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -1,260 +1,95 @@
11
<script setup>
2-
import {
3-
onKeyDown, onKeyUp, useStorage, onKeyStroke
4-
} from '@vueuse/core';
5-
import { useClamp } from '@vueuse/math';
6-
import { context } from 'tone';
7-
import { onMounted, reactive, computed, watch } from 'vue';
8-
import AMYCodes from '#/db/amy-codes.yaml'
9-
import { midi } from '#/use/midi'
2+
import { useAMY } from './useAMY';
103
11-
var amy_started = false;
12-
var amy_audio_buffer = null;
13-
var amy_play_message = null;
14-
var amy_start_web = null;
15-
var callback = null
164
17-
const amy = reactive({
18-
started: false,
19-
codes: AMYCodes,
20-
21-
message: computed(() => {
22-
const arr = []
23-
for (let p in amy.knobs) {
24-
arr.push(`${p}${amy.knobs[p]}`)
25-
}
26-
arr.push('A0,0.2,150,1,250,0T59')
27-
28-
return arr.join('')
29-
}),
30-
waveforms: ['SINE', 'PULSE', 'SAW_DOWN', 'SAW_UP', 'TRI', 'NOISE', 'KS', 'PCM', 'ALGO', 'PARTIAL', 'PARTIALS'],
31-
32-
knobs: {
33-
p: useClamp(useStorage('amy-patch', 248), 0, 1024),
34-
w: useClamp(useStorage('amy-waveform', 7), 0, 11),
35-
V: useClamp(useStorage('amy-volume', 1), 0, 10),
36-
d: useClamp(useStorage('amy-duty', 0.5), 0.001, 0.999),
37-
o: useClamp(useStorage('amy-algo', 1), 1, 32),
38-
b: useClamp(useStorage('amy-feedback', 0), 0, 1),
39-
},
40-
note: useClamp(60, 10, 127),
41-
42-
43-
history: []
44-
})
45-
46-
function useAMY() {
47-
48-
watch(() => midi.note, n => {
49-
amy.play(n.number, n.velocity / 127)
50-
})
51-
52-
watch(() => amy.message, m => {
53-
amy_play_message(m)
54-
amy.history.unshift(m)
55-
})
56-
57-
onKeyDown('a', () => { amy.play(amy.note, 1) })
58-
onKeyUp('a', () => { amy.play(amy.note, 0) })
59-
60-
onKeyStroke('ArrowLeft', () => { amy.knobs.patch-- })
61-
onKeyStroke('ArrowRight', () => { amy.knobs.patch++ })
62-
63-
onKeyStroke('ArrowUp', (e) => { e.preventDefault(); amy.note++ })
64-
onKeyStroke('ArrowDown', (e) => { e.preventDefault(); amy.note-- })
65-
66-
onMounted(() => {
67-
import('./amyJS.js').then(amy => {
68-
const { Module } = amy
69-
Module.onRuntimeInitialized = function () {
70-
amy_audio_buffer = Module.cwrap(
71-
'web_audio_buffer', 'number', ['number', 'number']
72-
);
73-
amy_play_message = Module.cwrap(
74-
'amy_play_message', null, ['string']
75-
);
76-
amy_start_web = Module.cwrap(
77-
'amy_start_web', null, ['number']
78-
);
79-
}
80-
81-
var dataHeap = null;
82-
var dataPtr = null;
83-
var data = new Float32Array();
84-
85-
callback = function audioCallback(l) {
86-
if (dataHeap == null || dataHeap.length == 0) {
87-
data = new Float32Array(l.length);
88-
var nDataBytes = data.length * data.BYTES_PER_ELEMENT;
89-
dataPtr = Module._malloc(nDataBytes);
90-
dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes);
91-
dataHeap.set(new Uint8Array(data.buffer));
92-
}
93-
94-
amy_audio_buffer(dataHeap.byteOffset, l.length);
95-
var result = new Float32Array(dataHeap.buffer, dataHeap.byteOffset, data.length);
96-
97-
for (let i = 0; i < l.length; i++) {
98-
l[i] = result[i];
99-
}
100-
}
101-
})
102-
})
103-
104-
var audioRunning = false;
105-
var scriptNode = null;
106-
var source = null;
107-
var audioCtx = null;
108-
109-
function setupAudio(fn) {
110-
111-
var AudioContext = window.AudioContext
112-
|| window.webkitAudioContext
113-
|| false;
114-
115-
if (!AudioContext) {
116-
console.error("No Audio")
117-
return
118-
}
119-
audioCtx = new AudioContext({
120-
sampleRate: 48000
121-
});
122-
source = audioCtx.createBufferSource();
123-
scriptNode = audioCtx.createScriptProcessor(256, 0, 1);
124-
scriptNode.onaudioprocess = function (audioProcessingEvent) {
125-
fn(audioProcessingEvent.outputBuffer.getChannelData(0));
126-
};
127-
}
128-
129-
function startAudio() {
130-
131-
if (audioRunning) return;
132-
amy_start_web?.();
133-
setupAudio(callback);
134-
scriptNode.connect(audioCtx.destination);
135-
source.start();
136-
audioRunning = true;
137-
// setTimeout(() => {
138-
// console.log('started')
139-
// amy_play_message("v54l1w8n70p30")
140-
// }, 50)
141-
amy_started = true;
142-
}
143-
144-
function stopAudio() {
145-
audioRunning = false;
146-
amy_started = false;
147-
audioCtx.suspend().then(function () {
148-
console.log('stopped')
149-
});
150-
}
151-
152-
amy.play = (note, velocity = 0) => {
153-
if (!amy_started) startAudio()
154-
let osc = `v${note * 20}`
155-
if (velocity > 0) {
156-
let setup = osc + amy.message
157-
amy_play_message(setup)
158-
amy.history.unshift(setup)
159-
}
160-
161-
let msg = osc + `n${note}l${velocity.toFixed(2)}`
162-
amy.history.unshift(msg)
163-
amy_play_message(msg)
164-
}
165-
166-
amy.reset = (n = 100000) => {
167-
let msg = `S${n}`
168-
amy.history.unshift(msg)
169-
amy_play_message(msg)
170-
}
171-
172-
return amy
173-
}
174-
175-
176-
useAMY()
5+
const { knobs, waveforms, reset, note, play, message, history } = useAMY()
1776
1787
</script>
1798

1809
<template lang='pug'>
18110
.p-2.rounded-xl.border-1.m-2.select-none.flex.flex-wrap.gap-2
18211
transition-group(name="fade")
18312
control-rotary(
13+
key="Wave"
18414
:min="0"
18515
:max="11"
18616
param="Wave"
187-
v-model="amy.knobs.w"
17+
v-model="knobs.w"
18818
:fixed="0"
189-
:unit="amy.waveforms[amy.knobs.w]"
19+
:unit="waveforms[knobs.w]"
19020
)
19121
control-rotary(
22+
key="Patch"
19223
:min="1"
19324
:max="1024"
19425
param="Patch"
195-
v-model="amy.knobs.p"
196-
v-if="amy.knobs.w > 6"
26+
v-model="knobs.p"
27+
v-if="knobs.w > 6"
19728
:fixed="0"
19829
)
19930
control-rotary(
31+
key="Duty"
20032
:min="0.001"
20133
:max=".999"
20234
param="Duty"
20335
:step="0.01"
204-
v-if="amy.knobs.w == 1"
205-
v-model="amy.knobs.d"
36+
v-if="knobs.w == 1"
37+
v-model="knobs.d"
20638
:fixed="2"
20739
)
20840
control-rotary(
41+
key="Algo"
20942
:min="1"
21043
:max="32"
21144
param="ALGO"
212-
v-if="amy.knobs.w == 8"
213-
v-model="amy.knobs.o"
45+
v-if="knobs.w == 8"
46+
v-model="knobs.o"
21447
:fixed="0"
21548
)
21649
control-rotary(
50+
key="Feedback"
21751
:min="0"
21852
:max="1"
21953
param="Feedback"
220-
v-model="amy.knobs.b"
221-
v-if="amy.knobs.w>5"
54+
v-model="knobs.b"
55+
v-if="knobs.w > 5"
22256
:fixed="1"
22357
:step="0.01"
22458
)
22559
control-rotary(
60+
key="volume"
22661
:min="0"
22762
:max="10"
22863
param="Volume"
229-
v-model="amy.knobs.V"
64+
v-model="knobs.V"
23065
:fixed="1"
23166
:step="0.01"
23267
)
233-
.flex-auto
234-
button.text-button(@click="amy.reset()") RESET
68+
.flex-auto(key="flex")
69+
button.text-button(@click="reset()" key="reset") RESET
23570
.p-2.rounded-xl.border-1.m-2.select-none.flex.flex-wrap.gap-4
23671
control-rotary(
23772
:min="1"
23873
:max="127"
23974
param="Note"
240-
v-model="amy.note"
75+
v-model="note"
24176
:fixed="0"
24277
unit="MIDI"
24378
)
24479
button.flex.items-center.gap-4.select-none.p-4.rounded-xl.bg-green-300.dark-bg-green-900.active-font-bold(
245-
@mousedown.prevent.stop="amy.play(amy.note,1)"
246-
@touchstart.prevent.stop="amy.play(amy.note,1)"
247-
@mouseup.prevent.stop="amy.play(amy.note,0)"
248-
@touchend.prevent.stop="amy.play(amy.note,0)"
80+
@mousedown.prevent.stop="play(note, 1)"
81+
@touchstart.prevent.stop="play(note, 1)"
82+
@mouseup.prevent.stop="play(note, 0)"
83+
@touchend.prevent.stop="play(note, 0)"
24984
)
25085
.i-la-play.text-4xl
25186

252-
.p-4.flex.items-center.font-mono {{ amy.message }}
87+
.p-4.flex.items-center.font-mono {{ message }}
25388

25489
.top-16.right-4.p-4.w-80.max-h-80vh.overflow-hidden.flex.flex-col.opacity-30.pointer-events-none.font-mono.text-sm.fixed
255-
.font-bold.border-b-2 {{ amy.message }}
90+
.font-bold.border-b-2 {{ message }}
25691
transition-group(name="fade")
257-
.p-0(v-for="rec in amy.history" :key="rec") {{ rec }}
92+
.p-0(v-for="rec in history" :key="rec") {{ rec }}
25893
//- .flex.flex-wrap.gap-4
25994
//- .text-2xl Floats
26095
//- .p-2.flex(v-for="(knob,key) in amy.knobs" :key="key")

content/practice/synth/amy/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ onBeforeUnmount(() => synthEnabled.value = true)
2222

2323
<SynthAmy/>
2424

25+
<MidiKeys></MidiKeys>
26+
2527
## AMY Synth
2628

2729
### the Additive Music synthesizer librarY
2830

2931
Highly experimental. [Issue pending](https://github.com/bwhitman/amy/issues/35)
3032

31-
3233
- Press `A` on your keyboard to play a note. Or push the PLAY button.
3334
- Use <i class="p-3 i-la-arrow-left"></i> and <i class="p-3 i-la-arrow-right"></i> keys to browse patches
3435

0 commit comments

Comments
 (0)