@siteed/audio-studio 3.2.1-beta.0 → 3.2.1-beta.2

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 (64) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +41 -1
  3. package/android/src/main/java/net/siteed/audiostudio/AudioRecorderManager.kt +130 -0
  4. package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +1 -0
  5. package/android/src/main/java/net/siteed/audiostudio/Constants.kt +2 -1
  6. package/android/src/main/java/net/siteed/audiostudio/RecordingConfig.kt +5 -1
  7. package/build/cjs/AudioStudio.types.js.map +1 -1
  8. package/build/cjs/AudioStudio.web.js +125 -13
  9. package/build/cjs/AudioStudio.web.js.map +1 -1
  10. package/build/cjs/AudioStudioModule.js +6 -1
  11. package/build/cjs/AudioStudioModule.js.map +1 -1
  12. package/build/cjs/events.js +4 -0
  13. package/build/cjs/events.js.map +1 -1
  14. package/build/cjs/index.js +3 -1
  15. package/build/cjs/index.js.map +1 -1
  16. package/build/cjs/useAudioRecorder.js +187 -30
  17. package/build/cjs/useAudioRecorder.js.map +1 -1
  18. package/build/cjs/utils/nativeRecordingOptions.js +13 -0
  19. package/build/cjs/utils/nativeRecordingOptions.js.map +1 -0
  20. package/build/cjs/utils/nativeRecordingOptions.test.js +30 -0
  21. package/build/cjs/utils/nativeRecordingOptions.test.js.map +1 -0
  22. package/build/esm/AudioStudio.types.js.map +1 -1
  23. package/build/esm/AudioStudio.web.js +125 -13
  24. package/build/esm/AudioStudio.web.js.map +1 -1
  25. package/build/esm/AudioStudioModule.js +6 -1
  26. package/build/esm/AudioStudioModule.js.map +1 -1
  27. package/build/esm/events.js +3 -0
  28. package/build/esm/events.js.map +1 -1
  29. package/build/esm/index.js +1 -0
  30. package/build/esm/index.js.map +1 -1
  31. package/build/esm/useAudioRecorder.js +188 -31
  32. package/build/esm/useAudioRecorder.js.map +1 -1
  33. package/build/esm/utils/nativeRecordingOptions.js +10 -0
  34. package/build/esm/utils/nativeRecordingOptions.js.map +1 -0
  35. package/build/esm/utils/nativeRecordingOptions.test.js +28 -0
  36. package/build/esm/utils/nativeRecordingOptions.test.js.map +1 -0
  37. package/build/types/AudioStudio.types.d.ts +58 -1
  38. package/build/types/AudioStudio.types.d.ts.map +1 -1
  39. package/build/types/AudioStudio.web.d.ts +17 -1
  40. package/build/types/AudioStudio.web.d.ts.map +1 -1
  41. package/build/types/AudioStudioModule.d.ts.map +1 -1
  42. package/build/types/events.d.ts +2 -1
  43. package/build/types/events.d.ts.map +1 -1
  44. package/build/types/index.d.ts +1 -0
  45. package/build/types/index.d.ts.map +1 -1
  46. package/build/types/useAudioRecorder.d.ts +4 -1
  47. package/build/types/useAudioRecorder.d.ts.map +1 -1
  48. package/build/types/utils/nativeRecordingOptions.d.ts +28 -0
  49. package/build/types/utils/nativeRecordingOptions.d.ts.map +1 -0
  50. package/build/types/utils/nativeRecordingOptions.test.d.ts +2 -0
  51. package/build/types/utils/nativeRecordingOptions.test.d.ts.map +1 -0
  52. package/ios/AudioStreamManager.swift +103 -9
  53. package/ios/AudioStreamManagerDelegate.swift +1 -0
  54. package/ios/AudioStudioModule.swift +6 -0
  55. package/ios/RecordingSettings.swift +48 -43
  56. package/package.json +1 -1
  57. package/src/AudioStudio.types.ts +70 -1
  58. package/src/AudioStudio.web.ts +152 -13
  59. package/src/AudioStudioModule.ts +6 -1
  60. package/src/events.ts +13 -1
  61. package/src/index.ts +1 -0
  62. package/src/useAudioRecorder.tsx +260 -45
  63. package/src/utils/nativeRecordingOptions.test.ts +29 -0
  64. package/src/utils/nativeRecordingOptions.ts +20 -0
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.addAudioEventListener = addAudioEventListener;
8
8
  exports.addAudioAnalysisListener = addAudioAnalysisListener;
9
9
  exports.addRecordingInterruptionListener = addRecordingInterruptionListener;
10
+ exports.addMaxDurationReachedListener = addMaxDurationReachedListener;
10
11
  const expo_modules_core_1 = require("expo-modules-core");
11
12
  const AudioStudioModule_1 = __importDefault(require("./AudioStudioModule"));
12
13
  const emitter = new expo_modules_core_1.LegacyEventEmitter(AudioStudioModule_1.default);
@@ -26,4 +27,7 @@ function addRecordingInterruptionListener(listener) {
26
27
  });
27
28
  return subscription;
28
29
  }
30
+ function addMaxDurationReachedListener(listener) {
31
+ return emitter.addListener('MaxDurationReached', listener);
32
+ }
29
33
  //# sourceMappingURL=events.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":";AAAA,sCAAsC;;;;;AAgCtC,sDAIC;AAKD,4DAIC;AAED,4EAeC;AA5DD,yDAA8E;AAI9E,4EAAmD;AAEnD,MAAM,OAAO,GAAG,IAAI,sCAAkB,CAAC,2BAAiB,CAAC,CAAA;AAwBzD,SAAgB,qBAAqB,CACjC,QAAqD;IAErD,OAAO,OAAO,CAAC,WAAW,CAAoB,WAAW,EAAE,QAAQ,CAAC,CAAA;AACxE,CAAC;AAKD,SAAgB,wBAAwB,CACpC,QAAsD;IAEtD,OAAO,OAAO,CAAC,WAAW,CAAqB,eAAe,EAAE,QAAQ,CAAC,CAAA;AAC7E,CAAC;AAED,SAAgB,gCAAgC,CAC5C,QAAqD;IAErD,oBAAoB;IACpB,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAEvD,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,CACpC,wBAAwB,EAAE,+CAA+C;IACzE,CAAC,KAAK,EAAE,EAAE;QACN,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAA;QAC9D,QAAQ,CAAC,KAAK,CAAC,CAAA;IACnB,CAAC,CACJ,CAAA;IAED,OAAO,YAAY,CAAA;AACvB,CAAC","sourcesContent":["// packages/audio-studio/src/events.ts\n\nimport { LegacyEventEmitter, type EventSubscription } from 'expo-modules-core'\n\nimport { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'\nimport { RecordingInterruptionEvent } from './AudioStudio.types'\nimport AudioStudioModule from './AudioStudioModule'\n\nconst emitter = new LegacyEventEmitter(AudioStudioModule)\n\n// Internal event payload from native module\nexport interface AudioEventPayload {\n encoded?: string\n /** Float32 samples in [-1, 1] — sent by native when streamFormat='float32'.\n * Android new arch delivers Float32Array; iOS delivers number[]. */\n pcmFloat32?: Float32Array | number[]\n buffer?: Float32Array\n fileUri: string\n lastEmittedSize: number\n position: number\n deltaSize: number\n totalSize: number\n mimeType: string\n streamUuid: string\n compression?: {\n data?: string | Blob // Base64 (native) or Float32Array (web) encoded compressed data chunk\n position: number\n eventDataSize: number\n totalSize: number\n }\n}\n\nexport function addAudioEventListener(\n listener: (event: AudioEventPayload) => Promise<void>\n): EventSubscription {\n return emitter.addListener<AudioEventPayload>('AudioData', listener)\n}\n\n// Only aliasing the AudioAnalysis type for the event payload\nexport interface AudioAnalysisEvent extends AudioAnalysis {}\n\nexport function addAudioAnalysisListener(\n listener: (event: AudioAnalysisEvent) => Promise<void>\n): EventSubscription {\n return emitter.addListener<AudioAnalysisEvent>('AudioAnalysis', listener)\n}\n\nexport function addRecordingInterruptionListener(\n listener: (event: RecordingInterruptionEvent) => void\n): EventSubscription {\n // Add debug logging\n console.debug('Adding recording interruption listener')\n\n const subscription = emitter.addListener<RecordingInterruptionEvent>(\n 'onRecordingInterrupted', // Make sure this matches the native event name\n (event) => {\n console.debug('Recording interruption event received:', event)\n listener(event)\n }\n )\n\n return subscription\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":";AAAA,sCAAsC;;;;;AAmCtC,sDAIC;AAKD,4DAIC;AAED,4EAeC;AAED,sEAOC;AAxED,yDAA8E;AAO9E,4EAAmD;AAEnD,MAAM,OAAO,GAAG,IAAI,sCAAkB,CAAC,2BAAiB,CAAC,CAAA;AAwBzD,SAAgB,qBAAqB,CACjC,QAAqD;IAErD,OAAO,OAAO,CAAC,WAAW,CAAoB,WAAW,EAAE,QAAQ,CAAC,CAAA;AACxE,CAAC;AAKD,SAAgB,wBAAwB,CACpC,QAAsD;IAEtD,OAAO,OAAO,CAAC,WAAW,CAAqB,eAAe,EAAE,QAAQ,CAAC,CAAA;AAC7E,CAAC;AAED,SAAgB,gCAAgC,CAC5C,QAAqD;IAErD,oBAAoB;IACpB,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAEvD,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,CACpC,wBAAwB,EAAE,+CAA+C;IACzE,CAAC,KAAK,EAAE,EAAE;QACN,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAA;QAC9D,QAAQ,CAAC,KAAK,CAAC,CAAA;IACnB,CAAC,CACJ,CAAA;IAED,OAAO,YAAY,CAAA;AACvB,CAAC;AAED,SAAgB,6BAA6B,CACzC,QAAkD;IAElD,OAAO,OAAO,CAAC,WAAW,CACtB,oBAAoB,EACpB,QAAQ,CACX,CAAA;AACL,CAAC","sourcesContent":["// packages/audio-studio/src/events.ts\n\nimport { LegacyEventEmitter, type EventSubscription } from 'expo-modules-core'\n\nimport { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'\nimport type {\n MaxDurationReachedEvent,\n RecordingInterruptionEvent,\n} from './AudioStudio.types'\nimport AudioStudioModule from './AudioStudioModule'\n\nconst emitter = new LegacyEventEmitter(AudioStudioModule)\n\n// Internal event payload from native module\nexport interface AudioEventPayload {\n encoded?: string\n /** Float32 samples in [-1, 1] — sent by native when streamFormat='float32'.\n * Android new arch delivers Float32Array; iOS delivers number[]. */\n pcmFloat32?: Float32Array | number[]\n buffer?: Float32Array\n fileUri: string\n lastEmittedSize: number\n position: number\n deltaSize: number\n totalSize: number\n mimeType: string\n streamUuid: string\n compression?: {\n data?: string | Blob // Base64 (native) or Float32Array (web) encoded compressed data chunk\n position: number\n eventDataSize: number\n totalSize: number\n }\n}\n\nexport function addAudioEventListener(\n listener: (event: AudioEventPayload) => Promise<void>\n): EventSubscription {\n return emitter.addListener<AudioEventPayload>('AudioData', listener)\n}\n\n// Only aliasing the AudioAnalysis type for the event payload\nexport interface AudioAnalysisEvent extends AudioAnalysis {}\n\nexport function addAudioAnalysisListener(\n listener: (event: AudioAnalysisEvent) => Promise<void>\n): EventSubscription {\n return emitter.addListener<AudioAnalysisEvent>('AudioAnalysis', listener)\n}\n\nexport function addRecordingInterruptionListener(\n listener: (event: RecordingInterruptionEvent) => void\n): EventSubscription {\n // Add debug logging\n console.debug('Adding recording interruption listener')\n\n const subscription = emitter.addListener<RecordingInterruptionEvent>(\n 'onRecordingInterrupted', // Make sure this matches the native event name\n (event) => {\n console.debug('Recording interruption event received:', event)\n listener(event)\n }\n )\n\n return subscription\n}\n\nexport function addMaxDurationReachedListener(\n listener: (event: MaxDurationReachedEvent) => void\n): EventSubscription {\n return emitter.addListener<MaxDurationReachedEvent>(\n 'MaxDurationReached',\n listener\n )\n}\n"]}
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.ExpoAudioStreamModule = exports.mapStreamError = exports.AudioStreamError = exports.mapExtractionError = exports.AudioExtractionError = exports.useSharedAudioRecorder = exports.useAudioRecorder = exports.MAX_DURATION_MS = exports.computeMelFrameWasm = exports.initMelStreamingWasm = exports.extractMelSpectrogram = exports.getAudioDecodeCapabilities = exports.streamAudioData = exports.extractAudioData = exports.trimAudio = exports.extractPreview = exports.extractAudioAnalysis = exports.extractRawWavAnalysis = exports.AudioStudioModule = exports.AudioRecorderProvider = exports.extractPreviewBars = exports.setMelSpectrogramWasmUrl = exports.useAudioDevices = exports.audioDeviceManager = exports.AudioDeviceManager = exports.validateRecordingConfig = exports.getFallbackBitDepth = exports.getFallbackEncoding = exports.isBitDepthSupported = exports.isEncodingSupported = exports.getPlatformCapabilities = void 0;
21
+ exports.ExpoAudioStreamModule = exports.mapStreamError = exports.AudioStreamError = exports.mapExtractionError = exports.AudioExtractionError = exports.useSharedAudioRecorder = exports.useAudioRecorder = exports.MAX_DURATION_MS = exports.computeMelFrameWasm = exports.initMelStreamingWasm = exports.extractMelSpectrogram = exports.getAudioDecodeCapabilities = exports.streamAudioData = exports.extractAudioData = exports.trimAudio = exports.extractPreview = exports.extractAudioAnalysis = exports.extractRawWavAnalysis = exports.AudioStudioModule = exports.AudioRecorderProvider = exports.extractPreviewBars = exports.setMelSpectrogramWasmUrl = exports.useAudioDevices = exports.audioDeviceManager = exports.AudioDeviceManager = exports.validateRecordingConfig = exports.getFallbackBitDepth = exports.getFallbackEncoding = exports.isBitDepthSupported = exports.isEncodingSupported = exports.getPlatformCapabilities = exports.addMaxDurationReachedListener = void 0;
22
22
  const extractAudioAnalysis_1 = require("./AudioAnalysis/extractAudioAnalysis");
23
23
  Object.defineProperty(exports, "extractRawWavAnalysis", { enumerable: true, get: function () { return extractAudioAnalysis_1.extractRawWavAnalysis; } });
24
24
  Object.defineProperty(exports, "extractAudioAnalysis", { enumerable: true, get: function () { return extractAudioAnalysis_1.extractAudioAnalysis; } });
@@ -44,6 +44,8 @@ const trimAudio_1 = require("./trimAudio");
44
44
  Object.defineProperty(exports, "trimAudio", { enumerable: true, get: function () { return trimAudio_1.trimAudio; } });
45
45
  const useAudioRecorder_1 = require("./useAudioRecorder");
46
46
  Object.defineProperty(exports, "useAudioRecorder", { enumerable: true, get: function () { return useAudioRecorder_1.useAudioRecorder; } });
47
+ var events_1 = require("./events");
48
+ Object.defineProperty(exports, "addMaxDurationReachedListener", { enumerable: true, get: function () { return events_1.addMaxDurationReachedListener; } });
47
49
  __exportStar(require("./utils/convertPCMToFloat32"), exports);
48
50
  __exportStar(require("./utils/getWavFileInfo"), exports);
49
51
  __exportStar(require("./utils/writeWavHeader"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,eAAe;;;;;;;;;;;;;;;;;;;;AAEf,+EAG6C;AAkDzC,sGApDA,4CAAqB,OAoDA;AACrB,qGApDA,2CAAoB,OAoDA;AAlDxB,uEAAmE;AAqD/D,iGArDK,mCAAgB,OAqDL;AApDpB,iFAG8C;AAoD1C,sGAtDA,6CAAqB,OAsDA;AAGrB,gGAxDA,uCAAe,OAwDA;AAtDnB,mEAA+D;AA8C3D,+FA9CK,+BAAc,OA8CL;AA7ClB,2EAG2C;AAgDvC,qGAlDA,yCAAoB,OAkDA;AACpB,oGAlDA,wCAAmB,OAkDA;AAhDvB,qEAGiC;AAkC7B,sGApCA,8CAAqB,OAoCA;AAcrB,uGAjDA,+CAAsB,OAiDA;AA/C1B,4EAAmD;AAkC/C,4BAlCG,2BAAiB,CAkCH;AAjCrB,uDAG0B;AAqCtB,2GAvCA,4CAA0B,OAuCA;AAD1B,gGArCA,iCAAe,OAqCA;AAnCnB,2CAAuC;AAiCnC,0FAjCK,qBAAS,OAiCL;AAhCb,yDAAqD;AAwCjD,iGAxCK,mCAAgB,OAwCL;AAtCpB,8DAA2C;AAC3C,yDAAsC;AACtC,yDAAsC;AAEtC,+BAA+B;AAC/B,uEAQwC;AAPpC,8HAAA,uBAAuB,OAAA;AACvB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,8HAAA,uBAAuB,OAAA;AAI3B,4BAA4B;AAC5B,2DAA6E;AAApE,wHAAA,kBAAkB,OAAA;AAAE,wHAAA,kBAAkB,OAAA;AAE/C,8BAA8B;AAC9B,2DAAyD;AAAhD,kHAAA,eAAe,OAAA;AAExB,yDAAqE;AAA5D,sHAAA,wBAAwB,OAAA;AACjC,yEAAuE;AAA9D,wHAAA,kBAAkB,OAAA;AAoB3B,sEAGsC;AAFlC,4HAAA,oBAAoB,OAAA;AACpB,0HAAA,kBAAkB,OAAA;AAOtB,8DAGkC;AAF9B,oHAAA,gBAAgB,OAAA;AAChB,kHAAA,cAAc,OAAA;AAoBlB,gDAAgD;AACnC,QAAA,qBAAqB,GAAG,2BAAiB,CAAA","sourcesContent":["// src/index.ts\n\nimport {\n extractRawWavAnalysis,\n extractAudioAnalysis,\n} from './AudioAnalysis/extractAudioAnalysis'\nimport { extractAudioData } from './AudioAnalysis/extractAudioData'\nimport {\n extractMelSpectrogram,\n MAX_DURATION_MS,\n} from './AudioAnalysis/extractMelSpectrogram'\nimport { extractPreview } from './AudioAnalysis/extractPreview'\nimport {\n initMelStreamingWasm,\n computeMelFrameWasm,\n} from './AudioAnalysis/melSpectrogramWasm'\nimport {\n AudioRecorderProvider,\n useSharedAudioRecorder,\n} from './AudioRecorder.provider'\nimport AudioStudioModule from './AudioStudioModule'\nimport {\n getAudioDecodeCapabilities,\n streamAudioData,\n} from './streamAudioData'\nimport { trimAudio } from './trimAudio'\nimport { useAudioRecorder } from './useAudioRecorder'\n\nexport * from './utils/convertPCMToFloat32'\nexport * from './utils/getWavFileInfo'\nexport * from './utils/writeWavHeader'\n\n// Export platform capabilities\nexport {\n getPlatformCapabilities,\n isEncodingSupported,\n isBitDepthSupported,\n getFallbackEncoding,\n getFallbackBitDepth,\n validateRecordingConfig,\n type PlatformCapabilities,\n} from './constants/platformLimitations'\n\n// Export AudioDeviceManager\nexport { AudioDeviceManager, audioDeviceManager } from './AudioDeviceManager'\n\n// Export useAudioDevices hook\nexport { useAudioDevices } from './hooks/useAudioDevices'\n\nexport { setMelSpectrogramWasmUrl } from './AudioAnalysis/wasmConfig'\nexport { extractPreviewBars } from './AudioAnalysis/extractPreviewBars'\n\nexport {\n AudioRecorderProvider,\n AudioStudioModule,\n extractRawWavAnalysis,\n extractAudioAnalysis,\n extractPreview,\n trimAudio,\n extractAudioData,\n streamAudioData,\n getAudioDecodeCapabilities,\n extractMelSpectrogram,\n initMelStreamingWasm,\n computeMelFrameWasm,\n MAX_DURATION_MS,\n useAudioRecorder,\n useSharedAudioRecorder,\n}\n\nexport {\n AudioExtractionError,\n mapExtractionError,\n} from './errors/AudioExtractionError'\nexport type {\n AudioExtractionErrorCode,\n AudioExtractionErrorPayload,\n} from './errors/AudioExtractionError'\n\nexport {\n AudioStreamError,\n mapStreamError,\n} from './errors/AudioStreamError'\nexport type {\n AudioStreamErrorCode,\n AudioStreamErrorPayload,\n} from './errors/AudioStreamError'\n\nexport type {\n StreamAudioDataOptions,\n StreamAudioDataChunk,\n StreamAudioDataProgress,\n StreamAudioDataResult,\n StreamAudioDataCallbacks,\n AudioDecodeCapabilities,\n} from './streamAudioData'\n\n// Export all types\nexport type * from './AudioAnalysis/AudioAnalysis.types'\nexport type * from './AudioStudio.types'\n\n/** @deprecated Use AudioStudioModule instead */\nexport const ExpoAudioStreamModule = AudioStudioModule\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,eAAe;;;;;;;;;;;;;;;;;;;;AAEf,+EAG6C;AAmDzC,sGArDA,4CAAqB,OAqDA;AACrB,qGArDA,2CAAoB,OAqDA;AAnDxB,uEAAmE;AAsD/D,iGAtDK,mCAAgB,OAsDL;AArDpB,iFAG8C;AAqD1C,sGAvDA,6CAAqB,OAuDA;AAGrB,gGAzDA,uCAAe,OAyDA;AAvDnB,mEAA+D;AA+C3D,+FA/CK,+BAAc,OA+CL;AA9ClB,2EAG2C;AAiDvC,qGAnDA,yCAAoB,OAmDA;AACpB,oGAnDA,wCAAmB,OAmDA;AAjDvB,qEAGiC;AAmC7B,sGArCA,8CAAqB,OAqCA;AAcrB,uGAlDA,+CAAsB,OAkDA;AAhD1B,4EAAmD;AAmC/C,4BAnCG,2BAAiB,CAmCH;AAlCrB,uDAG0B;AAsCtB,2GAxCA,4CAA0B,OAwCA;AAD1B,gGAtCA,iCAAe,OAsCA;AApCnB,2CAAuC;AAkCnC,0FAlCK,qBAAS,OAkCL;AAjCb,yDAAqD;AAyCjD,iGAzCK,mCAAgB,OAyCL;AAxCpB,mCAAwD;AAA/C,uHAAA,6BAA6B,OAAA;AAEtC,8DAA2C;AAC3C,yDAAsC;AACtC,yDAAsC;AAEtC,+BAA+B;AAC/B,uEAQwC;AAPpC,8HAAA,uBAAuB,OAAA;AACvB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,0HAAA,mBAAmB,OAAA;AACnB,8HAAA,uBAAuB,OAAA;AAI3B,4BAA4B;AAC5B,2DAA6E;AAApE,wHAAA,kBAAkB,OAAA;AAAE,wHAAA,kBAAkB,OAAA;AAE/C,8BAA8B;AAC9B,2DAAyD;AAAhD,kHAAA,eAAe,OAAA;AAExB,yDAAqE;AAA5D,sHAAA,wBAAwB,OAAA;AACjC,yEAAuE;AAA9D,wHAAA,kBAAkB,OAAA;AAoB3B,sEAGsC;AAFlC,4HAAA,oBAAoB,OAAA;AACpB,0HAAA,kBAAkB,OAAA;AAOtB,8DAGkC;AAF9B,oHAAA,gBAAgB,OAAA;AAChB,kHAAA,cAAc,OAAA;AAoBlB,gDAAgD;AACnC,QAAA,qBAAqB,GAAG,2BAAiB,CAAA","sourcesContent":["// src/index.ts\n\nimport {\n extractRawWavAnalysis,\n extractAudioAnalysis,\n} from './AudioAnalysis/extractAudioAnalysis'\nimport { extractAudioData } from './AudioAnalysis/extractAudioData'\nimport {\n extractMelSpectrogram,\n MAX_DURATION_MS,\n} from './AudioAnalysis/extractMelSpectrogram'\nimport { extractPreview } from './AudioAnalysis/extractPreview'\nimport {\n initMelStreamingWasm,\n computeMelFrameWasm,\n} from './AudioAnalysis/melSpectrogramWasm'\nimport {\n AudioRecorderProvider,\n useSharedAudioRecorder,\n} from './AudioRecorder.provider'\nimport AudioStudioModule from './AudioStudioModule'\nimport {\n getAudioDecodeCapabilities,\n streamAudioData,\n} from './streamAudioData'\nimport { trimAudio } from './trimAudio'\nimport { useAudioRecorder } from './useAudioRecorder'\nexport { addMaxDurationReachedListener } from './events'\n\nexport * from './utils/convertPCMToFloat32'\nexport * from './utils/getWavFileInfo'\nexport * from './utils/writeWavHeader'\n\n// Export platform capabilities\nexport {\n getPlatformCapabilities,\n isEncodingSupported,\n isBitDepthSupported,\n getFallbackEncoding,\n getFallbackBitDepth,\n validateRecordingConfig,\n type PlatformCapabilities,\n} from './constants/platformLimitations'\n\n// Export AudioDeviceManager\nexport { AudioDeviceManager, audioDeviceManager } from './AudioDeviceManager'\n\n// Export useAudioDevices hook\nexport { useAudioDevices } from './hooks/useAudioDevices'\n\nexport { setMelSpectrogramWasmUrl } from './AudioAnalysis/wasmConfig'\nexport { extractPreviewBars } from './AudioAnalysis/extractPreviewBars'\n\nexport {\n AudioRecorderProvider,\n AudioStudioModule,\n extractRawWavAnalysis,\n extractAudioAnalysis,\n extractPreview,\n trimAudio,\n extractAudioData,\n streamAudioData,\n getAudioDecodeCapabilities,\n extractMelSpectrogram,\n initMelStreamingWasm,\n computeMelFrameWasm,\n MAX_DURATION_MS,\n useAudioRecorder,\n useSharedAudioRecorder,\n}\n\nexport {\n AudioExtractionError,\n mapExtractionError,\n} from './errors/AudioExtractionError'\nexport type {\n AudioExtractionErrorCode,\n AudioExtractionErrorPayload,\n} from './errors/AudioExtractionError'\n\nexport {\n AudioStreamError,\n mapStreamError,\n} from './errors/AudioStreamError'\nexport type {\n AudioStreamErrorCode,\n AudioStreamErrorPayload,\n} from './errors/AudioStreamError'\n\nexport type {\n StreamAudioDataOptions,\n StreamAudioDataChunk,\n StreamAudioDataProgress,\n StreamAudioDataResult,\n StreamAudioDataCallbacks,\n AudioDecodeCapabilities,\n} from './streamAudioData'\n\n// Export all types\nexport type * from './AudioAnalysis/AudioAnalysis.types'\nexport type * from './AudioStudio.types'\n\n/** @deprecated Use AudioStudioModule instead */\nexport const ExpoAudioStreamModule = AudioStudioModule\n"]}
@@ -11,7 +11,7 @@ const AudioDeviceManager_1 = require("./AudioDeviceManager");
11
11
  const AudioStudioModule_1 = __importDefault(require("./AudioStudioModule"));
12
12
  const platformLimitations_1 = require("./constants/platformLimitations");
13
13
  const events_1 = require("./events");
14
- const cleanNativeOptions_1 = require("./utils/cleanNativeOptions");
14
+ const nativeRecordingOptions_1 = require("./utils/nativeRecordingOptions");
15
15
  const defaultAnalysis = {
16
16
  segmentDurationMs: 100,
17
17
  bitDepth: 32,
@@ -30,6 +30,31 @@ const defaultAnalysis = {
30
30
  },
31
31
  extractionTimeMs: 0,
32
32
  };
33
+ function finiteOrZero(value) {
34
+ return Number.isFinite(value) ? value : 0;
35
+ }
36
+ function sanitizeSerializableValue(value) {
37
+ if (typeof value === 'number') {
38
+ return finiteOrZero(value);
39
+ }
40
+ if (Array.isArray(value)) {
41
+ return value.map((item) => sanitizeSerializableValue(item));
42
+ }
43
+ if (value && typeof value === 'object') {
44
+ const sanitized = {};
45
+ for (const [key, nestedValue] of Object.entries(value)) {
46
+ sanitized[key] = sanitizeSerializableValue(nestedValue);
47
+ }
48
+ return sanitized;
49
+ }
50
+ return value;
51
+ }
52
+ function createSerializableAnalysis(analysis) {
53
+ return sanitizeSerializableValue(analysis);
54
+ }
55
+ function createRecordingSnapshot(recording) {
56
+ return sanitizeSerializableValue(recording);
57
+ }
33
58
  function audioRecorderReducer(state, action) {
34
59
  switch (action.type) {
35
60
  case 'START':
@@ -41,6 +66,9 @@ function audioRecorderReducer(state, action) {
41
66
  size: 0,
42
67
  compression: undefined,
43
68
  analysisData: defaultAnalysis,
69
+ maxDurationMs: undefined,
70
+ maxDurationReached: false,
71
+ lastRecordingReason: undefined,
44
72
  };
45
73
  case 'STOP':
46
74
  return {
@@ -51,6 +79,9 @@ function audioRecorderReducer(state, action) {
51
79
  size: 0,
52
80
  compression: undefined,
53
81
  analysisData: undefined,
82
+ lastRecordingReason: action.payload.reason,
83
+ // Preserve max-duration state after stop so UI and agentic
84
+ // validation can explain why recording ended. START resets it.
54
85
  };
55
86
  case 'PAUSE':
56
87
  return { ...state, isPaused: true, isRecording: false };
@@ -75,9 +106,17 @@ function audioRecorderReducer(state, action) {
75
106
  format: action.payload.compression.format,
76
107
  }
77
108
  : undefined,
109
+ maxDurationMs: action.payload.maxDurationMs,
110
+ maxDurationReached: action.payload.maxDurationReached,
78
111
  };
79
112
  return newState;
80
113
  }
114
+ case 'MAX_DURATION_REACHED':
115
+ return {
116
+ ...state,
117
+ maxDurationMs: action.payload.maxDurationMs,
118
+ maxDurationReached: true,
119
+ };
81
120
  case 'UPDATE_ANALYSIS':
82
121
  return {
83
122
  ...state,
@@ -102,6 +141,9 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
102
141
  size: 0,
103
142
  compression: undefined,
104
143
  analysisData: undefined,
144
+ maxDurationMs: undefined,
145
+ maxDurationReached: false,
146
+ lastRecordingReason: undefined,
105
147
  });
106
148
  const startResultRef = (0, react_1.useRef)(null);
107
149
  const analysisListenerRef = (0, react_1.useRef)(null);
@@ -126,8 +168,12 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
126
168
  durationMs: 0,
127
169
  size: 0,
128
170
  compression: undefined,
171
+ maxDurationMs: undefined,
172
+ maxDurationReached: false,
129
173
  });
130
174
  const recordingConfigRef = (0, react_1.useRef)(null);
175
+ const maxDurationHandledRef = (0, react_1.useRef)(false);
176
+ const stopFinalizationRef = (0, react_1.useRef)(null);
131
177
  // Generate unique instance ID for debugging
132
178
  const instanceId = (0, react_1.useId)().replace(/:/g, '').slice(0, 5);
133
179
  const handleAudioAnalysis = (0, react_1.useCallback)(async ({ analysis, visualizationDuration, }) => {
@@ -293,10 +339,105 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
293
339
  logger?.error(`Error processing audio event:`, error);
294
340
  }
295
341
  }, []);
342
+ const finalizeRecordingStop = (0, react_1.useCallback)(async (reason) => {
343
+ if (stopFinalizationRef.current) {
344
+ return stopFinalizationRef.current;
345
+ }
346
+ const finalizePromise = (async () => {
347
+ const nativeStopResult = await audioStudio.stopRecording();
348
+ if (!nativeStopResult) {
349
+ throw new Error('Failed to stop recording');
350
+ }
351
+ const stopResult = createRecordingSnapshot(nativeStopResult);
352
+ if (shouldKeepFullAnalysis(recordingConfigRef.current)) {
353
+ stopResult.analysisData = createSerializableAnalysis(fullAnalysisRef.current);
354
+ }
355
+ else {
356
+ // `keepFullAnalysis` is a hook-level retention policy. If a platform
357
+ // starts returning native analysisData in the future, keep opt-out
358
+ // semantics explicit and avoid leaking a full history here.
359
+ delete stopResult.analysisData;
360
+ }
361
+ if (analysisListenerRef.current) {
362
+ analysisListenerRef.current.remove();
363
+ analysisListenerRef.current = null;
364
+ }
365
+ onAudioStreamRef.current = null;
366
+ stateRef.current.isRecording = false;
367
+ stateRef.current.isPaused = false;
368
+ // Note: We deliberately DON'T clear recordingConfigRef here to preserve callbacks.
369
+ logger?.debug(`recording stopped`, stopResult);
370
+ maxDurationHandledRef.current = false;
371
+ dispatch({
372
+ type: 'STOP',
373
+ payload: { reason },
374
+ });
375
+ const stoppedCallback = recordingConfigRef.current?.onRecordingStopped;
376
+ if (stoppedCallback) {
377
+ try {
378
+ void Promise.resolve(stoppedCallback(stopResult, reason)).catch((error) => {
379
+ logger?.error(`Error in recording stopped callback:`, error);
380
+ });
381
+ }
382
+ catch (error) {
383
+ logger?.error(`Error in recording stopped callback:`, error);
384
+ }
385
+ }
386
+ return stopResult;
387
+ })();
388
+ stopFinalizationRef.current = finalizePromise;
389
+ try {
390
+ return await finalizePromise;
391
+ }
392
+ finally {
393
+ stopFinalizationRef.current = null;
394
+ }
395
+ }, [audioStudio, dispatch, logger]);
396
+ const handleMaxDurationReached = (0, react_1.useCallback)(async (event) => {
397
+ if (maxDurationHandledRef.current) {
398
+ return;
399
+ }
400
+ maxDurationHandledRef.current = true;
401
+ const config = recordingConfigRef.current;
402
+ const callbackEvent = {
403
+ ...event,
404
+ autoStopped: event.autoStopped || !!config?.autoStopOnMaxDuration,
405
+ };
406
+ stateRef.current.maxDurationMs = callbackEvent.maxDurationMs;
407
+ stateRef.current.maxDurationReached = true;
408
+ dispatch({
409
+ type: 'MAX_DURATION_REACHED',
410
+ payload: callbackEvent,
411
+ });
412
+ try {
413
+ config?.onMaxDurationReached?.(callbackEvent);
414
+ }
415
+ catch (error) {
416
+ logger?.error(`Error in max duration callback:`, error);
417
+ }
418
+ if (config?.autoStopOnMaxDuration && stateRef.current.isRecording) {
419
+ try {
420
+ await finalizeRecordingStop('maxDuration');
421
+ }
422
+ catch (error) {
423
+ logger?.error(`Error auto-stopping on max duration:`, error);
424
+ }
425
+ }
426
+ }, [dispatch, finalizeRecordingStop, logger]);
296
427
  const checkStatus = (0, react_1.useCallback)(async () => {
297
428
  try {
298
429
  const status = audioStudio.status();
299
430
  logger?.debug(`Status: paused: ${status.isPaused} isRecording: ${status.isRecording} durationMs: ${status.durationMs} size: ${status.size}`, status.compression);
431
+ if (status.maxDurationReached === true &&
432
+ status.maxDurationMs != null &&
433
+ !stateRef.current.maxDurationReached) {
434
+ await handleMaxDurationReached({
435
+ durationMs: status.durationMs,
436
+ maxDurationMs: status.maxDurationMs,
437
+ overrunMs: Math.max(0, status.durationMs - status.maxDurationMs),
438
+ autoStopped: false,
439
+ });
440
+ }
300
441
  // Only dispatch if values actually changed
301
442
  if (status.isRecording !== stateRef.current.isRecording ||
302
443
  status.isPaused !== stateRef.current.isPaused) {
@@ -310,17 +451,34 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
310
451
  },
311
452
  });
312
453
  }
454
+ const statusMaxDurationReached = status.maxDurationReached ?? false;
455
+ const preserveStoppedMaxDuration = !status.isRecording &&
456
+ !status.isPaused &&
457
+ stateRef.current.maxDurationReached &&
458
+ !statusMaxDurationReached;
459
+ const nextMaxDurationMs = preserveStoppedMaxDuration
460
+ ? stateRef.current.maxDurationMs
461
+ : status.maxDurationMs;
462
+ const nextMaxDurationReached = preserveStoppedMaxDuration
463
+ ? true
464
+ : statusMaxDurationReached;
313
465
  if (status.durationMs !== stateRef.current.durationMs ||
314
- status.size !== stateRef.current.size) {
466
+ status.size !== stateRef.current.size ||
467
+ nextMaxDurationMs !== stateRef.current.maxDurationMs ||
468
+ nextMaxDurationReached !== stateRef.current.maxDurationReached) {
315
469
  stateRef.current.durationMs = status.durationMs;
316
470
  stateRef.current.size = status.size;
317
471
  stateRef.current.compression = status.compression;
472
+ stateRef.current.maxDurationMs = nextMaxDurationMs;
473
+ stateRef.current.maxDurationReached = nextMaxDurationReached;
318
474
  dispatch({
319
475
  type: 'UPDATE_STATUS',
320
476
  payload: {
321
477
  durationMs: status.durationMs,
322
478
  size: status.size,
323
479
  compression: status.compression,
480
+ maxDurationMs: nextMaxDurationMs,
481
+ maxDurationReached: nextMaxDurationReached,
324
482
  },
325
483
  });
326
484
  }
@@ -328,7 +486,7 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
328
486
  catch (error) {
329
487
  logger?.error(`Error getting status:`, error);
330
488
  }
331
- }, [audioStudio, logger]); // Only depend on audioStudio and logger
489
+ }, [audioStudio, handleMaxDurationReached, logger]);
332
490
  // Update ref when state changes
333
491
  (0, react_1.useEffect)(() => {
334
492
  stateRef.current = {
@@ -337,6 +495,8 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
337
495
  durationMs: state.durationMs,
338
496
  size: state.size,
339
497
  compression: state.compression,
498
+ maxDurationMs: state.maxDurationMs,
499
+ maxDurationReached: state.maxDurationReached ?? false,
340
500
  };
341
501
  }, [
342
502
  state.isRecording,
@@ -344,6 +504,8 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
344
504
  state.durationMs,
345
505
  state.size,
346
506
  state.compression,
507
+ state.maxDurationMs,
508
+ state.maxDurationReached,
347
509
  ]);
348
510
  const startRecording = (0, react_1.useCallback)(async (recordingOptions) => {
349
511
  // Validate the encoding configuration
@@ -362,11 +524,11 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
362
524
  encoding: validationResult.encoding,
363
525
  };
364
526
  recordingConfigRef.current = validatedOptions;
527
+ maxDurationHandledRef.current = false;
365
528
  logger?.debug(`start recording with validated config`, validatedOptions);
366
529
  analysisRef.current = { ...defaultAnalysis }; // Reset analysis data
367
530
  fullAnalysisRef.current = { ...defaultAnalysis };
368
- const { onAudioStream, onRecordingInterrupted, onAudioAnalysis, keepFullAnalysis: _keepFullAnalysis, ...options } = validatedOptions;
369
- const { enableProcessing } = options;
531
+ const { onAudioStream, enableProcessing } = validatedOptions;
370
532
  const maxRecentDataDuration = 10000; // TODO compute maxRecentDataDuration based on screen dimensions
371
533
  if (typeof onAudioStream === 'function') {
372
534
  onAudioStreamRef.current = onAudioStream;
@@ -375,8 +537,10 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
375
537
  logger?.warn(`onAudioStream is not a function`, onAudioStream);
376
538
  onAudioStreamRef.current = null;
377
539
  }
378
- // Strip undefined values and functions that can't cross the native bridge
379
- const cleanOptions = (0, cleanNativeOptions_1.cleanNativeOptions)(options);
540
+ // Strip hook-only values and undefineds that can't cross the native bridge.
541
+ // autoStopOnMaxDuration stays hook-owned so finalization can expose
542
+ // the same AudioRecording result as a manual stop.
543
+ const cleanOptions = (0, nativeRecordingOptions_1.createNativeRecordingOptions)(validatedOptions);
380
544
  const startResult = await audioStudio.startRecording(cleanOptions);
381
545
  dispatch({ type: 'START' });
382
546
  startResultRef.current = startResult;
@@ -402,7 +566,7 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
402
566
  logger?.debug(`preparing recording`, recordingOptions);
403
567
  analysisRef.current = { ...defaultAnalysis }; // Reset analysis data
404
568
  fullAnalysisRef.current = { ...defaultAnalysis };
405
- const { onAudioStream, onRecordingInterrupted, onAudioAnalysis, keepFullAnalysis: _keepFullAnalysis, ...options } = recordingOptions;
569
+ const { onAudioStream } = recordingOptions;
406
570
  // Store onAudioStream for later use when recording starts
407
571
  if (typeof onAudioStream === 'function') {
408
572
  onAudioStreamRef.current = onAudioStream;
@@ -411,34 +575,16 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
411
575
  logger?.warn(`onAudioStream is not a function`, onAudioStream);
412
576
  onAudioStreamRef.current = null;
413
577
  }
414
- // Strip undefined values and functions that can't cross the native bridge
415
- const cleanOptions = (0, cleanNativeOptions_1.cleanNativeOptions)(options);
578
+ // Strip hook-only values and undefineds that can't cross the native bridge.
579
+ const cleanOptions = (0, nativeRecordingOptions_1.createNativeRecordingOptions)(recordingOptions);
416
580
  // Call the native prepareRecording method
417
581
  await audioStudio.prepareRecording(cleanOptions);
418
582
  logger?.debug(`recording prepared successfully`);
419
583
  }, []);
420
584
  const stopRecording = (0, react_1.useCallback)(async () => {
421
585
  logger?.debug(`stoping recording`);
422
- const stopResult = await audioStudio.stopRecording();
423
- if (shouldKeepFullAnalysis(recordingConfigRef.current)) {
424
- stopResult.analysisData = fullAnalysisRef.current;
425
- }
426
- else {
427
- // `keepFullAnalysis` is a hook-level retention policy. If a platform
428
- // starts returning native analysisData in the future, keep opt-out
429
- // semantics explicit and avoid leaking a full history here.
430
- delete stopResult.analysisData;
431
- }
432
- if (analysisListenerRef.current) {
433
- analysisListenerRef.current.remove();
434
- analysisListenerRef.current = null;
435
- }
436
- onAudioStreamRef.current = null;
437
- // Note: We deliberately DON'T clear recordingConfigRef here to preserve interruption callback
438
- logger?.debug(`recording stopped`, stopResult);
439
- dispatch({ type: 'STOP' });
440
- return stopResult;
441
- }, [dispatch]);
586
+ return finalizeRecordingStop('manual');
587
+ }, [finalizeRecordingStop, logger]);
442
588
  const pauseRecording = (0, react_1.useCallback)(async () => {
443
589
  logger?.debug(`pause recording`);
444
590
  const pauseResult = await audioStudio.pauseRecording();
@@ -451,6 +597,14 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
451
597
  dispatch({ type: 'RESUME' });
452
598
  return resumeResult;
453
599
  }, [dispatch]);
600
+ (0, react_1.useEffect)(() => {
601
+ const subscription = (0, events_1.addMaxDurationReachedListener)(async (event) => {
602
+ await handleMaxDurationReached(event);
603
+ });
604
+ return () => {
605
+ subscription.remove();
606
+ };
607
+ }, [handleMaxDurationReached]);
454
608
  (0, react_1.useEffect)(() => {
455
609
  let intervalId;
456
610
  if (state.isRecording || state.isPaused) {
@@ -548,6 +702,9 @@ function useAudioRecorder({ logger, audioWorkletUrl, featuresExtratorUrl, } = {}
548
702
  size: state.size,
549
703
  compression: state.compression,
550
704
  analysisData: state.analysisData,
705
+ maxDurationMs: state.maxDurationMs,
706
+ maxDurationReached: state.maxDurationReached,
707
+ lastRecordingReason: state.lastRecordingReason,
551
708
  };
552
709
  }
553
710
  //# sourceMappingURL=useAudioRecorder.js.map