mirror of
https://github.com/holub/mame
synced 2025-04-23 08:49:55 +03:00
tabs to spaces
This commit is contained in:
parent
21edda29c8
commit
9b3214af37
@ -34,405 +34,405 @@
|
||||
class sound_pa : public osd_module, public sound_module
|
||||
{
|
||||
public:
|
||||
sound_pa()
|
||||
: osd_module(OSD_SOUND_PROVIDER, "portaudio"), sound_module()
|
||||
{
|
||||
}
|
||||
virtual ~sound_pa() { }
|
||||
sound_pa()
|
||||
: osd_module(OSD_SOUND_PROVIDER, "portaudio"), sound_module()
|
||||
{
|
||||
}
|
||||
virtual ~sound_pa() { }
|
||||
|
||||
virtual int init(osd_options const &options);
|
||||
virtual void exit();
|
||||
virtual int init(osd_options const &options);
|
||||
virtual void exit();
|
||||
|
||||
// sound_module
|
||||
// sound_module
|
||||
|
||||
virtual void update_audio_stream(bool is_throttled, const s16 *buffer, int samples_this_frame);
|
||||
virtual void set_mastervolume(int attenuation);
|
||||
virtual void update_audio_stream(bool is_throttled, const s16 *buffer, int samples_this_frame);
|
||||
virtual void set_mastervolume(int attenuation);
|
||||
|
||||
private:
|
||||
/* Lock free SPSC ring buffer */
|
||||
template <typename T>
|
||||
struct audio_buffer {
|
||||
T* buf;
|
||||
int size;
|
||||
int reserve;
|
||||
std::atomic<int> rd_pos, wr_pos;
|
||||
/* Lock free SPSC ring buffer */
|
||||
template <typename T>
|
||||
struct audio_buffer {
|
||||
T* buf;
|
||||
int size;
|
||||
int reserve;
|
||||
std::atomic<int> rd_pos, wr_pos;
|
||||
|
||||
audio_buffer(int size, int reserve) : size(size), reserve(reserve) {
|
||||
rd_pos = wr_pos = 0;
|
||||
buf = new T[size]();
|
||||
}
|
||||
audio_buffer(int size, int reserve) : size(size), reserve(reserve) {
|
||||
rd_pos = wr_pos = 0;
|
||||
buf = new T[size]();
|
||||
}
|
||||
|
||||
~audio_buffer() { delete[] buf; }
|
||||
~audio_buffer() { delete[] buf; }
|
||||
|
||||
int count() {
|
||||
int diff;
|
||||
diff = wr_pos - rd_pos;
|
||||
diff = diff < 0 ? size + diff : diff;
|
||||
diff -= reserve;
|
||||
return diff < 0 ? 0 : diff;
|
||||
}
|
||||
int count() {
|
||||
int diff;
|
||||
diff = wr_pos - rd_pos;
|
||||
diff = diff < 0 ? size + diff : diff;
|
||||
diff -= reserve;
|
||||
return diff < 0 ? 0 : diff;
|
||||
}
|
||||
|
||||
void increment_wrpos(int n) {
|
||||
wr_pos = (wr_pos + n) % size;
|
||||
}
|
||||
void increment_wrpos(int n) {
|
||||
wr_pos = (wr_pos + n) % size;
|
||||
}
|
||||
|
||||
int write(const T* src, int n, int attenuation) {
|
||||
n = std::min<int>(n, size - count());
|
||||
int write(const T* src, int n, int attenuation) {
|
||||
n = std::min<int>(n, size - count());
|
||||
|
||||
if (wr_pos + n > size) {
|
||||
att_memcpy(buf + wr_pos, src, sizeof(T) * (size - wr_pos), attenuation);
|
||||
att_memcpy(buf, src + (size - wr_pos), sizeof(T) * (n - (size - wr_pos)), attenuation);
|
||||
} else {
|
||||
att_memcpy(buf + wr_pos, src, sizeof(T) * n, attenuation);
|
||||
}
|
||||
if (wr_pos + n > size) {
|
||||
att_memcpy(buf + wr_pos, src, sizeof(T) * (size - wr_pos), attenuation);
|
||||
att_memcpy(buf, src + (size - wr_pos), sizeof(T) * (n - (size - wr_pos)), attenuation);
|
||||
} else {
|
||||
att_memcpy(buf + wr_pos, src, sizeof(T) * n, attenuation);
|
||||
}
|
||||
|
||||
increment_wrpos(n);
|
||||
increment_wrpos(n);
|
||||
|
||||
return n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void increment_rdpos(int n) {
|
||||
rd_pos = (rd_pos + n) % size;
|
||||
}
|
||||
void increment_rdpos(int n) {
|
||||
rd_pos = (rd_pos + n) % size;
|
||||
}
|
||||
|
||||
int read(T* dst, int n) {
|
||||
n = std::min<int>(n, count());
|
||||
int read(T* dst, int n) {
|
||||
n = std::min<int>(n, count());
|
||||
|
||||
if (rd_pos + n > size) {
|
||||
std::memcpy(dst, buf + rd_pos, sizeof(T) * (size - rd_pos));
|
||||
std::memcpy(dst + (size - rd_pos), buf, sizeof(T) * (n - (size - rd_pos)));
|
||||
} else {
|
||||
std::memcpy(dst, buf + rd_pos, sizeof(T) * n);
|
||||
}
|
||||
if (rd_pos + n > size) {
|
||||
std::memcpy(dst, buf + rd_pos, sizeof(T) * (size - rd_pos));
|
||||
std::memcpy(dst + (size - rd_pos), buf, sizeof(T) * (n - (size - rd_pos)));
|
||||
} else {
|
||||
std::memcpy(dst, buf + rd_pos, sizeof(T) * n);
|
||||
}
|
||||
|
||||
increment_rdpos(n);
|
||||
increment_rdpos(n);
|
||||
|
||||
return n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int clear(int n) {
|
||||
n = std::min<int>(n, size - count());
|
||||
int clear(int n) {
|
||||
n = std::min<int>(n, size - count());
|
||||
|
||||
if (wr_pos + n > size) {
|
||||
std::memset(buf + wr_pos, 0, sizeof(T) * (size - wr_pos));
|
||||
std::memset(buf, 0, sizeof(T) * (n - (size - wr_pos)));
|
||||
} else {
|
||||
std::memset(buf + wr_pos, 0, sizeof(T) * n);
|
||||
}
|
||||
if (wr_pos + n > size) {
|
||||
std::memset(buf + wr_pos, 0, sizeof(T) * (size - wr_pos));
|
||||
std::memset(buf, 0, sizeof(T) * (n - (size - wr_pos)));
|
||||
} else {
|
||||
std::memset(buf + wr_pos, 0, sizeof(T) * n);
|
||||
}
|
||||
|
||||
increment_wrpos(n);
|
||||
increment_wrpos(n);
|
||||
|
||||
return n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void att_memcpy(T* dest, const T* data, int n, int attenuation) {
|
||||
int level = powf(10.0, attenuation / 20.0) * 32768;
|
||||
n /= sizeof(T);
|
||||
while (n--)
|
||||
*dest++ = (*data++ * level) >> 15;
|
||||
}
|
||||
};
|
||||
void att_memcpy(T* dest, const T* data, int n, int attenuation) {
|
||||
int level = powf(10.0, attenuation / 20.0) * 32768;
|
||||
n /= sizeof(T);
|
||||
while (n--)
|
||||
*dest++ = (*data++ * level) >> 15;
|
||||
}
|
||||
};
|
||||
|
||||
int callback(s16* output_buffer, size_t number_of_frames);
|
||||
static int _callback(const void*,
|
||||
void *output_buffer,
|
||||
unsigned long number_of_frames,
|
||||
const PaStreamCallbackTimeInfo*,
|
||||
PaStreamCallbackFlags,
|
||||
void *arg) { return static_cast<sound_pa*> (arg)->
|
||||
callback((s16*) output_buffer, number_of_frames * 2); }
|
||||
int callback(s16* output_buffer, size_t number_of_frames);
|
||||
static int _callback(const void*,
|
||||
void *output_buffer,
|
||||
unsigned long number_of_frames,
|
||||
const PaStreamCallbackTimeInfo*,
|
||||
PaStreamCallbackFlags,
|
||||
void *arg) { return static_cast<sound_pa*> (arg)->
|
||||
callback((s16*) output_buffer, number_of_frames * 2); }
|
||||
|
||||
PaDeviceIndex list_get_devidx(const char* api_str, const char* device_str);
|
||||
PaDeviceIndex list_get_devidx(const char* api_str, const char* device_str);
|
||||
|
||||
PaStream* m_pa_stream;
|
||||
PaError err;
|
||||
PaStream* m_pa_stream;
|
||||
PaError err;
|
||||
|
||||
int m_attenuation;
|
||||
int m_attenuation;
|
||||
|
||||
audio_buffer<s16>* m_ab;
|
||||
audio_buffer<s16>* m_ab;
|
||||
|
||||
std::atomic<bool> m_has_underflowed;
|
||||
std::atomic<bool> m_has_overflowed;
|
||||
unsigned m_underflows;
|
||||
unsigned m_overflows;
|
||||
std::atomic<bool> m_has_underflowed;
|
||||
std::atomic<bool> m_has_overflowed;
|
||||
unsigned m_underflows;
|
||||
unsigned m_overflows;
|
||||
|
||||
int m_skip_threshold; // this many samples in the buffer ~1 second in a row count as an overflow
|
||||
osd_ticks_t m_osd_ticks;
|
||||
osd_ticks_t m_skip_threshold_ticks;
|
||||
osd_ticks_t m_osd_tps;
|
||||
int m_buffer_min_ct;
|
||||
int m_skip_threshold; // this many samples in the buffer ~1 second in a row count as an overflow
|
||||
osd_ticks_t m_osd_ticks;
|
||||
osd_ticks_t m_skip_threshold_ticks;
|
||||
osd_ticks_t m_osd_tps;
|
||||
int m_buffer_min_ct;
|
||||
|
||||
#if LOG_BUFCNT
|
||||
std::stringstream m_log;
|
||||
std::stringstream m_log;
|
||||
#endif
|
||||
};
|
||||
|
||||
int sound_pa::init(osd_options const &options)
|
||||
{
|
||||
PaStreamParameters stream_params;
|
||||
const PaStreamInfo* stream_info;
|
||||
const PaHostApiInfo* api_info;
|
||||
const PaDeviceInfo* device_info;
|
||||
PaStreamParameters stream_params;
|
||||
const PaStreamInfo* stream_info;
|
||||
const PaHostApiInfo* api_info;
|
||||
const PaDeviceInfo* device_info;
|
||||
|
||||
unsigned long frames_per_callback = paFramesPerBufferUnspecified;
|
||||
double callback_interval;
|
||||
unsigned long frames_per_callback = paFramesPerBufferUnspecified;
|
||||
double callback_interval;
|
||||
|
||||
if (!sample_rate())
|
||||
return 0;
|
||||
if (!sample_rate())
|
||||
return 0;
|
||||
|
||||
m_attenuation = options.volume();
|
||||
m_underflows = 0;
|
||||
m_overflows = 0;
|
||||
m_has_overflowed = false;
|
||||
m_has_underflowed = false;
|
||||
m_skip_threshold_ticks = 0;
|
||||
m_attenuation = options.volume();
|
||||
m_underflows = 0;
|
||||
m_overflows = 0;
|
||||
m_has_overflowed = false;
|
||||
m_has_underflowed = false;
|
||||
m_skip_threshold_ticks = 0;
|
||||
|
||||
try {
|
||||
m_ab = new audio_buffer<s16>(m_sample_rate, 2);
|
||||
} catch (std::bad_alloc&) {
|
||||
osd_printf_verbose("PortAudio: Unable to allocate audio buffer, sound is disabled\n");
|
||||
goto error;
|
||||
}
|
||||
try {
|
||||
m_ab = new audio_buffer<s16>(m_sample_rate, 2);
|
||||
} catch (std::bad_alloc&) {
|
||||
osd_printf_verbose("PortAudio: Unable to allocate audio buffer, sound is disabled\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
m_osd_tps = osd_ticks_per_second();
|
||||
m_osd_tps = osd_ticks_per_second();
|
||||
|
||||
err = Pa_Initialize();
|
||||
err = Pa_Initialize();
|
||||
|
||||
if (err != paNoError) goto pa_error;
|
||||
if (err != paNoError) goto pa_error;
|
||||
|
||||
stream_params.device = list_get_devidx(options.pa_api(), options.pa_device());
|
||||
stream_params.device = list_get_devidx(options.pa_api(), options.pa_device());
|
||||
|
||||
stream_params.channelCount = 2;
|
||||
stream_params.sampleFormat = paInt16;
|
||||
stream_params.hostApiSpecificStreamInfo = NULL;
|
||||
stream_params.channelCount = 2;
|
||||
stream_params.sampleFormat = paInt16;
|
||||
stream_params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
device_info = Pa_GetDeviceInfo(stream_params.device);
|
||||
device_info = Pa_GetDeviceInfo(stream_params.device);
|
||||
|
||||
// 0 = use default
|
||||
stream_params.suggestedLatency = options.pa_latency() ? options.pa_latency() : device_info->defaultLowOutputLatency;
|
||||
// 0 = use default
|
||||
stream_params.suggestedLatency = options.pa_latency() ? options.pa_latency() : device_info->defaultLowOutputLatency;
|
||||
|
||||
#ifdef WIN32
|
||||
PaWasapiStreamInfo wasapi_stream_info;
|
||||
PaWasapiStreamInfo wasapi_stream_info;
|
||||
|
||||
// if requested latency is less than 20 ms, we need to use exclusive mode
|
||||
if (Pa_GetHostApiInfo(device_info->hostApi)->type == paWASAPI && stream_params.suggestedLatency < 0.020)
|
||||
{
|
||||
wasapi_stream_info.size = sizeof(PaWasapiStreamInfo);
|
||||
wasapi_stream_info.hostApiType = paWASAPI;
|
||||
wasapi_stream_info.flags = paWinWasapiExclusive;
|
||||
wasapi_stream_info.version = 1;
|
||||
// if requested latency is less than 20 ms, we need to use exclusive mode
|
||||
if (Pa_GetHostApiInfo(device_info->hostApi)->type == paWASAPI && stream_params.suggestedLatency < 0.020)
|
||||
{
|
||||
wasapi_stream_info.size = sizeof(PaWasapiStreamInfo);
|
||||
wasapi_stream_info.hostApiType = paWASAPI;
|
||||
wasapi_stream_info.flags = paWinWasapiExclusive;
|
||||
wasapi_stream_info.version = 1;
|
||||
|
||||
stream_params.hostApiSpecificStreamInfo = &wasapi_stream_info;
|
||||
stream_params.hostApiSpecificStreamInfo = &wasapi_stream_info;
|
||||
|
||||
// for latencies lower than ~16 ms, we need to use event mode
|
||||
if (stream_params.suggestedLatency < 0.016)
|
||||
{
|
||||
// only way to control output latency with event mode
|
||||
frames_per_callback = stream_params.suggestedLatency * m_sample_rate;
|
||||
// for latencies lower than ~16 ms, we need to use event mode
|
||||
if (stream_params.suggestedLatency < 0.016)
|
||||
{
|
||||
// only way to control output latency with event mode
|
||||
frames_per_callback = stream_params.suggestedLatency * m_sample_rate;
|
||||
|
||||
// needed for event mode to work
|
||||
stream_params.suggestedLatency = 0;
|
||||
}
|
||||
}
|
||||
// needed for event mode to work
|
||||
stream_params.suggestedLatency = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err = Pa_OpenStream(&m_pa_stream,
|
||||
NULL,
|
||||
&stream_params,
|
||||
m_sample_rate,
|
||||
frames_per_callback,
|
||||
paClipOff,
|
||||
_callback,
|
||||
this);
|
||||
err = Pa_OpenStream(&m_pa_stream,
|
||||
NULL,
|
||||
&stream_params,
|
||||
m_sample_rate,
|
||||
frames_per_callback,
|
||||
paClipOff,
|
||||
_callback,
|
||||
this);
|
||||
|
||||
if (err != paNoError) goto pa_error;
|
||||
if (err != paNoError) goto pa_error;
|
||||
|
||||
stream_info = Pa_GetStreamInfo(m_pa_stream);
|
||||
api_info = Pa_GetHostApiInfo(device_info->hostApi);
|
||||
stream_info = Pa_GetStreamInfo(m_pa_stream);
|
||||
api_info = Pa_GetHostApiInfo(device_info->hostApi);
|
||||
|
||||
// in milliseconds
|
||||
callback_interval = static_cast<double>(stream_info->outputLatency) * 1000.0;
|
||||
// in milliseconds
|
||||
callback_interval = static_cast<double>(stream_info->outputLatency) * 1000.0;
|
||||
|
||||
// clamp to a probable figure
|
||||
callback_interval = std::min<double>(callback_interval, 20.0);
|
||||
// clamp to a probable figure
|
||||
callback_interval = std::min<double>(callback_interval, 20.0);
|
||||
|
||||
// set the best guess callback interval to allowed count, each audio_latency step > 1 adds 20 ms
|
||||
m_skip_threshold = ((std::max<double>(callback_interval, 10.0) + (m_audio_latency - 1) * 20.0) / 1000.0) * m_sample_rate * 2 + 0.5f;
|
||||
// set the best guess callback interval to allowed count, each audio_latency step > 1 adds 20 ms
|
||||
m_skip_threshold = ((std::max<double>(callback_interval, 10.0) + (m_audio_latency - 1) * 20.0) / 1000.0) * m_sample_rate * 2 + 0.5f;
|
||||
|
||||
osd_printf_verbose("PortAudio: Using device \"%s\" on API \"%s\"\n", device_info->name, api_info->name);
|
||||
osd_printf_verbose("PortAudio: Sample rate is %0.0f Hz, device output latency is %0.2f ms\n",
|
||||
stream_info->sampleRate, stream_info->outputLatency * 1000.0);
|
||||
osd_printf_verbose("PortAudio: Allowed additional buffering latency is %0.2f ms/%d frames\n",
|
||||
(m_skip_threshold / 2.0) / (m_sample_rate / 1000.0), m_skip_threshold / 2);
|
||||
osd_printf_verbose("PortAudio: Using device \"%s\" on API \"%s\"\n", device_info->name, api_info->name);
|
||||
osd_printf_verbose("PortAudio: Sample rate is %0.0f Hz, device output latency is %0.2f ms\n",
|
||||
stream_info->sampleRate, stream_info->outputLatency * 1000.0);
|
||||
osd_printf_verbose("PortAudio: Allowed additional buffering latency is %0.2f ms/%d frames\n",
|
||||
(m_skip_threshold / 2.0) / (m_sample_rate / 1000.0), m_skip_threshold / 2);
|
||||
|
||||
err = Pa_StartStream(m_pa_stream);
|
||||
err = Pa_StartStream(m_pa_stream);
|
||||
|
||||
if (err != paNoError) goto pa_error;
|
||||
if (err != paNoError) goto pa_error;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
pa_error:
|
||||
osd_printf_verbose("PortAudio error: %s\n", Pa_GetErrorText(err));
|
||||
Pa_Terminate();
|
||||
osd_printf_verbose("PortAudio error: %s\n", Pa_GetErrorText(err));
|
||||
Pa_Terminate();
|
||||
error:
|
||||
m_sample_rate = 0;
|
||||
return -1;
|
||||
m_sample_rate = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
PaDeviceIndex sound_pa::list_get_devidx(const char* api_str, const char* device_str)
|
||||
{
|
||||
PaDeviceIndex selected_devidx = -1;
|
||||
PaDeviceIndex selected_devidx = -1;
|
||||
|
||||
for (PaHostApiIndex api_idx = 0; api_idx < Pa_GetHostApiCount(); api_idx++)
|
||||
{
|
||||
const PaHostApiInfo *api_info = Pa_GetHostApiInfo(api_idx);
|
||||
for (PaHostApiIndex api_idx = 0; api_idx < Pa_GetHostApiCount(); api_idx++)
|
||||
{
|
||||
const PaHostApiInfo *api_info = Pa_GetHostApiInfo(api_idx);
|
||||
|
||||
osd_printf_verbose("PortAudio: API %s has %d devices\n", api_info->name, api_info->deviceCount);
|
||||
osd_printf_verbose("PortAudio: API %s has %d devices\n", api_info->name, api_info->deviceCount);
|
||||
|
||||
for (int api_devidx = 0; api_devidx < api_info->deviceCount; api_devidx++)
|
||||
{
|
||||
PaDeviceIndex devidx = Pa_HostApiDeviceIndexToDeviceIndex(api_idx, api_devidx);
|
||||
const PaDeviceInfo *device_info = Pa_GetDeviceInfo(devidx);
|
||||
for (int api_devidx = 0; api_devidx < api_info->deviceCount; api_devidx++)
|
||||
{
|
||||
PaDeviceIndex devidx = Pa_HostApiDeviceIndexToDeviceIndex(api_idx, api_devidx);
|
||||
const PaDeviceInfo *device_info = Pa_GetDeviceInfo(devidx);
|
||||
|
||||
// specified API and device is found
|
||||
if (!strcmp(api_str, api_info->name) && !strcmp(device_str, device_info->name))
|
||||
selected_devidx = devidx;
|
||||
// specified API and device is found
|
||||
if (!strcmp(api_str, api_info->name) && !strcmp(device_str, device_info->name))
|
||||
selected_devidx = devidx;
|
||||
|
||||
// if specified device cannot be found, use the default device of the specified API
|
||||
if (!strcmp(api_str, api_info->name) && api_devidx == api_info->deviceCount - 1 && selected_devidx == -1)
|
||||
selected_devidx = api_info->defaultOutputDevice;
|
||||
// if specified device cannot be found, use the default device of the specified API
|
||||
if (!strcmp(api_str, api_info->name) && api_devidx == api_info->deviceCount - 1 && selected_devidx == -1)
|
||||
selected_devidx = api_info->defaultOutputDevice;
|
||||
|
||||
osd_printf_verbose("PortAudio: %s: \"%s\"%s\n",
|
||||
api_info->name, device_info->name, api_info->defaultOutputDevice == devidx ? " (default)" : "");
|
||||
}
|
||||
}
|
||||
osd_printf_verbose("PortAudio: %s: \"%s\"%s\n",
|
||||
api_info->name, device_info->name, api_info->defaultOutputDevice == devidx ? " (default)" : "");
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_devidx < 0)
|
||||
{
|
||||
osd_printf_verbose("PortAudio: Unable to find specified API or device or none set, reverting to default\n");
|
||||
return Pa_GetDefaultOutputDevice();
|
||||
}
|
||||
if (selected_devidx < 0)
|
||||
{
|
||||
osd_printf_verbose("PortAudio: Unable to find specified API or device or none set, reverting to default\n");
|
||||
return Pa_GetDefaultOutputDevice();
|
||||
}
|
||||
|
||||
return selected_devidx;
|
||||
return selected_devidx;
|
||||
}
|
||||
|
||||
int sound_pa::callback(s16* output_buffer, size_t number_of_samples)
|
||||
{
|
||||
int buf_ct = m_ab->count();
|
||||
int buf_ct = m_ab->count();
|
||||
|
||||
if (buf_ct >= number_of_samples)
|
||||
{
|
||||
m_ab->read(output_buffer, number_of_samples);
|
||||
if (buf_ct >= number_of_samples)
|
||||
{
|
||||
m_ab->read(output_buffer, number_of_samples);
|
||||
|
||||
// keep track of the minimum buffer count, skip samples adaptively to respect the audio_latency setting
|
||||
buf_ct -= number_of_samples;
|
||||
// keep track of the minimum buffer count, skip samples adaptively to respect the audio_latency setting
|
||||
buf_ct -= number_of_samples;
|
||||
|
||||
if (buf_ct < m_buffer_min_ct)
|
||||
m_buffer_min_ct = buf_ct;
|
||||
if (buf_ct < m_buffer_min_ct)
|
||||
m_buffer_min_ct = buf_ct;
|
||||
|
||||
// if we are below the threshold, reset the counter
|
||||
if (buf_ct < m_skip_threshold)
|
||||
m_skip_threshold_ticks = m_osd_ticks;
|
||||
// if we are below the threshold, reset the counter
|
||||
if (buf_ct < m_skip_threshold)
|
||||
m_skip_threshold_ticks = m_osd_ticks;
|
||||
|
||||
// if we have been above the set threshold for ~1 second, skip forward
|
||||
if (m_osd_ticks - m_skip_threshold_ticks > m_osd_tps)
|
||||
{
|
||||
int adjust = m_buffer_min_ct - m_skip_threshold / 2;
|
||||
// if we have been above the set threshold for ~1 second, skip forward
|
||||
if (m_osd_ticks - m_skip_threshold_ticks > m_osd_tps)
|
||||
{
|
||||
int adjust = m_buffer_min_ct - m_skip_threshold / 2;
|
||||
|
||||
// if adjustment is less than two milliseconds, don't bother
|
||||
if (adjust / 2 > sample_rate() / 500) {
|
||||
m_ab->increment_rdpos(adjust);
|
||||
m_has_overflowed = true;
|
||||
}
|
||||
// if adjustment is less than two milliseconds, don't bother
|
||||
if (adjust / 2 > sample_rate() / 500) {
|
||||
m_ab->increment_rdpos(adjust);
|
||||
m_has_overflowed = true;
|
||||
}
|
||||
|
||||
m_skip_threshold_ticks = m_osd_ticks;
|
||||
m_buffer_min_ct = INT_MAX;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ab->read(output_buffer, buf_ct);
|
||||
std::memset(output_buffer + buf_ct, 0, (number_of_samples - buf_ct) * sizeof(s16));
|
||||
m_skip_threshold_ticks = m_osd_ticks;
|
||||
m_buffer_min_ct = INT_MAX;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ab->read(output_buffer, buf_ct);
|
||||
std::memset(output_buffer + buf_ct, 0, (number_of_samples - buf_ct) * sizeof(s16));
|
||||
|
||||
// rd_pos == wr_pos only happens when buffer hasn't received any samples,
|
||||
// i.e. before update_audio_stream has been called
|
||||
if (m_ab->rd_pos != m_ab->wr_pos)
|
||||
m_has_underflowed = true;
|
||||
// rd_pos == wr_pos only happens when buffer hasn't received any samples,
|
||||
// i.e. before update_audio_stream has been called
|
||||
if (m_ab->rd_pos != m_ab->wr_pos)
|
||||
m_has_underflowed = true;
|
||||
|
||||
m_skip_threshold_ticks = m_osd_ticks;
|
||||
}
|
||||
m_skip_threshold_ticks = m_osd_ticks;
|
||||
}
|
||||
|
||||
return paContinue;
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
void sound_pa::update_audio_stream(bool is_throttled, const s16 *buffer, int samples_this_frame)
|
||||
{
|
||||
if (!sample_rate())
|
||||
return;
|
||||
if (!sample_rate())
|
||||
return;
|
||||
|
||||
// for determining buffer overflows, take the sample here instead of in the callback
|
||||
m_osd_ticks = osd_ticks();
|
||||
// for determining buffer overflows, take the sample here instead of in the callback
|
||||
m_osd_ticks = osd_ticks();
|
||||
|
||||
#if LOG_BUFCNT
|
||||
if (m_log.good())
|
||||
m_log << m_ab->count() << std::endl;
|
||||
if (m_log.good())
|
||||
m_log << m_ab->count() << std::endl;
|
||||
#endif
|
||||
|
||||
if (m_has_underflowed)
|
||||
{
|
||||
m_underflows++;
|
||||
// add some silence to prevent immediate underflows
|
||||
m_ab->clear(m_skip_threshold / 4);
|
||||
m_has_underflowed = false;
|
||||
}
|
||||
if (m_has_underflowed)
|
||||
{
|
||||
m_underflows++;
|
||||
// add some silence to prevent immediate underflows
|
||||
m_ab->clear(m_skip_threshold / 4);
|
||||
m_has_underflowed = false;
|
||||
}
|
||||
|
||||
if (m_has_overflowed)
|
||||
{
|
||||
m_overflows++;
|
||||
m_has_overflowed = false;
|
||||
}
|
||||
if (m_has_overflowed)
|
||||
{
|
||||
m_overflows++;
|
||||
m_has_overflowed = false;
|
||||
}
|
||||
|
||||
m_ab->write(buffer, samples_this_frame * 2, m_attenuation);
|
||||
m_ab->write(buffer, samples_this_frame * 2, m_attenuation);
|
||||
}
|
||||
|
||||
void sound_pa::set_mastervolume(int attenuation)
|
||||
{
|
||||
m_attenuation = attenuation;
|
||||
m_attenuation = attenuation;
|
||||
}
|
||||
|
||||
void sound_pa::exit()
|
||||
{
|
||||
if (!sample_rate())
|
||||
return;
|
||||
if (!sample_rate())
|
||||
return;
|
||||
|
||||
#if LOG_BUFCNT
|
||||
if (m_log.good())
|
||||
{
|
||||
std::ofstream m_logfile(LOG_FILE);
|
||||
if (m_log.good())
|
||||
{
|
||||
std::ofstream m_logfile(LOG_FILE);
|
||||
|
||||
if (m_logfile.is_open()) {
|
||||
m_logfile << m_log.str();
|
||||
m_logfile.close();
|
||||
} else {
|
||||
osd_printf_verbose("PortAudio: Could not write log.\n");
|
||||
}
|
||||
}
|
||||
if (m_logfile.is_open()) {
|
||||
m_logfile << m_log.str();
|
||||
m_logfile.close();
|
||||
} else {
|
||||
osd_printf_verbose("PortAudio: Could not write log.\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Pa_StopStream(m_pa_stream);
|
||||
err = Pa_Terminate();
|
||||
|
||||
if (err != paNoError)
|
||||
osd_printf_verbose("PortAudio error: %s\n", Pa_GetErrorText(err));
|
||||
Pa_StopStream(m_pa_stream);
|
||||
err = Pa_Terminate();
|
||||
|
||||
delete m_ab;
|
||||
if (err != paNoError)
|
||||
osd_printf_verbose("PortAudio error: %s\n", Pa_GetErrorText(err));
|
||||
|
||||
if (m_overflows || m_underflows)
|
||||
osd_printf_verbose("Sound: overflows=%d underflows=%d\n", m_overflows, m_underflows);
|
||||
delete m_ab;
|
||||
|
||||
if (m_overflows || m_underflows)
|
||||
osd_printf_verbose("Sound: overflows=%d underflows=%d\n", m_overflows, m_underflows);
|
||||
}
|
||||
|
||||
#else
|
||||
MODULE_NOT_SUPPORTED(sound_pa, OSD_SOUND_PROVIDER, "portaudio")
|
||||
MODULE_NOT_SUPPORTED(sound_pa, OSD_SOUND_PROVIDER, "portaudio")
|
||||
#endif
|
||||
|
||||
MODULE_DEFINITION(SOUND_PORTAUDIO, sound_pa)
|
||||
|
Loading…
Reference in New Issue
Block a user