Skip to content

Commit

Permalink
looper prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
felixroos committed Jun 6, 2024
1 parent 0df6f3e commit 86cc9b8
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
2 changes: 2 additions & 0 deletions packages/core/controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export function registerControl(names, ...aliases) {
*/
export const { s, sound } = registerControl(['s', 'n', 'gain'], 'sound');

export const { rec } = registerControl(['rec', 'n']);

/**
* Define a custom webaudio node to use as a sound source.
*
Expand Down
4 changes: 2 additions & 2 deletions packages/superdough/sampler.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { getAudioContext, registerSound } from './index.mjs';
import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs';
import { logger } from './logger.mjs';

const bufferCache = {}; // string: Promise<ArrayBuffer>
const loadCache = {}; // string: Promise<ArrayBuffer>
export const bufferCache = {}; // string: Promise<ArrayBuffer>
export const loadCache = {}; // string: Promise<ArrayBuffer>

export const getCachedBuffer = (url) => bufferCache[url];

Expand Down
53 changes: 53 additions & 0 deletions packages/superdough/superdough.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ function loadWorklets() {
return workletsLoading;
}

let stream;
// this function should be called on first user interaction (to avoid console warning)
export async function initAudio(options = {}) {
const { disableWorklets = false } = options;
Expand All @@ -112,6 +113,7 @@ export async function initAudio(options = {}) {
} catch (err) {
console.warn('could not load AudioWorklet effects', err);
}
stream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
logger('[superdough] ready');
}
let audioReady;
Expand Down Expand Up @@ -308,6 +310,48 @@ export function resetGlobalEffects() {
analysersData = {};
}

/* async */ function record(name, begin, hapDuration) {
registerSound(
name,
() => {
console.log('trigger recording before its ready...', getAudioContext().currentTime);
},
{},
);
const ac = getAudioContext();
try {
const inputNode = ac.createMediaStreamSource(stream);
const samples = Math.round(hapDuration * ac.sampleRate);
const options = { samples, begin, end: begin + hapDuration };
const recorder = getWorklet(ac, 'recording-processor', {});
recorder.port.postMessage(options);
inputNode.connect(recorder);
/* return */ new Promise((resolve) => {
recorder.port.onmessage = async (e) => {
const audioBuffer = ac.createBuffer(1, samples, ac.sampleRate);
audioBuffer.getChannelData(0).set(e.data.buffer);
const url = `rec:${name}`;
bufferCache[url] = audioBuffer;

Check failure on line 334 in packages/superdough/superdough.mjs

View workflow job for this annotation

GitHub Actions / build (20)

'bufferCache' is not defined
loadCache[url] = audioBuffer;

Check failure on line 335 in packages/superdough/superdough.mjs

View workflow job for this annotation

GitHub Actions / build (20)

'loadCache' is not defined
const value = [url];
console.log('register recording', getAudioContext().currentTime);
registerSound(name, (t, hapValue, onended) => onTriggerSample(t, hapValue, onended, value), {

Check failure on line 338 in packages/superdough/superdough.mjs

View workflow job for this annotation

GitHub Actions / build (20)

'onTriggerSample' is not defined
type: 'sample',
samples: value,
baseUrl: undefined,
prebake: false,
tag: undefined,
});
resolve(name);
};
});
return recorder;
} catch (err) {
console.log('err', err);
reject(err);

Check failure on line 351 in packages/superdough/superdough.mjs

View workflow job for this annotation

GitHub Actions / build (20)

'reject' is not defined
}
}

export const superdough = async (value, t, hapDuration) => {
const ac = getAudioContext();
if (typeof value !== 'object') {
Expand All @@ -330,6 +374,7 @@ export const superdough = async (value, t, hapDuration) => {
// destructure
let {
s = getDefaultValue('s'),
rec,
bank,
source,
gain = getDefaultValue('gain'),
Expand Down Expand Up @@ -411,11 +456,19 @@ export const superdough = async (value, t, hapDuration) => {
if (bank && s) {
s = `${bank}_${s}`;
}
if (rec && getSound(rec)) {
s = rec;
value.s = rec;
}

// get source AudioNode
let sourceNode;
if (source) {
sourceNode = source(t, value, hapDuration);
} else if (rec && !getSound(rec)) {
console.log('record', rec);
const recorder = record(rec, t, hapDuration);
sourceNode = recorder;
} else if (getSound(s)) {
const { onTrigger } = getSound(s);
const soundHandle = await onTrigger(t, value, onended);
Expand Down
44 changes: 44 additions & 0 deletions packages/superdough/worklets.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,47 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor {
}

registerProcessor('supersaw-oscillator', SuperSawOscillatorProcessor);

class RecordingProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.done = false;
this.head = 0;
this.nudge = 0.1;
this.port.onmessage = (e) => {
this.begin = e.data.begin + this.nudge;

this.samples = e.data.samples;
this.buffer = new Float32Array(this.samples);
};
}
process(inputs, outputs) {
// noop if scheduled recording begin hasn't been reached
if (currentTime < this.begin) {

Check failure on line 483 in packages/superdough/worklets.mjs

View workflow job for this annotation

GitHub Actions / build (20)

'currentTime' is not defined
return true;
}
if (!this.buffer) {
console.log('buffer not ready..');
return true;
}
// stop when the buffer is full
if (!this.done && this.head >= this.samples) {
this.done = true;
this.port.postMessage({ buffer: this.buffer });
return false;
}

// so far only 1 channel
const input = inputs[0];
// const output = outputs[0];
for (let i = 0; i < input[0].length; i++) {
this.buffer[this.head] = input[0][i] * 0.25;
/* output[0][i] = input[0][i];
output[1][i] = input[0][i]; */
this.head++;
}
return true;
}
}

registerProcessor('recording-processor', RecordingProcessor);

0 comments on commit 86cc9b8

Please sign in to comment.