rnbo patch as speaker and with 6 inputs working example
parent
f97d3136fe
commit
c5f605395b
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>9 x single-band rnbo patches</title>
|
<title>channel pass through rnbo patches</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,10 +1,11 @@
|
||||||
|
let CONNECTED = false
|
||||||
export function setupPlayButton(
|
export function setupPlayButton(
|
||||||
sources: Map<number, AudioBufferSourceNode>,
|
source: AudioNode[],
|
||||||
gains: Map<number, GainNode>,
|
gain: GainNode,
|
||||||
context: AudioContext
|
context: AudioContext
|
||||||
) {
|
) {
|
||||||
const button = document.createElement('button')
|
const button = document.createElement('button')
|
||||||
button.textContent = '▶ Play All'
|
button.textContent = '▶ Connect Source'
|
||||||
button.style.fontSize = '1.5rem'
|
button.style.fontSize = '1.5rem'
|
||||||
button.style.padding = '10px'
|
button.style.padding = '10px'
|
||||||
button.style.marginTop = '20px'
|
button.style.marginTop = '20px'
|
||||||
|
@ -12,32 +13,22 @@ export function setupPlayButton(
|
||||||
button.onclick = () => {
|
button.onclick = () => {
|
||||||
const startTime = context.currentTime + 0.1 // 100ms in Zukunft
|
const startTime = context.currentTime + 0.1 // 100ms in Zukunft
|
||||||
const rampTime = 2
|
const rampTime = 2
|
||||||
// 1️⃣ Master-Gain auf 0 dB hochrampen
|
if(CONNECTED){
|
||||||
master.gain.setValueAtTime(0.001, startTime)
|
source.forEach((n) => {
|
||||||
master.gain.exponentialRampToValueAtTime(1.0, startTime + rampTime)
|
if(n instanceof AudioBufferSourceNode) {
|
||||||
|
n.disconnect()
|
||||||
sources.forEach((source, freq) => {
|
button.textContent = 'Disconnected...'
|
||||||
const gainNode = gains.get(freq)
|
CONNECTED=false
|
||||||
if (!gainNode) return
|
|
||||||
|
|
||||||
// Ramp up GainNode
|
|
||||||
const gain = gainNode.gain
|
|
||||||
gain.setValueAtTime(0.001, startTime)
|
|
||||||
gain.exponentialRampToValueAtTime(1.0, startTime + rampTime)
|
|
||||||
|
|
||||||
// Start BufferSource
|
|
||||||
try {
|
|
||||||
source.start(startTime)
|
|
||||||
console.log(`Started ${freq}Hz at ${startTime.toFixed(2)}`)
|
|
||||||
} catch (err) {
|
|
||||||
console.warn(`Start error for ${freq}Hz`, err)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}else {
|
||||||
button.disabled = true
|
source.forEach(n => n.start())
|
||||||
button.textContent = '🔊 Playing...'
|
gain.gain.setValueAtTime(0.001, startTime)
|
||||||
|
if(source instanceof AudioBufferSourceNode) source.start(startTime)
|
||||||
|
gain.gain.linearRampToValueAtTime(1, startTime+rampTime)
|
||||||
|
button.textContent = '🔊 Connected and Playing...'
|
||||||
|
CONNECTED=true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.appendChild(button)
|
document.body.appendChild(button)
|
||||||
}
|
}
|
||||||
|
|
411
src/main.ts
411
src/main.ts
|
@ -1,129 +1,291 @@
|
||||||
import { createDevice, type Device } from '@rnbo/js'
|
import RNBO, { createDevice, TimeNow } from '@rnbo/js'
|
||||||
import { attachDBValueListener } from './dbValueListener'
|
import { fetchOrLoadBuffer } from './composables/BufferStore'
|
||||||
import { fetchOrLoadBuffer, createBufferSource } from './composables/BufferStore'
|
|
||||||
import { setupPlayButton } from './composables/Player';
|
import { setupPlayButton } from './composables/Player';
|
||||||
import { getPreferredDevice } from './lib/microphone';
|
|
||||||
import { getAudioContext } from './audio/context'
|
import { getAudioContext } from './audio/context'
|
||||||
|
|
||||||
import patcherUrl from '../public/patches/controlvalue/LAF-Controll-Values_Simple_Band1000.rnbopat.export.json?url'
|
import patcherUrl from '../public/patches/passthrough_6Kanal_Test.rnbopat.export.json?url'
|
||||||
|
|
||||||
interface DeviceInfo {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
device: Device;
|
|
||||||
audioNode: AudioNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// RNBO
|
// RNBO
|
||||||
// RNBO
|
// RNBO
|
||||||
let patcherPromise: Promise<any>
|
let patcherPromise: Promise<any>
|
||||||
|
|
||||||
// create context & master gain
|
|
||||||
const mobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Gains to control the audio
|
|
||||||
//
|
|
||||||
|
|
||||||
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() {
|
async function getPatcher() {
|
||||||
if (!patcherPromise) patcherPromise = fetch(patcherUrl).then(r => r.json())
|
if (!patcherPromise) patcherPromise = fetch(patcherUrl).then(r => r.json())
|
||||||
return patcherPromise
|
return patcherPromise
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
// Schritt 0: Mic-Recht
|
|
||||||
await initMicrophone()
|
|
||||||
// Schritt 1: Kontext
|
// Schritt 1: Kontext
|
||||||
const ctx = getAudioContext()
|
const ctx = getAudioContext()
|
||||||
await ctx.suspend()
|
await ctx.suspend()
|
||||||
|
initMicrophone(ctx)
|
||||||
// Schritt 2: Fetch Patcher
|
// Schritt 2: Fetch Patcher
|
||||||
await getPatcher()
|
await getPatcher()
|
||||||
// Schritt 3: Erstelle alle Devices
|
// Schritt 3: Erstelle alle Devices
|
||||||
const newDevices = await Promise.all(
|
const patcher = await getPatcher()
|
||||||
bands.map(async (freq, index) => {
|
const rnboDevice = await createDevice({
|
||||||
const device = await createBandDevice(freq, ctx) as DeviceInfo
|
context: ctx,
|
||||||
|
patcher
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// Initiale Werte
|
||||||
|
const events = {
|
||||||
|
/**
|
||||||
|
* Schickt einen numerischen Event-Input an das Gerät.
|
||||||
|
* @param {string} name – genau wie im Max-Patch benannt
|
||||||
|
* @param {number} val
|
||||||
|
* @param {number} [when=context.currentTime] – Sekunden relativ zur AudioClock
|
||||||
|
*/
|
||||||
|
send(name: any, val: any, when = TimeNow) {
|
||||||
|
const event = new RNBO.MessageEvent(when, name, val)
|
||||||
|
rnboDevice.scheduleEvent(event)
|
||||||
|
console.log(`Input Event ${name} schedueled.`)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// PARAMETER
|
||||||
|
// PARAMETER
|
||||||
|
// PARAMETER
|
||||||
|
|
||||||
|
let in7 = 0 // Hier soll das aus dem jeweiligen Textfeld
|
||||||
|
let in8 = 0
|
||||||
|
let in9 = 0
|
||||||
|
let in10 = 0
|
||||||
|
let in11 = 0
|
||||||
|
let in12 = 0
|
||||||
|
|
||||||
|
events.send("in7", in7) // channel 1 auf mute
|
||||||
|
events.send("in8", in8) // channel 2 auf mute
|
||||||
|
events.send("in9", in9) // channel 3 auf mute
|
||||||
|
events.send("in10", in10) // channel 4 auf mute
|
||||||
|
events.send("in11", in11) // channel 5 auf mute
|
||||||
|
events.send("in12", in12) // channel 6 auf mute
|
||||||
|
console.log("Alle Ins gesetzt")
|
||||||
|
|
||||||
// Schritt 4 Fetch alle AudioBuffer
|
// Schritt 4 Fetch alle AudioBuffer
|
||||||
await fetchOrLoadBuffer(freq.toString(),
|
const buffer1 = await fetchOrLoadBuffer("100000",
|
||||||
urlsWebM[index].url,
|
'audio/LMusik_RSprache.mp3',
|
||||||
ctx
|
ctx
|
||||||
)
|
)
|
||||||
const sourceNode = await createBufferSource(freq.toString(),ctx)
|
|
||||||
const bandGainNode = ctx.createGain()
|
const buffer2 = await fetchOrLoadBuffer("100001",
|
||||||
|
'audio/stereo-test1.mp3',
|
||||||
|
ctx
|
||||||
|
)
|
||||||
|
|
||||||
|
const buffer3 = await fetchOrLoadBuffer("100002",
|
||||||
|
'audio/stereo-test2.wav',
|
||||||
|
ctx
|
||||||
|
)
|
||||||
|
const sourceNode1 = ctx.createBufferSource() // stereo
|
||||||
|
const sourceNode2 = ctx.createBufferSource() // stereo
|
||||||
|
const sourceNode3 = ctx.createBufferSource() // stereo
|
||||||
|
|
||||||
|
|
||||||
|
sourceNode1.buffer = buffer1
|
||||||
|
sourceNode2.buffer = buffer2
|
||||||
|
sourceNode3.buffer = buffer3
|
||||||
|
sourceNode1.loop = true
|
||||||
|
sourceNode2.loop = true
|
||||||
|
sourceNode3.loop = true
|
||||||
|
|
||||||
|
const channelMergerL = ctx.createChannelMerger()
|
||||||
|
const channelMergerR = ctx.createChannelMerger()
|
||||||
|
|
||||||
|
|
||||||
|
// CHANNEL INTERPRETATION AND COUNT MODE
|
||||||
|
channelMergerL.channelInterpretation = 'speakers'
|
||||||
|
channelMergerR.channelInterpretation = 'speakers'
|
||||||
|
rnboDevice.node.channelCountMode = 'max'
|
||||||
|
rnboDevice.node.channelInterpretation = 'speakers'
|
||||||
|
|
||||||
|
const inputGainNode = ctx.createGain()
|
||||||
|
|
||||||
|
// Connecte Audios auf ein Merger
|
||||||
|
|
||||||
|
const { left: sourceNode1L, right: sourceNode1R } = splitStereoIntoMonoChannels(sourceNode1)
|
||||||
|
const {left: sourceNode2L, right: sourceNode2R } = splitStereoIntoMonoChannels(sourceNode2)
|
||||||
|
const {left: sourceNode3L, right: sourceNode3R } = splitStereoIntoMonoChannels(sourceNode3)
|
||||||
|
console.log({sourceNode1L, sourceNode1R }, {sourceNode2L, sourceNode2R },{sourceNode3L, sourceNode3R })
|
||||||
|
console.log({ channelMergerL, channelMergerR })
|
||||||
|
|
||||||
|
|
||||||
|
// sourceNode1L.connect(ctx.destination, 0, 0)
|
||||||
|
|
||||||
|
sourceNode1L.connect(rnboDevice.node, 0, 0)
|
||||||
|
sourceNode1R.connect(rnboDevice.node, 0, 1)
|
||||||
|
|
||||||
|
sourceNode2L.connect(rnboDevice.node, 0, 2)
|
||||||
|
sourceNode2R.connect(rnboDevice.node, 0 ,3)
|
||||||
|
sourceNode3L.connect(rnboDevice.node, 0 ,4)
|
||||||
|
sourceNode3R.connect(rnboDevice.node, 0, 5)
|
||||||
|
|
||||||
|
const channelSplitter = ctx.createChannelSplitter(6)
|
||||||
|
|
||||||
|
channelMergerL.connect(channelSplitter)
|
||||||
|
|
||||||
// Schritt 5 Verbinde AudioNodes
|
// Schritt 5 Verbinde AudioNodes
|
||||||
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}
|
|
||||||
}
|
|
||||||
))
|
|
||||||
registerDevicesWithListeners(newDevices)
|
|
||||||
console.log("RNBO Devices created")
|
|
||||||
|
|
||||||
devices.forEach((devices, freq)=> {
|
// channelMerger.connect(ctx.destination) // 6
|
||||||
try{
|
|
||||||
if(devices && devices.device) {
|
rnboDevice.node.connect(ctx.destination)
|
||||||
attachDBValueListener(devices.device,freq)
|
console.log("RNBO Device created connected with input source")
|
||||||
}
|
|
||||||
}catch {
|
|
||||||
console.warn("couldnt attach rnbo listener")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
console.log("Everything is setup, display a play button")
|
console.log("Everything is setup, display a play button")
|
||||||
setupPlayButton(sources, gains,ctx)
|
setupPlayButton([sourceNode1,sourceNode2,sourceNode3], inputGainNode , ctx)
|
||||||
|
|
||||||
masterGain.connect(ctx.destination)
|
// UI
|
||||||
|
|
||||||
|
const ui = document.createElement('div')
|
||||||
|
ui.style.padding = '20px'
|
||||||
|
|
||||||
|
// Report-Infos anhängen
|
||||||
|
ui.appendChild(createNodeInspector('RNBO Device', rnboDevice.node))
|
||||||
|
|
||||||
|
ui.appendChild(createNodeInspectorAndToggle('Scape Source LEFT 1', sourceNode1L, 0, events))
|
||||||
|
ui.appendChild(createNodeInspectorAndToggle('Scape Source 2', sourceNode2, 1, events))
|
||||||
|
ui.appendChild(createNodeInspectorAndToggle('Scape Source 3', sourceNode3, 2, events))
|
||||||
|
ui.appendChild(createNodeInspector('ChannelMerger', channelMergerL))
|
||||||
|
ui.appendChild(createNodeInspector('ChannelMerger', channelMergerR))
|
||||||
|
ui.appendChild(createNodeInspector('channelSplitter', channelSplitter))
|
||||||
|
|
||||||
|
console.log("rnboDevice.numInputChannels= "+ rnboDevice.numInputChannels)
|
||||||
|
console.log("rnboDevice.numOutputChannels = "+ rnboDevice.numOutputChannels)
|
||||||
|
console.log("rnboDevice.node.numberOfInputs = "+ rnboDevice.node.numberOfInputs)
|
||||||
|
console.log("rnboDevice.node.numberOfOutputs = "+ rnboDevice.node.numberOfOutputs)
|
||||||
|
|
||||||
|
document.body.appendChild(ui)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init() // hey ho lets go
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables the Microphone to use it.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function registerDevicesWithListeners(newDevices: { freq: number, device: any }[]) {
|
// resume only after interaction inside the iframe (mobile safe)
|
||||||
const devices = new Map<number, any>()
|
window.addEventListener('click', () => getAudioContext().resume(), { once: true })
|
||||||
|
console.log("Await unlock context")
|
||||||
|
|
||||||
newDevices.forEach(({ freq, device }) => {
|
|
||||||
devices.set(freq, device)
|
|
||||||
|
|
||||||
try {
|
function createNodeInspector(label: string, node: AudioNode): HTMLElement {
|
||||||
if (device) {
|
const container = document.createElement('div')
|
||||||
attachDBValueListener(device, freq)
|
container.style.marginTop = '10px'
|
||||||
|
container.style.padding = '8px'
|
||||||
|
container.style.border = '1px solid #ccc'
|
||||||
|
container.style.borderRadius = '6px'
|
||||||
|
container.style.fontFamily = 'monospace'
|
||||||
|
container.style.background = '#f9f9f9'
|
||||||
|
|
||||||
|
const title = document.createElement('strong')
|
||||||
|
title.textContent = `🎧 ${label}`
|
||||||
|
container.appendChild(title)
|
||||||
|
|
||||||
|
const list = document.createElement('ul')
|
||||||
|
list.style.listStyle = 'none'
|
||||||
|
list.style.padding = '4px'
|
||||||
|
list.style.margin = '4px 0'
|
||||||
|
|
||||||
|
const addRow = (key: string, value: string | number) => {
|
||||||
|
|
||||||
|
const li = document.createElement('li')
|
||||||
|
li.textContent = `${key}: ${value}`
|
||||||
|
list.appendChild(li)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.warn(`Could not attach RNBO listener for ${freq}Hz`, err)
|
addRow('channelCount', node.channelCount)
|
||||||
|
addRow('channelCountMode', node.channelCountMode)
|
||||||
|
addRow('channelInterpretation', node.channelInterpretation)
|
||||||
|
addRow('numberOfInputs', node.numberOfInputs)
|
||||||
|
addRow('numberOfOutputs', node.numberOfOutputs)
|
||||||
|
|
||||||
|
container.appendChild(list)
|
||||||
|
return container
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNodeInspectorAndToggle(label: string, node: AudioNode, sourceIndex: number, events: { send: (tag: string, value: number) => void }): HTMLElement {
|
||||||
|
const container = document.createElement('div')
|
||||||
|
container.style.marginTop = '10px'
|
||||||
|
container.style.padding = '8px'
|
||||||
|
container.style.border = '1px solid #ccc'
|
||||||
|
container.style.borderRadius = '6px'
|
||||||
|
container.style.fontFamily = 'monospace'
|
||||||
|
container.style.background = '#f9f9f9'
|
||||||
|
|
||||||
|
const title = document.createElement('strong')
|
||||||
|
title.textContent = `🎧 ${label}`
|
||||||
|
container.appendChild(title)
|
||||||
|
|
||||||
|
const list = document.createElement('ul')
|
||||||
|
list.style.listStyle = 'none'
|
||||||
|
list.style.padding = '4px'
|
||||||
|
list.style.margin = '4px 0'
|
||||||
|
|
||||||
|
const addRow = (key: string, value: string | number) => {
|
||||||
|
const li = document.createElement('li')
|
||||||
|
li.textContent = `${key}: ${value}`
|
||||||
|
list.appendChild(li)
|
||||||
|
}
|
||||||
|
|
||||||
|
addRow('channelCount', node.channelCount)
|
||||||
|
addRow('channelCountMode', node.channelCountMode)
|
||||||
|
addRow('channelInterpretation', node.channelInterpretation)
|
||||||
|
addRow('numberOfInputs', node.numberOfInputs)
|
||||||
|
addRow('numberOfOutputs', node.numberOfOutputs)
|
||||||
|
|
||||||
|
container.appendChild(list)
|
||||||
|
if(node instanceof AudioBufferSourceNode || node instanceof GainNode) {
|
||||||
|
// Button-UI für L/R Kanalsteuerung (toggle 0–1–2)
|
||||||
|
const buttonContainer = document.createElement('div')
|
||||||
|
buttonContainer.style.marginTop = '8px'
|
||||||
|
|
||||||
|
;['L', 'R'].forEach((side, i) => {
|
||||||
|
const modeBtn = document.createElement('button')
|
||||||
|
modeBtn.textContent = `Mute ${side}: 0`
|
||||||
|
modeBtn.style.marginRight = '8px'
|
||||||
|
|
||||||
|
let currentMode = 0
|
||||||
|
const paramTag = `in${7 + sourceIndex * 2 + i}` // z. B. in7, in8, ..., in12
|
||||||
|
|
||||||
|
modeBtn.addEventListener('click', () => {
|
||||||
|
currentMode = (currentMode + 1) % 3
|
||||||
|
modeBtn.textContent = `Mute ${side}: ${currentMode}`
|
||||||
|
events.send(paramTag, currentMode)
|
||||||
|
console.log(`🔁 Sent ${paramTag} = ${currentMode}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log("✅ RNBO Devices registered and listeners attached")
|
buttonContainer.appendChild(modeBtn)
|
||||||
return devices
|
})
|
||||||
|
|
||||||
|
container.appendChild(buttonContainer)
|
||||||
|
}
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitStereoIntoMonoChannels(node: AudioNode): { left: GainNode, right: GainNode } {
|
||||||
|
console.log("BEFORE SPLIT: channelCount -> "+ node.channelCount)
|
||||||
|
const ctx = getAudioContext()
|
||||||
|
const splitter = ctx.createChannelSplitter(2)
|
||||||
|
|
||||||
|
node.connect(splitter)
|
||||||
|
|
||||||
|
const leftGain = ctx.createGain()
|
||||||
|
leftGain.channelCount = 1
|
||||||
|
leftGain.channelCountMode = 'explicit'
|
||||||
|
leftGain.channelInterpretation = 'discrete'
|
||||||
|
leftGain.gain.setValueAtTime(1,leftGain.context.currentTime)
|
||||||
|
|
||||||
|
|
||||||
|
const rightGain = ctx.createGain()
|
||||||
|
rightGain.channelCount = 1
|
||||||
|
rightGain.channelCountMode = 'explicit'
|
||||||
|
rightGain.channelInterpretation = 'discrete'
|
||||||
|
rightGain.gain.setValueAtTime(1,rightGain.context.currentTime)
|
||||||
|
splitter.connect(leftGain, 0) // Kanal 0 = Links
|
||||||
|
splitter.connect(rightGain, 1) // Kanal 1 = Rechts
|
||||||
|
|
||||||
|
console.log("AFTER SPLIT: channelCount -> ",{ left: leftGain, right: rightGain } )
|
||||||
|
return { left: leftGain, right: rightGain }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,15 +293,21 @@ function registerDevicesWithListeners(newDevices: { freq: number, device: any }[
|
||||||
* Enables the Microphone to use it.
|
* Enables the Microphone to use it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function initMicrophone() {
|
async function initMicrophone(ctx:AudioContext) {
|
||||||
|
try {
|
||||||
|
await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||||
|
} catch {
|
||||||
|
window.alert("Keine Zugriff aufs Mikrofon.")
|
||||||
|
}
|
||||||
|
const availableDevices = await updateAvailableDevices()
|
||||||
|
console.log("MIKROFONE ", {availableDevices})
|
||||||
|
const preferredDevice = availableDevices[3]
|
||||||
|
const deviceJabra = "fdab2256f4654ac7c3fb7c7f334d88a6e468cf155fb00bb4c33d0f2f2479f653"
|
||||||
|
const externesMic = "276fc63a34034d08b7d5957cda539d268d20598e1708932112de199d85cfed1f"
|
||||||
try {
|
try {
|
||||||
const preferredDevice = await getPreferredDevice()
|
|
||||||
console.log("INIT MIC", { preferredDevice})
|
|
||||||
if(preferredDevice){
|
|
||||||
console.log("prefered device !!! ")
|
console.log("prefered device !!! ")
|
||||||
const constraints: MediaStreamConstraints = {
|
const constraints: MediaStreamConstraints = {
|
||||||
audio: { deviceId: { exact: preferredDevice.deviceId }, echoCancellation: false, noiseSuppression: false,
|
audio: { deviceId: { exact: "276fc63a34034d08b7d5957cda539d268d20598e1708932112de199d85cfed1f" }, echoCancellation: false, noiseSuppression: false,
|
||||||
autoGainControl: false },
|
autoGainControl: false },
|
||||||
video: false
|
video: false
|
||||||
}
|
}
|
||||||
|
@ -147,53 +315,32 @@ async function initMicrophone() {
|
||||||
constraints
|
constraints
|
||||||
)
|
)
|
||||||
const source = ctx.createMediaStreamSource(stream)
|
const source = ctx.createMediaStreamSource(stream)
|
||||||
|
createNodeInspector("MIKROFON", source)
|
||||||
console.log("SOURCE MICROFON", {source})
|
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) {
|
} catch (error) {
|
||||||
console.error("Error!: connecting the microphone to deivces... connected")
|
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 function updates the List of possible microphone sources.
|
||||||
* this devices sends out control Values as messages.
|
|
||||||
* @param freq
|
|
||||||
* @param ctx
|
|
||||||
* @returns RNBO Device
|
|
||||||
*/
|
*/
|
||||||
async function createBandDevice(freq: number, ctx: AudioContext): Promise<DeviceInfo> {
|
async function updateAvailableDevices (): Promise<MediaDeviceInfo[]> {
|
||||||
const patcher = await getPatcher()
|
try {
|
||||||
const rnboDevice = await createDevice({
|
const devices = await navigator.mediaDevices.enumerateDevices()
|
||||||
context: ctx,
|
const newDevices = devices.filter(
|
||||||
patcher
|
device => device.kind === 'audioinput'
|
||||||
})
|
)
|
||||||
const param = rnboDevice.parametersById.get('centerFrequency')
|
if (newDevices.length === 0) {
|
||||||
param.value = freq
|
console.warn(
|
||||||
console.log("Device created for "+ freq)
|
'No audio input devices found. This might be due to missing permissions or no connected devices.'
|
||||||
return { id: freq, name: ""+freq, device: rnboDevice, audioNode: rnboDevice.node }
|
)
|
||||||
|
}
|
||||||
|
return newDevices
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error enumerating devices:', error)
|
||||||
|
}
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
Loading…
Reference in New Issue