Entropy Piano Tuner  1.1.3 (documentation not yet complete)
An open-source experimental software for piano tuning by entropy minimization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
audioplayerthreadforqt.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2015 Haye Hinrichsen, Christoph Wick
3  *
4  * This file is part of Entropy Piano Tuner.
5  *
6  * Entropy Piano Tuner is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * Entropy Piano Tuner is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * Entropy Piano Tuner. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 #include "audioplayerthreadforqt.h"
21 
22 #include <qdebug.h>
23 
24 #include "../core/system/log.h"
25 #include "../core/system/simplethreadhandler.h"
26 
28 
29 //=============================================================================
30 // CLASS AudioPlayerThreadForQt
31 //=============================================================================
32 
37 
39  mAudioSource(audio),
40  mAudioSink(nullptr),
41  mIODevice(nullptr),
42  mThreadRunning(true),
43  mPause(false)
44 {}
45 
46 
47 //-----------------------------------------------------------------------------
48 // Initialization
49 //-----------------------------------------------------------------------------
50 
58 
60 {
61  // (does not work yet) SimpleThreadHandler::setThreadName("AudioPlayer");
62 
63  // If the player does not exist no action is taken
64  if (not mAudioSource) return;
65 
66  // Format specification:
67  QAudioFormat format;
68  format.setSampleRate(mAudioSource->getSamplingRate());
69  format.setChannelCount(mAudioSource->getChannelCount());
70  format.setCodec("audio/pcm");
71  format.setSampleSize(sizeof(DataFormat) * 8);
72  format.setSampleType(QAudioFormat::SignedInt);
73 
74  // Find the audio device:
75  QAudioDeviceInfo device(QAudioDeviceInfo::defaultOutputDevice());
76  if (mAudioSource->getDeviceName().size() > 0)
77  {
78  QList<QAudioDeviceInfo> devices(QAudioDeviceInfo::availableDevices(QAudio::AudioOutput));
79  for (const QAudioDeviceInfo &i : devices)
80  {
81  if (i.deviceName().toStdString() == mAudioSource->getDeviceName())
82  {
83  device = i;
84  break;
85  }
86 
87  if (!device.isFormatSupported(format))
88  LogE("Selected device settings are not supported!");
89  }
90  }
91  else
92  {
93  if (not device.isFormatSupported(format))
94  {
95  LogW("Raw audio format not supported by backend, falling back to default supported");
96  format = device.preferredFormat();
97  // update sampling rate, buffer type has to stay the same!
98  if (not device.isFormatSupported(format))
99  {
100  LogW("Fallback failed. Probably there is no output device available.");
101  return;
102  }
103  mAudioSource->setSamplingRate(format.sampleRate());
104  if (format.sampleSize() != sizeof(DataFormat) * 8)
105  {
106  LogW("Sample size not supported");
107  return;
108  }
109  if (format.sampleType() != QAudioFormat::SignedInt)
110  {
111  LogW("Sample format not supported");
112  return;
113  }
114  }
115  }
116  mAudioSource->setDeviceName(device.deviceName().toStdString());
117 
118  // Open the audio output stream
119  mAudioSink = new QAudioOutput(device, format);
120  if (mAudioSink->error() != QAudio::NoError)
121  {
122  LogE("Error opening QAudioOutput with error %d", mAudioSink->error());
123  return;
124  }
125 
126  // set volume
127  mAudioSink->setVolume(1);
128 
129 
130  // Specify the size of the Qt-internal buffer
131  const size_t BufferSize = mAudioSource->getChannelCount() *
132  ((mAudioSource->getSamplingRate() * BufferMilliseconds)/1000);
133  mAudioSink->setBufferSize(BufferSize);
134  if (mAudioSink->error() != QAudio::NoError) {
135  LogE("Error opening QAudioOutput with error %d", mAudioSink->error());
136  return;
137  }
138 
139  if (mAudioSource->getWriter()) {
140  mAudioSource->getWriter()->init(mAudioSource->getSamplingRate(), mAudioSource->getChannelCount());
141  }
142 
143  LogI("Initialized Qt audio player using device: %s", mAudioSource->getDeviceName().c_str());
144 }
145 
146 
147 //-----------------------------------------------------------------------------
148 // Exit
149 //-----------------------------------------------------------------------------
150 
154 
156 {
157  stop();
158  if (mAudioSink)
159  {
160  mAudioSink->reset();
161  delete mAudioSink;
162  mAudioSink = nullptr;
163  mIODevice = nullptr;
164  }
165 
167  {
169  }
170  LogI("Qt audio player closed.");
171 }
172 
173 
174 //-----------------------------------------------------------------------------
175 // Start audio device
176 //-----------------------------------------------------------------------------
177 
181 
183 {
184  LogI("Start Qt audio device")
185  if (not mAudioSink)
186  {
187  LogI("Audio device was not created and thus cannot be started.");
188  return;
189  }
190  if (not mIODevice)
191  {
192  mIODevice = mAudioSink->start();
193  if (mAudioSink->error() != QAudio::NoError)
194  {
195  qWarning() << "Error opening QAudioOutput with error " << mAudioSink->error();
196  return;
197  }
198  }
199 }
200 
201 
202 //-----------------------------------------------------------------------------
203 // Stop audio device
204 //-----------------------------------------------------------------------------
205 
209 
211 {
212  LogI("Stop Qt audio device");
213  if (not mAudioSink) return;
214  if (mIODevice)
215  {
216  mAudioSink->stop();
217  mIODevice = nullptr;
218  }
219 }
220 
221 
222 
223 //-----------------------------------------------------------------------------
224 // Stop audio device
225 //-----------------------------------------------------------------------------
226 
231 
233 {
234  mPause = pause;
235 }
236 
237 
238 //-----------------------------------------------------------------------------
239 // Main worker function
240 //-----------------------------------------------------------------------------
241 
245 
247 {
248  //setThreadName("QtAudioManager");
249 
250  // Determine the scaling constant
251  const auto scaling = std::numeric_limits<AudioPlayerThreadForQt::DataFormat>::max();
252 
253  init();
254  start();
255  if (!mAudioSink) {
256  // error while creating audio device
257  return;
258  }
259 
260  mAudioSink->suspend();
261 
262  while (mThreadRunning)
263  {
264  if (mPause or not mAudioSource->getWriter() or mAudioSource->isMuted()) {
265  QThread::msleep(500);
266  continue;
267  }
268 
269  AudioBase::PacketType packet(mAudioSink->bytesFree() / sizeof(DataFormat));
270  const bool requestPause = !mAudioSource->getWriter()->generateAudioSignal(packet);
271 
272  if (requestPause) {
273  if (mAudioSink->state() != QAudio::SuspendedState) {
274  mAudioSink->suspend();
275  }
276  QThread::msleep(10);
277  continue;
278  } else {
279  if (mAudioSink->state() == QAudio::SuspendedState) {
280  mAudioSink->resume();
281  }
282  if (packet.size() == 0) {QThread::msleep(5); continue;}
283 
284  std::vector<DataFormat> buffer(packet.size());
285  for (size_t i=0; i<buffer.size(); ++i) buffer[i] = static_cast<DataFormat>(packet[i] * scaling);
286  mIODevice->write((const char*)buffer.data(), buffer.size() * sizeof(DataFormat));
287  }
288  }
289  exit();
290  emit finished();
291 }
QAudioOutput * mAudioSink
Audio sink to which the data is sent.
std::vector< PCMDataType > PacketType
Type definition of a PCM packet (vector of PCM values).
Definition: audiobase.h:51
AudioPlayerThreadForQt(AudioPlayerForQt *audio)
Constructor.
void start()
Start the Qt audio device.
bool isMuted()
Returns true if the player is muted.
void stop()
Stop the Qt audio device.
QIODevice * mIODevice
Qt IO device pointer.
AudioPlayerForQt * mAudioSource
Audio source where the data comes from.
#define LogW(...)
Definition: log.h:56
PCMWriterInterface * getWriter()
Get pointer to the PCM writer.
The AudioPlayerForQt class.
void workerFunction()
Main worker function of the Qt audio manager.
virtual bool generateAudioSignal(AudioBase::PacketType &outputPacket)=0
Function that generates new sound and copies it to the outputPacket.
#define LogI(...)
Definition: log.h:50
std::atomic< bool > mThreadRunning
Boolean indicating that the thread is running.
#define LogE(...)
Definition: log.h:62
void init()
Initialize the audio player.
virtual void exit()=0
Exit, shut down writer interface.
std::atomic< bool > mPause
Boolean indicating that the thread is pausing.
void exit()
Exit from the Qt audio player.
void setPause(bool pause)
Pause the audio player.
static const double BufferMilliseconds