adds mono-mode

This commit is contained in:
André Michelle
2026-01-20 11:33:03 +01:00
parent 0577102a4b
commit c8adb45885
3 changed files with 36 additions and 30 deletions
+1
View File
@@ -20,3 +20,4 @@ lerna-debug.log
/certs/localhost-key.pem /certs/localhost-key.pem
/packages/app/soundfont/public/soundfonts/ /packages/app/soundfont/public/soundfonts/
/packages/studio/scripting/src/api.declaration.d.ts /packages/studio/scripting/src/api.declaration.d.ts
tsconfig.tsbuildinfo
@@ -28,7 +28,7 @@ type Construct = {
} }
export const AudioUnitChannelControls = ({lifecycle, service, adapter}: Construct) => { export const AudioUnitChannelControls = ({lifecycle, service, adapter}: Construct) => {
const {project, audioContext} = service const {project} = service
const {captureDevices, editing, midiLearning} = project const {captureDevices, editing, midiLearning} = project
const {volume, panning, mute, solo} = adapter.namedParameter const {volume, panning, mute, solo} = adapter.namedParameter
const volumeControl = ( const volumeControl = (
@@ -77,29 +77,34 @@ export const AudioUnitChannelControls = ({lifecycle, service, adapter}: Construc
attachParameterContextMenu(editing, midiLearning, adapter.tracks, panning, panningControl), attachParameterContextMenu(editing, midiLearning, adapter.tracks, panning, panningControl),
attachParameterContextMenu(editing, midiLearning, adapter.tracks, mute, muteControl), attachParameterContextMenu(editing, midiLearning, adapter.tracks, mute, muteControl),
attachParameterContextMenu(editing, midiLearning, adapter.tracks, solo, soloControl), attachParameterContextMenu(editing, midiLearning, adapter.tracks, solo, soloControl),
captureOption.ifSome(capture => { captureOption.match({
if (!isInstanceOf(capture, CaptureAudio)) {return} none: () => Terminable.Empty,
const streamLifeCycle = lifecycle.own(new Terminator()) some: capture => {
capture.stream.catchupAndSubscribe(optStream => { if (!isInstanceOf(capture, CaptureAudio)) {return Terminable.Empty}
streamRunning = false const meterLifeCycle = lifecycle.own(new Terminator())
streamLifeCycle.terminate() const connectMeter = () => {
return optStream.ifSome(stream => { streamRunning = false
const numberOfChannels = stream.getAudioTracks().at(0)?.getSettings().channelCount ?? 2 meterLifeCycle.terminate()
const meterWorklet = service.audioWorklets.createMeter(numberOfChannels) capture.outputNode.ifSome(outputNode => {
const streamSource = audioContext.createMediaStreamSource(stream) const numberOfChannels = capture.effectiveChannelCount
streamSource.connect(meterWorklet) const meterWorklet = service.audioWorklets.createMeter(numberOfChannels)
streamRunning = true outputNode.connect(meterWorklet)
streamLifeCycle.ownAll( streamRunning = true
Terminable.create(() => streamSource.disconnect()), meterLifeCycle.ownAll(
meterWorklet.subscribe(({peak}) => { meterWorklet.subscribe(({peak}) => {
peaksInDb[0] = gainToDb(peak[0]) peaksInDb[0] = gainToDb(peak[0])
peaksInDb[1] = gainToDb(peak[1] ?? peak[0]) peaksInDb[1] = gainToDb(peak[1] ?? peak[0])
}), }),
meterWorklet meterWorklet
) )
}) })
}) }
}) ?? Terminable.Empty, return Terminable.many(
capture.stream.catchupAndSubscribe(() => connectMeter()),
capture.captureBox.requestChannels.subscribe(() => connectMeter())
)
}
}),
project.liveStreamReceiver.subscribeFloats(adapter.address, values => { project.liveStreamReceiver.subscribeFloats(adapter.address, values => {
if (streamRunning) {return} if (streamRunning) {return}
peaksInDb[0] = gainToDb(values[0]) peaksInDb[0] = gainToDb(values[0])
@@ -25,7 +25,6 @@ export class CaptureAudio extends Capture<CaptureAudioBox> {
#isMonitoring: boolean = false #isMonitoring: boolean = false
#requestChannels: Option<1 | 2> = Option.None #requestChannels: Option<1 | 2> = Option.None
#gainDb: number = 0.0 #gainDb: number = 0.0
#audioChain: Nullable<{ #audioChain: Nullable<{
sourceNode: MediaStreamAudioSourceNode sourceNode: MediaStreamAudioSourceNode
gainNode: GainNode gainNode: GainNode
@@ -78,9 +77,7 @@ export class CaptureAudio extends Capture<CaptureAudioBox> {
} }
get gainDb(): number {return this.#gainDb} get gainDb(): number {return this.#gainDb}
get requestChannels(): Option<1 | 2> {return this.#requestChannels} get requestChannels(): Option<1 | 2> {return this.#requestChannels}
set requestChannels(value: 1 | 2) { set requestChannels(value: 1 | 2) {this.captureBox.requestChannels.setValue(value)}
this.captureBox.requestChannels.setValue(value)
}
get stream(): MutableObservableOption<MediaStream> {return this.#stream} get stream(): MutableObservableOption<MediaStream> {return this.#stream}
get streamDeviceId(): Option<string> { get streamDeviceId(): Option<string> {
return this.streamMediaTrack.map(settings => settings.getSettings().deviceId ?? "") return this.streamMediaTrack.map(settings => settings.getSettings().deviceId ?? "")
@@ -90,6 +87,8 @@ export class CaptureAudio extends Capture<CaptureAudioBox> {
get streamMediaTrack(): Option<MediaStreamTrack> { get streamMediaTrack(): Option<MediaStreamTrack> {
return this.#stream.flatMap(stream => Option.wrap(stream.getAudioTracks().at(0))) return this.#stream.flatMap(stream => Option.wrap(stream.getAudioTracks().at(0)))
} }
get outputNode(): Option<AudioNode> {return Option.wrap(this.#audioChain?.gainNode)}
get effectiveChannelCount(): number {return this.#audioChain?.gainNode.channelCount ?? 1}
async prepareRecording(): Promise<void> { async prepareRecording(): Promise<void> {
const {project} = this.manager const {project} = this.manager
@@ -157,8 +156,8 @@ export class CaptureAudio extends Capture<CaptureAudioBox> {
const gotDeviceId = settings?.deviceId const gotDeviceId = settings?.deviceId
console.debug(`new stream. device requested: ${deviceId ?? "default"}, got: ${gotDeviceId ?? "unknown"}. channelCount requested: ${channelCount}, got: ${settings?.channelCount}`) console.debug(`new stream. device requested: ${deviceId ?? "default"}, got: ${gotDeviceId ?? "unknown"}. channelCount requested: ${channelCount}, got: ${settings?.channelCount}`)
if (isUndefined(deviceId) || deviceId === gotDeviceId) { if (isUndefined(deviceId) || deviceId === gotDeviceId) {
this.#stream.wrap(stream)
this.#rebuildAudioChain(stream) this.#rebuildAudioChain(stream)
this.#stream.wrap(stream)
} else { } else {
stream.getAudioTracks().forEach(track => track.stop()) stream.getAudioTracks().forEach(track => track.stop())
return Errors.warn(`Could not find audio device with id: '${deviceId}' (got: '${gotDeviceId}')`) return Errors.warn(`Could not find audio device with id: '${deviceId}' (got: '${gotDeviceId}')`)
@@ -209,7 +208,8 @@ export class CaptureAudio extends Capture<CaptureAudioBox> {
#disconnectMonitoring(): void { #disconnectMonitoring(): void {
if (isDefined(this.#audioChain)) { if (isDefined(this.#audioChain)) {
this.#audioChain.gainNode.disconnect() const {audioContext} = this.manager.project.env
this.#audioChain.gainNode.disconnect(audioContext.destination)
} }
} }
} }