@siteed/expo-audio-stream 1.0.1 → 1.0.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.
- package/README.md +6 -6
- package/android/build.gradle +5 -0
- package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +120 -0
- package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +34 -4
- package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +635 -0
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +194 -79
- package/android/src/main/java/net/siteed/audiostream/Constants.kt +1 -0
- package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +48 -2
- package/android/src/main/java/net/siteed/audiostream/FFT.kt +44 -0
- package/android/src/main/java/net/siteed/audiostream/Features.kt +56 -0
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +12 -0
- package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +56 -0
- package/app.plugin.js +1 -1
- package/build/AudioRecorder.provider.js +1 -1
- package/build/AudioRecorder.provider.js.map +1 -1
- package/build/ExpoAudioStream.native.d.ts +3 -0
- package/build/ExpoAudioStream.native.d.ts.map +1 -0
- package/build/ExpoAudioStream.native.js +6 -0
- package/build/ExpoAudioStream.native.js.map +1 -0
- package/build/ExpoAudioStream.types.d.ts +79 -6
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +41 -0
- package/build/ExpoAudioStream.web.d.ts.map +1 -0
- package/build/ExpoAudioStream.web.js +184 -0
- package/build/ExpoAudioStream.web.js.map +1 -0
- package/build/ExpoAudioStreamModule.d.ts +2 -2
- package/build/ExpoAudioStreamModule.d.ts.map +1 -1
- package/build/ExpoAudioStreamModule.js +12 -3
- package/build/ExpoAudioStreamModule.js.map +1 -1
- package/build/WebRecorder.d.ts +47 -0
- package/build/WebRecorder.d.ts.map +1 -0
- package/build/WebRecorder.js +243 -0
- package/build/WebRecorder.js.map +1 -0
- package/build/index.d.ts +14 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +106 -7
- package/build/index.js.map +1 -1
- package/build/inlineAudioWebWorker.d.ts +3 -0
- package/build/inlineAudioWebWorker.d.ts.map +1 -0
- package/build/inlineAudioWebWorker.js +340 -0
- package/build/inlineAudioWebWorker.js.map +1 -0
- package/build/useAudioRecording.d.ts +24 -9
- package/build/useAudioRecording.d.ts.map +1 -1
- package/build/useAudioRecording.js +107 -29
- package/build/useAudioRecording.js.map +1 -1
- package/build/utils.d.ts +31 -0
- package/build/utils.d.ts.map +1 -0
- package/build/utils.js +143 -0
- package/build/utils.js.map +1 -0
- package/expo-module.config.json +13 -4
- package/ios/AudioAnalysisData.swift +39 -0
- package/ios/AudioProcessingHelpers.swift +59 -0
- package/ios/AudioProcessor.swift +317 -0
- package/ios/AudioStreamError.swift +7 -0
- package/ios/AudioStreamManager.swift +204 -52
- package/ios/AudioStreamManagerDelegate.swift +4 -0
- package/ios/DataPoint.swift +41 -0
- package/ios/ExpoAudioStreamModule.swift +188 -6
- package/ios/Features.swift +44 -0
- package/ios/RecordingResult.swift +19 -0
- package/ios/RecordingSettings.swift +13 -0
- package/ios/WaveformExtractor.swift +105 -0
- package/package.json +9 -9
- package/plugin/tsconfig.json +13 -8
- package/publish.sh +8 -0
- package/src/AudioRecorder.provider.tsx +1 -1
- package/src/ExpoAudioStream.native.ts +6 -0
- package/src/ExpoAudioStream.types.ts +97 -11
- package/src/ExpoAudioStream.web.ts +228 -0
- package/src/ExpoAudioStreamModule.ts +17 -3
- package/src/WebRecorder.ts +364 -0
- package/src/index.ts +166 -20
- package/src/inlineAudioWebWorker.tsx +340 -0
- package/src/useAudioRecording.tsx +410 -0
- package/src/utils.ts +189 -0
- package/build/ExpoAudioStreamModule.web.d.ts +0 -37
- package/build/ExpoAudioStreamModule.web.d.ts.map +0 -1
- package/build/ExpoAudioStreamModule.web.js +0 -156
- package/build/ExpoAudioStreamModule.web.js.map +0 -1
- package/docs/demo.gif +0 -0
- package/release-it.js +0 -18
- package/src/ExpoAudioStreamModule.web.ts +0 -181
- package/src/useAudioRecording.ts +0 -268
- package/yarn-error.log +0 -7793
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAudioRecording.js","sourceRoot":"","sources":["../src/useAudioRecording.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from \"expo-modules-core\";\nimport { useCallback, useEffect, useReducer, useRef } from \"react\";\n\nimport { addAudioEventListener } from \".\";\nimport {\n AudioEventPayload,\n AudioStreamResult,\n AudioStreamStatus,\n RecordingConfig,\n StartAudioStreamResult,\n} from \"./ExpoAudioStream.types\";\nimport ExpoAudioStreamModule from \"./ExpoAudioStreamModule\";\n\nexport interface AudioDataEvent {\n data: string | Blob;\n position: number;\n fileUri: string;\n eventDataSize: number;\n totalSize: number;\n}\n\nexport interface UseAudioRecorderProps {\n debug?: boolean;\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 duration: number; // Duration of the recording\n size: number; // Size in bytes of the recorded audio\n}\n\ninterface RecorderState {\n isRecording: boolean;\n isPaused: boolean;\n duration: number;\n size: number;\n}\n\ntype RecorderAction =\n | { type: \"START\" | \"STOP\" | \"PAUSE\" | \"RESUME\" }\n | { type: \"UPDATE_STATUS\"; payload: { duration: number; size: number } };\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 duration: 0,\n size: 0,\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 duration: action.payload.duration,\n size: action.payload.size,\n };\n default:\n return state;\n }\n}\nconst TAG = \"[ useAudioRecorder ] \";\n\nexport function useAudioRecorder({\n debug = false,\n}: UseAudioRecorderProps = {}): UseAudioRecorderState {\n const [state, dispatch] = useReducer(recorderReducer, {\n isRecording: false,\n isPaused: false,\n duration: 0,\n size: 0,\n });\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 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(`useAudioRecorder] 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 = ExpoAudioStreamModule.status();\n if (debug) {\n logDebug(`${TAG} Status:`, status);\n }\n\n if (!status.isRecording) {\n dispatch({ type: \"STOP\" });\n } else {\n dispatch({\n type: \"UPDATE_STATUS\",\n payload: { duration: status.duration, size: status.size },\n });\n }\n } catch (error) {\n console.error(`${TAG} Error getting status:`, error);\n }\n }, [state.isRecording, logDebug]);\n\n useEffect(() => {\n let interval: number;\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(`${TAG} Registering audio event listener`);\n const subscribe = addAudioEventListener(handleAudioEvent);\n logDebug(`${TAG} Subscribed to audio event listener`, subscribe);\n\n return () => {\n logDebug(`${TAG} Removing audio event listener`);\n subscribe.remove();\n };\n }, [handleAudioEvent, logDebug]);\n\n const startRecording = useCallback(\n async (recordingOptions: RecordingConfig) => {\n if (debug) {\n logDebug(`${TAG} start recoding`, recordingOptions);\n }\n // remove onAudioStream from recordingOptions\n const { onAudioStream, ...options } = recordingOptions;\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 ExpoAudioStreamModule.startRecording(options);\n dispatch({ type: \"START\" });\n\n return startResult;\n },\n [logDebug],\n );\n\n const stopRecording = useCallback(async () => {\n logDebug(`${TAG} stoping recording`);\n const stopResult: AudioStreamResult =\n await ExpoAudioStreamModule.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 ExpoAudioStreamModule.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 ExpoAudioStreamModule.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 duration: state.duration,\n size: state.size,\n };\n}\n"]}
|
|
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
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
package/expo-module.config.json
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"platforms": [
|
|
2
|
+
"platforms": [
|
|
3
|
+
"ios",
|
|
4
|
+
"tvos",
|
|
5
|
+
"android",
|
|
6
|
+
"web"
|
|
7
|
+
],
|
|
3
8
|
"ios": {
|
|
4
|
-
"modules": [
|
|
9
|
+
"modules": [
|
|
10
|
+
"ExpoAudioStreamModule"
|
|
11
|
+
]
|
|
5
12
|
},
|
|
6
13
|
"android": {
|
|
7
|
-
"modules": [
|
|
14
|
+
"modules": [
|
|
15
|
+
"net.siteed.audiostream.ExpoAudioStreamModule"
|
|
16
|
+
]
|
|
8
17
|
}
|
|
9
|
-
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// AudioAnalysisData.swift
|
|
3
|
+
// ExpoAudioStream
|
|
4
|
+
//
|
|
5
|
+
// Created by Arthur Breton on 23/6/2024.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
public struct AudioAnalysisData {
|
|
11
|
+
public var pointsPerSecond: Int
|
|
12
|
+
public var durationMs: Float
|
|
13
|
+
public var bitDepth: Int
|
|
14
|
+
public var numberOfChannels: Int
|
|
15
|
+
public var sampleRate: Float
|
|
16
|
+
public var samples: Int
|
|
17
|
+
public var dataPoints: [DataPoint]
|
|
18
|
+
public var amplitudeRange: (min: Float, max: Float)
|
|
19
|
+
public var speakerChanges: [(timestamp: Float, speaker: Int)]?
|
|
20
|
+
public var extractionTimeMs: Float
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
extension AudioAnalysisData {
|
|
24
|
+
func toDictionary() -> [String: Any] {
|
|
25
|
+
let dataPointsArray = dataPoints.map { $0.toDictionary() }
|
|
26
|
+
return [
|
|
27
|
+
"pointsPerSecond": pointsPerSecond,
|
|
28
|
+
"durationMs": durationMs,
|
|
29
|
+
"bitDepth": bitDepth,
|
|
30
|
+
"numberOfChannels": numberOfChannels,
|
|
31
|
+
"sampleRate": sampleRate,
|
|
32
|
+
"samples": samples,
|
|
33
|
+
"dataPoints": dataPointsArray,
|
|
34
|
+
"amplitudeRange": ["min": amplitudeRange.min, "max": amplitudeRange.max],
|
|
35
|
+
"speakerChanges": speakerChanges?.map { ["timestamp": $0.timestamp, "speaker": $0.speaker] } ?? [],
|
|
36
|
+
"extractionTimeMs": extractionTimeMs
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// AudioProcessingHelpers.swift
|
|
2
|
+
|
|
3
|
+
import Accelerate
|
|
4
|
+
|
|
5
|
+
func extractMFCC(from segment: [Float], sampleRate: Float) -> [Float] {
|
|
6
|
+
// Placeholder for MFCC extraction logic
|
|
7
|
+
return []
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
func extractSpectralCentroid(from segment: [Float], sampleRate: Float) -> Float {
|
|
11
|
+
Logger.debug("Extracting Spectral Centroid from segment of length \(segment.count)")
|
|
12
|
+
return 0.0 // TODO: Implement spectral centroid extraction logic
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
func extractSpectralFlatness(from segment: [Float]) -> Float {
|
|
16
|
+
Logger.debug("Extracting Spectral Flatness from segment of length \(segment.count)")
|
|
17
|
+
|
|
18
|
+
var mean: Float = 0.0
|
|
19
|
+
var geometricMean: Float = 1.0
|
|
20
|
+
let count = vDSP_Length(segment.count)
|
|
21
|
+
|
|
22
|
+
vDSP_meamgv(segment, 1, &mean, count)
|
|
23
|
+
|
|
24
|
+
var sumLogValues: Float = 0.0
|
|
25
|
+
for value in segment {
|
|
26
|
+
let adjustedValue = max(value, 1e-10)
|
|
27
|
+
sumLogValues += log(adjustedValue)
|
|
28
|
+
}
|
|
29
|
+
geometricMean = exp(sumLogValues / Float(count))
|
|
30
|
+
|
|
31
|
+
let spectralFlatness = mean > 0 ? geometricMean / mean : 0.0
|
|
32
|
+
Logger.debug("Spectral Flatness: \(spectralFlatness)")
|
|
33
|
+
return spectralFlatness
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func extractSpectralRollOff(from segment: [Float], sampleRate: Float) -> Float {
|
|
37
|
+
// Implement spectral roll-off extraction logic
|
|
38
|
+
return 0.0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func extractSpectralBandwidth(from segment: [Float], sampleRate: Float) -> Float {
|
|
42
|
+
// Implement spectral bandwidth extraction logic
|
|
43
|
+
return 0.0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func extractChromagram(from segment: [Float], sampleRate: Float) -> [Float] {
|
|
47
|
+
// Implement chromagram extraction logic
|
|
48
|
+
return []
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func extractTempo(from segment: [Float], sampleRate: Float) -> Float {
|
|
52
|
+
// Implement tempo extraction logic
|
|
53
|
+
return 0.0
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func extractHNR(from segment: [Float]) -> Float {
|
|
57
|
+
// Implement harmonic-to-noise ratio extraction logic
|
|
58
|
+
return 0.0
|
|
59
|
+
}
|