UNPKG

whisper.rn

Version:

React Native binding of whisper.cpp

312 lines (286 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SimulateFileAudioStreamAdapter = void 0; var _WavFileReader = require("../../utils/WavFileReader"); class SimulateFileAudioStreamAdapter { config = null; isInitialized = false; recording = false; currentBytePosition = 0; startTime = 0; pausedTime = 0; hasReachedEnd = false; constructor(options) { this.options = { playbackSpeed: 1.0, chunkDurationMs: 100, loop: false, logger: () => {}, ...options }; this.fileReader = new _WavFileReader.WavFileReader(this.options.fs, this.options.filePath); } async initialize(config) { if (this.isInitialized) { await this.release(); } try { this.config = config; // Initialize the WAV file reader await this.fileReader.initialize(); // Validate file format matches config const header = this.fileReader.getHeader(); if (!header) { throw new Error('Failed to read WAV file header'); } // Warn about mismatched formats but allow processing if (header.sampleRate !== config.sampleRate) { this.log(`WAV file sample rate (${header.sampleRate}Hz) differs from config (${config.sampleRate}Hz)`); } if (header.channels !== config.channels) { this.log(`WAV file channels (${header.channels}) differs from config (${config.channels})`); } if (header.bitsPerSample !== config.bitsPerSample) { this.log(`WAV file bits per sample (${header.bitsPerSample}) differs from config (${config.bitsPerSample})`); } this.isInitialized = true; this.log(`Simulate audio stream initialized: ${header.duration.toFixed(2)}s at ${this.options.playbackSpeed}x speed`); } catch (error) { var _this$errorCallback; const errorMessage = error instanceof Error ? error.message : 'Unknown initialization error'; (_this$errorCallback = this.errorCallback) === null || _this$errorCallback === void 0 ? void 0 : _this$errorCallback.call(this, errorMessage); throw new Error(`Failed to initialize SimulateFileAudioStreamAdapter: ${errorMessage}`); } } async start() { if (!this.isInitialized || !this.config) { throw new Error('Adapter not initialized'); } if (this.recording) { return; } try { var _this$statusCallback; this.recording = true; this.hasReachedEnd = false; this.startTime = Date.now() - this.pausedTime; (_this$statusCallback = this.statusCallback) === null || _this$statusCallback === void 0 ? void 0 : _this$statusCallback.call(this, true); // Start streaming chunks this.startStreaming(); this.log('File audio simulation started'); } catch (error) { var _this$statusCallback2, _this$errorCallback2; this.recording = false; (_this$statusCallback2 = this.statusCallback) === null || _this$statusCallback2 === void 0 ? void 0 : _this$statusCallback2.call(this, false); const errorMessage = error instanceof Error ? error.message : 'Unknown start error'; (_this$errorCallback2 = this.errorCallback) === null || _this$errorCallback2 === void 0 ? void 0 : _this$errorCallback2.call(this, errorMessage); throw error; } } async stop() { if (!this.recording) { return; } try { var _this$statusCallback3; this.recording = false; this.pausedTime = Date.now() - this.startTime; // Stop the streaming interval if (this.streamInterval) { clearInterval(this.streamInterval); this.streamInterval = undefined; } (_this$statusCallback3 = this.statusCallback) === null || _this$statusCallback3 === void 0 ? void 0 : _this$statusCallback3.call(this, false); this.log('File audio simulation stopped'); } catch (error) { var _this$errorCallback3; const errorMessage = error instanceof Error ? error.message : 'Unknown stop error'; (_this$errorCallback3 = this.errorCallback) === null || _this$errorCallback3 === void 0 ? void 0 : _this$errorCallback3.call(this, errorMessage); } } isRecording() { return this.recording; } onData(callback) { this.dataCallback = callback; } onError(callback) { this.errorCallback = callback; } onStatusChange(callback) { this.statusCallback = callback; } onEnd(callback) { this.options.onEndOfFile = callback; } async release() { await this.stop(); this.isInitialized = false; this.currentBytePosition = 0; this.pausedTime = 0; this.log('SimulateFileAudioStreamAdapter released'); } /** * Start the streaming process */ startStreaming() { if (!this.config || !this.isInitialized) { return; } const header = this.fileReader.getHeader(); if (!header) { var _this$errorCallback4; (_this$errorCallback4 = this.errorCallback) === null || _this$errorCallback4 === void 0 ? void 0 : _this$errorCallback4.call(this, 'WAV file header not available'); return; } // Calculate chunk size based on desired duration const chunkDurationSec = (this.options.chunkDurationMs || 100) / 1000; const bytesPerSecond = header.sampleRate * header.channels * (header.bitsPerSample / 8); const chunkSizeBytes = Math.floor(chunkDurationSec * bytesPerSecond); // Adjust interval timing based on playback speed const intervalMs = (this.options.chunkDurationMs || 100) / (this.options.playbackSpeed || 1.0); this.streamInterval = setInterval(() => { if (!this.recording) { return; } try { this.streamNextChunk(chunkSizeBytes); } catch (error) { var _this$errorCallback5; const errorMessage = error instanceof Error ? error.message : 'Streaming error'; (_this$errorCallback5 = this.errorCallback) === null || _this$errorCallback5 === void 0 ? void 0 : _this$errorCallback5.call(this, errorMessage); this.stop(); } }, intervalMs); } /** * Stream the next audio chunk */ streamNextChunk(chunkSizeBytes) { if (!this.dataCallback || !this.config) { return; } const header = this.fileReader.getHeader(); if (!header) { return; } // Get the next chunk of audio data const audioChunk = this.fileReader.getAudioSlice(this.currentBytePosition, chunkSizeBytes); if (!audioChunk || audioChunk.length === 0) { // End of file reached if (this.options.loop) { // Reset to beginning for looping this.currentBytePosition = 0; this.startTime = Date.now(); this.pausedTime = 0; this.hasReachedEnd = false; this.log('Looping audio file simulation'); return; } // Stop streaming due to no new buffer this.log('Audio file simulation completed - no new buffer available'); this.hasReachedEnd = true; // Call the end-of-file callback if provided if (this.options.onEndOfFile) { this.log('Calling onEndOfFile callback'); this.options.onEndOfFile(); } // Stop the stream this.stop(); return; } // Update position this.currentBytePosition += audioChunk.length; // Create stream data using the original file's format const streamData = { data: audioChunk, sampleRate: header.sampleRate, channels: header.channels, timestamp: Date.now() }; // Send the chunk this.dataCallback(streamData); } /** * Get current playback statistics */ getStatistics() { const header = this.fileReader.getHeader(); const currentTime = this.fileReader.byteToTime(this.currentBytePosition); return { filePath: this.options.filePath, isRecording: this.recording, currentTime, totalDuration: (header === null || header === void 0 ? void 0 : header.duration) || 0, progress: header ? currentTime / header.duration : 0, playbackSpeed: this.options.playbackSpeed, currentBytePosition: this.currentBytePosition, totalBytes: this.fileReader.getTotalDataSize(), hasReachedEnd: this.hasReachedEnd, header }; } /** * Seek to a specific time position */ seekToTime(timeSeconds) { const header = this.fileReader.getHeader(); if (!header) { return; } const clampedTime = Math.max(0, Math.min(timeSeconds, header.duration)); this.currentBytePosition = this.fileReader.timeToByte(clampedTime); // Reset timing if we're currently playing if (this.recording) { this.startTime = Date.now() - clampedTime * 1000 / (this.options.playbackSpeed || 1.0); this.pausedTime = 0; } this.log(`Seeked to ${clampedTime.toFixed(2)}s`); } /** * Set playback speed */ setPlaybackSpeed(speed) { if (speed <= 0) { throw new Error('Playback speed must be greater than 0'); } this.options.playbackSpeed = speed; // If currently playing, restart streaming with new speed if (this.recording) { this.stop().then(() => { this.start(); }); } this.log(`Playback speed set to ${speed}x`); } /** * Reset file buffer to beginning */ resetBuffer() { this.log('Resetting file buffer to beginning'); // Reset position and timing this.currentBytePosition = 0; this.startTime = Date.now(); this.pausedTime = 0; this.hasReachedEnd = false; // If currently playing, restart streaming from beginning if (this.recording) { this.log('Restarting streaming from beginning'); // Stop and restart to apply the reset this.stop().then(() => { this.start(); }); } } /** * Logger function */ log(message) { var _this$options$logger, _this$options; (_this$options$logger = (_this$options = this.options).logger) === null || _this$options$logger === void 0 ? void 0 : _this$options$logger.call(_this$options, `[SimulateFileAudioStreamAdapter] ${message}`); } } exports.SimulateFileAudioStreamAdapter = SimulateFileAudioStreamAdapter; //# sourceMappingURL=SimulateFileAudioStreamAdapter.js.map