=== modified file 'mixxx/src/dlgprefsound.cpp' --- mixxx/src/dlgprefsound.cpp 2012-09-04 21:04:36 +0000 +++ mixxx/src/dlgprefsound.cpp 2012-09-11 18:27:02 +0000 @@ -62,9 +62,9 @@ connect(sampleRateComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(sampleRateChanged(int))); connect(sampleRateComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(updateLatencies(int))); - connect(latencyComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(latencyChanged(int))); + this, SLOT(updateAudioBufferSizes(int))); + connect(audioBufferComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(audioBufferChanged(int))); initializePaths(); loadSettings(); @@ -73,7 +73,7 @@ this, SLOT(settingChanged())); connect(sampleRateComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(settingChanged())); - connect(latencyComboBox, SIGNAL(currentIndexChanged(int)), + connect(audioBufferComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(settingChanged())); connect(queryButton, SIGNAL(clicked()), @@ -95,10 +95,16 @@ new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Master]", "underflow_count"))); connect(m_pMasterUnderflowCount, SIGNAL(valueChanged(double)), this, SLOT(bufferUnderflow(double))); + + m_pMasterLatency = + new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Master]", "latency"))); + connect(m_pMasterLatency, SIGNAL(valueChanged(double)), + this, SLOT(masterLatencyChanged(double))); } DlgPrefSound::~DlgPrefSound() { delete m_pMasterUnderflowCount; + delete m_pMasterLatency; } /** @@ -290,16 +296,16 @@ int sampleRateIndex = sampleRateComboBox->findData(m_config.getSampleRate()); if (sampleRateIndex != -1) { sampleRateComboBox->setCurrentIndex(sampleRateIndex); - if (latencyComboBox->count() <= 0) { - updateLatencies(sampleRateIndex); // so the latency combo box is + if (audioBufferComboBox->count() <= 0) { + updateAudioBufferSizes(sampleRateIndex); // so the latency combo box is // sure to be populated, if setCurrentIndex is called with the // currentIndex, the currentIndexChanged signal won't fire and // the updateLatencies slot won't run -- bkgood lp bug 689373 } } - int latencyIndex = latencyComboBox->findData(m_config.getLatency()); - if (latencyIndex != -1) { - latencyComboBox->setCurrentIndex(latencyIndex); + int sizeIndex = audioBufferComboBox->findData(m_config.getAudioBufferSizeIndex()); + if (sizeIndex != -1) { + audioBufferComboBox->setCurrentIndex(sizeIndex); } emit(loadPaths(m_config)); m_loading = false; @@ -317,10 +323,10 @@ // JACK sets its own latency if (m_config.getAPI() == MIXXX_PORTAUDIO_JACK_STRING) { latencyLabel->setEnabled(false); - latencyComboBox->setEnabled(false); + audioBufferComboBox->setEnabled(false); } else { latencyLabel->setEnabled(true); - latencyComboBox->setEnabled(true); + audioBufferComboBox->setEnabled(true); } } @@ -357,42 +363,41 @@ * Slot called when the latency combo box is changed to update the * latency in the config. */ -void DlgPrefSound::latencyChanged(int index) { - m_config.setLatency( - latencyComboBox->itemData(index).toUInt()); +void DlgPrefSound::audioBufferChanged(int index) { + m_config.setAudioBufferSizeIndex( + audioBufferComboBox->itemData(index).toUInt()); } -/** - * Slot called whenever the selected sample rate is changed. Populates the - * latency input box with SMConfig::kMaxLatency values, starting at 1ms, - * representing a number of frames per buffer, which will always be a power - * of 2 (so the values displayed in ms won't be constant between sample rates, - * but they'll be close). - */ -void DlgPrefSound::updateLatencies(int sampleRateIndex) { + +// Slot called whenever the selected sample rate is changed. Populates the +// audio buffer input box with SMConfig::kMaxLatency values, starting at 1ms, +// representing a number of frames per buffer, which will always be a power +// of 2 (so the values displayed in ms won't be constant between sample rates, +// but they'll be close). +void DlgPrefSound::updateAudioBufferSizes(int sampleRateIndex) { double sampleRate = sampleRateComboBox->itemData(sampleRateIndex).toDouble(); - int oldLatency = latencyComboBox->currentIndex(); + int oldSizeIndex = audioBufferComboBox->currentIndex(); unsigned int framesPerBuffer = 1; // start this at 0 and inf loop happens - // we don't want to display any sub-1ms latencies (well maybe we do but I + // we don't want to display any sub-1ms buffer sizes (well maybe we do but I // don't right now!), so we iterate over all the buffer sizes until we - // find the first that gives us a latency >= 1 ms -- bkgood + // find the first that gives us a buffer size >= 1 ms -- bkgood // no div-by-0 in the next line because we don't allow srates of 0 in our // srate list when we construct it in the ctor -- bkgood for (; framesPerBuffer / sampleRate * 1000 < 1.0; framesPerBuffer *= 2); - latencyComboBox->clear(); - for (unsigned int i = 0; i < SoundManagerConfig::kMaxLatency; ++i) { + audioBufferComboBox->clear(); + for (unsigned int i = 0; i < SoundManagerConfig::kMaxAudioBufferSizeIndex; ++i) { float latency = framesPerBuffer / sampleRate * 1000; // i + 1 in the next line is a latency index as described in SSConfig - latencyComboBox->addItem(QString(tr("%1 ms")).arg(latency,0,'g',3), i + 1); + audioBufferComboBox->addItem(QString(tr("%1 ms")).arg(latency,0,'g',3), i + 1); framesPerBuffer <<= 1; // *= 2 } - if (oldLatency < latencyComboBox->count() && oldLatency >= 0) { - latencyComboBox->setCurrentIndex(oldLatency); + if (oldSizeIndex < audioBufferComboBox->count() && oldSizeIndex >= 0) { + audioBufferComboBox->setCurrentIndex(oldSizeIndex); } else { // set it to the max, let the user dig if they need better latency. better // than having a user get the pops on first use and thinking poorly of mixxx // because of it -- bkgood - latencyComboBox->setCurrentIndex(latencyComboBox->count() - 1); + audioBufferComboBox->setCurrentIndex(audioBufferComboBox->count() - 1); } } @@ -450,4 +455,8 @@ update(); } +void DlgPrefSound::masterLatencyChanged(double latency) { + currentLatency->setText(QString("%1 ms").arg(latency)); + update(); +} === modified file 'mixxx/src/dlgprefsound.h' --- mixxx/src/dlgprefsound.h 2012-09-04 21:04:36 +0000 +++ mixxx/src/dlgprefsound.h 2012-09-11 18:27:21 +0000 @@ -56,6 +56,7 @@ void slotApply(); // called on ok button void forceApply(); // called by DlgPrefVinyl to make slotApply call setupDevices void bufferUnderflow(double count); + void masterLatencyChanged(double latency); private: void initializePaths(); void connectSoundItem(DlgPrefSoundItem *item); @@ -65,6 +66,7 @@ PlayerManager *m_pPlayerManager; ConfigObject *m_pConfig; ControlObjectThreadMain* m_pMasterUnderflowCount; + ControlObjectThreadMain* m_pMasterLatency; QList m_inputDevices; QList m_outputDevices; bool m_settingsModified; @@ -78,8 +80,8 @@ void apiChanged(int index); void updateAPIs(); void sampleRateChanged(int index); - void latencyChanged(int index); - void updateLatencies(int sampleRateIndex); + void audioBufferChanged(int index); + void updateAudioBufferSizes(int sampleRateIndex); void refreshDevices(); void settingChanged(); void queryClicked(); === modified file 'mixxx/src/dlgprefsounddlg.ui' --- mixxx/src/dlgprefsounddlg.ui 2012-09-04 21:04:36 +0000 +++ mixxx/src/dlgprefsounddlg.ui 2012-09-11 18:44:09 +0000 @@ -6,8 +6,8 @@ 0 0 - 520 - 300 + 729 + 363 @@ -20,7 +20,7 @@ Sound API - + apiComboBox @@ -37,7 +37,7 @@ Sample Rate - + sampleRateComboBox @@ -48,19 +48,40 @@ + + + + + Audio Buffer + + + audioBufferComboBox + + + + + + + + + - Latency + Known Latency - - latencyComboBox + + audioBufferComboBox - + + + 20 ms + + @@ -71,6 +92,9 @@ Buffer Underflow Count + + audioBufferComboBox + @@ -82,6 +106,9 @@ + + + @@ -94,8 +121,8 @@ <ul> -<li>Increase your latency if the underflow counter is increasing or you hear pops during playback</li> -<li>Reduce your latency to improve Mixxx's responsiveness</li> +<li>Enlarge your audio buffer if the underflow counter is increasing or you hear pops during playback</li> +<li>Downsize your audio buffer to improve Mixxx's responsiveness</li> </ul> === modified file 'mixxx/src/engine/enginemaster.cpp' --- mixxx/src/engine/enginemaster.cpp 2012-09-06 23:30:46 +0000 +++ mixxx/src/engine/enginemaster.cpp 2012-09-11 18:30:35 +0000 @@ -52,8 +52,8 @@ m_pMasterSampleRate = new ControlObject(ConfigKey(group, "samplerate")); m_pMasterSampleRate->set(44100.); - // Latency control m_pMasterLatency = new ControlObject(ConfigKey(group, "latency")); + m_pMasterAudioBufferSize = new ControlObject(ConfigKey(group, "audio_buffer_size")); m_pMasterUnderflowCount = new ControlObject(ConfigKey(group, "underflow_count")); // Master rate @@ -137,6 +137,7 @@ delete m_pMasterSampleRate; delete m_pMasterLatency; + delete m_pMasterAudioBufferSize; delete m_pMasterRate; SampleUtil::free(m_pHead); === modified file 'mixxx/src/engine/enginemaster.h' --- mixxx/src/engine/enginemaster.h 2012-09-06 23:30:46 +0000 +++ mixxx/src/engine/enginemaster.h 2012-09-11 18:31:12 +0000 @@ -140,6 +140,7 @@ ControlObject* m_pHeadVolume; ControlObject* m_pMasterSampleRate; ControlObject* m_pMasterLatency; + ControlObject* m_pMasterAudioBufferSize; ControlObject* m_pMasterUnderflowCount; ControlPotmeter* m_pMasterRate; EngineClipping *clipping, *head_clipping; === modified file 'mixxx/src/sounddeviceportaudio.cpp' --- mixxx/src/sounddeviceportaudio.cpp 2012-09-04 21:04:36 +0000 +++ mixxx/src/sounddeviceportaudio.cpp 2012-09-11 18:32:44 +0000 @@ -110,8 +110,8 @@ //Get latency in milleseconds qDebug() << "framesPerBuffer:" << m_framesPerBuffer; - double latencyMSec = m_framesPerBuffer / m_dSampleRate * 1000; - qDebug() << "Requested sample rate: " << m_dSampleRate << "Hz, latency:" << latencyMSec << "ms"; + double bufferMSec = m_framesPerBuffer / m_dSampleRate * 1000; + qDebug() << "Requested sample rate: " << m_dSampleRate << "Hz, latency:" << bufferMSec << "ms"; qDebug() << "Output channels:" << m_outputParams.channelCount << "| Input channels:" << m_inputParams.channelCount; @@ -151,12 +151,12 @@ //Fill out the rest of the info. m_outputParams.device = m_devId; m_outputParams.sampleFormat = paFloat32; - m_outputParams.suggestedLatency = latencyMSec / 1000.0; + m_outputParams.suggestedLatency = bufferMSec / 1000.0; m_outputParams.hostApiSpecificStreamInfo = NULL; m_inputParams.device = m_devId; m_inputParams.sampleFormat = paInt16; //This is how our vinyl control stuff like samples. - m_inputParams.suggestedLatency = latencyMSec / 1000.0; + m_inputParams.suggestedLatency = bufferMSec / 1000.0; m_inputParams.hostApiSpecificStreamInfo = NULL; qDebug() << "Opening stream with id" << m_devId; @@ -218,8 +218,8 @@ // Get the actual details of the stream & update Mixxx's data const PaStreamInfo* streamDetails = Pa_GetStreamInfo(m_pStream); m_dSampleRate = streamDetails->sampleRate; - latencyMSec = streamDetails->outputLatency*1000; - qDebug() << " Actual sample rate: " << m_dSampleRate << "Hz, latency:" << latencyMSec << "ms"; + double currentLatencyMSec = streamDetails->outputLatency * 1000; + qDebug() << " Actual sample rate: " << m_dSampleRate << "Hz, latency:" << currentLatencyMSec << "ms"; //Update the samplerate and latency ControlObjects, which allow the waveform view to properly correct //for the latency. @@ -228,14 +228,17 @@ new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Master]","samplerate"))); ControlObjectThreadMain* pControlObjectLatency = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Master]","latency"))); + ControlObjectThreadMain* pControlObjectAudioBufferSize = + new ControlObjectThreadMain(ControlObject::getControl(ConfigKey("[Master]","audio_buffer_size"))); if (m_pMasterUnderflowCount == NULL) { m_pMasterUnderflowCount = ControlObject::getControl(ConfigKey("[Master]", "underflow_count")); } ControlObjectThreadMain* pMasterUnderflowCount = new ControlObjectThreadMain(m_pMasterUnderflowCount); - pControlObjectLatency->slotSet(latencyMSec); + pControlObjectLatency->slotSet(currentLatencyMSec); pControlObjectSampleRate->slotSet(m_dSampleRate); + pControlObjectAudioBufferSize->slotSet(bufferMSec); pMasterUnderflowCount->slotSet(0); //qDebug() << "SampleRate" << pControlObjectSampleRate->get(); @@ -243,6 +246,7 @@ delete pControlObjectLatency; delete pControlObjectSampleRate; + delete pControlObjectAudioBufferSize; delete pMasterUnderflowCount; return OK; } === modified file 'mixxx/src/soundmanagerconfig.cpp' --- mixxx/src/soundmanagerconfig.cpp 2012-09-06 07:15:26 +0000 +++ mixxx/src/soundmanagerconfig.cpp 2012-09-11 18:36:41 +0000 @@ -20,17 +20,17 @@ #include "mixxx.h" // this (7) represents latency values from 1 ms to about 80 ms -- bkgood -const unsigned int SoundManagerConfig::kMaxLatency = 7; +const unsigned int SoundManagerConfig::kMaxAudioBufferSizeIndex = 7; const QString SoundManagerConfig::kDefaultAPI = QString("None"); const unsigned int SoundManagerConfig::kDefaultSampleRate = 48000; -// latency=5 means about 21 ms of latency which is default in trunk r2453 -- bkgood -const int SoundManagerConfig::kDefaultLatency = 5; +// audioBufferSizeIndex=5 means about 21 ms of latency which is default in trunk r2453 -- bkgood +const int SoundManagerConfig::kDefaultAudioBufferSizeIndex = 5; SoundManagerConfig::SoundManagerConfig() : m_api("None") , m_sampleRate(kDefaultSampleRate) - , m_latency(kDefaultLatency) { + , m_audioBufferSizeIndex(kDefaultAudioBufferSizeIndex) { m_configFile = QFileInfo(CmdlineArgs::Instance().getSettingsPath() + SOUNDMANAGERCONFIG_FILENAME); } @@ -59,7 +59,8 @@ rootElement = doc.documentElement(); setAPI(rootElement.attribute("api")); setSampleRate(rootElement.attribute("samplerate", "0").toUInt()); - setLatency(rootElement.attribute("latency", "0").toUInt()); + // audioBufferSizeIndex is refereed as "latency" in the config file + setAudioBufferSizeIndex(rootElement.attribute("latency", "0").toUInt()); clearOutputs(); clearInputs(); QDomNodeList devElements(rootElement.elementsByTagName("SoundDevice")); @@ -111,7 +112,8 @@ QDomElement docElement(doc.createElement("SoundManagerConfig")); docElement.setAttribute("api", m_api); docElement.setAttribute("samplerate", m_sampleRate); - docElement.setAttribute("latency", m_latency); + // audioBufferSizeIndex is refereed as "latency" in the config file + docElement.setAttribute("latency", m_audioBufferSizeIndex); doc.appendChild(docElement); foreach (QString device, m_outputs.keys().toSet().unite(m_inputs.keys().toSet())) { QDomElement devElement(doc.createElement("SoundDevice")); @@ -182,35 +184,34 @@ return true; } -unsigned int SoundManagerConfig::getLatency() const { - return m_latency; +unsigned int SoundManagerConfig::getAudioBufferSizeIndex() const { + return m_audioBufferSizeIndex; } unsigned int SoundManagerConfig::getFramesPerBuffer() const { - Q_ASSERT(m_latency > 0); // endless loop otherwise + Q_ASSERT(m_audioBufferSizeIndex > 0); // endless loop otherwise unsigned int framesPerBuffer = 1; double sampleRate = m_sampleRate; // need this to avoid int division // first, get to the framesPerBuffer value corresponding to latency index 1 for (; framesPerBuffer / sampleRate * 1000 < 1.0; framesPerBuffer *= 2); // then, keep going until we get to our desired latency index (if not 1) - for (unsigned int latencyIndex = 1; latencyIndex < m_latency; ++latencyIndex) { + for (unsigned int latencyIndex = 1; latencyIndex < m_audioBufferSizeIndex; ++latencyIndex) { framesPerBuffer <<= 1; // *= 2 } return framesPerBuffer; } -/** - * Set the latency value. - * @warning This IS NOT a value in milliseconds, or a number of frames per - * buffer. It is an index, where 1 is the first power-of-two buffer size (in - * frames) which corresponds to a latency greater than or equal to 1 ms, 2 is - * the second, etc. This is so that latency values are roughly equivalent - * between different sample rates. - */ -void SoundManagerConfig::setLatency(unsigned int latency) { - // latency should be either the min of kMaxLatency and the passed value + +// Set the audio buffer size +// @warning This IS NOT a value in milliseconds, or a number of frames per +// buffer. It is an index, where 1 is the first power-of-two buffer size (in +// frames) which corresponds to a latency greater than or equal to 1 ms, 2 is +// the second, etc. This is so that latency values are roughly equivalent +// between different sample rates. +void SoundManagerConfig::setAudioBufferSizeIndex(unsigned int sizeIndex) { + // latency should be either the min of kMaxAudioBufferSizeIndex and the passed value // if it's 0, pretend it was 1 -- bkgood - m_latency = latency != 0 ? math_min(latency, kMaxLatency) : 1; + m_audioBufferSizeIndex = sizeIndex != 0 ? math_min(sizeIndex, kMaxAudioBufferSizeIndex) : 1; } void SoundManagerConfig::addOutput(const QString &device, const AudioOutput &out) { @@ -337,6 +338,6 @@ qWarning() << "got empty sample rate list from SoundManager, this is a bug"; m_sampleRate = kDefaultSampleRate; } - m_latency = kDefaultLatency; + m_audioBufferSizeIndex = kDefaultAudioBufferSizeIndex; } } === modified file 'mixxx/src/soundmanagerconfig.h' --- mixxx/src/soundmanagerconfig.h 2011-07-19 17:16:39 +0000 +++ mixxx/src/soundmanagerconfig.h 2012-09-11 18:34:20 +0000 @@ -35,10 +35,10 @@ OTHER = (1 << 2), ALL = (API | DEVICES | OTHER), }; - static const unsigned int kMaxLatency; + static const unsigned int kMaxAudioBufferSizeIndex; static const QString kDefaultAPI; static const unsigned int kDefaultSampleRate; - static const int kDefaultLatency; + static const int kDefaultAudioBufferSizeIndex; SoundManagerConfig(); ~SoundManagerConfig(); @@ -50,9 +50,9 @@ unsigned int getSampleRate() const; void setSampleRate(unsigned int sampleRate); bool checkSampleRate(const SoundManager &soundManager); - unsigned int getLatency() const; + unsigned int getAudioBufferSizeIndex() const; unsigned int getFramesPerBuffer() const; - void setLatency(unsigned int latency); + void setAudioBufferSizeIndex(unsigned int latency); void addOutput(const QString &device, const AudioOutput &out); void addInput(const QString &device, const AudioInput &in); QMultiHash getOutputs() const; @@ -73,7 +73,7 @@ // higher indices represent subsequently higher latencies (storing // latency as milliseconds or frames per buffer is bad because those // values vary with sample rate) -- bkgood - unsigned int m_latency; + unsigned int m_audioBufferSizeIndex; QMultiHash m_outputs; QMultiHash m_inputs; };