170 lines
5.5 KiB
TypeScript
170 lines
5.5 KiB
TypeScript
import { createDevice, type Device } from '@rnbo/js'
|
|
import { attachDBValueListener } from './dbValueListener'
|
|
import { fetchOrLoadBuffer, createBufferSource } from './composables/BufferStore'
|
|
import { setupPlayButton } from './composables/Player';
|
|
import { getPreferredDevice } from './lib/microphone';
|
|
import patcherUrl from '../public/patches/controlvalue/LAF-Controll-Values_Simple_Band1000.rnbopat.export.json?url'
|
|
|
|
interface DeviceInfo {
|
|
id: number;
|
|
name: string;
|
|
device: Device;
|
|
audioNode: AudioNode;
|
|
}
|
|
|
|
// RNBO
|
|
// RNBO
|
|
let patcherPromise: Promise<any>
|
|
|
|
// create context & master gain
|
|
let WAContext = window.AudioContext || window.webkitAudioContext
|
|
const ctx = new WAContext() // match your assets
|
|
const mobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent)
|
|
console.log("AudioContext created")
|
|
|
|
//
|
|
// Gains to control the audio
|
|
//
|
|
const masterGain = ctx.createGain()
|
|
|
|
const bands = mobile ? [150, 1500, 8000] : [63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000]
|
|
const devices = new Map<number, DeviceInfo>()
|
|
const sources = new Map<number, AudioBufferSourceNode>()
|
|
const gains = new Map<number, GainNode>()
|
|
|
|
// Audio Files
|
|
const urlsWebM = mobile ? [
|
|
{ name: bands[0], url: 'audio/masking/3bands/low_band_256kbps.webm' },
|
|
{ name: bands[1], url: 'audio/masking/3bands/mid_band_256kbps.webm' },
|
|
{ name: bands[2], url: 'audio/masking/3bands/high_band_256kbps.webm' },
|
|
] :
|
|
[
|
|
{ name: bands[0], url: 'audio/masking/9bands/Quellrauschen63.webm' },
|
|
{ name: bands[1], url: 'audio/masking/9bands/Quellrauschen125.webm' },
|
|
{ name: bands[2], url: 'audio/masking/9bands/Quellrauschen250.webm' },
|
|
{ name: bands[3], url: 'audio/masking/9bands/Quellrauschen500.webm' },
|
|
{ name: bands[4], url: 'audio/masking/9bands/Quellrauschen1000.webm' },
|
|
{ name: bands[5], url: 'audio/masking/9bands/Quellrauschen2000.webm' },
|
|
{ name: bands[6], url: 'audio/masking/9bands/Quellrauschen4000.webm' },
|
|
{ name: bands[7], url: 'audio/masking/9bands/Quellrauschen8000.webm' },
|
|
{ name: bands[8], url: 'audio/masking/9bands/Quellrauschen16000.webm' },
|
|
]
|
|
|
|
async function getPatcher() {
|
|
if (!patcherPromise) patcherPromise = fetch(patcherUrl).then(r => r.json())
|
|
return patcherPromise
|
|
}
|
|
|
|
async function init() {
|
|
await getPatcher()
|
|
const newDevices = await Promise.all(
|
|
bands.map(async (freq, index) => {
|
|
const device = await createBandDevice(freq, ctx) as DeviceInfo
|
|
await fetchOrLoadBuffer(freq.toString(),
|
|
urlsWebM[index].url,
|
|
ctx
|
|
)
|
|
const sourceNode = await createBufferSource(freq.toString(),ctx)
|
|
const bandGainNode = ctx.createGain()
|
|
// patch them together
|
|
sourceNode.node?.connect(bandGainNode)
|
|
bandGainNode.connect(masterGain)
|
|
//update the source list
|
|
if(sourceNode.node instanceof AudioBufferSourceNode && bandGainNode instanceof GainNode ) {
|
|
sources.set(freq, sourceNode.node)
|
|
gains.set(freq, bandGainNode)
|
|
}
|
|
return {freq, device}
|
|
}
|
|
))
|
|
newDevices.forEach(({freq, device}) => {
|
|
devices.set(freq, device)
|
|
})
|
|
console.log("RNBO Devices created")
|
|
|
|
await initMicrophone()
|
|
devices.forEach((devices, freq)=> {
|
|
try{
|
|
if(devices && devices.device) {
|
|
attachDBValueListener(devices.device,freq)
|
|
}
|
|
}catch {
|
|
console.warn("couldnt attach rnbo listener")
|
|
}
|
|
})
|
|
console.log("Everything is setup, display a play button")
|
|
setupPlayButton(sources, gains,ctx)
|
|
masterGain.connect(ctx.destination)
|
|
}
|
|
|
|
/**
|
|
* Enables the Microphone to use it.
|
|
*/
|
|
|
|
async function initMicrophone() {
|
|
|
|
try {
|
|
const preferredDevice = await getPreferredDevice()
|
|
console.log("INIT MIC", { preferredDevice})
|
|
if(preferredDevice){
|
|
console.log("prefered device !!! ")
|
|
const constraints: MediaStreamConstraints = {
|
|
audio: { deviceId: { exact: preferredDevice.deviceId }, echoCancellation: false, noiseSuppression: false,
|
|
autoGainControl: false },
|
|
video: false
|
|
}
|
|
const stream = await navigator.mediaDevices.getUserMedia(
|
|
constraints
|
|
)
|
|
const source = ctx.createMediaStreamSource(stream)
|
|
console.log("SOURCE MICROFON", {source})
|
|
const splitter = ctx.createChannelSplitter(source.channelCount)
|
|
|
|
try {
|
|
source.connect(splitter)
|
|
devices.forEach((device, freq) => {
|
|
splitter.connect(device.audioNode, 0, 0)
|
|
console.log('🎤 Mikrofon mono-signal wurde an RNBO-Device mit Freq: '+freq+' geroutet.')
|
|
})
|
|
} catch (error) {
|
|
console.error("Error!: connecting the microphone to deivces... connected")
|
|
}
|
|
|
|
|
|
|
|
|
|
}else {
|
|
console.log("Das Gerät wurde nicht gefunden. Lieber nochmal den Mikrofon-Zugriff checken")
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
console.error('❌ Fehler beim Zugriff auf Mikrofon:', err)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function creates a RNBODevice with a given frequency. If the microphone is connected
|
|
* this devices sends out control Values as messages.
|
|
* @param freq
|
|
* @param ctx
|
|
* @returns RNBO Device
|
|
*/
|
|
async function createBandDevice(freq: number, ctx: AudioContext): Promise<DeviceInfo> {
|
|
const patcher = await getPatcher()
|
|
const rnboDevice = await createDevice({
|
|
context: ctx,
|
|
patcher
|
|
})
|
|
const param = rnboDevice.parametersById.get('centerFrequency')
|
|
param.value = freq
|
|
console.log("Device created for "+ freq)
|
|
return { id: freq, name: ""+freq, device: rnboDevice, audioNode: rnboDevice.node }
|
|
}
|
|
|
|
init() // hey ho lets go
|
|
|
|
// resume only after interaction inside the iframe (mobile safe)
|
|
window.addEventListener('click', () => ctx.resume(), { once: true })
|
|
console.log("Await unlock context")
|