annotate audio/AudioRecordTarget.cpp @ 570:6f54789f3127 3.0-integration

Fix race condition in first-time recording, where adding the recording wave model would prompt the audio play source to note that its channel count had increased (from 0 to, say, 2) and thus to cause the audio device to be reopened, stopping recording. Fix is to make this only happen if channel count increases beyond that of the device, which shouldn't happen in the recording case
author Chris Cannam
date Wed, 04 Jan 2017 11:48:03 +0000
parents e348e0c52115
children 9fb190c6521b
rev   line source
Chris@476 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@476 2
Chris@476 3 /*
Chris@476 4 Sonic Visualiser
Chris@476 5 An audio file viewer and annotation editor.
Chris@476 6 Centre for Digital Music, Queen Mary, University of London.
Chris@476 7
Chris@476 8 This program is free software; you can redistribute it and/or
Chris@476 9 modify it under the terms of the GNU General Public License as
Chris@476 10 published by the Free Software Foundation; either version 2 of the
Chris@476 11 License, or (at your option) any later version. See the file
Chris@476 12 COPYING included with this distribution for more information.
Chris@476 13 */
Chris@476 14
Chris@476 15 #include "AudioRecordTarget.h"
Chris@476 16
Chris@476 17 #include "base/ViewManagerBase.h"
Chris@476 18 #include "base/TempDirectory.h"
Chris@476 19
Chris@477 20 #include "data/model/WritableWaveFileModel.h"
Chris@476 21
Chris@476 22 #include <QDir>
Chris@476 23
Chris@476 24 AudioRecordTarget::AudioRecordTarget(ViewManagerBase *manager,
Chris@476 25 QString clientName) :
Chris@476 26 m_viewManager(manager),
Chris@476 27 m_clientName(clientName.toUtf8().data()),
Chris@476 28 m_recording(false),
Chris@476 29 m_recordSampleRate(44100),
Chris@546 30 m_recordChannelCount(2),
Chris@485 31 m_frameCount(0),
Chris@477 32 m_model(0)
Chris@476 33 {
Chris@476 34 }
Chris@476 35
Chris@476 36 AudioRecordTarget::~AudioRecordTarget()
Chris@476 37 {
Chris@476 38 QMutexLocker locker(&m_mutex);
Chris@476 39 }
Chris@476 40
Chris@559 41 int
Chris@559 42 AudioRecordTarget::getApplicationSampleRate() const
Chris@559 43 {
Chris@559 44 return 0; // don't care
Chris@559 45 }
Chris@559 46
Chris@559 47 int
Chris@559 48 AudioRecordTarget::getApplicationChannelCount() const
Chris@559 49 {
Chris@559 50 return m_recordChannelCount;
Chris@559 51 }
Chris@559 52
Chris@476 53 void
Chris@491 54 AudioRecordTarget::setSystemRecordBlockSize(int)
Chris@476 55 {
Chris@476 56 }
Chris@476 57
Chris@476 58 void
Chris@476 59 AudioRecordTarget::setSystemRecordSampleRate(int n)
Chris@476 60 {
Chris@476 61 m_recordSampleRate = n;
Chris@476 62 }
Chris@476 63
Chris@476 64 void
Chris@491 65 AudioRecordTarget::setSystemRecordLatency(int)
Chris@476 66 {
Chris@476 67 }
Chris@476 68
Chris@476 69 void
Chris@546 70 AudioRecordTarget::setSystemRecordChannelCount(int c)
Chris@546 71 {
Chris@546 72 m_recordChannelCount = c;
Chris@546 73 }
Chris@546 74
Chris@546 75 void
Chris@560 76 AudioRecordTarget::putSamples(const float *const *samples, int, int nframes)
Chris@476 77 {
Chris@485 78 bool secChanged = false;
Chris@485 79 sv_frame_t frameToEmit = 0;
Chris@485 80
Chris@485 81 {
Chris@485 82 QMutexLocker locker(&m_mutex); //!!! bad here
Chris@485 83 if (!m_recording) return;
Chris@485 84
Chris@485 85 m_model->addSamples(samples, nframes);
Chris@485 86
Chris@485 87 sv_frame_t priorFrameCount = m_frameCount;
Chris@485 88 m_frameCount += nframes;
Chris@485 89
Chris@485 90 RealTime priorRT = RealTime::frame2RealTime
Chris@485 91 (priorFrameCount, m_recordSampleRate);
Chris@485 92 RealTime postRT = RealTime::frame2RealTime
Chris@485 93 (m_frameCount, m_recordSampleRate);
Chris@485 94
Chris@485 95 secChanged = (postRT.sec > priorRT.sec);
Chris@485 96 if (secChanged) frameToEmit = m_frameCount;
Chris@485 97 }
Chris@485 98
Chris@485 99 if (secChanged) {
Chris@485 100 emit recordDurationChanged(frameToEmit, m_recordSampleRate);
Chris@485 101 }
Chris@476 102 }
Chris@476 103
Chris@476 104 void
Chris@570 105 AudioRecordTarget::setInputLevels(float left, float right)
Chris@476 106 {
Chris@570 107 cerr << "AudioRecordTarget::setInputLevels(" << left << "," << right << ")"
Chris@570 108 << endl;
Chris@476 109 }
Chris@476 110
Chris@477 111 void
Chris@477 112 AudioRecordTarget::modelAboutToBeDeleted()
Chris@477 113 {
Chris@477 114 QMutexLocker locker(&m_mutex);
Chris@477 115 if (sender() == m_model) {
Chris@477 116 m_model = 0;
Chris@477 117 m_recording = false;
Chris@477 118 }
Chris@477 119 }
Chris@477 120
Chris@483 121 QString
Chris@508 122 AudioRecordTarget::getRecordContainerFolder()
Chris@508 123 {
Chris@508 124 QDir parent(TempDirectory::getInstance()->getContainingPath());
Chris@508 125 QString subdirname("recorded");
Chris@508 126
Chris@508 127 if (!parent.mkpath(subdirname)) {
Chris@570 128 SVCERR << "ERROR: AudioRecordTarget::getRecordContainerFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl;
Chris@508 129 return "";
Chris@508 130 } else {
Chris@508 131 return parent.filePath(subdirname);
Chris@508 132 }
Chris@508 133 }
Chris@508 134
Chris@508 135 QString
Chris@483 136 AudioRecordTarget::getRecordFolder()
Chris@483 137 {
Chris@508 138 QDir parent(getRecordContainerFolder());
Chris@508 139 QDateTime now = QDateTime::currentDateTime();
Chris@508 140 QString subdirname = QString("%1").arg(now.toString("yyyyMMdd"));
Chris@508 141
Chris@483 142 if (!parent.mkpath(subdirname)) {
Chris@570 143 SVCERR << "ERROR: AudioRecordTarget::getRecordFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl;
Chris@483 144 return "";
Chris@483 145 } else {
Chris@483 146 return parent.filePath(subdirname);
Chris@483 147 }
Chris@483 148 }
Chris@483 149
Chris@477 150 WritableWaveFileModel *
Chris@476 151 AudioRecordTarget::startRecording()
Chris@476 152 {
Chris@477 153 {
Chris@570 154 QMutexLocker locker(&m_mutex);
Chris@498 155
Chris@570 156 if (m_recording) {
Chris@570 157 SVCERR << "WARNING: AudioRecordTarget::startRecording: We are already recording" << endl;
Chris@570 158 return 0;
Chris@570 159 }
Chris@476 160
Chris@570 161 m_model = 0;
Chris@570 162 m_frameCount = 0;
Chris@477 163
Chris@570 164 QString folder = getRecordFolder();
Chris@570 165 if (folder == "") return 0;
Chris@570 166 QDir recordedDir(folder);
Chris@476 167
Chris@570 168 QDateTime now = QDateTime::currentDateTime();
Chris@476 169
Chris@570 170 // Don't use QDateTime::toString(Qt::ISODate) as the ":" character
Chris@570 171 // isn't permitted in filenames on Windows
Chris@570 172 QString nowString = now.toString("yyyyMMdd-HHmmss-zzz");
Chris@570 173
Chris@570 174 QString filename = tr("recorded-%1.wav").arg(nowString);
Chris@570 175 QString label = tr("Recorded %1").arg(nowString);
Chris@476 176
Chris@570 177 m_audioFileName = recordedDir.filePath(filename);
Chris@476 178
Chris@570 179 m_model = new WritableWaveFileModel(m_recordSampleRate,
Chris@570 180 m_recordChannelCount,
Chris@570 181 m_audioFileName);
Chris@477 182
Chris@570 183 if (!m_model->isOK()) {
Chris@570 184 SVCERR << "ERROR: AudioRecordTarget::startRecording: Recording failed"
Chris@570 185 << endl;
Chris@570 186 //!!! and throw?
Chris@570 187 delete m_model;
Chris@570 188 m_model = 0;
Chris@570 189 return 0;
Chris@570 190 }
Chris@476 191
Chris@570 192 m_model->setObjectName(label);
Chris@570 193 m_recording = true;
Chris@477 194 }
Chris@477 195
Chris@477 196 emit recordStatusChanged(true);
Chris@477 197 return m_model;
Chris@476 198 }
Chris@476 199
Chris@476 200 void
Chris@476 201 AudioRecordTarget::stopRecording()
Chris@476 202 {
Chris@477 203 {
Chris@570 204 QMutexLocker locker(&m_mutex);
Chris@570 205 if (!m_recording) {
Chris@570 206 SVCERR << "WARNING: AudioRecordTarget::startRecording: Not recording" << endl;
Chris@570 207 return;
Chris@570 208 }
Chris@476 209
Chris@570 210 m_model->writeComplete();
Chris@570 211 m_model = 0;
Chris@570 212 m_recording = false;
Chris@477 213 }
Chris@477 214
Chris@477 215 emit recordStatusChanged(false);
Chris@497 216 emit recordCompleted();
Chris@476 217 }
Chris@476 218
Chris@476 219