replace AudioFileBox with new UUID

This commit is contained in:
André Michelle
2026-03-12 09:12:52 +01:00
parent e44aaa58f6
commit 2b68a217bd
6 changed files with 54 additions and 13 deletions
+3 -3
View File
@@ -8329,9 +8329,9 @@
"license": "MIT"
},
"node_modules/mediabunny": {
"version": "1.39.0",
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.39.0.tgz",
"integrity": "sha512-NDeht2Sf7kYfrUu7b/9AhVK4KdQhy6UeTF/EPNXW+PdpIYvSv36ZQ/SRBoFAJN2LRl6XrGb8JYY4MCnhsGQ2yg==",
"version": "1.39.1",
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.39.1.tgz",
"integrity": "sha512-gaQKDWtfLhgSN9tRj37lgznHf9MOvey7sbT6GdQA7mWl1kaE5R+P1lfl9CDsgtzongx12IG9X/y2vgZmTHzGDg==",
"license": "MPL-2.0",
"workspaces": [
"packages/*"
+1
View File
@@ -22,6 +22,7 @@ export namespace AssetService {
export type ImportArgs = {
uuid?: UUID.Bytes
name?: string,
bpm?: number,
arrayBuffer: ArrayBuffer,
progressHandler?: Progress.Handler,
origin?: "import" | "recording"
+17 -2
View File
@@ -1,4 +1,15 @@
import {int, Notifier, Observer, Option, panic, Procedure, Subscription, Terminable, Terminator, UUID} from "@opendaw/lib-std"
import {
int,
Notifier,
Observer,
Option,
panic,
Procedure,
Subscription,
Terminable,
Terminator,
UUID
} from "@opendaw/lib-std"
import {AudioData} from "@opendaw/lib-dsp"
import {Peaks} from "@opendaw/lib-fusion"
import {mergeChunkPlanes, RingBuffer, SampleLoader, SampleLoaderState} from "@opendaw/studio-adapters"
@@ -23,6 +34,7 @@ export class RecordingWorklet extends AudioWorkletNode implements Terminable, Sa
#state: SampleLoaderState = {type: "record"}
#onSaved: Option<Procedure<UUID.Bytes>> = Option.None
#sampleService: Option<SampleService> = Option.None
#bpm: Option<number> = Option.None
constructor(context: BaseAudioContext, config: RingBuffer.Config) {
super(context, "recording-processor", {
@@ -57,6 +69,7 @@ export class RecordingWorklet extends AudioWorkletNode implements Terminable, Sa
}
set onSaved(callback: Procedure<UUID.Bytes>) {this.#onSaved = Option.wrap(callback)}
set bpm(value: number) {this.#bpm = Option.wrap(value)}
set sampleService(service: SampleService) {this.#sampleService = Option.wrap(service)}
setFillLength(value: int): void {this.#peakWriter.numFrames = value}
@@ -94,7 +107,9 @@ export class RecordingWorklet extends AudioWorkletNode implements Terminable, Sa
const audioData = AudioData.create(this.context.sampleRate, totalSamples, this.channelCount)
mergedFrames.forEach((frame, index) => audioData.frames[index].set(frame))
this.#data = Option.wrap(audioData)
const sample = await this.#sampleService.unwrap("SampleService not set").importRecording(audioData)
const sample = await this.#sampleService
.unwrap("SampleService not set")
.importRecording(audioData, this.#bpm.unwrapOrElse(120))
this.#onSaved.ifSome(callback => callback(UUID.parse(sample.uuid)))
this.#setState({type: "loaded"})
this.terminate()
@@ -122,6 +122,7 @@ export class CaptureAudio extends Capture<CaptureAudioBox> {
}
const {gainNode, channelCount} = audioChain
const recordingWorklet = audioWorklets.createRecording(channelCount, RenderQuantum)
recordingWorklet.bpm = project.timelineBox.bpm.getValue()
recordingWorklet.sampleService = sampleService
sampleManager.record(recordingWorklet)
gainNode.connect(recordingWorklet)
@@ -124,7 +124,27 @@ export namespace RecordAudio {
currentTake = Option.wrap(createTakeRegion(position, currentWaveformOffset, previousTrack))
}
recordingWorklet.onSaved = uuid => project.trackUserCreatedSample(uuid)
recordingWorklet.onSaved = uuid => {
project.trackUserCreatedSample(uuid)
editing.modify(() => {
fileBox.ifSome(oldFileBox => {
editing.modify(() => {
const newFileBox = AudioFileBox.create(boxGraph, uuid, box => {
box.fileName.setValue(oldFileBox.fileName.getValue())
box.startInSeconds.setValue(oldFileBox.startInSeconds.getValue())
box.endInSeconds.setValue(oldFileBox.endInSeconds.getValue())
})
for (const pointer of [...oldFileBox.pointerHub.incoming()]) {
pointer.refer(newFileBox)
}
for (const pointer of [...oldFileBox.transientMarkers.pointerHub.incoming()]) {
pointer.refer(newFileBox.transientMarkers)
}
oldFileBox.delete()
})
})
})
}
terminator.ownAll(
Terminable.create(() => {
tryCatch(() => sourceNode.disconnect(recordingWorklet))
@@ -1,4 +1,4 @@
import {Arrays, Class, Progress, tryCatch, UUID} from "@opendaw/lib-std"
import {Arrays, Class, isDefined, Progress, tryCatch, UUID} from "@opendaw/lib-std"
import {Box} from "@opendaw/lib-box"
import {AudioData, estimateBpm} from "@opendaw/lib-dsp"
import {Promises} from "@opendaw/lib-runtime"
@@ -20,18 +20,19 @@ export class SampleService extends AssetService<Sample> {
constructor(readonly audioContext: AudioContext) {super()}
async importRecording(audioData: AudioData, name: string = "Recording"): Promise<Sample> {
async importRecording(audioData: AudioData, bpm: number, name: string = "Recording"): Promise<Sample> {
const arrayBuffer = WavFile.encodeFloats({
frames: audioData.frames.slice(),
numberOfFrames: audioData.numberOfFrames,
numberOfChannels: audioData.numberOfChannels,
sampleRate: audioData.sampleRate
})
return this.importFile({name, arrayBuffer, origin: "recording"})
return this.importFile({name, bpm, arrayBuffer, origin: "recording"})
}
async importFile({uuid, name, arrayBuffer, progressHandler = Progress.Empty, origin = "import"}
: AssetService.ImportArgs): Promise<Sample> {
async importFile({uuid, name, bpm, arrayBuffer, progressHandler = Progress.Empty, origin = "import"}
: AssetService.ImportArgs,
transformMeta?: (meta: SampleMetaData, audioData: Readonly<AudioData>) => Promise<void>): Promise<Sample> {
console.debug(`importSample '${name}' (${arrayBuffer.byteLength >> 10}kb)`)
uuid ??= await UUID.sha256(arrayBuffer)
const audioData = await this.#decodeAudio(arrayBuffer)
@@ -44,14 +45,17 @@ export class SampleService extends AssetService<Sample> {
audioData.numberOfFrames,
audioData.numberOfChannels) as ArrayBuffer
const meta: SampleMetaData = {
bpm: estimateBpm(duration),
bpm: bpm ?? estimateBpm(duration),
name: name ?? "Unnnamed",
duration,
sample_rate: audioData.sampleRate,
origin
}
await SampleStorage.get().save({uuid, audio: audioData, peaks, meta})
if (isDefined(transformMeta)) {
await transformMeta(meta, audioData)
}
const sample = {uuid: UUID.toString(uuid), ...meta}
await SampleStorage.get().save({uuid, audio: audioData, peaks, meta})
this.notifier.notify(sample)
return sample
}