diff --git a/doc/en/sidplayfp.ini.pod b/doc/en/sidplayfp.ini.pod index d718e31..9eb7821 100644 --- a/doc/en/sidplayfp.ini.pod +++ b/doc/en/sidplayfp.ini.pod @@ -162,6 +162,12 @@ Number of bits ber sample, used only for wav/au output. Using values other than the ones specified will produce invalid output. +=item B=I<< >> + +Length of the buffer in milliseconds, default is 250. +Increase if you experience audio problems or reduce to +improve latency. + =back diff --git a/src/IniConfig.cpp b/src/IniConfig.cpp index 59b7ddc..dbb7101 100644 --- a/src/IniConfig.cpp +++ b/src/IniConfig.cpp @@ -106,6 +106,7 @@ void IniConfig::clear() audio_s.frequency = SidConfig::DEFAULT_SAMPLING_FREQ; audio_s.channels = 0; audio_s.precision = 16; + audio_s.bufLength = 250; emulation_s.modelDefault = SidConfig::PAL; emulation_s.modelForced = false; @@ -376,6 +377,8 @@ void IniConfig::readAudio(iniHandler &ini) readInt(ini, TEXT("Channels"), audio_s.channels); readInt(ini, TEXT("BitsPerSample"), audio_s.precision); + + readInt(ini, TEXT("BufferLength"), audio_s.bufLength); } diff --git a/src/IniConfig.h b/src/IniConfig.h index 7f19b8d..74f8af9 100644 --- a/src/IniConfig.h +++ b/src/IniConfig.h @@ -63,9 +63,11 @@ class IniConfig struct audio_section { // INI Section - [Audio] - int frequency; - int channels; - int precision; + int frequency; // sample rate + int channels; // number of channels + int precision; // sample precision in bits + int bufLength; // buffer length in milliseconds + int getBufSize() const { return (bufLength * frequency) / 1000; } }; struct emulation_section diff --git a/src/audio/AudioConfig.h b/src/audio/AudioConfig.h index 9379457..dc5f7c5 100644 --- a/src/audio/AudioConfig.h +++ b/src/audio/AudioConfig.h @@ -29,15 +29,13 @@ class AudioConfig uint_least32_t frequency; int precision; int channels; - uint_least32_t bufSize; // sample buffer size + uint_least32_t bufSize; // sample buffer size measured in frames AudioConfig() : frequency(48000), precision(16), channels(1), bufSize(0) {} - - uint_least32_t bytesPerMillis() const { return (precision/8 * channels * frequency) / 1000; } }; #endif // AUDIOCONFIG_H diff --git a/src/audio/AudioDrv.cpp b/src/audio/AudioDrv.cpp index 9179d36..deb1d2a 100644 --- a/src/audio/AudioDrv.cpp +++ b/src/audio/AudioDrv.cpp @@ -47,6 +47,8 @@ # define HAVE_NULL #endif +#include + bool audioDrv::open(AudioConfig &cfg) { bool res = false; @@ -69,6 +71,7 @@ bool audioDrv::open(AudioConfig &cfg) { audio.reset(new Audio_ALSA()); res = audio->open(cfg); + if (res) std::cerr << "ALSA" << std::endl; } #endif #ifdef HAVE_OSS diff --git a/src/audio/AudioDrv.h b/src/audio/AudioDrv.h index 3509b96..605a655 100644 --- a/src/audio/AudioDrv.h +++ b/src/audio/AudioDrv.h @@ -37,7 +37,7 @@ class audioDrv : public IAudio bool open(AudioConfig &cfg) override; void reset() override { audio->reset(); } - bool write(uint_least32_t size) override { return audio->write(size); } + bool write(uint_least32_t frames) override { return audio->write(frames); } void close() override { audio->close(); } void pause() override { audio->pause(); } short *buffer() const override { return audio->buffer(); } diff --git a/src/audio/IAudio.h b/src/audio/IAudio.h index 53eaace..c4711c0 100644 --- a/src/audio/IAudio.h +++ b/src/audio/IAudio.h @@ -33,7 +33,7 @@ class IAudio virtual bool open(AudioConfig &cfg) = 0; virtual void reset() = 0; - virtual bool write(uint_least32_t size) = 0; + virtual bool write(uint_least32_t frames) = 0; virtual void close() = 0; virtual void pause() = 0; virtual short *buffer() const = 0; diff --git a/src/audio/alsa/audiodrv.cpp b/src/audio/alsa/audiodrv.cpp index 5836188..1c881a8 100644 --- a/src/audio/alsa/audiodrv.cpp +++ b/src/audio/alsa/audiodrv.cpp @@ -84,7 +84,7 @@ bool Audio_ALSA::open(AudioConfig &cfg) tmpCfg.frequency = rate; } - snd_pcm_uframes_t buffer_size = tmpCfg.frequency / 5; + snd_pcm_uframes_t buffer_size = tmpCfg.bufSize; checkResult(snd_pcm_hw_params_set_buffer_size_near(_audioHandle, hw_params, &buffer_size)); tmpCfg.bufSize = buffer_size; @@ -136,7 +136,7 @@ void Audio_ALSA::close() } } -bool Audio_ALSA::write(uint_least32_t size) +bool Audio_ALSA::write(uint_least32_t frames) { if (_audioHandle == nullptr) { @@ -144,7 +144,7 @@ bool Audio_ALSA::write(uint_least32_t size) return false; } - int err = snd_pcm_writei(_audioHandle, _sampleBuffer, size); + int err = snd_pcm_writei(_audioHandle, _sampleBuffer, frames); if (err < 0) { err = snd_pcm_recover(_audioHandle, err, 0); diff --git a/src/audio/alsa/audiodrv.h b/src/audio/alsa/audiodrv.h index cf4fdc2..44edb13 100644 --- a/src/audio/alsa/audiodrv.h +++ b/src/audio/alsa/audiodrv.h @@ -54,7 +54,7 @@ class Audio_ALSA: public AudioBase bool open (AudioConfig &cfg) override; void close () override; void reset () override {} - bool write (uint_least32_t size) override; + bool write (uint_least32_t frames) override; void pause () override {} }; diff --git a/src/audio/au/auFile.cpp b/src/audio/au/auFile.cpp index 0147545..262efc6 100644 --- a/src/audio/au/auFile.cpp +++ b/src/audio/au/auFile.cpp @@ -186,20 +186,21 @@ auFile::auFile(const std::string &name) : auHdr(defaultAuHdr), file(nullptr), headerWritten(false), - precision(32) + m_precision(32) {} bool auFile::open(AudioConfig &cfg) { - precision = cfg.precision; + m_precision = cfg.precision; + m_channels = cfg.channels; - unsigned short bits = precision; - unsigned long format = (precision == 16) ? 3 : 6; - unsigned long channels = cfg.channels; + unsigned short bits = m_precision; + unsigned long format = (m_precision == 16) ? 3 : 6; + unsigned long channels = m_channels; unsigned long freq = cfg.frequency; unsigned short blockAlign = (bits>>3)*channels; unsigned long bufSize = freq * blockAlign; - cfg.bufSize = bufSize; + cfg.bufSize = freq; if (name.empty()) return false; @@ -238,10 +239,11 @@ bool auFile::open(AudioConfig &cfg) return true; } -bool auFile::write(uint_least32_t size) +bool auFile::write(uint_least32_t frames) { if (file && !file->fail()) { + uint_least32_t size = frames * m_channels; unsigned long int bytes = size; if (!headerWritten) { @@ -249,7 +251,7 @@ bool auFile::write(uint_least32_t size) headerWritten = true; } - if (precision == 16) + if (m_precision == 16) { std::vector buffer(size); bytes *= 2; diff --git a/src/audio/au/auFile.h b/src/audio/au/auFile.h index 2c07838..962995e 100644 --- a/src/audio/au/auFile.h +++ b/src/audio/au/auFile.h @@ -57,7 +57,8 @@ class auFile: public AudioBase std::ostream *file; bool headerWritten; - int precision; + int m_precision; + int m_channels; public: auFile(const std::string &name); diff --git a/src/audio/directx/audiodrv.cpp b/src/audio/directx/audiodrv.cpp index e6a2611..61ced14 100644 --- a/src/audio/directx/audiodrv.cpp +++ b/src/audio/directx/audiodrv.cpp @@ -131,8 +131,7 @@ bool Audio_DirectX::open (AudioConfig &cfg, HWND hwnd) } lpDsbPrimary->Release (); - // Buffer size reduced to 2 blocks of 500ms - bufSize = wfm.nSamplesPerSec / 2 * wfm.nBlockAlign; + bufSize = cfg.bufSize * wfm.nBlockAlign; // Allocate secondary buffers memset (&dsbdesc, 0, sizeof(DSBUFFERDESC)); @@ -182,7 +181,7 @@ bool Audio_DirectX::open (AudioConfig &cfg, HWND hwnd) } // Update the users settings - cfg.bufSize = bufSize / 2; + m_frameSize = wfm.nBlockAlign; _settings = cfg; isPlaying = false; _sampleBuffer = (short*)lpvData; @@ -198,15 +197,16 @@ bool Audio_DirectX::open (AudioConfig &cfg, HWND hwnd) } } -bool Audio_DirectX::write (uint_least32_t size) +bool Audio_DirectX::write (uint_least32_t frames) { if (!isOpen) { setError("Device not open."); return false; } - - size *= 2; + + // get the number of bytes + DWORD size = frames * m_frameSize; // Unlock the current buffer for playing lpDsb->Unlock (lpvData, size, NULL, 0); diff --git a/src/audio/directx/audiodrv.h b/src/audio/directx/audiodrv.h index a63804e..ca5aee2 100644 --- a/src/audio/directx/audiodrv.h +++ b/src/audio/directx/audiodrv.h @@ -59,6 +59,8 @@ class Audio_DirectX: public AudioBase HANDLE rghEvent[AUDIO_DIRECTX_BUFFERS]; DWORD bufSize; + int m_frameSize; + bool isOpen; bool isPlaying; @@ -75,7 +77,7 @@ class Audio_DirectX: public AudioBase bool open (AudioConfig &cfg, HWND hwnd); void close () override; void reset () override; - bool write (uint_least32_t size) override; + bool write (uint_least32_t frames) override; void pause () override; }; diff --git a/src/audio/mmsystem/audiodrv.cpp b/src/audio/mmsystem/audiodrv.cpp index c6aeb95..de4aea7 100644 --- a/src/audio/mmsystem/audiodrv.cpp +++ b/src/audio/mmsystem/audiodrv.cpp @@ -113,11 +113,10 @@ bool Audio_MMSystem::open(AudioConfig &cfg) wfm.cbSize = 0; // Rev 1.3 (saw) - Calculate buffer to hold 250ms of data - bufSize = wfm.nSamplesPerSec / 4 * wfm.nBlockAlign; + bufSize = cfg.bufSize * wfm.nBlockAlign; try { - cfg.bufSize = bufSize / 2; checkResult(waveOutOpen(&waveHandle, WAVE_MAPPER, &wfm, 0, 0, 0)); _settings = cfg; @@ -160,6 +159,7 @@ bool Audio_MMSystem::open(AudioConfig &cfg) header->dwFlags = WHDR_DONE; /* mark the block is done */ } + m_frameSize = wfm.nBlockAlign; blockNum = 0; _sampleBuffer = blocks[blockNum]; return true; @@ -173,7 +173,7 @@ bool Audio_MMSystem::open(AudioConfig &cfg) } } -bool Audio_MMSystem::write(uint_least32_t size) +bool Audio_MMSystem::write(uint_least32_t frames) { if (!isOpen) { @@ -181,9 +181,12 @@ bool Audio_MMSystem::write(uint_least32_t size) return false; } + // get the number of bytes + DWORD size = frames * m_frameSize; + /* Reset wave header fields: */ blockHeaders[blockNum]->dwFlags = 0; - blockHeaders[blockNum]->dwBufferLength = size * 2; + blockHeaders[blockNum]->dwBufferLength = size; /* Prepare block header: */ checkResult(waveOutPrepareHeader(waveHandle, blockHeaders[blockNum], sizeof(WAVEHDR))); diff --git a/src/audio/mmsystem/audiodrv.h b/src/audio/mmsystem/audiodrv.h index 9b96e02..79f474c 100644 --- a/src/audio/mmsystem/audiodrv.h +++ b/src/audio/mmsystem/audiodrv.h @@ -53,6 +53,8 @@ class Audio_MMSystem: public AudioBase HGLOBAL blockHeaderHandles[MAXBUFBLOCKS]; int blockNum; int bufSize; + int m_frameSize; + bool isOpen; private: @@ -66,7 +68,7 @@ class Audio_MMSystem: public AudioBase bool open (AudioConfig &cfg) override; void close () override; void reset () override; - bool write (uint_least32_t size) override; + bool write (uint_least32_t frames) override; void pause () override {} }; diff --git a/src/audio/null/null.h b/src/audio/null/null.h index dfa7ec4..4392791 100644 --- a/src/audio/null/null.h +++ b/src/audio/null/null.h @@ -50,7 +50,7 @@ class Audio_Null: public AudioBase bool open (AudioConfig &cfg) override; void close () override; void reset () override {} - bool write (uint_least32_t size) override; + bool write (uint_least32_t frames) override; void pause () override {} }; diff --git a/src/audio/oss/audiodrv.cpp b/src/audio/oss/audiodrv.cpp index c892c48..8f0abd3 100644 --- a/src/audio/oss/audiodrv.cpp +++ b/src/audio/oss/audiodrv.cpp @@ -117,6 +117,7 @@ bool Audio_OSS::open (AudioConfig &cfg) } // Setup internal Config + m_frameSize = 2 * cfg.channels; _settings = cfg; return true; } @@ -155,7 +156,7 @@ void Audio_OSS::reset () } } -bool Audio_OSS::write (uint_least32_t size) +bool Audio_OSS::write (uint_least32_t frames) { if (_audiofd == -1) { @@ -163,7 +164,8 @@ bool Audio_OSS::write (uint_least32_t size) return false; } - ::write (_audiofd, _sampleBuffer, 2 * size); + size_t const bytes = static_cast(frames) * m_frameSize; + ::write (_audiofd, _sampleBuffer, bytes); return true; } diff --git a/src/audio/oss/audiodrv.h b/src/audio/oss/audiodrv.h index 3a250b9..95959d9 100644 --- a/src/audio/oss/audiodrv.h +++ b/src/audio/oss/audiodrv.h @@ -59,6 +59,9 @@ class Audio_OSS: public AudioBase static const char AUDIODEVICE[]; volatile int _audiofd; + int m_frameSize; + +private: void outOfOrder (); public: // --------------------------------------------------------- public @@ -68,7 +71,7 @@ class Audio_OSS: public AudioBase bool open (AudioConfig &cfg) override; void close () override; void reset () override; - bool write (uint_least32_t size) override; + bool write (uint_least32_t frames) override; void pause () override {} }; diff --git a/src/audio/out123/audiodrv.cpp b/src/audio/out123/audiodrv.cpp index cf80569..334ff33 100644 --- a/src/audio/out123/audiodrv.cpp +++ b/src/audio/out123/audiodrv.cpp @@ -77,11 +77,11 @@ bool Audio_OUT123::open(AudioConfig &cfg) throw error(out123_strerror(_audiofd)); } - cfg.bufSize = 8192; // FIXME make configurable? + m_frameSize = 2 * cfg.channels; try { - _sampleBuffer = new short[cfg.bufSize]; + _sampleBuffer = new short[cfg.bufSize * m_frameSize]; } catch (std::bad_alloc const &ba) { @@ -126,7 +126,7 @@ void Audio_OUT123::reset() } } -bool Audio_OUT123::write(uint_least32_t size) +bool Audio_OUT123::write(uint_least32_t frames) { if (_audiofd == nullptr) { @@ -134,7 +134,16 @@ bool Audio_OUT123::write(uint_least32_t size) return false; } - out123_play(_audiofd, _sampleBuffer, 2 * size); - // FIXME check return value? + size_t const bytes = static_cast(frames) * m_frameSize; + size_t res = out123_play(_audiofd, _sampleBuffer, bytes); + if (res < bytes) + { + int err = out123_errcode(_audiofd); + if (err != OUT123_OK) + { + setError(out123_plain_strerror(err)); + return false; + } + } return true; } diff --git a/src/audio/out123/audiodrv.h b/src/audio/out123/audiodrv.h index 346989b..cda0b55 100644 --- a/src/audio/out123/audiodrv.h +++ b/src/audio/out123/audiodrv.h @@ -37,6 +37,9 @@ class Audio_OUT123: public AudioBase private: // ------------------------------------------------------- private out123_handle *_audiofd; + int m_frameSize; + +private: void outOfOrder (); public: // --------------------------------------------------------- public @@ -46,7 +49,7 @@ class Audio_OUT123: public AudioBase bool open (AudioConfig &cfg) override; void close () override; void reset () override; - bool write (uint_least32_t size) override; + bool write (uint_least32_t frames) override; void pause () override {} }; diff --git a/src/audio/pulse/audiodrv.cpp b/src/audio/pulse/audiodrv.cpp index e083a7b..4867d36 100644 --- a/src/audio/pulse/audiodrv.cpp +++ b/src/audio/pulse/audiodrv.cpp @@ -71,8 +71,6 @@ bool Audio_Pulse::open(AudioConfig &cfg) throw error(pa_strerror(err)); } - cfg.bufSize = 4096; - try { _sampleBuffer = new short[cfg.bufSize]; @@ -82,6 +80,7 @@ bool Audio_Pulse::open(AudioConfig &cfg) throw error("Unable to allocate memory for sample buffers."); } + m_frameSize = 2 * cfg.channels; _settings = cfg; return true; @@ -115,7 +114,7 @@ void Audio_Pulse::close() } } -bool Audio_Pulse::write(uint_least32_t size) +bool Audio_Pulse::write(uint_least32_t frames) { if (_audioHandle == nullptr) { @@ -124,7 +123,8 @@ bool Audio_Pulse::write(uint_least32_t size) } int err; - if (pa_simple_write(_audioHandle, _sampleBuffer, size * 2, &err) < 0) + size_t const bytes = static_cast(frames) * m_frameSize; + if (pa_simple_write(_audioHandle, _sampleBuffer, bytes, &err) < 0) { setError(pa_strerror(err)); return false; diff --git a/src/audio/pulse/audiodrv.h b/src/audio/pulse/audiodrv.h index f2c6d5f..6472eea 100644 --- a/src/audio/pulse/audiodrv.h +++ b/src/audio/pulse/audiodrv.h @@ -39,6 +39,9 @@ class Audio_Pulse: public AudioBase { private: // ------------------------------------------------------- private pa_simple *_audioHandle; + int m_frameSize; + +private: void outOfOrder (); public: // --------------------------------------------------------- public @@ -48,7 +51,7 @@ class Audio_Pulse: public AudioBase bool open (AudioConfig &cfg) override; void close () override; void reset () override {} - bool write (uint_least32_t size) override; + bool write (uint_least32_t frames) override; void pause () override {} }; diff --git a/src/audio/wav/WavFile.cpp b/src/audio/wav/WavFile.cpp index 4c36cba..976d476 100644 --- a/src/audio/wav/WavFile.cpp +++ b/src/audio/wav/WavFile.cpp @@ -126,20 +126,21 @@ WavFile::WavFile(const std::string &name) : file(nullptr), headerWritten(false), hasListInfo(false), - precision(32) + m_precision(32) {} bool WavFile::open(AudioConfig &cfg) { - precision = cfg.precision; + m_precision = cfg.precision; + m_channels = cfg.channels; - unsigned short bits = precision; - unsigned short format = (precision == 16) ? 1 : 3; - unsigned short channels = cfg.channels; + unsigned short bits = m_precision; + unsigned short format = (m_precision == 16) ? 1 : 3; + unsigned short channels = m_channels; unsigned long freq = cfg.frequency; unsigned short blockAlign = (bits>>3)*channels; unsigned long bufSize = freq * blockAlign; - cfg.bufSize = bufSize; + cfg.bufSize = freq; if (name.empty()) return false; @@ -183,10 +184,11 @@ bool WavFile::open(AudioConfig &cfg) return true; } -bool WavFile::write(uint_least32_t size) +bool WavFile::write(uint_least32_t frames) { if (file && !file->fail()) { + uint_least32_t size = frames * m_channels; unsigned long int bytes = size; if (!headerWritten) { @@ -198,7 +200,7 @@ bool WavFile::write(uint_least32_t size) } /* XXX endianness... */ - if (precision == 16) + if (m_precision == 16) { bytes *= 2; file->write((char*)_sampleBuffer, bytes); diff --git a/src/audio/wav/WavFile.h b/src/audio/wav/WavFile.h index 75a4f86..73a392a 100644 --- a/src/audio/wav/WavFile.h +++ b/src/audio/wav/WavFile.h @@ -96,7 +96,8 @@ class WavFile: public AudioBase std::ostream *file; bool headerWritten; bool hasListInfo; - int precision; + int m_precision; + int m_channels; public: WavFile(const std::string &name); @@ -114,7 +115,7 @@ class WavFile: public AudioBase // After write call old buffer is invalid and you should // use the new buffer provided instead. - bool write(uint_least32_t size) override; + bool write(uint_least32_t frames) override; void close() override; void pause() override {} void reset() override {} diff --git a/src/player.cpp b/src/player.cpp index 42a3b31..7dcd304 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -378,6 +378,7 @@ ConsolePlayer::ConsolePlayer (const char * const name) : m_engCfg.fastSampling = emulation.fastSampling; m_channels = audio.channels; m_precision = audio.precision; + m_buffer_size = audio.getBufSize(); m_filter.enabled = emulation.filter; m_filter.bias = emulation.bias; m_filter.filterCurve6581 = emulation.filterCurve6581; @@ -542,9 +543,9 @@ bool ConsolePlayer::createOutput (output_t driver, const SidTuneInfo *tuneInfo) // Configure with user settings m_driver.cfg.frequency = m_engCfg.frequency; - m_driver.cfg.channels = m_channels ? m_channels : tuneChannels; + m_driver.cfg.channels = m_channels ? m_channels : tuneChannels; m_driver.cfg.precision = m_precision; - m_driver.cfg.bufSize = 0; // Recalculate + m_driver.cfg.bufSize = m_buffer_size; { // Open the hardware bool err = false; @@ -942,27 +943,32 @@ void ConsolePlayer::emuflush () // Out play loop to be externally called bool ConsolePlayer::play() { - uint_least32_t retSize = 0; + uint_least32_t frames = 0; if (m_state == playerRunning) { updateDisplay(); // Fill buffer short *buffer = m_driver.selected->buffer(); - const uint_least32_t length = getBufSize(); - retSize = m_engine.play(buffer, length); - if ((retSize < length) || !m_engine.isPlaying()) + // getBufSize returns the number of frames + // multiply by number of channels to get the count of 16bit samples + const uint_least32_t length = getBufSize() * m_driver.cfg.channels; + uint_least32_t samples = m_engine.play(buffer, length); + if ((samples < length) || !m_engine.isPlaying()) { cerr << m_engine.error(); m_state = playerError; return false; } + // m_engine.play returns the number of 16bit samples + // divide by number of channels to get the count of frames + frames = samples / m_driver.cfg.channels; } switch (m_state) { case playerRunning: - if (!m_driver.selected->write(retSize)) + if (!m_driver.selected->write(frames)) { cerr << m_driver.selected->getErrorString(); m_state = playerError; @@ -1020,7 +1026,7 @@ uint_least32_t ConsolePlayer::getBufSize() { // Switch audio drivers. m_timer.starting = false; m_driver.selected = m_driver.device; - memset(m_driver.selected->buffer (), 0, m_driver.cfg.bufSize); + memset(m_driver.selected->buffer (), 0, m_driver.cfg.bufSize); // FIXME m_speed.current = 1; m_engine.fastForward(100); if (m_cpudebug) @@ -1048,8 +1054,8 @@ uint_least32_t ConsolePlayer::getBufSize() } else { - uint_least32_t remaining = m_timer.stop - m_timer.current; - uint_least32_t bufSize = remaining * m_driver.cfg.bytesPerMillis(); + uint_least32_t remainingMs = m_timer.stop - m_timer.current; + uint_least32_t bufSize = (remainingMs * m_driver.cfg.frequency) / 1000; if (bufSize < m_driver.cfg.bufSize) return bufSize; } diff --git a/src/player.h b/src/player.h index a5c6f71..399d06f 100644 --- a/src/player.h +++ b/src/player.h @@ -182,6 +182,7 @@ class ConsolePlayer int m_channels; int m_precision; + int m_buffer_size; struct m_filter_t {