/*****************************************************************************\ Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. This file is licensed under the Snes9x License. For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ #include "s9x_sound_driver_cubeb.hpp" #include bool S9xCubebSoundDriver::write_samples(int16_t *data, int samples) { bool retval = true; auto empty = buffer.space_empty(); if (samples > empty) { retval = false; buffer.dump(buffer.buffer_size / 2 - empty); } buffer.push(data, samples); return retval; } S9xCubebSoundDriver::S9xCubebSoundDriver() { } S9xCubebSoundDriver::~S9xCubebSoundDriver() { deinit(); } void S9xCubebSoundDriver::init() { if (!context) cubeb_init(&context, "Snes9x", nullptr); stop(); } void S9xCubebSoundDriver::deinit() { stop(); if (stream) { cubeb_stream_destroy(stream); stream = nullptr; } if (context) { cubeb_destroy(context); context = nullptr; } } void S9xCubebSoundDriver::start() { if (stream) cubeb_stream_start(stream); } void S9xCubebSoundDriver::stop() { if (stream) cubeb_stream_stop(stream); } void state_callback(cubeb_stream *stream, void *user_ptr, cubeb_state state) { } long data_callback(cubeb_stream *stream, void *user_ptr, void const *input_buffer, void *output_buffer, long nframes) { return ((S9xCubebSoundDriver *)user_ptr)->data_callback(stream, input_buffer, output_buffer, nframes); } long S9xCubebSoundDriver::data_callback(cubeb_stream *stream, void const *input_buffer, void *output_buffer, long nframes) { auto avail = buffer.avail(); if (avail < nframes * 2) { auto zeroed_samples = nframes * 2 - avail; memset(output_buffer, 0, zeroed_samples); buffer.read((int16_t *)output_buffer + zeroed_samples, nframes * 2 - zeroed_samples); buffer.add_silence(buffer.buffer_size / 2); } else { buffer.read((int16_t *)output_buffer, nframes * 2); } return nframes; } bool S9xCubebSoundDriver::open_device(int playback_rate, int buffer_size) { cubeb_stream_params params{}; params.channels = 2; params.format = CUBEB_SAMPLE_S16LE; params.layout = CUBEB_LAYOUT_UNDEFINED; params.rate = playback_rate; params.prefs = CUBEB_STREAM_PREF_NONE; uint32_t suggested_latency = playback_rate * buffer_size / 1000; uint32_t min_latency; cubeb_get_min_latency(context, ¶ms, &min_latency); auto retval = cubeb_stream_init(context, &stream, "Snes9x", nullptr, nullptr, nullptr, ¶ms, min_latency, &::data_callback, &state_callback, this); if (retval != CUBEB_OK) { printf("Failed to start stream. Error: %d!\n", retval); stream = nullptr; return false; } buffer.resize(suggested_latency * 2); return true; } int S9xCubebSoundDriver::space_free() { return buffer.space_empty(); } std::pair S9xCubebSoundDriver::buffer_level() { return { buffer.space_empty(), buffer.buffer_size }; }