Just Intonation  Version 1.3.1 (19)
Explore key-independent dynamically adapting tuning in just intonation
audiooutputdevice.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2016-2017 Karolin Stange, Christoph Wick, and Haye Hinrichsen
3  *
4  * This file is part of JustIntonation.
5  *
6  * JustIntonation 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  * JustIntonation 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
14  * for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with JustIntonation. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 //=============================================================================
21 // Audio output device
22 //=============================================================================
23 
24 #include "audiooutputdevice.h"
25 
26 #include "audiooutput.h"
27 #include "audiooutputdevice.h"
28 
29 // Note that the default sample rates etc are specified in
30 
31 //-----------------------------------------------------------------------------
32 // Constructor
33 //-----------------------------------------------------------------------------
38 
40  : pAudioOutput(audiooutput)
41  , pQtAudioOutputStream(nullptr)
42 {
43  setModuleName("Audio");
44 }
45 
46 
47 //-----------------------------------------------------------------------------
48 // Start the audio output device
49 //-----------------------------------------------------------------------------
57 
59 {
60  if (not pAudioOutput) return false;
61 
62  // Copy the parameters. The finally realized version may be different
63  AudioParameters realizedParameters(parameters);
64 
65  // If sample rate is zero set default value
66  if (realizedParameters.sampleRate <= 0) realizedParameters.sampleRate = 44100;
67 
68  // If channel count is zero set default value
69  if (realizedParameters.channelCount <= 0) realizedParameters.channelCount = 2;
70 
71  LOGSTATUS<< "Connect audio device: Device name =" << realizedParameters.deviceName <<
72  ", Sample rate =" << realizedParameters.sampleRate <<
73  ", Sample size =" << realizedParameters.sampleSize <<
74  ", Channel count =" << realizedParameters.channelCount <<
75  ", Buffer size =" << realizedParameters.bufferSize;
76 
77  QAudioDeviceInfo info;
78  // If device name is empty look for default device
79  if (realizedParameters.deviceName.size()==0)
80  {
81  info = QAudioDeviceInfo::defaultOutputDevice();
82  realizedParameters.deviceName = info.deviceName();
83  if (realizedParameters.deviceName.size()==0)
84  {
85  LOGWARNING << "No default device found";
86  return false;
87  }
88  else LOGSTATUS << "Found default device named" << realizedParameters.deviceName;
89  }
90  else // if device name is not empty
91  {
92  // Look up whether the selected device exists
93  LOGSTATUS << "Now searching for an audio device named" << realizedParameters.deviceName;
94  bool found = false;
95  QStringList deviceList; // several devices may be compatible with the name
96  QList<QAudioDeviceInfo> availableDevices(QAudioDeviceInfo::availableDevices(QAudio::AudioOutput));
97  for (const QAudioDeviceInfo &deviceinfo : availableDevices)
98  {
99  if (deviceinfo.deviceName() == realizedParameters.deviceName)
100  {
101  info = deviceinfo;
102  found = true;
103  deviceList << deviceinfo.deviceName();
104  break;
105  }
106  }
107  if (not found)
108  {
109  LOGWARNING << "Could not find the audio device" << realizedParameters.deviceName
110  << "in the list" << deviceList;
111  LOGWARNING << "Now Trying to connect to default device";
112  LOGSTATUS << "Recursive call of connect()";
113  realizedParameters.deviceName="";
114  return connect(realizedParameters);
115  }
116  else
117  {
118  LOGSTATUS << "Found available audio device" << realizedParameters.deviceName;
119  }
120  }
121  LOGMESSAGE << "Trying to open audio device" << realizedParameters.deviceName;
122  LOGMESSAGE << "Tentative parameter set: samplerate =" << realizedParameters.sampleRate
123  << "samplesize =" << realizedParameters.sampleSize
124  << "channels =" << realizedParameters.channelCount
125  << "buffersize =" << realizedParameters.bufferSize;
126 
127  // Format specification:
128  QAudioFormat format;
129  format.setSampleRate(realizedParameters.sampleRate);
130  format.setChannelCount(realizedParameters.channelCount);
131  format.setCodec("audio/pcm");
132  format.setSampleSize(16);
133  format.setByteOrder(QAudioFormat::LittleEndian);
134  format.setSampleType(QAudioFormat::SignedInt);
135 
136  // Look up whether the format is supported
137  if (not info.isFormatSupported(format))
138  {
139  LOGWARNING << "Audio format is not supported by backend,"
140  << "falling back to nearest format";
141  format = info.nearestFormat(format);
142  if (not info.isFormatSupported(format))
143  {
144  LOGWARNING << "Fallback failed. Probably there is"
145  << "no audio output device available.";
146  return false;
147  }
148  }
149 
150  LOGMESSAGE << "Format sample size, type and rate =" << format.sampleSize()
151  << format.sampleType() << format.sampleRate();
152  if ((format.sampleSize()!=16 and format.sampleSize()!=24)
153  or format.sampleType() != QAudioFormat::SignedInt)
154  {
155  LOGWARNING << "Sample type and/or size are not supported by the application.";
156  return false;
157  }
158 
159  // If the wanted sample rate cannot be met reset to the suggested values:
160  if (format.sampleRate() != realizedParameters.sampleRate)
161  {
162  LOGMESSAGE << "Resetting sample rate to " << format.sampleRate();
163  realizedParameters.sampleRate = format.sampleRate();
164  }
165 
166  // If the wanted channel count cannot be met reset to the suggested values:
167  if (format.channelCount() != realizedParameters.channelCount)
168  {
169  LOGMESSAGE << "Resetting channel count to " << format.channelCount();
170  realizedParameters.channelCount = format.channelCount();
171  }
172 
173  mSampleSize = format.sampleSize();
174 
175  // ############### CREATE QT AUDIO OUTPUT STREAM ##################
176 
177  pQtAudioOutputStream = new QAudioOutput(info, format);
178 
179  // set volume to 100%
180  pQtAudioOutputStream->setVolume(1);
181 
182  // Set audio buffer size
183  if (realizedParameters.bufferSize > 0)
184  {
185  LOGMESSAGE << "Set Qt audio buffer size to" << realizedParameters.bufferSize;
186  pQtAudioOutputStream->setBufferSize(realizedParameters.bufferSize);
187  }
188 
189  // If error detected terminate
190  if (pQtAudioOutputStream->error() != QAudio::NoError)
191  {
192  LOGWARNING << "Error opening QAudioOutput with error:" << pQtAudioOutputStream->error();
193  return false;
194  }
195 
196 
197  // ################ OPEN QT AUDIO OUTPUT STREAM ##################
198  if (not open(QIODevice::ReadOnly))
199  {
200  LOGWARNING << "QAudioOutput could not be opened, aborting.";
201  return false;
202  }
203 
204  // ################ START QT AUDIO OUTPUT STREAM ##################
205  pQtAudioOutputStream->start(this);
206  if (pQtAudioOutputStream->error() != QAudio::NoError)
207  {
208  LOGWARNING << "Error starting QAudioOutput with error ",pQtAudioOutputStream->error();
209  return false;
210  }
211 
212  // This should not happen
213  if (realizedParameters.deviceName != info.deviceName())
214  {
215  LOGERROR << "Severe consistency error";
216  return false;
217  }
218 
219  // Update current device name
220  LOGMESSAGE << "Opened audio output device:" << realizedParameters.deviceName;
221  realizedParameters.supportedSampleRates = info.supportedSampleRates();
222  realizedParameters.active = true;
223  pAudioOutput->setActualParameters(realizedParameters);
224  return true;
225 }
226 
227 
228 //-----------------------------------------------------------------------------
229 // Stop the audio output device
230 //-----------------------------------------------------------------------------
234 
236 {
238  {
239  pQtAudioOutputStream->stop();
240  delete pQtAudioOutputStream;
241  pQtAudioOutputStream = nullptr;
242  }
243 }
244 
245 
246 //-----------------------------------------------------------------------------
247 // Set global volume
248 //-----------------------------------------------------------------------------
253 
254 void AudioOutputDevice::setVolume(double volume)
255 {
256  if (pQtAudioOutputStream) pQtAudioOutputStream->setVolume(volume);
257 }
258 
259 
260 //-----------------------------------------------------------------------------
261 // Read PCM data
262 //-----------------------------------------------------------------------------
271 
272 qint64 AudioOutputDevice::readData (char* data, qint64 maxSize)
273 {
274  return static_cast<qint64>(
275  pAudioOutput->getAudioGenerator()->generateSound(data,(size_t)maxSize));
276 }
277 
278 
279 //-----------------------------------------------------------------------------
280 // Empty implementation for writing
281 //-----------------------------------------------------------------------------
290 
291 qint64 AudioOutputDevice::writeData (const char * data, qint64 maxSize)
292 { (void)data; (void)maxSize; return 0; }
293 
void disconnect()
Stop the audio output device.
AudioOutputDevice(AudioOutput *audioplayerthread)
Constructor, resetting the member variables.
void setModuleName(const QString &name)
Specify the name of the class-specific module.
Definition: log.cpp:82
int sampleSize
Sample size (16 or 24)
qint64 writeData(const char *data, qint64 maxSize) override final
Empty implementation for writing without functionality.
bool active
True of device is active.
#define LOGMESSAGE
Definition: log.h:43
QAudioOutput * pQtAudioOutputStream
Pointer to System-Qt audio driver.
QString deviceName
Name of the audio device.
bool connect(const AudioDeviceParameters &parameters)
Structure holding the parameters and status of an audio device.
virtual size_t generateSound(char *data, size_t maxSize)
Virtual function for sound generation. This function generates an A440 on the right and an A220 on th...
int sampleRate
Actual sample rate.
qint64 readData(char *data, qint64 maxSize) override final
Read PCM data.
#define LOGERROR
Definition: log.h:45
int mSampleSize
Sample size in bits (16/24)
Class for audio output.
Definition: audiooutput.h:45
int bufferSize
Buffer size of the device if applicable.
#define LOGSTATUS
Definition: log.h:42
void setVolume(double volume)
Set the global volume.
AudioGenerator * getAudioGenerator() const
Definition: audiooutput.h:58
QList< int > supportedSampleRates
List of supported sample rates.
int channelCount
Number of channels (mono=1, stereo=2)
AudioOutput * pAudioOutput
Pointer back to AudioOutputThread.
#define LOGWARNING
Definition: log.h:44