@siteed/expo-audio-stream 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/.size-limit.json +6 -0
  2. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +76 -0
  3. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  4. package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
  5. package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  6. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +4 -0
  7. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  8. package/build/AudioAnalysis/extractAudioAnalysis.js +101 -0
  9. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  10. package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
  11. package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  12. package/build/AudioAnalysis/extractWaveform.js +14 -0
  13. package/build/AudioAnalysis/extractWaveform.js.map +1 -0
  14. package/build/AudioRecorder.provider.d.ts +14 -1
  15. package/build/AudioRecorder.provider.d.ts.map +1 -1
  16. package/build/AudioRecorder.provider.js +17 -4
  17. package/build/AudioRecorder.provider.js.map +1 -1
  18. package/build/ExpoAudioStream.types.d.ts +26 -84
  19. package/build/ExpoAudioStream.types.d.ts.map +1 -1
  20. package/build/ExpoAudioStream.types.js.map +1 -1
  21. package/build/ExpoAudioStream.web.d.ts +6 -5
  22. package/build/ExpoAudioStream.web.d.ts.map +1 -1
  23. package/build/ExpoAudioStream.web.js +9 -8
  24. package/build/ExpoAudioStream.web.js.map +1 -1
  25. package/build/ExpoAudioStreamModule.d.ts.map +1 -1
  26. package/build/ExpoAudioStreamModule.js +5 -1
  27. package/build/ExpoAudioStreamModule.js.map +1 -1
  28. package/build/{WebRecorder.d.ts → WebRecorder.web.d.ts} +7 -3
  29. package/build/WebRecorder.web.d.ts.map +1 -0
  30. package/build/{WebRecorder.js → WebRecorder.web.js} +74 -29
  31. package/build/WebRecorder.web.js.map +1 -0
  32. package/build/constants.d.ts +11 -0
  33. package/build/constants.d.ts.map +1 -0
  34. package/build/constants.js +14 -0
  35. package/build/constants.js.map +1 -0
  36. package/build/events.d.ts +6 -0
  37. package/build/events.d.ts.map +1 -0
  38. package/build/events.js +15 -0
  39. package/build/events.js.map +1 -0
  40. package/build/index.d.ts +8 -16
  41. package/build/index.d.ts.map +1 -1
  42. package/build/index.js +6 -112
  43. package/build/index.js.map +1 -1
  44. package/build/logger.d.ts +9 -0
  45. package/build/logger.d.ts.map +1 -0
  46. package/build/logger.js +17 -0
  47. package/build/logger.js.map +1 -0
  48. package/build/{useAudioRecording.d.ts → useAudioRecorder.d.ts} +6 -7
  49. package/build/useAudioRecorder.d.ts.map +1 -0
  50. package/build/{useAudioRecording.js → useAudioRecorder.js} +69 -65
  51. package/build/useAudioRecorder.js.map +1 -0
  52. package/build/utils/convertPCMToFloat32.d.ts +11 -0
  53. package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
  54. package/build/utils/convertPCMToFloat32.js +41 -0
  55. package/build/utils/convertPCMToFloat32.js.map +1 -0
  56. package/build/utils/encodingToBitDepth.d.ts +5 -0
  57. package/build/utils/encodingToBitDepth.d.ts.map +1 -0
  58. package/build/utils/encodingToBitDepth.js +13 -0
  59. package/build/utils/encodingToBitDepth.js.map +1 -0
  60. package/build/utils/getWavFileInfo.d.ts +25 -0
  61. package/build/utils/getWavFileInfo.d.ts.map +1 -0
  62. package/build/utils/getWavFileInfo.js +89 -0
  63. package/build/utils/getWavFileInfo.js.map +1 -0
  64. package/build/utils/writeWavHeader.d.ts +9 -0
  65. package/build/utils/writeWavHeader.d.ts.map +1 -0
  66. package/build/utils/writeWavHeader.js +41 -0
  67. package/build/utils/writeWavHeader.js.map +1 -0
  68. package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  69. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  70. package/build/workers/InlineFeaturesExtractor.web.js +303 -0
  71. package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
  72. package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
  73. package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  74. package/build/workers/inlineAudioWebWorker.web.js +243 -0
  75. package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
  76. package/ios/AudioStreamManager.swift +39 -2
  77. package/ios/ExpoAudioStreamModule.swift +10 -0
  78. package/package.json +7 -6
  79. package/plugin/tsconfig.json +1 -1
  80. package/publish.sh +0 -0
  81. package/src/AudioAnalysis/AudioAnalysis.types.ts +85 -0
  82. package/src/AudioAnalysis/extractAudioAnalysis.ts +136 -0
  83. package/src/AudioAnalysis/extractWaveform.ts +25 -0
  84. package/src/AudioRecorder.provider.tsx +35 -7
  85. package/src/ExpoAudioStream.types.ts +33 -94
  86. package/src/ExpoAudioStream.web.ts +17 -16
  87. package/src/ExpoAudioStreamModule.ts +6 -1
  88. package/src/{WebRecorder.ts → WebRecorder.web.ts} +85 -33
  89. package/src/constants.ts +18 -0
  90. package/src/events.ts +25 -0
  91. package/src/index.ts +8 -169
  92. package/src/logger.ts +26 -0
  93. package/src/{useAudioRecording.tsx → useAudioRecorder.tsx} +141 -136
  94. package/src/utils/convertPCMToFloat32.ts +48 -0
  95. package/src/utils/encodingToBitDepth.ts +18 -0
  96. package/src/utils/getWavFileInfo.ts +125 -0
  97. package/src/utils/writeWavHeader.ts +56 -0
  98. package/src/workers/InlineFeaturesExtractor.web.tsx +302 -0
  99. package/src/workers/inlineAudioWebWorker.web.tsx +242 -0
  100. package/build/WebRecorder.d.ts.map +0 -1
  101. package/build/WebRecorder.js.map +0 -1
  102. package/build/inlineAudioWebWorker.d.ts +0 -3
  103. package/build/inlineAudioWebWorker.d.ts.map +0 -1
  104. package/build/inlineAudioWebWorker.js +0 -340
  105. package/build/inlineAudioWebWorker.js.map +0 -1
  106. package/build/useAudioRecording.d.ts.map +0 -1
  107. package/build/useAudioRecording.js.map +0 -1
  108. package/build/utils.d.ts +0 -31
  109. package/build/utils.d.ts.map +0 -1
  110. package/build/utils.js +0 -143
  111. package/build/utils.js.map +0 -1
  112. package/src/inlineAudioWebWorker.tsx +0 -340
  113. package/src/utils.ts +0 -189
@@ -1 +0,0 @@
1
- {"version":3,"file":"inlineAudioWebWorker.js","sourceRoot":"","sources":["../src/inlineAudioWebWorker.tsx"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8J1C,CAAC;AAEF,oHAAoH;AACpH,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkLrC,CAAC","sourcesContent":["export const SimpleInlineProcessorScript = `\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.recordedBuffers = []; // Float32Array\n this.newRecBuffer = []; // Float32Array\n this.exportIntervalSamples = 0;\n this.samplesSinceLastExport = 0;\n this.recordSampleRate = 44100; // To be overwritten\n this.exportSampleRate = 44100; // To be overwritten\n this.channels = 1; // Default to 1 channel (mono)\n this.bitDepth = 32; // Default to 32-bit depth\n this.isRecording = true;\n this.port.onmessage = this.handleMessage.bind(this);\n }\n\n handleMessage(event) {\n switch (event.data.command) {\n case 'init':\n this.recordSampleRate = event.data.recordSampleRate;\n this.exportSampleRate = event.data.exportSampleRate || event.data.recordSampleRate;\n this.exportIntervalSamples = this.recordSampleRate * (event.data.interval / 1000);\n break;\n case 'stop':\n this.isRecording = false;\n const fullRecordedData = this.getAllRecordedData();\n this.port.postMessage({ command: 'recordedData', recordedData: fullRecordedData });\n break;\n }\n }\n\n process(inputs, outputs, parameters) {\n if (!this.isRecording) return true;\n const input = inputs[0];\n if (input.length > 0) {\n const newBuffer = new Float32Array(input[0]);\n this.newRecBuffer.push(newBuffer);\n this.recordedBuffers.push(newBuffer);\n this.samplesSinceLastExport += newBuffer.length;\n\n if (this.samplesSinceLastExport >= this.exportIntervalSamples) {\n this.exportNewData();\n this.samplesSinceLastExport = 0;\n }\n }\n return true;\n }\n\n mergeBuffers(bufferArray, recLength) {\n const result = new Float32Array(recLength);\n let offset = 0;\n for (let i = 0; i < bufferArray.length; i++) {\n result.set(bufferArray[i], offset);\n offset += bufferArray[i].length;\n }\n return result;\n }\n\n floatTo16BitPCM(output, offset, input) {\n for (let i = 0; i < input.length; i++, offset += 2) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);\n }\n console.debug('Float to 16-bit PCM conversion complete. Output byte length:', offset);\n }\n\n floatTo32BitPCM(output, offset, input) {\n for (let i = 0; i < input.length; i++, offset += 4) {\n output.setFloat32(offset, input[i], true);\n }\n console.debug('Float to 32-bit PCM (no conversion) complete. Output byte length:', offset);\n }\n\n writeString(view, offset, string) {\n for (let i = 0; i < string.length; i++) {\n view.setUint8(offset + i, string.charCodeAt(i));\n }\n }\n\n encodeWAV(samples, includeHeader = true) {\n const sampleCount = samples.length;\n const buffer = new ArrayBuffer((includeHeader ? 44 : 0) + sampleCount * 4);\n const view = new DataView(buffer);\n\n if (includeHeader) {\n this.writeString(view, 0, 'RIFF');\n view.setUint32(4, 36 + sampleCount * 4, true); // File size - 8 bytes\n this.writeString(view, 8, 'WAVE');\n this.writeString(view, 12, 'fmt ');\n view.setUint32(16, 16, true); // PCM format\n view.setUint16(20, 3, true); // Format code 3 for float\n view.setUint16(22, 1, true); // Mono channel\n view.setUint32(24, this.recordSampleRate, true); // Sample rate\n view.setUint32(28, this.recordSampleRate * 4, true); // Byte rate\n view.setUint16(32, 4, true); // Block align (4 bytes for 32-bit float)\n view.setUint16(34, 32, true); // Bits per sample (32-bit float)\n this.writeString(view, 36, 'data');\n view.setUint32(40, sampleCount * 4, true); // Data chunk size\n }\n\n console.debug('Writing PCM samples to DataView. Offset:', includeHeader ? 44 : 0, 'Samples length:', sampleCount);\n this.floatTo32BitPCM(view, includeHeader ? 44 : 0, samples);\n // this.floatTo16BitPCM(view, includeHeader ? 44 : 0, samples);\n\n console.debug('Encoded WAV DataView:', view);\n console.debug('Encoded WAV length:', view.byteLength);\n\n return view;\n }\n\n\n exportNewData() {\n // Calculate the total length of the new recorded buffers\n const length = this.newRecBuffer.reduce((acc, buffer) => acc + buffer.length, 0);\n\n // Merge all new recorded buffers into a single buffer\n const mergedBuffer = this.mergeBuffers(this.newRecBuffer, length);\n\n // Encode the merged buffer into a WAV format\n const encodedWav = this.encodeWAV(mergedBuffer, false);\n\n // Clear the new recorded buffers after they have been processed\n this.newRecBuffer.length = 0;\n\n // Post the message to the main thread\n // The first argument is the message data, containing the encoded WAV buffer\n // The second argument is the transfer list, which transfers ownership of the ArrayBuffer\n // to the main thread, avoiding the need to copy the buffer and improving performance\n this.port.postMessage({ recordedData: encodedWav.buffer }, [encodedWav.buffer]);\n }\n\n getAllRecordedData() {\n const length = this.recordedBuffers.reduce((acc, buffer) => acc + buffer.length, 0);\n const mergedBuffer = this.mergeBuffers(this.recordedBuffers, length);\n\n\n // Calculate the duration based on the sample count and sample rate\n const sampleCount = mergedBuffer.length;\n const mergedBufferDuration = sampleCount / this.recordSampleRate;\n\n const encodedWav = this.encodeWAV(mergedBuffer, true);\n\n // Calculate and log the duration for encodedWav.buffer based on sample count\n const encodedWavBufferDuration = sampleCount / this.recordSampleRate;\n\n this.recordedBuffers.length = 0; // Clear the buffers after extraction\n\n // Returning both for testing, comment one of the returns based on your test\n console.debug('mergedBuffer:', mergedBuffer);\n console.debug('encodedWav.buffer:', encodedWav.buffer);\n\n // Uncomment the appropriate return for testing\n // return mergedBuffer; // This works when played\n return encodedWav.buffer; // This doesn't work when played\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n`;\n\n// Because we use expo and needs to include the worker script in the shared library, better inline it in the module.\nexport const InlineProcessorScrippt = `\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.recLength = 0;\n this.recBuffer = [];\n this.headerSent = false;\n this.newRecBuffer = [];\n this.exportIntervalSamples = 0;\n this.samplesSinceLastExport = 0;\n this.recordSampleRate = 44100; // To be overwrited\n this.exportSampleRate = 44100; // To be overwrited\n this.isRecording = true;\n\n this.port.onmessage = this.handleMessage.bind(this);\n }\n\n handleMessage(event) {\n switch (event.data.command) {\n case 'init':\n this.recordSampleRate = event.data.recordSampleRate;\n this.exportSampleRate = event.data.exportSampleRate || event.data.recordSampleRate;\n this.exportIntervalSamples = this.recordSampleRate * (event.data.interval / 1000);\n break;\n case 'stop':\n this.isRecording = false;\n break;\n case 'getRecordedData':\n const recordedData = this.getRecordedData();\n this.port.postMessage({ command: 'recordedData', recordedData });\n break;\n }\n }\n\n process(inputs) {\n if (!this.isRecording) {\n return true; // Exit early if not recording\n }\n\n if (inputs.length === 0 || inputs[0].length === 0) {\n console.warn('RecorderProcessor -- No input received.');\n return true; // Exit early if no input\n }\n\n const buffer = inputs[0];\n if (!buffer) {\n console.error('Input buffer is null.');\n return true; // Exit early if buffer is null\n }\n\n try {\n this.record(buffer);\n\n // this.samplesSinceLastExport += buffer.length;\n // if (this.samplesSinceLastExport >= this.exportIntervalSamples) {\n // this.exportBuffer();\n // this.samplesSinceLastExport = 0;\n // }\n } catch (error) {\n console.error('Error during processing:', error);\n }\n return true;\n }\n\n record(inputBuffer) {\n this.recBuffer.push(inputBuffer);\n this.newRecBuffer.push(inputBuffer);\n this.recLength += inputBuffer.length;\n }\n\n exportBuffer() {\n const mergedBuffers = this.mergeBuffers(this.newRecBuffer, this.newRecBuffer.reduce((len, buf) => len + buf.length, 0));\n console.log('Merged buffer length:', mergedBuffers.length); // Debug log\n\n const downsampledBuffer = this.downsampleBuffer(mergedBuffers, this.exportSampleRate);\n console.log('Downsampled buffer length:', downsampledBuffer.length); // Debug log\n\n const encodedWav = downsampledBuffer;\n // const encodedWav = this.encodeWAV(downsampledBuffer);\n // console.log('Encoded WAV length:', encodedWav.byteLength); // Debug log\n\n this.port.postMessage({ encodedWav, sampleRate: this.exportSampleRate });\n this.newRecBuffer = []; // Clear the new data buffer after export\n this.headerSent = true; // Indicate that the header has been sent\n }\n\n downsampleBuffer(buffer, exportSampleRate) {\n console.log('Original buffer length:', buffer.length); // Debug log\n console.log('Record sample rate:', this.recordSampleRate); // Debug log\n console.log('Export sample rate:', exportSampleRate); // Debug log\n if (exportSampleRate === this.recordSampleRate) {\n return buffer;\n }\n\n const sampleRateRatio = this.recordSampleRate / exportSampleRate;\n const newLength = Math.round(buffer.length / sampleRateRatio);\n console.log('Sample rate ratio:', sampleRateRatio); // Debug log\n console.log('New length after downsampling:', newLength); // Debug log\n \n if (newLength <= 0) {\n console.error('New length is zero or negative, returning empty buffer.'); // Debug log\n return new Float32Array(0);\n }\n const result = new Float32Array(newLength);\n let offsetResult = 0;\n let offsetBuffer = 0;\n while (offsetResult < result.length) {\n const nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);\n let accum = 0, count = 0;\n for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {\n accum += buffer[i];\n count++;\n }\n result[offsetResult] = accum / count;\n offsetResult++;\n offsetBuffer = nextOffsetBuffer;\n }\n return result;\n }\n\n mergeBuffers(bufferArray, recLength) {\n const result = new Float32Array(recLength);\n let offset = 0;\n for (let i = 0; i < bufferArray.length; i++) {\n result.set(bufferArray[i], offset);\n offset += bufferArray[i].length;\n }\n return result;\n }\n\n floatTo16BitPCM(output, offset, input) {\n for (let i = 0; i < input.length; i++, offset += 2) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n }\n\n writeString(view, offset, string) {\n for (let i = 0; i < string.length; i++) {\n view.setUint8(offset + i, string.charCodeAt(i));\n }\n }\n\n getRecordedData() {\n const length = this.recBuffer.reduce((acc, buffer) => acc + buffer.length, 0);\n const result = new Float32Array(length);\n let offset = 0;\n for (const buffer of this.recBuffer) {\n result.set(buffer, offset);\n offset += buffer.length;\n }\n this.recBuffer.length = 0; // Clear the buffers after extraction\n\n return result;\n }\n\n encodeWAV(samples) {\n const buffer = new ArrayBuffer(44 + samples.length * 2);\n const view = new DataView(buffer);\n this.writeString(view, 0, 'RIFF');\n view.setUint32(4, 32 + samples.length * 2, true);\n this.writeString(view, 8, 'WAVE');\n this.writeString(view, 12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n this.writeString(view, 36, 'data');\n view.setUint32(40, samples.length * 2, true);\n this.floatTo16BitPCM(view, 44, samples);\n return view;\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n`;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"useAudioRecording.d.ts","sourceRoot":"","sources":["../src/useAudioRecording.tsx"],"names":[],"mappings":"AAKA,OAAO,EACL,iBAAiB,EAGjB,oBAAoB,EACpB,iBAAiB,EAEjB,eAAe,EACf,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACxE,aAAa,EAAE,MAAM,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACvD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAkED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAa,EACb,eAAe,EACf,mBAAmB,GACpB,GAAE,qBAA0B,GAAG,qBAAqB,CA8RpD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"useAudioRecording.js","sourceRoot":"","sources":["../src/useAudioRecording.tsx"],"names":[],"mappings":"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","sourcesContent":["// src/useAudioRecording.ts\nimport { Platform, Subscription } from \"expo-modules-core\";\nimport { useCallback, useEffect, useReducer, useRef } from \"react\";\n\nimport { addAudioAnalysisListener, addAudioEventListener } from \".\";\nimport {\n AudioAnalysisData,\n AudioDataEvent,\n AudioEventPayload,\n AudioFeaturesOptions,\n AudioStreamResult,\n AudioStreamStatus,\n RecordingConfig,\n StartAudioStreamResult,\n} from \"./ExpoAudioStream.types\";\nimport ExpoAudioStreamModule from \"./ExpoAudioStreamModule\";\nimport { WavFileInfo } from \"./utils\";\n\nexport interface ExtractMetadataProps {\n fileUri?: string; // should provide either fileUri or arrayBuffer\n wavMetadata?: WavFileInfo;\n arrayBuffer?: ArrayBuffer;\n bitDepth?: number;\n skipWavHeader?: boolean;\n durationMs?: number;\n sampleRate?: number;\n numberOfChannels?: number;\n algorithm?: \"peak\" | \"rms\";\n position?: number; // Optional number of bytes to skip. Default is 0\n length?: number; // Optional number of bytes to read.\n pointsPerSecond?: number; // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.\n features?: AudioFeaturesOptions;\n featuresExtratorUrl?: string;\n}\n\nexport interface UseAudioRecorderProps {\n debug?: boolean;\n audioWorkletUrl?: string;\n featuresExtratorUrl?: string;\n}\n\nexport interface UseAudioRecorderState {\n startRecording: (_: RecordingConfig) => Promise<StartAudioStreamResult>;\n stopRecording: () => Promise<AudioStreamResult | null>;\n pauseRecording: () => void;\n resumeRecording: () => void;\n isRecording: boolean;\n isPaused: boolean;\n durationMs: number; // Duration of the recording\n size: number; // Size in bytes of the recorded audio\n analysisData?: AudioAnalysisData;\n audioWorkletUrl?: string;\n featuresExtratorUrl?: string;\n}\n\ninterface RecorderState {\n isRecording: boolean;\n isPaused: boolean;\n durationMs: number;\n size: number;\n analysisData?: AudioAnalysisData;\n}\n\ntype RecorderAction =\n | { type: \"START\" | \"STOP\" | \"PAUSE\" | \"RESUME\" }\n | { type: \"UPDATE_STATUS\"; payload: { durationMs: number; size: number } }\n | { type: \"UPDATE_ANALYSIS\"; payload: AudioAnalysisData };\n\nconst defaultAnalysis: AudioAnalysisData = {\n pointsPerSecond: 20,\n bitDepth: 32,\n numberOfChannels: 1,\n durationMs: 0,\n sampleRate: 44100,\n samples: 0,\n dataPoints: [],\n amplitudeRange: {\n min: Number.POSITIVE_INFINITY,\n max: Number.NEGATIVE_INFINITY,\n },\n};\n\nfunction recorderReducer(\n state: RecorderState,\n action: RecorderAction,\n): RecorderState {\n switch (action.type) {\n case \"START\":\n return {\n ...state,\n isRecording: true,\n isPaused: false,\n durationMs: 0,\n size: 0,\n analysisData: defaultAnalysis, // Reset analysis data\n };\n case \"STOP\":\n return { ...state, isRecording: false, isPaused: false };\n case \"PAUSE\":\n return { ...state, isPaused: true, isRecording: false };\n case \"RESUME\":\n return { ...state, isPaused: false, isRecording: true };\n case \"UPDATE_STATUS\":\n return {\n ...state,\n durationMs: action.payload.durationMs,\n size: action.payload.size,\n };\n case \"UPDATE_ANALYSIS\":\n return {\n ...state,\n analysisData: action.payload,\n };\n default:\n return state;\n }\n}\nconst TAG = \"[ useAudioRecorder ] \";\n\nexport function useAudioRecorder({\n debug = false,\n audioWorkletUrl,\n featuresExtratorUrl,\n}: UseAudioRecorderProps = {}): UseAudioRecorderState {\n const [state, dispatch] = useReducer(recorderReducer, {\n isRecording: false,\n isPaused: false,\n durationMs: 0,\n size: 0,\n analysisData: undefined,\n });\n\n const analysisListenerRef = useRef<Subscription | null>(null);\n const analysisRef = useRef<AudioAnalysisData>({ ...defaultAnalysis });\n\n // Instantiate the module for web with URLs\n const ExpoAudioStream =\n Platform.OS === \"web\"\n ? ExpoAudioStreamModule({ audioWorkletUrl, featuresExtratorUrl })\n : ExpoAudioStreamModule;\n\n const onAudioStreamRef = useRef<\n ((_: AudioDataEvent) => Promise<void>) | null\n >(null);\n\n const logDebug = useCallback(\n (message: string, data?: any) => {\n if (debug) {\n if (data) {\n console.log(`${TAG} ${message}`, data);\n } else {\n console.log(`${TAG} ${message}`);\n }\n }\n },\n [debug],\n );\n\n const handleAudioAnalysis = useCallback(\n async (analysis: AudioAnalysisData, visualizationDuration: number) => {\n const savedAnalysisData = analysisRef.current || { ...defaultAnalysis };\n\n const maxDuration = visualizationDuration;\n\n logDebug(\n `[handleAudioAnalysis] Received audio analysis: maxDuration=${maxDuration} analysis.dataPoints=${analysis.dataPoints.length} analysisData.dataPoints=${savedAnalysisData.dataPoints.length}`,\n analysis,\n );\n\n // Combine data points\n const combinedDataPoints = [\n ...savedAnalysisData.dataPoints,\n ...analysis.dataPoints,\n ];\n\n // Calculate the new duration\n const pointsPerSecond =\n analysis.pointsPerSecond || savedAnalysisData.pointsPerSecond;\n const maxDataPoints = (pointsPerSecond * visualizationDuration) / 1000;\n\n logDebug(\n `[handleAudioAnalysis] Combined data points before trimming: pointsPerSecond=${pointsPerSecond} visualizationDuration=${visualizationDuration} combinedDataPointsLength=${combinedDataPoints.length} vs maxDataPoints=${maxDataPoints}`,\n );\n\n // Trim data points to keep within the maximum number of data points\n if (combinedDataPoints.length > maxDataPoints) {\n combinedDataPoints.splice(0, combinedDataPoints.length - maxDataPoints);\n }\n\n savedAnalysisData.dataPoints = combinedDataPoints;\n savedAnalysisData.bitDepth =\n analysis.bitDepth || savedAnalysisData.bitDepth;\n savedAnalysisData.durationMs =\n combinedDataPoints.length * (1000 / pointsPerSecond);\n\n // Update amplitude range\n const newMin = Math.min(\n savedAnalysisData.amplitudeRange.min,\n analysis.amplitudeRange.min,\n );\n const newMax = Math.max(\n savedAnalysisData.amplitudeRange.max,\n analysis.amplitudeRange.max,\n );\n\n savedAnalysisData.amplitudeRange = {\n min: newMin,\n max: newMax,\n };\n\n logDebug(\n `[handleAudioAnalysis] Updated analysis data: durationMs=${savedAnalysisData.durationMs}`,\n savedAnalysisData,\n );\n\n // Update the ref\n analysisRef.current = savedAnalysisData;\n\n // Dispatch the updated analysis data to state to trigger re-render\n // need to use spread operator otherwise it doesnt trigger update.\n dispatch({ type: \"UPDATE_ANALYSIS\", payload: { ...savedAnalysisData } });\n },\n [logDebug],\n );\n\n const handleAudioEvent = useCallback(\n async (eventData: AudioEventPayload) => {\n const {\n fileUri,\n deltaSize,\n totalSize,\n lastEmittedSize,\n position,\n streamUuid,\n encoded,\n mimeType,\n buffer,\n } = eventData;\n logDebug(`[handleAudioEvent] Received audio event:`, {\n fileUri,\n deltaSize,\n totalSize,\n position,\n mimeType,\n lastEmittedSize,\n streamUuid,\n encodedLength: encoded?.length,\n });\n if (deltaSize === 0) {\n // Ignore packet with no data\n return;\n }\n try {\n // Coming from native ( ios / android ) otherwise buffer is set\n if (Platform.OS !== \"web\") {\n // Read the audio file as a base64 string for comparison\n if (!encoded) {\n console.error(`${TAG} Encoded audio data is missing`);\n throw new Error(\"Encoded audio data is missing\");\n }\n onAudioStreamRef.current?.({\n data: encoded,\n position,\n fileUri,\n eventDataSize: deltaSize,\n totalSize,\n });\n } else if (buffer) {\n // Coming from web\n onAudioStreamRef.current?.({\n data: buffer,\n position,\n fileUri,\n eventDataSize: deltaSize,\n totalSize,\n });\n }\n } catch (error) {\n console.error(`${TAG} Error processing audio event:`, error);\n }\n },\n [logDebug],\n );\n\n const checkStatus = useCallback(async () => {\n try {\n if (!state.isRecording) {\n logDebug(`${TAG} Not recording, exiting status check.`);\n return;\n }\n\n const status: AudioStreamStatus = ExpoAudioStream.status();\n if (debug) {\n logDebug(`${TAG} Status:`, status);\n }\n\n dispatch({\n type: \"UPDATE_STATUS\",\n payload: { durationMs: status.durationMs, size: status.size },\n });\n } catch (error) {\n console.error(`${TAG} Error getting status:`, error);\n }\n }, [state.isRecording, logDebug]);\n\n useEffect(() => {\n let interval: ReturnType<typeof setTimeout>;\n if (state.isRecording) {\n interval = setInterval(checkStatus, 1000);\n }\n return () => {\n if (interval) {\n clearInterval(interval);\n }\n };\n }, [checkStatus, state.isRecording]);\n\n useEffect(() => {\n logDebug(`Registering audio event listener`);\n const subscribeAudio = addAudioEventListener(handleAudioEvent);\n\n logDebug(`Subscribed to audio event listener and analysis listener`, {\n subscribeAudio,\n });\n\n return () => {\n logDebug(`Removing audio event listener`);\n subscribeAudio.remove();\n };\n }, [handleAudioEvent, handleAudioAnalysis, logDebug]);\n\n const startRecording = useCallback(\n async (recordingOptions: RecordingConfig) => {\n if (debug) {\n logDebug(`start recoding`, recordingOptions);\n }\n\n analysisRef.current = { ...defaultAnalysis }; // Reset analysis data\n\n const { onAudioStream, ...options } = recordingOptions;\n const { maxRecentDataDuration = 10000, enableProcessing } = options;\n if (typeof onAudioStream === \"function\") {\n onAudioStreamRef.current = onAudioStream;\n } else {\n console.warn(`${TAG} onAudioStream is not a function`, onAudioStream);\n onAudioStreamRef.current = null;\n }\n const startResult: StartAudioStreamResult =\n await ExpoAudioStream.startRecording(options);\n dispatch({ type: \"START\" });\n\n if (enableProcessing) {\n logDebug(`Enabling audio analysis listener`);\n const listener = addAudioAnalysisListener(async (analysisData) => {\n try {\n await handleAudioAnalysis(analysisData, maxRecentDataDuration);\n } catch (error) {\n console.warn(`${TAG} Error processing audio analysis:`, error);\n }\n });\n\n analysisListenerRef.current = listener;\n }\n\n return startResult;\n },\n [logDebug],\n );\n\n const stopRecording = useCallback(async () => {\n logDebug(`${TAG} stoping recording`);\n\n if (analysisListenerRef.current) {\n analysisListenerRef.current.remove();\n analysisListenerRef.current = null;\n }\n\n const stopResult: AudioStreamResult = await ExpoAudioStream.stopRecording();\n onAudioStreamRef.current = null;\n logDebug(`${TAG} recording stopped`, stopResult);\n dispatch({ type: \"STOP\" });\n return stopResult;\n }, [logDebug]);\n\n const pauseRecording = useCallback(async () => {\n logDebug(`${TAG} pause recording`);\n const pauseResult = await ExpoAudioStream.pauseRecording();\n dispatch({ type: \"PAUSE\" });\n return pauseResult;\n }, [logDebug]);\n\n const resumeRecording = useCallback(async () => {\n logDebug(`${TAG} resume recording`);\n const resumeResult = await ExpoAudioStream.resumeRecording();\n dispatch({ type: \"RESUME\" });\n return resumeResult;\n }, [logDebug]);\n\n return {\n startRecording,\n stopRecording,\n pauseRecording,\n resumeRecording,\n isPaused: state.isPaused,\n isRecording: state.isRecording,\n durationMs: state.durationMs,\n size: state.size,\n analysisData: state.analysisData,\n };\n}\n"]}
package/build/utils.d.ts DELETED
@@ -1,31 +0,0 @@
1
- import { EncodingType } from "./ExpoAudioStream.types";
2
- export declare const WAV_HEADER_SIZE = 44;
3
- export declare const convertPCMToFloat32: ({ bitDepth, buffer, skipWavHeader, }: {
4
- buffer: ArrayBuffer;
5
- bitDepth: number;
6
- skipWavHeader?: boolean;
7
- }) => {
8
- pcmValues: Float32Array;
9
- min: number;
10
- max: number;
11
- };
12
- interface WavHeaderOptions {
13
- buffer: ArrayBuffer;
14
- sampleRate: number;
15
- numChannels: number;
16
- bitDepth: number;
17
- }
18
- export declare const writeWavHeader: ({ buffer, sampleRate, numChannels, bitDepth, }: WavHeaderOptions) => ArrayBuffer;
19
- export interface WavFileInfo {
20
- sampleRate: number;
21
- numChannels: number;
22
- bitDepth: number;
23
- size: number;
24
- durationMs: number;
25
- }
26
- export declare const getWavFileInfo: (arrayBuffer: ArrayBuffer) => Promise<WavFileInfo>;
27
- export declare const encodingToBitDepth: ({ encoding, }: {
28
- encoding: EncodingType;
29
- }) => number;
30
- export {};
31
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,mBAAmB,yCAI7B;IACD,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,KAAG;IAAE,SAAS,EAAE,YAAY,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAsCtD,CAAC;AAEF,UAAU,gBAAgB;IACxB,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,cAAc,mDAKxB,gBAAgB,KAAG,WA0CrB,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,cAAc,gBACZ,WAAW,KACvB,OAAO,CAAC,WAAW,CAsDrB,CAAC;AAEF,eAAO,MAAM,kBAAkB,kBAE5B;IACD,QAAQ,EAAE,YAAY,CAAC;CACxB,KAAG,MAWH,CAAC"}
package/build/utils.js DELETED
@@ -1,143 +0,0 @@
1
- export const WAV_HEADER_SIZE = 44;
2
- export const convertPCMToFloat32 = ({ bitDepth, buffer, skipWavHeader = false, }) => {
3
- const dataView = new DataView(buffer);
4
- const headerOffset = skipWavHeader ? WAV_HEADER_SIZE : 0;
5
- const dataLength = buffer.byteLength - headerOffset;
6
- const sampleLength = dataLength / (bitDepth / 8);
7
- const float32Array = new Float32Array(sampleLength);
8
- let min = Infinity;
9
- let max = -Infinity;
10
- for (let i = 0; i < sampleLength; i++) {
11
- let value = 0;
12
- const offset = headerOffset + i * (bitDepth / 8);
13
- switch (bitDepth) {
14
- case 8:
15
- value = dataView.getUint8(offset) / 128;
16
- break;
17
- case 16:
18
- value = dataView.getInt16(offset, true) / 32768;
19
- break;
20
- case 24:
21
- value =
22
- (dataView.getUint8(offset) +
23
- (dataView.getUint8(offset + 1) << 8) +
24
- (dataView.getUint8(offset + 2) << 16)) /
25
- 8388608;
26
- break;
27
- case 32:
28
- value = dataView.getFloat32(offset, true);
29
- break;
30
- default:
31
- throw new Error(`Unsupported bit depth: ${bitDepth}`);
32
- }
33
- if (value < min)
34
- min = value;
35
- if (value > max)
36
- max = value;
37
- float32Array[i] = value;
38
- }
39
- return { pcmValues: float32Array, min, max };
40
- };
41
- export const writeWavHeader = ({ buffer, sampleRate, numChannels, bitDepth, }) => {
42
- const bytesPerSample = bitDepth / 8;
43
- const numSamples = buffer.byteLength / (numChannels * bytesPerSample);
44
- const view = new DataView(buffer);
45
- const blockAlign = numChannels * bytesPerSample;
46
- const byteRate = sampleRate * blockAlign;
47
- // Function to write a string to the DataView
48
- const writeString = (view, offset, string) => {
49
- for (let i = 0; i < string.length; i++) {
50
- view.setUint8(offset + i, string.charCodeAt(i));
51
- }
52
- };
53
- // Check if the buffer already has a WAV header by looking for "RIFF" at the start
54
- const existingHeader = view.getUint32(0, false) === 0x52494646; // "RIFF" in ASCII
55
- if (!existingHeader) {
56
- // Write the WAV header
57
- writeString(view, 0, "RIFF"); // ChunkID
58
- view.setUint32(4, 36 + numSamples * blockAlign, true); // ChunkSize
59
- writeString(view, 8, "WAVE"); // Format
60
- writeString(view, 12, "fmt "); // Subchunk1ID
61
- view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)
62
- view.setUint16(20, bitDepth === 32 ? 3 : 1, true); // AudioFormat (3 for float, 1 for PCM)
63
- view.setUint16(22, numChannels, true); // NumChannels
64
- view.setUint32(24, sampleRate, true); // SampleRate
65
- view.setUint32(28, byteRate, true); // ByteRate
66
- view.setUint16(32, blockAlign, true); // BlockAlign
67
- view.setUint16(34, bitDepth, true); // BitsPerSample
68
- writeString(view, 36, "data"); // Subchunk2ID
69
- view.setUint32(40, numSamples * blockAlign, true); // Subchunk2Size
70
- }
71
- else {
72
- // Update the existing WAV header if necessary
73
- view.setUint32(4, 36 + numSamples * blockAlign, true); // Update ChunkSize
74
- view.setUint32(24, sampleRate, true); // Update SampleRate
75
- view.setUint32(28, byteRate, true); // Update ByteRate
76
- view.setUint16(32, blockAlign, true); // Update BlockAlign
77
- view.setUint32(40, numSamples * blockAlign, true); // Update Subchunk2Size
78
- }
79
- return buffer;
80
- };
81
- export const getWavFileInfo = async (arrayBuffer) => {
82
- const view = new DataView(arrayBuffer);
83
- // Check if the file is a valid RIFF/WAVE file
84
- const riffHeader = view.getUint32(0, false); // "RIFF"
85
- const waveHeader = view.getUint32(8, false); // "WAVE"
86
- if (riffHeader !== 0x52494646 || waveHeader !== 0x57415645) {
87
- throw new Error("Invalid WAV file");
88
- }
89
- // Locate the "fmt " chunk
90
- let fmtChunkOffset = 12;
91
- let sampleRate = 0;
92
- let numChannels = 0;
93
- let bitDepth = 0;
94
- let dataChunkSize = 0;
95
- let audioFormat = 0;
96
- while (fmtChunkOffset < view.byteLength) {
97
- const chunkId = view.getUint32(fmtChunkOffset, false);
98
- const chunkSize = view.getUint32(fmtChunkOffset + 4, true);
99
- if (chunkId === 0x666d7420) {
100
- // "fmt "
101
- audioFormat = view.getUint16(fmtChunkOffset + 8, true);
102
- if (audioFormat !== 1 && audioFormat !== 3) {
103
- throw new Error("Unsupported WAV file format");
104
- }
105
- numChannels = view.getUint16(fmtChunkOffset + 10, true);
106
- sampleRate = view.getUint32(fmtChunkOffset + 12, true);
107
- bitDepth = view.getUint16(fmtChunkOffset + 22, true);
108
- }
109
- else if (chunkId === 0x64617461) {
110
- // "data"
111
- dataChunkSize = chunkSize;
112
- break;
113
- }
114
- fmtChunkOffset += 8 + chunkSize;
115
- }
116
- if (!sampleRate || !numChannels || !bitDepth || !dataChunkSize) {
117
- throw new Error("Incomplete WAV file information");
118
- }
119
- // Calculate duration
120
- const bytesPerSample = bitDepth / 8;
121
- const numSamples = dataChunkSize / (numChannels * bytesPerSample);
122
- const durationMs = (numSamples / sampleRate) * 1000;
123
- return {
124
- sampleRate,
125
- numChannels,
126
- bitDepth,
127
- size: arrayBuffer.byteLength,
128
- durationMs,
129
- };
130
- };
131
- export const encodingToBitDepth = ({ encoding, }) => {
132
- switch (encoding) {
133
- case "pcm_32bit":
134
- return 32;
135
- case "pcm_16bit":
136
- return 16;
137
- case "pcm_8bit":
138
- return 8;
139
- default:
140
- throw new Error(`Unsupported encoding type: ${encoding}`);
141
- }
142
- };
143
- //# sourceMappingURL=utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { EncodingType } from \"./ExpoAudioStream.types\";\n\nexport const WAV_HEADER_SIZE = 44;\nexport const convertPCMToFloat32 = ({\n bitDepth,\n buffer,\n skipWavHeader = false,\n}: {\n buffer: ArrayBuffer;\n bitDepth: number;\n skipWavHeader?: boolean;\n}): { pcmValues: Float32Array; min: number; max: number } => {\n const dataView = new DataView(buffer);\n const headerOffset = skipWavHeader ? WAV_HEADER_SIZE : 0;\n const dataLength = buffer.byteLength - headerOffset;\n const sampleLength = dataLength / (bitDepth / 8);\n const float32Array = new Float32Array(sampleLength);\n let min = Infinity;\n let max = -Infinity;\n\n for (let i = 0; i < sampleLength; i++) {\n let value = 0;\n const offset = headerOffset + i * (bitDepth / 8);\n switch (bitDepth) {\n case 8:\n value = dataView.getUint8(offset) / 128;\n break;\n case 16:\n value = dataView.getInt16(offset, true) / 32768;\n break;\n case 24:\n value =\n (dataView.getUint8(offset) +\n (dataView.getUint8(offset + 1) << 8) +\n (dataView.getUint8(offset + 2) << 16)) /\n 8388608;\n break;\n case 32:\n value = dataView.getFloat32(offset, true);\n break;\n default:\n throw new Error(`Unsupported bit depth: ${bitDepth}`);\n }\n if (value < min) min = value;\n if (value > max) max = value;\n float32Array[i] = value;\n }\n\n return { pcmValues: float32Array, min, max };\n};\n\ninterface WavHeaderOptions {\n buffer: ArrayBuffer;\n sampleRate: number;\n numChannels: number;\n bitDepth: number;\n}\n\nexport const writeWavHeader = ({\n buffer,\n sampleRate,\n numChannels,\n bitDepth,\n}: WavHeaderOptions): ArrayBuffer => {\n const bytesPerSample = bitDepth / 8;\n const numSamples = buffer.byteLength / (numChannels * bytesPerSample);\n const view = new DataView(buffer);\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n\n // Function to write a string to the DataView\n const writeString = (view: DataView, offset: number, string: string) => {\n for (let i = 0; i < string.length; i++) {\n view.setUint8(offset + i, string.charCodeAt(i));\n }\n };\n\n // Check if the buffer already has a WAV header by looking for \"RIFF\" at the start\n const existingHeader = view.getUint32(0, false) === 0x52494646; // \"RIFF\" in ASCII\n\n if (!existingHeader) {\n // Write the WAV header\n writeString(view, 0, \"RIFF\"); // ChunkID\n view.setUint32(4, 36 + numSamples * blockAlign, true); // ChunkSize\n writeString(view, 8, \"WAVE\"); // Format\n writeString(view, 12, \"fmt \"); // Subchunk1ID\n view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)\n view.setUint16(20, bitDepth === 32 ? 3 : 1, true); // AudioFormat (3 for float, 1 for PCM)\n view.setUint16(22, numChannels, true); // NumChannels\n view.setUint32(24, sampleRate, true); // SampleRate\n view.setUint32(28, byteRate, true); // ByteRate\n view.setUint16(32, blockAlign, true); // BlockAlign\n view.setUint16(34, bitDepth, true); // BitsPerSample\n writeString(view, 36, \"data\"); // Subchunk2ID\n view.setUint32(40, numSamples * blockAlign, true); // Subchunk2Size\n } else {\n // Update the existing WAV header if necessary\n view.setUint32(4, 36 + numSamples * blockAlign, true); // Update ChunkSize\n view.setUint32(24, sampleRate, true); // Update SampleRate\n view.setUint32(28, byteRate, true); // Update ByteRate\n view.setUint16(32, blockAlign, true); // Update BlockAlign\n view.setUint32(40, numSamples * blockAlign, true); // Update Subchunk2Size\n }\n\n return buffer;\n};\n\nexport interface WavFileInfo {\n sampleRate: number;\n numChannels: number;\n bitDepth: number;\n size: number; // in bytes\n durationMs: number; // in seconds\n}\n\nexport const getWavFileInfo = async (\n arrayBuffer: ArrayBuffer,\n): Promise<WavFileInfo> => {\n const view = new DataView(arrayBuffer);\n\n // Check if the file is a valid RIFF/WAVE file\n const riffHeader = view.getUint32(0, false); // \"RIFF\"\n const waveHeader = view.getUint32(8, false); // \"WAVE\"\n if (riffHeader !== 0x52494646 || waveHeader !== 0x57415645) {\n throw new Error(\"Invalid WAV file\");\n }\n\n // Locate the \"fmt \" chunk\n let fmtChunkOffset = 12;\n let sampleRate = 0;\n let numChannels = 0;\n let bitDepth = 0;\n let dataChunkSize = 0;\n let audioFormat = 0;\n\n while (fmtChunkOffset < view.byteLength) {\n const chunkId = view.getUint32(fmtChunkOffset, false);\n const chunkSize = view.getUint32(fmtChunkOffset + 4, true);\n if (chunkId === 0x666d7420) {\n // \"fmt \"\n audioFormat = view.getUint16(fmtChunkOffset + 8, true);\n if (audioFormat !== 1 && audioFormat !== 3) {\n throw new Error(\"Unsupported WAV file format\");\n }\n numChannels = view.getUint16(fmtChunkOffset + 10, true);\n sampleRate = view.getUint32(fmtChunkOffset + 12, true);\n bitDepth = view.getUint16(fmtChunkOffset + 22, true);\n } else if (chunkId === 0x64617461) {\n // \"data\"\n dataChunkSize = chunkSize;\n break;\n }\n fmtChunkOffset += 8 + chunkSize;\n }\n\n if (!sampleRate || !numChannels || !bitDepth || !dataChunkSize) {\n throw new Error(\"Incomplete WAV file information\");\n }\n\n // Calculate duration\n const bytesPerSample = bitDepth / 8;\n const numSamples = dataChunkSize / (numChannels * bytesPerSample);\n const durationMs = (numSamples / sampleRate) * 1000;\n\n return {\n sampleRate,\n numChannels,\n bitDepth,\n size: arrayBuffer.byteLength,\n durationMs,\n };\n};\n\nexport const encodingToBitDepth = ({\n encoding,\n}: {\n encoding: EncodingType;\n}): number => {\n switch (encoding) {\n case \"pcm_32bit\":\n return 32;\n case \"pcm_16bit\":\n return 16;\n case \"pcm_8bit\":\n return 8;\n default:\n throw new Error(`Unsupported encoding type: ${encoding}`);\n }\n};\n"]}
@@ -1,340 +0,0 @@
1
- export const SimpleInlineProcessorScript = `
2
- class RecorderProcessor extends AudioWorkletProcessor {
3
- constructor() {
4
- super();
5
- this.recordedBuffers = []; // Float32Array
6
- this.newRecBuffer = []; // Float32Array
7
- this.exportIntervalSamples = 0;
8
- this.samplesSinceLastExport = 0;
9
- this.recordSampleRate = 44100; // To be overwritten
10
- this.exportSampleRate = 44100; // To be overwritten
11
- this.channels = 1; // Default to 1 channel (mono)
12
- this.bitDepth = 32; // Default to 32-bit depth
13
- this.isRecording = true;
14
- this.port.onmessage = this.handleMessage.bind(this);
15
- }
16
-
17
- handleMessage(event) {
18
- switch (event.data.command) {
19
- case 'init':
20
- this.recordSampleRate = event.data.recordSampleRate;
21
- this.exportSampleRate = event.data.exportSampleRate || event.data.recordSampleRate;
22
- this.exportIntervalSamples = this.recordSampleRate * (event.data.interval / 1000);
23
- break;
24
- case 'stop':
25
- this.isRecording = false;
26
- const fullRecordedData = this.getAllRecordedData();
27
- this.port.postMessage({ command: 'recordedData', recordedData: fullRecordedData });
28
- break;
29
- }
30
- }
31
-
32
- process(inputs, outputs, parameters) {
33
- if (!this.isRecording) return true;
34
- const input = inputs[0];
35
- if (input.length > 0) {
36
- const newBuffer = new Float32Array(input[0]);
37
- this.newRecBuffer.push(newBuffer);
38
- this.recordedBuffers.push(newBuffer);
39
- this.samplesSinceLastExport += newBuffer.length;
40
-
41
- if (this.samplesSinceLastExport >= this.exportIntervalSamples) {
42
- this.exportNewData();
43
- this.samplesSinceLastExport = 0;
44
- }
45
- }
46
- return true;
47
- }
48
-
49
- mergeBuffers(bufferArray, recLength) {
50
- const result = new Float32Array(recLength);
51
- let offset = 0;
52
- for (let i = 0; i < bufferArray.length; i++) {
53
- result.set(bufferArray[i], offset);
54
- offset += bufferArray[i].length;
55
- }
56
- return result;
57
- }
58
-
59
- floatTo16BitPCM(output, offset, input) {
60
- for (let i = 0; i < input.length; i++, offset += 2) {
61
- const s = Math.max(-1, Math.min(1, input[i]));
62
- output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
63
- }
64
- console.debug('Float to 16-bit PCM conversion complete. Output byte length:', offset);
65
- }
66
-
67
- floatTo32BitPCM(output, offset, input) {
68
- for (let i = 0; i < input.length; i++, offset += 4) {
69
- output.setFloat32(offset, input[i], true);
70
- }
71
- console.debug('Float to 32-bit PCM (no conversion) complete. Output byte length:', offset);
72
- }
73
-
74
- writeString(view, offset, string) {
75
- for (let i = 0; i < string.length; i++) {
76
- view.setUint8(offset + i, string.charCodeAt(i));
77
- }
78
- }
79
-
80
- encodeWAV(samples, includeHeader = true) {
81
- const sampleCount = samples.length;
82
- const buffer = new ArrayBuffer((includeHeader ? 44 : 0) + sampleCount * 4);
83
- const view = new DataView(buffer);
84
-
85
- if (includeHeader) {
86
- this.writeString(view, 0, 'RIFF');
87
- view.setUint32(4, 36 + sampleCount * 4, true); // File size - 8 bytes
88
- this.writeString(view, 8, 'WAVE');
89
- this.writeString(view, 12, 'fmt ');
90
- view.setUint32(16, 16, true); // PCM format
91
- view.setUint16(20, 3, true); // Format code 3 for float
92
- view.setUint16(22, 1, true); // Mono channel
93
- view.setUint32(24, this.recordSampleRate, true); // Sample rate
94
- view.setUint32(28, this.recordSampleRate * 4, true); // Byte rate
95
- view.setUint16(32, 4, true); // Block align (4 bytes for 32-bit float)
96
- view.setUint16(34, 32, true); // Bits per sample (32-bit float)
97
- this.writeString(view, 36, 'data');
98
- view.setUint32(40, sampleCount * 4, true); // Data chunk size
99
- }
100
-
101
- console.debug('Writing PCM samples to DataView. Offset:', includeHeader ? 44 : 0, 'Samples length:', sampleCount);
102
- this.floatTo32BitPCM(view, includeHeader ? 44 : 0, samples);
103
- // this.floatTo16BitPCM(view, includeHeader ? 44 : 0, samples);
104
-
105
- console.debug('Encoded WAV DataView:', view);
106
- console.debug('Encoded WAV length:', view.byteLength);
107
-
108
- return view;
109
- }
110
-
111
-
112
- exportNewData() {
113
- // Calculate the total length of the new recorded buffers
114
- const length = this.newRecBuffer.reduce((acc, buffer) => acc + buffer.length, 0);
115
-
116
- // Merge all new recorded buffers into a single buffer
117
- const mergedBuffer = this.mergeBuffers(this.newRecBuffer, length);
118
-
119
- // Encode the merged buffer into a WAV format
120
- const encodedWav = this.encodeWAV(mergedBuffer, false);
121
-
122
- // Clear the new recorded buffers after they have been processed
123
- this.newRecBuffer.length = 0;
124
-
125
- // Post the message to the main thread
126
- // The first argument is the message data, containing the encoded WAV buffer
127
- // The second argument is the transfer list, which transfers ownership of the ArrayBuffer
128
- // to the main thread, avoiding the need to copy the buffer and improving performance
129
- this.port.postMessage({ recordedData: encodedWav.buffer }, [encodedWav.buffer]);
130
- }
131
-
132
- getAllRecordedData() {
133
- const length = this.recordedBuffers.reduce((acc, buffer) => acc + buffer.length, 0);
134
- const mergedBuffer = this.mergeBuffers(this.recordedBuffers, length);
135
-
136
-
137
- // Calculate the duration based on the sample count and sample rate
138
- const sampleCount = mergedBuffer.length;
139
- const mergedBufferDuration = sampleCount / this.recordSampleRate;
140
-
141
- const encodedWav = this.encodeWAV(mergedBuffer, true);
142
-
143
- // Calculate and log the duration for encodedWav.buffer based on sample count
144
- const encodedWavBufferDuration = sampleCount / this.recordSampleRate;
145
-
146
- this.recordedBuffers.length = 0; // Clear the buffers after extraction
147
-
148
- // Returning both for testing, comment one of the returns based on your test
149
- console.debug('mergedBuffer:', mergedBuffer);
150
- console.debug('encodedWav.buffer:', encodedWav.buffer);
151
-
152
- // Uncomment the appropriate return for testing
153
- // return mergedBuffer; // This works when played
154
- return encodedWav.buffer; // This doesn't work when played
155
- }
156
- }
157
-
158
- registerProcessor('recorder-processor', RecorderProcessor);
159
- `;
160
-
161
- // Because we use expo and needs to include the worker script in the shared library, better inline it in the module.
162
- export const InlineProcessorScrippt = `
163
- class RecorderProcessor extends AudioWorkletProcessor {
164
- constructor() {
165
- super();
166
- this.recLength = 0;
167
- this.recBuffer = [];
168
- this.headerSent = false;
169
- this.newRecBuffer = [];
170
- this.exportIntervalSamples = 0;
171
- this.samplesSinceLastExport = 0;
172
- this.recordSampleRate = 44100; // To be overwrited
173
- this.exportSampleRate = 44100; // To be overwrited
174
- this.isRecording = true;
175
-
176
- this.port.onmessage = this.handleMessage.bind(this);
177
- }
178
-
179
- handleMessage(event) {
180
- switch (event.data.command) {
181
- case 'init':
182
- this.recordSampleRate = event.data.recordSampleRate;
183
- this.exportSampleRate = event.data.exportSampleRate || event.data.recordSampleRate;
184
- this.exportIntervalSamples = this.recordSampleRate * (event.data.interval / 1000);
185
- break;
186
- case 'stop':
187
- this.isRecording = false;
188
- break;
189
- case 'getRecordedData':
190
- const recordedData = this.getRecordedData();
191
- this.port.postMessage({ command: 'recordedData', recordedData });
192
- break;
193
- }
194
- }
195
-
196
- process(inputs) {
197
- if (!this.isRecording) {
198
- return true; // Exit early if not recording
199
- }
200
-
201
- if (inputs.length === 0 || inputs[0].length === 0) {
202
- console.warn('RecorderProcessor -- No input received.');
203
- return true; // Exit early if no input
204
- }
205
-
206
- const buffer = inputs[0];
207
- if (!buffer) {
208
- console.error('Input buffer is null.');
209
- return true; // Exit early if buffer is null
210
- }
211
-
212
- try {
213
- this.record(buffer);
214
-
215
- // this.samplesSinceLastExport += buffer.length;
216
- // if (this.samplesSinceLastExport >= this.exportIntervalSamples) {
217
- // this.exportBuffer();
218
- // this.samplesSinceLastExport = 0;
219
- // }
220
- } catch (error) {
221
- console.error('Error during processing:', error);
222
- }
223
- return true;
224
- }
225
-
226
- record(inputBuffer) {
227
- this.recBuffer.push(inputBuffer);
228
- this.newRecBuffer.push(inputBuffer);
229
- this.recLength += inputBuffer.length;
230
- }
231
-
232
- exportBuffer() {
233
- const mergedBuffers = this.mergeBuffers(this.newRecBuffer, this.newRecBuffer.reduce((len, buf) => len + buf.length, 0));
234
- console.log('Merged buffer length:', mergedBuffers.length); // Debug log
235
-
236
- const downsampledBuffer = this.downsampleBuffer(mergedBuffers, this.exportSampleRate);
237
- console.log('Downsampled buffer length:', downsampledBuffer.length); // Debug log
238
-
239
- const encodedWav = downsampledBuffer;
240
- // const encodedWav = this.encodeWAV(downsampledBuffer);
241
- // console.log('Encoded WAV length:', encodedWav.byteLength); // Debug log
242
-
243
- this.port.postMessage({ encodedWav, sampleRate: this.exportSampleRate });
244
- this.newRecBuffer = []; // Clear the new data buffer after export
245
- this.headerSent = true; // Indicate that the header has been sent
246
- }
247
-
248
- downsampleBuffer(buffer, exportSampleRate) {
249
- console.log('Original buffer length:', buffer.length); // Debug log
250
- console.log('Record sample rate:', this.recordSampleRate); // Debug log
251
- console.log('Export sample rate:', exportSampleRate); // Debug log
252
- if (exportSampleRate === this.recordSampleRate) {
253
- return buffer;
254
- }
255
-
256
- const sampleRateRatio = this.recordSampleRate / exportSampleRate;
257
- const newLength = Math.round(buffer.length / sampleRateRatio);
258
- console.log('Sample rate ratio:', sampleRateRatio); // Debug log
259
- console.log('New length after downsampling:', newLength); // Debug log
260
-
261
- if (newLength <= 0) {
262
- console.error('New length is zero or negative, returning empty buffer.'); // Debug log
263
- return new Float32Array(0);
264
- }
265
- const result = new Float32Array(newLength);
266
- let offsetResult = 0;
267
- let offsetBuffer = 0;
268
- while (offsetResult < result.length) {
269
- const nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
270
- let accum = 0, count = 0;
271
- for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
272
- accum += buffer[i];
273
- count++;
274
- }
275
- result[offsetResult] = accum / count;
276
- offsetResult++;
277
- offsetBuffer = nextOffsetBuffer;
278
- }
279
- return result;
280
- }
281
-
282
- mergeBuffers(bufferArray, recLength) {
283
- const result = new Float32Array(recLength);
284
- let offset = 0;
285
- for (let i = 0; i < bufferArray.length; i++) {
286
- result.set(bufferArray[i], offset);
287
- offset += bufferArray[i].length;
288
- }
289
- return result;
290
- }
291
-
292
- floatTo16BitPCM(output, offset, input) {
293
- for (let i = 0; i < input.length; i++, offset += 2) {
294
- const s = Math.max(-1, Math.min(1, input[i]));
295
- output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
296
- }
297
- }
298
-
299
- writeString(view, offset, string) {
300
- for (let i = 0; i < string.length; i++) {
301
- view.setUint8(offset + i, string.charCodeAt(i));
302
- }
303
- }
304
-
305
- getRecordedData() {
306
- const length = this.recBuffer.reduce((acc, buffer) => acc + buffer.length, 0);
307
- const result = new Float32Array(length);
308
- let offset = 0;
309
- for (const buffer of this.recBuffer) {
310
- result.set(buffer, offset);
311
- offset += buffer.length;
312
- }
313
- this.recBuffer.length = 0; // Clear the buffers after extraction
314
-
315
- return result;
316
- }
317
-
318
- encodeWAV(samples) {
319
- const buffer = new ArrayBuffer(44 + samples.length * 2);
320
- const view = new DataView(buffer);
321
- this.writeString(view, 0, 'RIFF');
322
- view.setUint32(4, 32 + samples.length * 2, true);
323
- this.writeString(view, 8, 'WAVE');
324
- this.writeString(view, 12, 'fmt ');
325
- view.setUint32(16, 16, true);
326
- view.setUint16(20, 1, true);
327
- view.setUint16(22, 1, true);
328
- view.setUint32(24, sampleRate, true);
329
- view.setUint32(28, sampleRate * 2, true);
330
- view.setUint16(32, 2, true);
331
- view.setUint16(34, 16, true);
332
- this.writeString(view, 36, 'data');
333
- view.setUint32(40, samples.length * 2, true);
334
- this.floatTo16BitPCM(view, 44, samples);
335
- return view;
336
- }
337
- }
338
-
339
- registerProcessor('recorder-processor', RecorderProcessor);
340
- `;