diff options
Diffstat (limited to 'src/osd/modules/sound/pa_sound.cpp')
-rw-r--r-- | src/osd/modules/sound/pa_sound.cpp | 97 |
1 files changed, 52 insertions, 45 deletions
diff --git a/src/osd/modules/sound/pa_sound.cpp b/src/osd/modules/sound/pa_sound.cpp index fb92689504f..dfba0cfa132 100644 --- a/src/osd/modules/sound/pa_sound.cpp +++ b/src/osd/modules/sound/pa_sound.cpp @@ -2,9 +2,9 @@ // copyright-holders:intealls, R.Belmont /*************************************************************************** - pa_sound.c + pa_sound.c - PortAudio interface. + PortAudio interface. *******************************************************************c********/ @@ -13,7 +13,7 @@ #ifndef NO_USE_PORTAUDIO -#include "portaudio/include/portaudio.h" +#include <portaudio.h> #include "modules/lib/osdobj_common.h" #include <iostream> @@ -49,78 +49,75 @@ public: virtual void set_mastervolume(int attenuation); private: - /* Lock free SPSC ring buffer */ + // Lock free SPSC ring buffer template <typename T> struct audio_buffer { T* buf; int size; int reserve; - std::atomic<int> rd_pos, wr_pos; + std::atomic<int> playpos, writepos; - 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(reserve) { + playpos = writepos = 0; + buf = new T[this->size]; } ~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 diff = writepos - playpos; + return diff < 0 ? size + diff : diff; } - void increment_wrpos(int n) { - wr_pos = (wr_pos + n) % size; + void increment_writepos(int n) { + writepos.store((writepos + n) % size); } int write(const T* src, int n, int attenuation) { - n = std::min<int>(n, size - count()); + n = std::min<int>(n, size - reserve - 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); + if (writepos + n > size) { + att_memcpy(buf + writepos, src, sizeof(T) * (size - writepos), attenuation); + att_memcpy(buf, src + (size - writepos), sizeof(T) * (n - (size - writepos)), attenuation); } else { - att_memcpy(buf + wr_pos, src, sizeof(T) * n, attenuation); + att_memcpy(buf + writepos, src, sizeof(T) * n, attenuation); } - increment_wrpos(n); + increment_writepos(n); return n; } - void increment_rdpos(int n) { - rd_pos = (rd_pos + n) % size; + void increment_playpos(int n) { + playpos.store((playpos + n) % size); } 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))); + if (playpos + n > size) { + std::memcpy(dst, buf + playpos, sizeof(T) * (size - playpos)); + std::memcpy(dst + (size - playpos), buf, sizeof(T) * (n - (size - playpos))); } else { - std::memcpy(dst, buf + rd_pos, sizeof(T) * n); + std::memcpy(dst, buf + playpos, sizeof(T) * n); } - increment_rdpos(n); + increment_playpos(n); return n; } int clear(int n) { - n = std::min<int>(n, size - count()); + n = std::min<int>(n, size - reserve - 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))); + if (writepos + n > size) { + std::memset(buf + writepos, 0, sizeof(T) * (size - writepos)); + std::memset(buf, 0, sizeof(T) * (n - (size - writepos))); } else { - std::memset(buf + wr_pos, 0, sizeof(T) * n); + std::memset(buf + writepos, 0, sizeof(T) * n); } - increment_wrpos(n); + increment_writepos(n); return n; } @@ -133,6 +130,12 @@ private: } }; + enum + { + LATENCY_MIN = 1, + LATENCY_MAX = 5, + }; + int callback(s16* output_buffer, size_t number_of_frames); static int _callback(const void*, void *output_buffer, @@ -185,7 +188,11 @@ int sound_pa::init(osd_options const &options) m_overflows = 0; m_has_overflowed = false; m_has_underflowed = false; + m_osd_ticks = 0; m_skip_threshold_ticks = 0; + m_osd_tps = osd_ticks_per_second(); + m_buffer_min_ct = INT_MAX; + m_audio_latency = std::min<int>(std::max<int>(m_audio_latency, LATENCY_MIN), LATENCY_MAX); try { m_ab = new audio_buffer<s16>(m_sample_rate, 2); @@ -194,8 +201,6 @@ int sound_pa::init(osd_options const &options) goto error; } - m_osd_tps = osd_ticks_per_second(); - err = Pa_Initialize(); if (err != paNoError) goto pa_error; @@ -342,7 +347,7 @@ int sound_pa::callback(s16* output_buffer, size_t number_of_samples) // if adjustment is less than two milliseconds, don't bother if (adjust / 2 > sample_rate() / 500) { - m_ab->increment_rdpos(adjust); + m_ab->increment_playpos(adjust); m_has_overflowed = true; } @@ -355,9 +360,8 @@ int sound_pa::callback(s16* output_buffer, size_t number_of_samples) 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) + // if update_audio_stream has been called, note the underflow + if (m_osd_ticks) m_has_underflowed = true; m_skip_threshold_ticks = m_osd_ticks; @@ -371,9 +375,6 @@ void sound_pa::update_audio_stream(bool is_throttled, const s16 *buffer, int sam if (!sample_rate()) return; - // 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; @@ -394,6 +395,9 @@ void sound_pa::update_audio_stream(bool is_throttled, const s16 *buffer, int sam } m_ab->write(buffer, samples_this_frame * 2, m_attenuation); + + // for determining buffer overflows, take the sample here instead of in the callback + m_osd_ticks = osd_ticks(); } void sound_pa::set_mastervolume(int attenuation) @@ -418,12 +422,15 @@ void sound_pa::exit() osd_printf_error("PortAudio: Error writing log.\n"); #endif - Pa_StopStream(m_pa_stream); - err = Pa_Terminate(); + err = Pa_StopStream(m_pa_stream); if (err != paNoError) goto error; + err = Pa_CloseStream(m_pa_stream); if (err != paNoError) goto error; + error: if (err != paNoError) osd_printf_error("PortAudio error: %s\n", Pa_GetErrorText(err)); + Pa_Terminate(); + delete m_ab; if (m_overflows || m_underflows) |