whisper.rn
Version:
React Native binding of whisper.cpp
181 lines (161 loc) • 5.34 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WavFileWriter = void 0;
var _common = require("./common");
class WavFileWriter {
dataSize = 0;
isWriting = false;
writeQueue = [];
constructor(fs, filePath, config) {
this.fs = fs;
this.filePath = filePath;
this.config = config;
}
/**
* Initialize the WAV file with headers
*/
async initialize() {
if (this.isWriting) {
return;
}
try {
// Create the initial WAV header (we'll update the size later)
const header = this.createWavHeader(0);
await this.fs.writeFile(this.filePath, (0, _common.uint8ArrayToBase64)(header), 'base64');
this.dataSize = 0;
this.isWriting = true;
this.writeQueue = [];
} catch (error) {
throw new Error(`Failed to initialize WAV file: ${error}`);
}
}
/**
* Append PCM audio data to the WAV file
*/
async appendAudioData(audioData) {
if (!this.isWriting) {
throw new Error('WAV file not initialized');
}
try {
// Queue the data for writing
this.writeQueue.push(audioData);
// Process the write queue
await this.processWriteQueue();
} catch (error) {
console.warn(`Failed to append audio data to WAV file: ${error}`);
}
}
/**
* Process the write queue to avoid blocking
*/
async processWriteQueue() {
if (this.writeQueue.length === 0) {
return;
}
try {
// Combine all queued data
const totalLength = this.writeQueue.reduce((sum, data) => sum + data.length, 0);
const combinedData = new Uint8Array(totalLength);
let offset = 0;
this.writeQueue.forEach(data => {
combinedData.set(new Uint8Array(data), offset);
offset += data.length;
});
// Append to file
const base64Data = (0, _common.uint8ArrayToBase64)(combinedData);
await this.fs.appendFile(this.filePath, base64Data, 'base64');
// Update data size
this.dataSize += combinedData.length;
// Clear the queue
this.writeQueue = [];
} catch (error) {
console.warn(`Failed to process WAV write queue: ${error}`);
// Don't throw here to avoid breaking the recording
}
}
/**
* Finalize the WAV file by updating the header with correct sizes
*/
async finalize() {
if (!this.isWriting) {
return;
}
try {
// Process any remaining queued data
await this.processWriteQueue();
// Read the current file
const currentData = await this.fs.readFile(this.filePath, 'base64');
const currentBytes = (0, _common.base64ToUint8Array)(currentData);
// Create the correct header with final data size
const correctHeader = this.createWavHeader(this.dataSize);
// Replace the header (first 44 bytes)
const finalData = new Uint8Array(correctHeader.length + this.dataSize);
finalData.set(correctHeader, 0);
finalData.set(currentBytes.slice(44), 44); // Skip old header
// Write the final file
const finalBase64 = (0, _common.uint8ArrayToBase64)(finalData);
await this.fs.writeFile(this.filePath, finalBase64, 'base64');
this.isWriting = false;
} catch (error) {
console.warn(`Failed to finalize WAV file: ${error}`);
}
}
/**
* Create WAV file header
*/
createWavHeader(dataSize) {
const header = new ArrayBuffer(44);
const view = new DataView(header);
// RIFF header
view.setUint32(0, 0x52494646, false); // "RIFF"
view.setUint32(4, 36 + dataSize, true); // File size - 8
view.setUint32(8, 0x57415645, false); // "WAVE"
// Format chunk
view.setUint32(12, 0x666d7420, false); // "fmt "
view.setUint32(16, 16, true); // Chunk size
view.setUint16(20, 1, true); // Audio format (PCM)
view.setUint16(22, this.config.channels, true); // Number of channels
view.setUint32(24, this.config.sampleRate, true); // Sample rate
view.setUint32(28, this.config.sampleRate * this.config.channels * (this.config.bitsPerSample / 8), true); // Byte rate
view.setUint16(32, this.config.channels * (this.config.bitsPerSample / 8), true); // Block align
view.setUint16(34, this.config.bitsPerSample, true); // Bits per sample
// Data chunk
view.setUint32(36, 0x64617461, false); // "data"
view.setUint32(40, dataSize, true); // Data size
return new Uint8Array(header);
}
/**
* Cancel writing and cleanup
*/
async cancel() {
this.isWriting = false;
this.writeQueue = [];
try {
// Delete the incomplete file
const exists = await this.fs.exists(this.filePath);
if (exists) {
await this.fs.unlink(this.filePath);
}
} catch (error) {
console.warn(`Failed to cleanup WAV file: ${error}`);
}
}
/**
* Get current file statistics
*/
getStatistics() {
const durationSec = this.dataSize / (this.config.sampleRate * this.config.channels * (this.config.bitsPerSample / 8));
return {
filePath: this.filePath,
dataSize: this.dataSize,
durationSec,
isWriting: this.isWriting,
queuedChunks: this.writeQueue.length,
estimatedFileSizeMB: (44 + this.dataSize) / (1024 * 1024)
};
}
}
exports.WavFileWriter = WavFileWriter;
//# sourceMappingURL=WavFileWriter.js.map