UNPKG

@siteed/expo-audio-studio

Version:

Comprehensive audio processing library for React Native and Expo with recording, analysis, visualization, and streaming capabilities across iOS, Android, and web

92 lines 3.71 kB
// packages/expo-audio-stream/src/utils/getWavFileInfo.ts import { DATA_CHUNK_ID, DEFAULT_BIT_DEPTH, DEFAULT_SAMPLE_RATE, FMT_CHUNK_ID, INFO_CHUNK_ID, RIFF_HEADER, WAVE_HEADER, } from '../constants'; // Audio format descriptions const AUDIO_FORMATS = { 1: 'PCM', 3: 'IEEE float', 6: '8-bit ITU-T G.711 A-law', 7: '8-bit ITU-T G.711 µ-law', 65534: 'WAVE_FORMAT_EXTENSIBLE', }; /** * Extracts metadata from a WAV buffer. * * @param arrayBuffer - The array buffer containing the WAV data. * @returns A promise that resolves to the extracted metadata. */ export const getWavFileInfo = async (arrayBuffer) => { const view = new DataView(arrayBuffer); // Check if the file is a valid RIFF/WAVE file const riffHeader = view.getUint32(0, false); const waveHeader = view.getUint32(8, false); if (riffHeader !== RIFF_HEADER || waveHeader !== WAVE_HEADER) { throw new Error('Invalid WAV file'); } // Initialize variables for the metadata let fmtChunkOffset = 12; let sampleRate = DEFAULT_SAMPLE_RATE; let numChannels = 0; let bitDepth = DEFAULT_BIT_DEPTH; let dataChunkSize = 0; let audioFormat = 0; let byteRate = 0; let blockAlign = 0; let creationDateTime = ''; let comments = ''; let dataChunkOffset = 0; // Parse chunks to find the "fmt " and "data" chunks while (fmtChunkOffset < view.byteLength) { const chunkId = view.getUint32(fmtChunkOffset, false); const chunkSize = view.getUint32(fmtChunkOffset + 4, true); if (chunkId === FMT_CHUNK_ID) { // "fmt " audioFormat = view.getUint16(fmtChunkOffset + 8, true); if (!AUDIO_FORMATS[audioFormat]) { throw new Error('Unsupported WAV file format'); } numChannels = view.getUint16(fmtChunkOffset + 10, true); sampleRate = view.getUint32(fmtChunkOffset + 12, true); byteRate = view.getUint32(fmtChunkOffset + 16, true); blockAlign = view.getUint16(fmtChunkOffset + 20, true); bitDepth = view.getUint16(fmtChunkOffset + 22, true); } else if (chunkId === DATA_CHUNK_ID) { // "data" dataChunkSize = chunkSize; dataChunkOffset = fmtChunkOffset + 8; // Position after chunk header break; } else if (chunkId === INFO_CHUNK_ID) { // "INFO" // Read INFO chunk (assuming it contains a text-based creation date/time and comments) const infoStart = fmtChunkOffset + 8; const infoText = new TextDecoder().decode(new Uint8Array(arrayBuffer.slice(infoStart, infoStart + chunkSize))); const infoParts = infoText.split('\0'); creationDateTime = infoParts[0]; comments = infoParts[1]; } fmtChunkOffset += 8 + chunkSize; } if (!sampleRate || !numChannels || !bitDepth || !dataChunkSize) { throw new Error('Incomplete WAV file information'); } // Calculate duration const bytesPerSample = bitDepth / 8; const numSamples = dataChunkSize / (numChannels * bytesPerSample); const durationMs = (numSamples / sampleRate) * 1000; return { sampleRate, numChannels, bitDepth, size: arrayBuffer.byteLength, durationMs, audioFormatDescription: AUDIO_FORMATS[audioFormat], byteRate, blockAlign, creationDateTime: creationDateTime || undefined, comments: comments || undefined, compressionType: audioFormat === 1 ? 'None' : AUDIO_FORMATS[audioFormat], dataChunkOffset, }; }; //# sourceMappingURL=getWavFileInfo.js.map