//
// Copyright 2024 Picovoice Inc.
//
// You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
// file accompanying this source.
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const os = require("os");
const path = require("path");
const pv_speaker_status_t_1 = require("./pv_speaker_status_t");
const errors_1 = require("./errors");
/**
 * PvSpeaker class for playing audio.
 */
class PvSpeaker {
    // eslint-disable-next-line
    static _pvSpeaker = require(PvSpeaker._getLibraryPath());
    _handle;
    _sampleRate;
    _bitsPerSample;
    _bufferSizeSecs;
    _version;
    /**
     * PvSpeaker constructor.
     *
     * @param sampleRate The sample rate of the audio to be played.
     * @param bitsPerSample The number of bits per sample.
     * @param options Optional configuration arguments.
     * @param options.bufferSizeSecs The size in seconds of the internal buffer used to buffer PCM data
     * - i.e. internal circular buffer will be of size `sampleRate` * `bufferSizeSecs`.
     * @param options.deviceIndex The audio device index to use to play audio. A value of (-1) will use the machine's default audio device.
     */
    constructor(sampleRate, bitsPerSample, options = {}) {
        let pvSpeakerHandleAndStatus;
        const { bufferSizeSecs = 20, deviceIndex = -1 } = options;
        try {
            pvSpeakerHandleAndStatus = PvSpeaker._pvSpeaker.init(sampleRate, bitsPerSample, bufferSizeSecs, deviceIndex);
        }
        catch (err) {
            throw (0, errors_1.default)(err.code, err);
        }
        const status = pvSpeakerHandleAndStatus.status;
        if (status !== pv_speaker_status_t_1.default.SUCCESS) {
            throw (0, errors_1.default)(status, "Failed to initialize PvSpeaker.");
        }
        this._handle = pvSpeakerHandleAndStatus.handle;
        this._sampleRate = sampleRate;
        this._bitsPerSample = bitsPerSample;
        this._bufferSizeSecs = bufferSizeSecs;
        this._version = PvSpeaker._pvSpeaker.version();
    }
    /**
     * @returns {number} The sample rate matching the value passed to the constructor.
     */
    get sampleRate() {
        return this._sampleRate;
    }
    /**
     * @returns {number} The bits per sample matching the value passed to the constructor.
     */
    get bitsPerSample() {
        return this._bitsPerSample;
    }
    /**
     * @returns {number} The buffer size in seconds matching the value passed to the constructor.
     */
    get bufferSizeSecs() {
        return this._bufferSizeSecs;
    }
    /**
     * @returns {number} The current version of PvSpeaker library.
     */
    get version() {
        return this._version;
    }
    /**
     * @returns {boolean} Whether the speaker has started and is available to receive pcm frames or not.
     */
    get isStarted() {
        return PvSpeaker._pvSpeaker.get_is_started(this._handle);
    }
    /**
     * Starts the audio output device.
     */
    start() {
        const status = PvSpeaker._pvSpeaker.start(this._handle);
        if (status !== pv_speaker_status_t_1.default.SUCCESS) {
            throw (0, errors_1.default)(status, "Failed to start device.");
        }
    }
    /**
     * Stops the audio output device.
     */
    stop() {
        const status = PvSpeaker._pvSpeaker.stop(this._handle);
        if (status !== pv_speaker_status_t_1.default.SUCCESS) {
            throw (0, errors_1.default)(status, "Failed to stop device.");
        }
    }
    /**
     * Synchronous call to write PCM data to the internal circular buffer for audio playback.
     * Only writes as much PCM data as the internal circular buffer can currently fit, and
     * returns the length of the PCM data that was successfully written.
     *
     * @param {ArrayBuffer} pcm PCM data to be played.
     * @returns {number} Length of the PCM data that was successfully written.
     */
    write(pcm) {
        const result = PvSpeaker._pvSpeaker.write(this._handle, this._bitsPerSample, pcm);
        if (result.status !== pv_speaker_status_t_1.default.SUCCESS) {
            throw (0, errors_1.default)(result.status, "Failed to write to device.");
        }
        return result.written_length;
    }
    /**
     * Synchronous call to write PCM data to the internal circular buffer for audio playback.
     * This call blocks the thread until all PCM data has been successfully written and played.
     *
     * @param {ArrayBuffer} pcm PCM data to be played.
     * @returns {number} The length of the PCM data that was successfully written.
     */
    flush(pcm = new ArrayBuffer(0)) {
        const result = PvSpeaker._pvSpeaker.flush(this._handle, this._bitsPerSample, pcm);
        if (result.status !== pv_speaker_status_t_1.default.SUCCESS) {
            throw (0, errors_1.default)(result.status, "Failed to flush PCM data.");
        }
        return result.written_length;
    }
    /**
     * Writes PCM data passed to PvSpeaker to a specified WAV file.
     *
     * @param {string} outputPath Path to the output WAV file where the PCM data will be written.
     */
    writeToFile(outputPath) {
        const status = PvSpeaker._pvSpeaker.write_to_file(this._handle, outputPath);
        if (status !== pv_speaker_status_t_1.default.SUCCESS) {
            throw (0, errors_1.default)(status, "Failed to open FILE object. PCM data will not be written.");
        }
    }
    /**
     * Gets the audio device that the given PvSpeaker instance is using.
     *
     * @returns {string} Name of the selected audio device.
     */
    getSelectedDevice() {
        const device = PvSpeaker._pvSpeaker.get_selected_device(this._handle);
        if ((device === undefined) || (device === null)) {
            throw new Error("Failed to get selected device.");
        }
        return device;
    }
    /**
     * Destructor. Releases resources acquired by PvSpeaker.
     */
    release() {
        PvSpeaker._pvSpeaker.delete(this._handle);
    }
    /**
     * Helper function to get the list of available audio devices.
     *
     * @returns {Array<string>} An array of the available device names.
     */
    static getAvailableDevices() {
        const devices = PvSpeaker._pvSpeaker.get_available_devices();
        if ((devices === undefined) || (devices === null)) {
            throw new Error("Failed to get audio devices.");
        }
        return devices;
    }
    static _getLibraryPath() {
        let scriptPath;
        if (os.platform() === "win32") {
            scriptPath = path.resolve(__dirname, "..", "scripts", "platform.bat");
        }
        else {
            scriptPath = path.resolve(__dirname, "..", "scripts", "platform.sh");
        }
        const output = (0, child_process_1.execSync)(`"${scriptPath}"`).toString();
        const [osName, cpu] = output.split(" ");
        return path.resolve(__dirname, "..", "lib", osName, cpu, "pv_speaker.node");
    }
}
exports.default = PvSpeaker;
//# sourceMappingURL=pv_speaker.js.map