Mercurial > hg > svapp
changeset 578:702272b78bbe 3.0-integration
Merge
author | Chris Cannam |
---|---|
date | Wed, 04 Jan 2017 19:10:32 +0000 |
parents | 58354f2934ec (current diff) c2e27ad7f408 (diff) |
children | 1a8a8980f39a |
files | audio/AudioRecordTarget.cpp audio/AudioRecordTarget.h framework/MainWindowBase.cpp framework/MainWindowBase.h |
diffstat | 9 files changed, 503 insertions(+), 338 deletions(-) [+] |
line wrap: on
line diff
--- a/audio/AudioCallbackPlaySource.cpp Wed Jan 04 18:54:50 2017 +0000 +++ b/audio/AudioCallbackPlaySource.cpp Wed Jan 04 19:10:32 2017 +0000 @@ -235,6 +235,8 @@ } if (!m_writeBuffers || (int)m_writeBuffers->size() < getTargetChannelCount()) { + cerr << "m_writeBuffers size = " << (m_writeBuffers ? m_writeBuffers->size() : 0) << endl; + cerr << "target channel count = " << (getTargetChannelCount()) << endl; clearRingBuffers(true, getTargetChannelCount()); buffersIncreased = true; } else { @@ -270,8 +272,13 @@ m_audioGenerator->setTargetChannelCount(getTargetChannelCount()); if (buffersIncreased) { - SVDEBUG << "AudioCallbackPlaySource::addModel: Number of buffers increased, signalling channelCountIncreased" << endl; - emit channelCountIncreased(); + SVDEBUG << "AudioCallbackPlaySource::addModel: Number of buffers increased to " << getTargetChannelCount() << endl; + if (getTargetChannelCount() > getDeviceChannelCount()) { + SVDEBUG << "AudioCallbackPlaySource::addModel: This is more than the device channel count, signalling channelCountIncreased" << endl; + emit channelCountIncreased(getTargetChannelCount()); + } else { + SVDEBUG << "AudioCallbackPlaySource::addModel: This is no more than the device channel count (" << getDeviceChannelCount() << "), so taking no action" << endl; + } } if (!m_fillThread) { @@ -958,8 +965,8 @@ void AudioCallbackPlaySource::setOutputLevels(float left, float right) { - m_outputLeft = left; - m_outputRight = right; + if (left > m_outputLeft) m_outputLeft = left; + if (right > m_outputRight) m_outputRight = right; } bool @@ -967,6 +974,8 @@ { left = m_outputLeft; right = m_outputRight; + m_outputLeft = 0.f; + m_outputRight = 0.f; return true; }
--- a/audio/AudioCallbackPlaySource.h Wed Jan 04 18:54:50 2017 +0000 +++ b/audio/AudioCallbackPlaySource.h Wed Jan 04 19:10:32 2017 +0000 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef AUDIO_CALLBACK_PLAY_SOURCE_H -#define AUDIO_CALLBACK_PLAY_SOURCE_H +#ifndef SV_AUDIO_CALLBACK_PLAY_SOURCE_H +#define SV_AUDIO_CALLBACK_PLAY_SOURCE_H #include "base/RingBuffer.h" #include "base/AudioPlaySource.h" @@ -304,7 +304,7 @@ sv_samplerate_t available, bool willResample); - void channelCountIncreased(); + void channelCountIncreased(int count); // target channel count (see getTargetChannelCount()) void audioOverloadPluginDisabled(); void audioTimeStretchMultiChannelDisabled();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/AudioCallbackRecordTarget.cpp Wed Jan 04 19:10:32 2017 +0000 @@ -0,0 +1,318 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "AudioCallbackRecordTarget.h" + +#include "base/ViewManagerBase.h" +#include "base/TempDirectory.h" + +#include "data/model/WritableWaveFileModel.h" + +#include <QDir> +#include <QTimer> + +AudioCallbackRecordTarget::AudioCallbackRecordTarget(ViewManagerBase *manager, + QString clientName) : + m_viewManager(manager), + m_clientName(clientName.toUtf8().data()), + m_recording(false), + m_recordSampleRate(44100), + m_recordChannelCount(2), + m_frameCount(0), + m_model(0), + m_buffers(0), + m_bufferCount(0), + m_inputLeft(0.f), + m_inputRight(0.f) +{ + m_viewManager->setAudioRecordTarget(this); + + connect(this, SIGNAL(recordStatusChanged(bool)), + m_viewManager, SLOT(recordStatusChanged(bool))); + + recreateBuffers(); +} + +AudioCallbackRecordTarget::~AudioCallbackRecordTarget() +{ + m_viewManager->setAudioRecordTarget(0); + + QMutexLocker locker(&m_bufPtrMutex); + for (int c = 0; c < m_bufferCount; ++c) { + delete m_buffers[c]; + } + delete[] m_buffers; +} + +void +AudioCallbackRecordTarget::recreateBuffers() +{ + static int bufferSize = 441000; + + int count = m_recordChannelCount; + + if (count > m_bufferCount) { + + RingBuffer<float> **newBuffers = new RingBuffer<float> *[count]; + for (int c = 0; c < m_bufferCount; ++c) { + newBuffers[c] = m_buffers[c]; + } + for (int c = m_bufferCount; c < count; ++c) { + newBuffers[c] = new RingBuffer<float>(bufferSize); + } + + // This is the only place where m_buffers is rewritten and + // should be the only possible source of contention against + // putSamples for this mutex (as the model-updating code is + // supposed to run in the same thread as this) + QMutexLocker locker(&m_bufPtrMutex); + delete[] m_buffers; + m_buffers = newBuffers; + m_bufferCount = count; + } +} + +int +AudioCallbackRecordTarget::getApplicationSampleRate() const +{ + return 0; // don't care +} + +int +AudioCallbackRecordTarget::getApplicationChannelCount() const +{ + return m_recordChannelCount; +} + +void +AudioCallbackRecordTarget::setSystemRecordBlockSize(int) +{ +} + +void +AudioCallbackRecordTarget::setSystemRecordSampleRate(int n) +{ + m_recordSampleRate = n; +} + +void +AudioCallbackRecordTarget::setSystemRecordLatency(int) +{ +} + +void +AudioCallbackRecordTarget::setSystemRecordChannelCount(int c) +{ + m_recordChannelCount = c; + recreateBuffers(); +} + +void +AudioCallbackRecordTarget::putSamples(const float *const *samples, int, int nframes) +{ + // This may be called from RT context, and in a different thread + // from everything else in this class. It takes a mutex that + // should almost never be contended (see recreateBuffers()) + if (!m_recording) return; + + QMutexLocker locker(&m_bufPtrMutex); + if (m_buffers && m_bufferCount >= m_recordChannelCount) { + for (int c = 0; c < m_recordChannelCount; ++c) { + m_buffers[c]->write(samples[c], nframes); + } + } +} + +void +AudioCallbackRecordTarget::updateModel() +{ + bool secChanged = false; + sv_frame_t frameToEmit = 0; + + int nframes = 0; + for (int c = 0; c < m_recordChannelCount; ++c) { + if (c == 0 || m_buffers[c]->getReadSpace() < nframes) { + nframes = m_buffers[c]->getReadSpace(); + } + } + + if (nframes == 0) { + return; + } + + float **samples = new float *[m_recordChannelCount]; + for (int c = 0; c < m_recordChannelCount; ++c) { + samples[c] = new float[nframes]; + m_buffers[c]->read(samples[c], nframes); + } + + m_model->addSamples(samples, nframes); + + for (int c = 0; c < m_recordChannelCount; ++c) { + delete[] samples[c]; + } + delete[] samples; + + sv_frame_t priorFrameCount = m_frameCount; + m_frameCount += nframes; + + RealTime priorRT = + RealTime::frame2RealTime(priorFrameCount, m_recordSampleRate); + + RealTime postRT = + RealTime::frame2RealTime(m_frameCount, m_recordSampleRate); + + secChanged = (postRT.sec > priorRT.sec); + if (secChanged) { + m_model->updateModel(); + frameToEmit = m_frameCount; + } + + if (secChanged) { + emit recordDurationChanged(frameToEmit, m_recordSampleRate); + } + + if (m_recording) { + QTimer::singleShot(1000, this, SLOT(updateModel())); + } +} + +void +AudioCallbackRecordTarget::setInputLevels(float left, float right) +{ + if (left > m_inputLeft) m_inputLeft = left; + if (right > m_inputRight) m_inputRight = right; +} + +bool +AudioCallbackRecordTarget::getInputLevels(float &left, float &right) +{ + left = m_inputLeft; + right = m_inputRight; + m_inputLeft = 0.f; + m_inputRight = 0.f; + return true; +} + +void +AudioCallbackRecordTarget::modelAboutToBeDeleted() +{ + if (sender() == m_model) { + m_model = 0; + m_recording = false; + } +} + +QString +AudioCallbackRecordTarget::getRecordContainerFolder() +{ + QDir parent(TempDirectory::getInstance()->getContainingPath()); + QString subdirname("recorded"); + + if (!parent.mkpath(subdirname)) { + SVCERR << "ERROR: AudioCallbackRecordTarget::getRecordContainerFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; + return ""; + } else { + return parent.filePath(subdirname); + } +} + +QString +AudioCallbackRecordTarget::getRecordFolder() +{ + QDir parent(getRecordContainerFolder()); + QDateTime now = QDateTime::currentDateTime(); + QString subdirname = QString("%1").arg(now.toString("yyyyMMdd")); + + if (!parent.mkpath(subdirname)) { + SVCERR << "ERROR: AudioCallbackRecordTarget::getRecordFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; + return ""; + } else { + return parent.filePath(subdirname); + } +} + +WritableWaveFileModel * +AudioCallbackRecordTarget::startRecording() +{ + if (m_recording) { + SVCERR << "WARNING: AudioCallbackRecordTarget::startRecording: We are already recording" << endl; + return 0; + } + + m_model = 0; + m_frameCount = 0; + + QString folder = getRecordFolder(); + if (folder == "") return 0; + QDir recordedDir(folder); + + QDateTime now = QDateTime::currentDateTime(); + + // Don't use QDateTime::toString(Qt::ISODate) as the ":" character + // isn't permitted in filenames on Windows + QString nowString = now.toString("yyyyMMdd-HHmmss-zzz"); + + QString filename = tr("recorded-%1.wav").arg(nowString); + QString label = tr("Recorded %1").arg(nowString); + + m_audioFileName = recordedDir.filePath(filename); + + m_model = new WritableWaveFileModel(m_recordSampleRate, + m_recordChannelCount, + m_audioFileName); + + if (!m_model->isOK()) { + SVCERR << "ERROR: AudioCallbackRecordTarget::startRecording: Recording failed" + << endl; + //!!! and throw? + delete m_model; + m_model = 0; + return 0; + } + + m_model->setObjectName(label); + m_recording = true; + + emit recordStatusChanged(true); + + QTimer::singleShot(1000, this, SLOT(updateModel())); + + return m_model; +} + +void +AudioCallbackRecordTarget::stopRecording() +{ + if (!m_recording) { + SVCERR << "WARNING: AudioCallbackRecordTarget::startRecording: Not recording" << endl; + return; + } + + m_recording = false; + + m_bufPtrMutex.lock(); + m_bufPtrMutex.unlock(); + + // buffers should now be up-to-date + updateModel(); + + m_model->writeComplete(); + m_model = 0; + + emit recordStatusChanged(false); + emit recordCompleted(); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/AudioCallbackRecordTarget.h Wed Jan 04 19:10:32 2017 +0000 @@ -0,0 +1,98 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_AUDIO_CALLBACK_RECORD_TARGET_H +#define SV_AUDIO_CALLBACK_RECORD_TARGET_H + +#include "base/AudioRecordTarget.h" + +#include <bqaudioio/ApplicationRecordTarget.h> + +#include <string> +#include <atomic> + +#include <QObject> +#include <QMutex> + +#include "base/BaseTypes.h" +#include "base/RingBuffer.h" + +class ViewManagerBase; +class WritableWaveFileModel; + +class AudioCallbackRecordTarget : public QObject, + public AudioRecordTarget, + public breakfastquay::ApplicationRecordTarget +{ + Q_OBJECT + +public: + AudioCallbackRecordTarget(ViewManagerBase *, QString clientName); + virtual ~AudioCallbackRecordTarget(); + + virtual std::string getClientName() const override { return m_clientName; } + + virtual int getApplicationSampleRate() const override; + virtual int getApplicationChannelCount() const override; + + virtual void setSystemRecordBlockSize(int) override; + virtual void setSystemRecordSampleRate(int) override; + virtual void setSystemRecordLatency(int) override; + virtual void setSystemRecordChannelCount(int) override; + + virtual void putSamples(const float *const *samples, int nchannels, int nframes) override; + + virtual void setInputLevels(float peakLeft, float peakRight) override; + + virtual void audioProcessingOverload() override { } + + QString getRecordContainerFolder(); + QString getRecordFolder(); + + virtual bool isRecording() const override { return m_recording; } + virtual sv_frame_t getRecordDuration() const override { return m_frameCount; } + + virtual bool getInputLevels(float &left, float &right) override; + + WritableWaveFileModel *startRecording(); // caller takes ownership of model + void stopRecording(); + +signals: + void recordStatusChanged(bool recording); + void recordDurationChanged(sv_frame_t, sv_samplerate_t); // emitted occasionally + void recordCompleted(); + +protected slots: + void modelAboutToBeDeleted(); + void updateModel(); + +private: + ViewManagerBase *m_viewManager; + std::string m_clientName; + std::atomic_bool m_recording; + sv_samplerate_t m_recordSampleRate; + int m_recordChannelCount; + sv_frame_t m_frameCount; + QString m_audioFileName; + WritableWaveFileModel *m_model; + RingBuffer<float> **m_buffers; + QMutex m_bufPtrMutex; + int m_bufferCount; + float m_inputLeft; + float m_inputRight; + + void recreateBuffers(); +}; + +#endif
--- a/audio/AudioRecordTarget.cpp Wed Jan 04 18:54:50 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,214 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "AudioRecordTarget.h" - -#include "base/ViewManagerBase.h" -#include "base/TempDirectory.h" - -#include "data/model/WritableWaveFileModel.h" - -#include <QDir> - -AudioRecordTarget::AudioRecordTarget(ViewManagerBase *manager, - QString clientName) : - m_viewManager(manager), - m_clientName(clientName.toUtf8().data()), - m_recording(false), - m_recordSampleRate(44100), - m_recordChannelCount(2), - m_frameCount(0), - m_model(0) -{ -} - -AudioRecordTarget::~AudioRecordTarget() -{ - QMutexLocker locker(&m_mutex); -} - -int -AudioRecordTarget::getApplicationSampleRate() const -{ - return 0; // don't care -} - -int -AudioRecordTarget::getApplicationChannelCount() const -{ - return m_recordChannelCount; -} - -void -AudioRecordTarget::setSystemRecordBlockSize(int) -{ -} - -void -AudioRecordTarget::setSystemRecordSampleRate(int n) -{ - m_recordSampleRate = n; -} - -void -AudioRecordTarget::setSystemRecordLatency(int) -{ -} - -void -AudioRecordTarget::setSystemRecordChannelCount(int c) -{ - m_recordChannelCount = c; -} - -void -AudioRecordTarget::putSamples(const float *const *samples, int, int nframes) -{ - bool secChanged = false; - sv_frame_t frameToEmit = 0; - - { - QMutexLocker locker(&m_mutex); //!!! bad here - if (!m_recording) return; - - m_model->addSamples(samples, nframes); - - sv_frame_t priorFrameCount = m_frameCount; - m_frameCount += nframes; - - RealTime priorRT = RealTime::frame2RealTime - (priorFrameCount, m_recordSampleRate); - RealTime postRT = RealTime::frame2RealTime - (m_frameCount, m_recordSampleRate); - - secChanged = (postRT.sec > priorRT.sec); - if (secChanged) frameToEmit = m_frameCount; - } - - if (secChanged) { - emit recordDurationChanged(frameToEmit, m_recordSampleRate); - } -} - -void -AudioRecordTarget::setInputLevels(float, float) -{ -} - -void -AudioRecordTarget::modelAboutToBeDeleted() -{ - QMutexLocker locker(&m_mutex); - if (sender() == m_model) { - m_model = 0; - m_recording = false; - } -} - -QString -AudioRecordTarget::getRecordContainerFolder() -{ - QDir parent(TempDirectory::getInstance()->getContainingPath()); - QString subdirname("recorded"); - - if (!parent.mkpath(subdirname)) { - cerr << "ERROR: AudioRecordTarget::getRecordContainerFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; - return ""; - } else { - return parent.filePath(subdirname); - } -} - -QString -AudioRecordTarget::getRecordFolder() -{ - QDir parent(getRecordContainerFolder()); - QDateTime now = QDateTime::currentDateTime(); - QString subdirname = QString("%1").arg(now.toString("yyyyMMdd")); - - if (!parent.mkpath(subdirname)) { - cerr << "ERROR: AudioRecordTarget::getRecordFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; - return ""; - } else { - return parent.filePath(subdirname); - } -} - -WritableWaveFileModel * -AudioRecordTarget::startRecording() -{ - { - QMutexLocker locker(&m_mutex); - - if (m_recording) { - cerr << "WARNING: AudioRecordTarget::startRecording: We are already recording" << endl; - return 0; - } - - m_model = 0; - m_frameCount = 0; - - QString folder = getRecordFolder(); - if (folder == "") return 0; - QDir recordedDir(folder); - - QDateTime now = QDateTime::currentDateTime(); - - // Don't use QDateTime::toString(Qt::ISODate) as the ":" character - // isn't permitted in filenames on Windows - QString filename = QString("recorded-%1.wav") - .arg(now.toString("yyyyMMdd-HHmmss-zzz")); - - m_audioFileName = recordedDir.filePath(filename); - - m_model = new WritableWaveFileModel(m_recordSampleRate, - m_recordChannelCount, - m_audioFileName); - - if (!m_model->isOK()) { - cerr << "ERROR: AudioRecordTarget::startRecording: Recording failed" - << endl; - //!!! and throw? - delete m_model; - m_model = 0; - return 0; - } - - m_recording = true; - } - - emit recordStatusChanged(true); - return m_model; -} - -void -AudioRecordTarget::stopRecording() -{ - { - QMutexLocker locker(&m_mutex); - if (!m_recording) { - cerr << "WARNING: AudioRecordTarget::startRecording: Not recording" << endl; - return; - } - - m_model->writeComplete(); - m_model = 0; - m_recording = false; - } - - emit recordStatusChanged(false); - emit recordCompleted(); -} - -
--- a/audio/AudioRecordTarget.h Wed Jan 04 18:54:50 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef AUDIO_RECORD_TARGET_H -#define AUDIO_RECORD_TARGET_H - -#include <bqaudioio/ApplicationRecordTarget.h> - -#include <string> - -#include <QObject> -#include <QMutex> - -#include "base/BaseTypes.h" - -class ViewManagerBase; -class WritableWaveFileModel; - -class AudioRecordTarget : public QObject, - public breakfastquay::ApplicationRecordTarget -{ - Q_OBJECT - -public: - AudioRecordTarget(ViewManagerBase *, QString clientName); - virtual ~AudioRecordTarget(); - - virtual std::string getClientName() const override { return m_clientName; } - - virtual int getApplicationSampleRate() const override; - virtual int getApplicationChannelCount() const override; - - virtual void setSystemRecordBlockSize(int) override; - virtual void setSystemRecordSampleRate(int) override; - virtual void setSystemRecordLatency(int) override; - virtual void setSystemRecordChannelCount(int) override; - - virtual void putSamples(const float *const *samples, int nchannels, int nframes) override; - - virtual void setInputLevels(float peakLeft, float peakRight) override; - - virtual void audioProcessingOverload() override { } - - QString getRecordContainerFolder(); - QString getRecordFolder(); - - bool isRecording() const { return m_recording; } - WritableWaveFileModel *startRecording(); // caller takes ownership - void stopRecording(); - -signals: - void recordStatusChanged(bool recording); - void recordDurationChanged(sv_frame_t, sv_samplerate_t); // emitted occasionally - void recordCompleted(); - -protected slots: - void modelAboutToBeDeleted(); - -private: - ViewManagerBase *m_viewManager; - std::string m_clientName; - bool m_recording; - sv_samplerate_t m_recordSampleRate; - int m_recordChannelCount; - sv_frame_t m_frameCount; - QString m_audioFileName; - WritableWaveFileModel *m_model; - QMutex m_mutex; -}; - -#endif
--- a/files.pri Wed Jan 04 18:54:50 2017 +0000 +++ b/files.pri Wed Jan 04 19:10:32 2017 +0000 @@ -1,7 +1,7 @@ SVAPP_HEADERS += \ audio/AudioCallbackPlaySource.h \ - audio/AudioRecordTarget.h \ + audio/AudioCallbackRecordTarget.h \ audio/AudioGenerator.h \ audio/ClipMixer.h \ audio/ContinuousSynth.h \ @@ -15,7 +15,7 @@ SVAPP_SOURCES += \ audio/AudioCallbackPlaySource.cpp \ - audio/AudioRecordTarget.cpp \ + audio/AudioCallbackRecordTarget.cpp \ audio/AudioGenerator.cpp \ audio/ClipMixer.cpp \ audio/ContinuousSynth.cpp \
--- a/framework/MainWindowBase.cpp Wed Jan 04 18:54:50 2017 +0000 +++ b/framework/MainWindowBase.cpp Wed Jan 04 19:10:32 2017 +0000 @@ -48,7 +48,7 @@ #include "widgets/InteractiveFileFinder.h" #include "audio/AudioCallbackPlaySource.h" -#include "audio/AudioRecordTarget.h" +#include "audio/AudioCallbackRecordTarget.h" #include "audio/PlaySpeedRangeMapper.h" #include "data/fileio/DataFileReaderFactory.h" @@ -230,27 +230,30 @@ this, SLOT(paneDropAccepted(Pane *, QString))); connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), this, SLOT(paneDeleteButtonClicked(Pane *))); - - m_playSource = new AudioCallbackPlaySource(m_viewManager, - QApplication::applicationName()); + + m_playSource = new AudioCallbackPlaySource + (m_viewManager, QApplication::applicationName()); + if (m_soundOptions & WithAudioInput) { - m_recordTarget = new AudioRecordTarget(m_viewManager, - QApplication::applicationName()); - connect(m_recordTarget, SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)), - this, SLOT(recordDurationChanged(sv_frame_t, sv_samplerate_t))); + m_recordTarget = new AudioCallbackRecordTarget + (m_viewManager, QApplication::applicationName()); + connect(m_recordTarget, + SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)), + this, + SLOT(recordDurationChanged(sv_frame_t, sv_samplerate_t))); } connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)), this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool))); - connect(m_playSource, SIGNAL(channelCountIncreased()), - this, SLOT(recreateAudioIO())); + connect(m_playSource, SIGNAL(channelCountIncreased(int)), + this, SLOT(audioChannelCountIncreased(int))); connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), this, SLOT(audioOverloadPluginDisabled())); connect(m_playSource, SIGNAL(audioTimeStretchMultiChannelDisabled()), this, SLOT(audioTimeStretchMultiChannelDisabled())); - connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), - this, SLOT(outputLevelsChanged(float, float))); + connect(m_viewManager, SIGNAL(monitoringLevelsChanged(float, float)), + this, SLOT(monitoringLevelsChanged(float, float))); connect(m_viewManager, SIGNAL(playbackFrameChanged(sv_frame_t)), this, SLOT(playbackFrameChanged(sv_frame_t))); @@ -480,7 +483,7 @@ QTimer *oscTimer = new QTimer(this); connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); oscTimer->start(1000); - cerr << "Finished setting up OSC interface" << endl; + SVCERR << "Finished setting up OSC interface" << endl; } } @@ -2343,17 +2346,21 @@ m_resamplerWrapper = new breakfastquay::ResamplerWrapper(m_playSource); m_playSource->setResamplerWrapper(m_resamplerWrapper); } + + std::string errorString; if (m_soundOptions & WithAudioInput) { m_audioIO = breakfastquay::AudioFactory:: - createCallbackIO(m_recordTarget, m_resamplerWrapper, preference); + createCallbackIO(m_recordTarget, m_resamplerWrapper, + preference, errorString); if (m_audioIO) { m_audioIO->suspend(); // start in suspended state m_playSource->setSystemPlaybackTarget(m_audioIO); } } else { m_playTarget = breakfastquay::AudioFactory:: - createCallbackPlayTarget(m_resamplerWrapper, preference); + createCallbackPlayTarget(m_resamplerWrapper, + preference, errorString); if (m_playTarget) { m_playTarget->suspend(); // start in suspended state m_playSource->setSystemPlaybackTarget(m_playTarget); @@ -2362,20 +2369,39 @@ if (!m_playTarget && !m_audioIO) { emit hideSplash(); + QString message; + QString error = errorString.c_str(); + QString firstBit, secondBit; if (implementation == "") { - QMessageBox::warning - (this, tr("Couldn't open audio device"), - tr("<b>No audio available</b><p>Could not open an audio device for playback.<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>"), - QMessageBox::Ok); + if (error == "") { + firstBit = tr("<b>No audio available</b><p>Could not open an audio device.</p>"); + } else { + firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error); + } + if (m_soundOptions & WithAudioInput) { + secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>"); + } else { + secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>"); + } } else { - QMessageBox::warning - (this, tr("Couldn't open audio device"), - tr("<b>No audio available</b><p>Failed to open your preferred audio device (\"%1\").<p>Audio playback will not be available during this session.</p>") - .arg(breakfastquay::AudioFactory:: - getImplementationDescription(implementation.toStdString()) - .c_str()), - QMessageBox::Ok); + QString driverName = breakfastquay::AudioFactory:: + getImplementationDescription(implementation.toStdString()) + .c_str(); + if (error == "") { + firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\").</p>").arg(driverName); + } else { + firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error); + } + if (m_soundOptions & WithAudioInput) { + secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>"); + } else { + secondBit = tr("<p>Audio playback will not be available during this session.</p>"); + } } + SVDEBUG << "createAudioIO: ERROR: Failed to open audio device \"" + << implementation << "\": error is: " << error << endl; + QMessageBox::warning(this, tr("Couldn't open audio device"), + firstBit + secondBit, QMessageBox::Ok); } } @@ -2410,6 +2436,12 @@ createAudioIO(); } +void +MainWindowBase::audioChannelCountIncreased(int) +{ + recreateAudioIO(); +} + WaveFileModel * MainWindowBase::getMainModel() { @@ -2886,11 +2918,12 @@ } if (!m_audioIO) { + cerr << "MainWindowBase::record: about to create audio IO" << endl; createAudioIO(); } if (!m_audioIO) { - //!!! report + // don't need to report this, createAudioIO already should have return; } @@ -2910,6 +2943,7 @@ if (m_viewManager) m_viewManager->setGlobalCentreFrame(0); + cerr << "MainWindowBase::record: about to resume" << endl; m_audioIO->resume(); WritableWaveFileModel *model = m_recordTarget->startRecording();
--- a/framework/MainWindowBase.h Wed Jan 04 18:54:50 2017 +0000 +++ b/framework/MainWindowBase.h Wed Jan 04 19:10:32 2017 +0000 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _MAIN_WINDOW_BASE_H_ -#define _MAIN_WINDOW_BASE_H_ +#ifndef SV_MAIN_WINDOW_BASE_H +#define SV_MAIN_WINDOW_BASE_H #include <QFrame> #include <QString> @@ -46,7 +46,7 @@ class WaveformLayer; class WaveFileModel; class AudioCallbackPlaySource; -class AudioRecordTarget; +class AudioCallbackRecordTarget; class CommandHistory; class QMenu; class AudioDial; @@ -248,6 +248,8 @@ virtual void playSelectionToggled(); virtual void playSoloToggled(); + virtual void audioChannelCountIncreased(int count); + virtual void sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool) = 0; virtual void audioOverloadPluginDisabled() = 0; virtual void audioTimeStretchMultiChannelDisabled() = 0; @@ -256,7 +258,7 @@ virtual void globalCentreFrameChanged(sv_frame_t); virtual void viewCentreFrameChanged(View *, sv_frame_t); virtual void viewZoomLevelChanged(View *, int, bool); - virtual void outputLevelsChanged(float, float) = 0; + virtual void monitoringLevelsChanged(float, float) = 0; virtual void recordDurationChanged(sv_frame_t, sv_samplerate_t); virtual void currentPaneChanged(Pane *); @@ -346,7 +348,7 @@ SoundOptions m_soundOptions; AudioCallbackPlaySource *m_playSource; - AudioRecordTarget *m_recordTarget; + AudioCallbackRecordTarget *m_recordTarget; breakfastquay::ResamplerWrapper *m_resamplerWrapper; breakfastquay::SystemPlaybackTarget *m_playTarget; // only one of this... breakfastquay::SystemAudioIO *m_audioIO; // ... and this exists @@ -386,7 +388,7 @@ RealTime m_defaultFfwdRwdStep; AudioRecordMode m_audioRecordMode; - + mutable QLabel *m_statusLabel; QLabel *getStatusLabel() const;