From d595a332c997e9e069025e324c5c5269ad77461b Mon Sep 17 00:00:00 2001 From: Will Hedgecock Date: Tue, 17 Sep 2024 13:58:50 -0500 Subject: [PATCH] Add function to encode samples into blob --- .../webaudioapi/encoders/WebmOpusEncoder.mjs | 12 ++--- library/webaudioapi/modules/Constants.mjs | 44 +++++++++---------- library/webaudioapi/modules/Encoder.mjs | 2 + library/webaudioapi/webAudioAPI.src.js | 26 +++++++++++ rollup.config.mjs | 3 +- 5 files changed, 58 insertions(+), 29 deletions(-) diff --git a/library/webaudioapi/encoders/WebmOpusEncoder.mjs b/library/webaudioapi/encoders/WebmOpusEncoder.mjs index 85beb22..f3078e3 100644 --- a/library/webaudioapi/encoders/WebmOpusEncoder.mjs +++ b/library/webaudioapi/encoders/WebmOpusEncoder.mjs @@ -24,12 +24,12 @@ export class WebmOpusEncoder extends EncoderBase { for (let ch = 0; ch < audioData.numberOfChannels; ++ch) audioInputDataFloats.set(audioData.getChannelData(ch), ch * audioData.length); const audioInput = new AudioData({ format: 'f32-planar', - sampleRate: audioData.sampleRate, - numberOfFrames: audioData.length, - numberOfChannels: audioData.numberOfChannels, - timestamp: 0, - data: audioInputData, - transfer: [audioInputData] }); + sampleRate: audioData.sampleRate, + numberOfFrames: audioData.length, + numberOfChannels: audioData.numberOfChannels, + timestamp: 0, + data: audioInputData, + transfer: [audioInputData] }); const bitRate = (encodingOptions && ('bitRate' in encodingOptions)) ? encodingOptions.bitRate : 96000; const audioEncoder = new AudioEncoder({ output: (chunk, meta) => webmMuxer.addAudioChunk(chunk, meta), error: e => console.error(e) }); audioEncoder.configure({ codec: 'opus', sampleRate: audioData.sampleRate, numberOfChannels: 1, bitrate: bitRate }); diff --git a/library/webaudioapi/modules/Constants.mjs b/library/webaudioapi/modules/Constants.mjs index d177efe..bd08f9e 100644 --- a/library/webaudioapi/modules/Constants.mjs +++ b/library/webaudioapi/modules/Constants.mjs @@ -11,34 +11,34 @@ */ export const Note = { Rest: 0, C0: 12, C0n: -12, D0bb: 12, C0s: 13, D0b: 13, D0: 14, D0n: -14, C0ss: 14, E0bb: 14, - D0s: 15, E0b: 15, F0bb: 15, E0: 16, E0n: -16, D0ss: 16, F0b: 16, F0: 17, F0n: -17, E0s: 17, G0bb: 17, + D0s: 15, E0b: 15, F0bb: 15, E0: 16, E0n: -16, D0ss: 16, F0b: 16, F0: 17, F0n: -17, E0s: 17, G0bb: 17, F0s: 18, E0ss: 18, G0b: 18, G0: 19, G0n: -19, F0ss: 19, A0bb: 19, G0s: 20, A0b: 20, - A0: 21, A0n: -21, G0ss: 21, B0bb: 21, A0s: 22, B0b: 22, C1bb: 22, B0: 23, B0n: -23, A0ss: 23, C1b: 23, - C1: 24, C1n: -24, B0s: 24, D1bb: 24, C1s: 25, B0ss: 25, D1b: 25, D1: 26, D1n: -26, C1ss: 26, E1bb: 26, - D1s: 27, E1b: 27, F1bb: 27, E1: 28, E1n: -28, D1ss: 28, F1b: 28, F1: 29, F1n: -29, E1s: 29, G1bb: 29, + A0: 21, A0n: -21, G0ss: 21, B0bb: 21, A0s: 22, B0b: 22, C1bb: 22, B0: 23, B0n: -23, A0ss: 23, C1b: 23, + C1: 24, C1n: -24, B0s: 24, D1bb: 24, C1s: 25, B0ss: 25, D1b: 25, D1: 26, D1n: -26, C1ss: 26, E1bb: 26, + D1s: 27, E1b: 27, F1bb: 27, E1: 28, E1n: -28, D1ss: 28, F1b: 28, F1: 29, F1n: -29, E1s: 29, G1bb: 29, F1s: 30, E1ss: 30, G1b: 30, G1: 31, G1n: -31, F1ss: 31, A1bb: 31, G1s: 32, A1b: 32, - A1: 33, A1n: -33, G1ss: 33, B1bb: 33, A1s: 34, B1b: 34, C2bb: 34, B1: 35, B1n: -35, A1ss: 35, C2b: 35, - C2: 36, C2n: -36, B1s: 36, D2bb: 36, C2s: 37, B1ss: 37, D2b: 37, D2: 38, D2n: -38, C2ss: 38, E2bb: 38, - D2s: 39, E2b: 39, F2bb: 39, E2: 40, E2n: -40, D2ss: 40, F2b: 40, F2: 41, F2n: -41, E2s: 41, G2bb: 41, + A1: 33, A1n: -33, G1ss: 33, B1bb: 33, A1s: 34, B1b: 34, C2bb: 34, B1: 35, B1n: -35, A1ss: 35, C2b: 35, + C2: 36, C2n: -36, B1s: 36, D2bb: 36, C2s: 37, B1ss: 37, D2b: 37, D2: 38, D2n: -38, C2ss: 38, E2bb: 38, + D2s: 39, E2b: 39, F2bb: 39, E2: 40, E2n: -40, D2ss: 40, F2b: 40, F2: 41, F2n: -41, E2s: 41, G2bb: 41, F2s: 42, E2ss: 42, G2b: 42, G2: 43, G2n: -43, F2ss: 43, A2bb: 43, G2s: 44, A2b: 44, - A2: 45, A2n: -45, G2ss: 45, B2bb: 45, A2s: 46, B2b: 46, C3bb: 46, B2: 47, B2n: -47, A2ss: 47, C3b: 47, - C3: 48, C3n: -48, B2s: 48, D3bb: 48, C3s: 49, B2ss: 49, D3b: 49, D3: 50, D3n: -50, C3ss: 50, E3bb: 50, - D3s: 51, E3b: 51, F3bb: 51, E3: 52, E3n: -52, D3ss: 52, F3b: 52, F3: 53, F3n: -53, E3s: 53, G3bb: 53, + A2: 45, A2n: -45, G2ss: 45, B2bb: 45, A2s: 46, B2b: 46, C3bb: 46, B2: 47, B2n: -47, A2ss: 47, C3b: 47, + C3: 48, C3n: -48, B2s: 48, D3bb: 48, C3s: 49, B2ss: 49, D3b: 49, D3: 50, D3n: -50, C3ss: 50, E3bb: 50, + D3s: 51, E3b: 51, F3bb: 51, E3: 52, E3n: -52, D3ss: 52, F3b: 52, F3: 53, F3n: -53, E3s: 53, G3bb: 53, F3s: 54, E3ss: 54, G3b: 54, G3: 55, G3n: -55, F3ss: 55, A3bb: 55, G3s: 56, A3b: 56, - A3: 57, A3n: -57, G3ss: 57, B3bb: 57, A3s: 58, B3b: 58, C4bb: 58, B3: 59, B3n: -59, A3ss: 59, C4b: 59, - C4: 60, C4n: -60, B3s: 60, D4bb: 60, C4s: 61, B3ss: 61, D4b: 61, D4: 62, D4n: -62, C4ss: 62, E4bb: 62, - D4s: 63, E4b: 63, F4bb: 63, E4: 64, E4n: -64, D4ss: 64, F4b: 64, F4: 65, F4n: -65, E4s: 65, G4bb: 65, + A3: 57, A3n: -57, G3ss: 57, B3bb: 57, A3s: 58, B3b: 58, C4bb: 58, B3: 59, B3n: -59, A3ss: 59, C4b: 59, + C4: 60, C4n: -60, B3s: 60, D4bb: 60, C4s: 61, B3ss: 61, D4b: 61, D4: 62, D4n: -62, C4ss: 62, E4bb: 62, + D4s: 63, E4b: 63, F4bb: 63, E4: 64, E4n: -64, D4ss: 64, F4b: 64, F4: 65, F4n: -65, E4s: 65, G4bb: 65, F4s: 66, E4ss: 66, G4b: 66, G4: 67, G4n: -67, F4ss: 67, A4bb: 67, G4s: 68, A4b: 68, - A4: 69, A4n: -69, G4ss: 69, B4bb: 69, A4s: 70, B4b: 70, C5bb: 70, B4: 71, B4n: -71, A4ss: 71, C5b: 71, - C5: 72, C5n: -72, B4s: 72, D5bb: 72, C5s: 73, B4ss: 73, D5b: 73, D5: 74, D5n: -74, C5ss: 74, E5bb: 74, - D5s: 75, E5b: 75, F5bb: 75, E5: 76, E5n: -76, D5ss: 76, F5b: 76, F5: 77, F5n: -77, E5s: 77, G5bb: 77, + A4: 69, A4n: -69, G4ss: 69, B4bb: 69, A4s: 70, B4b: 70, C5bb: 70, B4: 71, B4n: -71, A4ss: 71, C5b: 71, + C5: 72, C5n: -72, B4s: 72, D5bb: 72, C5s: 73, B4ss: 73, D5b: 73, D5: 74, D5n: -74, C5ss: 74, E5bb: 74, + D5s: 75, E5b: 75, F5bb: 75, E5: 76, E5n: -76, D5ss: 76, F5b: 76, F5: 77, F5n: -77, E5s: 77, G5bb: 77, F5s: 78, E5ss: 78, G5b: 78, G5: 79, G5n: -79, F5ss: 79, A5bb: 79, G5s: 80, A5b: 80, - A5: 81, A5n: -81, G5ss: 81, B5bb: 81, A5s: 82, B5b: 82, C6bb: 82, B5: 83, B5n: -83, A5ss: 83, C6b: 83, - C6: 84, C6n: -84, B5s: 84, D6bb: 84, C6s: 85, B5ss: 85, D6b: 85, D6: 86, D6n: -86, C6ss: 86, E6bb: 86, - D6s: 87, E6b: 87, F6bb: 87, E6: 88, E6n: -88, D6ss: 88, F6b: 88, F6: 89, F6n: -89, E6s: 89, G6bb: 89, + A5: 81, A5n: -81, G5ss: 81, B5bb: 81, A5s: 82, B5b: 82, C6bb: 82, B5: 83, B5n: -83, A5ss: 83, C6b: 83, + C6: 84, C6n: -84, B5s: 84, D6bb: 84, C6s: 85, B5ss: 85, D6b: 85, D6: 86, D6n: -86, C6ss: 86, E6bb: 86, + D6s: 87, E6b: 87, F6bb: 87, E6: 88, E6n: -88, D6ss: 88, F6b: 88, F6: 89, F6n: -89, E6s: 89, G6bb: 89, F6s: 90, E6ss: 90, G6b: 90, G6: 91, G6n: -91, F6ss: 91, A6bb: 91, G6s: 92, A6b: 92, - A6: 93, A6n: -93, G6ss: 93, B6bb: 93, A6s: 94, B6b: 94, C7bb: 94, B6: 95, B6n: -95, A6ss: 95, C7b: 95, - C7: 96, C7n: -96, B6s: 96, D7bb: 96, C7s: 97, B6ss: 97, D7b: 97, D7: 98, D7n: -98, C7ss: 98, E7bb: 98, + A6: 93, A6n: -93, G6ss: 93, B6bb: 93, A6s: 94, B6b: 94, C7bb: 94, B6: 95, B6n: -95, A6ss: 95, C7b: 95, + C7: 96, C7n: -96, B6s: 96, D7bb: 96, C7s: 97, B6ss: 97, D7b: 97, D7: 98, D7n: -98, C7ss: 98, E7bb: 98, D7s: 99, E7b: 99, F7bb: 99, E7: 100, E7n: -100, D7ss: 100, F7b: 100, F7: 101, F7n: -101, E7s: 101, G7bb: 101, F7s: 102, E7ss: 102, G7b: 102, G7: 103, G7n: -103, F7ss: 103, A7bb: 103, G7s: 104, A7b: 104, A7: 105, A7n: -105, G7ss: 105, B7bb: 105, A7s: 106, B7b: 106, C8bb: 106, B7: 107, B7n: -107, A7ss: 107, C8b: 107, @@ -139,7 +139,7 @@ export const AnalysisType = { * @constant {Object} */ export const EncodingType = { - WAV: 1 + WAV: 1, WEBM: 2 }; /** diff --git a/library/webaudioapi/modules/Encoder.mjs b/library/webaudioapi/modules/Encoder.mjs index 019ec74..47fd6b2 100644 --- a/library/webaudioapi/modules/Encoder.mjs +++ b/library/webaudioapi/modules/Encoder.mjs @@ -5,9 +5,11 @@ import { EncodingType } from './Constants.mjs'; import { WavFileEncoder } from '../encoders/WavFileEncoder.mjs'; +import { WebmOpusEncoder } from '../encoders/WebmOpusEncoder.mjs'; const EncoderClasses = { [EncodingType.WAV]: WavFileEncoder, + [EncodingType.WEBM]: WebmOpusEncoder, }; /** diff --git a/library/webaudioapi/webAudioAPI.src.js b/library/webaudioapi/webAudioAPI.src.js index 565840f..22dac21 100644 --- a/library/webaudioapi/webAudioAPI.src.js +++ b/library/webaudioapi/webAudioAPI.src.js @@ -1408,6 +1408,32 @@ export class WebAudioAPI { return { getRawData, getDuration, finalize, getEncodedData, notifyWhenComplete }; } + /** + * Encodes a 2D array of floating point `samples` into a {@link https://developer.mozilla.org/en-US/docs/Web/API/Blob Blob} + * containing raw audio data according to the specified `sampleRate` and {@link module:Constants.EncodingType EncodingType} + * specified in the `encodingType` parameter. + * + * @param {number} encodingType - Numeric value corresponding to the desired {@link module:Constants.EncodingType EncodingType} + * @param {number} sampleRate - Sample rate at which the audio data was recorded + * @param {Array>} samples - 2D array of floating point audio samples to encode + * @returns {Blob} Data {@link https://developer.mozilla.org/en-US/docs/Web/API/Blob Blob} containing the newly encoded audio data + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Blob Blob} + * @see {@link module:Constants.EncodingType EncodingType} + */ + async encodeAudioAs(encodingType, sampleRate, samples) { + if (!Object.values(EncodingType).includes(Number(encodingType))) + throw new WebAudioApiErrors.WebAudioTargetError(`An encoder for the target type identifier (${encodingType}) does not exist`); + if (!samples || !samples.length || !Array.isArray(samples) || !(Array.isArray(samples[0])) || !samples[0].length) + throw new WebAudioApiErrors.WebAudioValueError('Cannot encode these audio samples because they are not a 2D array of floats'); + const audioBuffer = this.#audioContext.createBuffer(samples.length, samples[0].length, sampleRate); + for (let ch = 0; ch < samples.length; ++ch) { + const channel_data = audioBuffer.getChannelData(ch); + for (let i = 0; i < samples[ch].length; ++i) + channel_data[i] = Math.min(Math.max(samples[ch][i], -1), 1); + } + return await getEncoderFor(Number(encodingType)).encode(audioBuffer); + } + /** * Starts the {@link WebAudioAPI} library and allows audio playback to resume. */ diff --git a/rollup.config.mjs b/rollup.config.mjs index a1b710e..a2a3001 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -20,7 +20,8 @@ export default { eslint({ exclude: [ 'package.json', - 'library/webaudioapi/modules/Constants.mjs' + 'library/webaudioapi/modules/Constants.mjs', + 'library/webaudioapi/encoders/webm-muxer.min.mjs', ], }), json()