Just Intonation  Version 1.3.0 (18)
Explore scale-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 //-----------------------------------------------------------------------------
30 // Constructor
31 //-----------------------------------------------------------------------------
36 
38  : pAudioOutput(audiooutput)
39  , pQtAudioOutputStream(nullptr)
40 {}
41 
42 
43 //-----------------------------------------------------------------------------
44 // Start the audio output device
45 //-----------------------------------------------------------------------------
51 
53 {
54  if (not pAudioOutput) return false;
55 
56  // Copy the parameters. The finally realized version may be different
57  AudioParameters realizedParameters(parameters);
58 
59  // If sample rate is zero set default value
60  if (realizedParameters.sampleRate <= 0) realizedParameters.sampleRate = 44100;
61 
62  // If channel count is zero set default value
63  if (realizedParameters.channelCount <= 0) realizedParameters.channelCount = 2;
64 
65  QAudioDeviceInfo info;
66  // If device name is empty look for default device
67  if (realizedParameters.deviceName.size()==0)
68  {
69  info = QAudioDeviceInfo::defaultOutputDevice();
70  realizedParameters.deviceName = info.deviceName();
71  if (realizedParameters.deviceName.size()==0)
72  {
73  LOGWARNING << "No default device found";
74  return false;
75  }
76  }
77  else
78  {
79  // Look up whether the selected device exists
80  bool found = false;
81  QStringList deviceList; // several devices may be compatible with the name
82  QList<QAudioDeviceInfo> availableDevices(QAudioDeviceInfo::availableDevices(QAudio::AudioOutput));
83  for (const QAudioDeviceInfo &deviceinfo : availableDevices)
84  {
85  if (deviceinfo.deviceName() == realizedParameters.deviceName)
86  {
87  info = deviceinfo;
88  found = true;
89  deviceList << deviceinfo.deviceName();
90  break;
91  }
92  }
93  if (not found)
94  {
95  LOGWARNING << "Could not find the audio device" << realizedParameters.deviceName
96  << "in the list" << deviceList;
97  return false;
98  }
99  }
100  LOGMESSAGE << "Trying to open" << realizedParameters.deviceName;
101  LOGMESSAGE << "Tentative parameter set: samplerate =" << realizedParameters.sampleRate
102  << "samplesize =" << realizedParameters.sampleSize
103  << "channels =" << realizedParameters.channelCount
104  << "buffersize =" << realizedParameters.bufferSize;
105 
106  // Format specification:
107  QAudioFormat format;
108  format.setSampleRate(realizedParameters.sampleRate);
109  format.setChannelCount(realizedParameters.channelCount);
110  format.setCodec("audio/pcm");
111  format.setSampleSize(16);
112  format.setByteOrder(QAudioFormat::LittleEndian);
113  format.setSampleType(QAudioFormat::SignedInt);
114 
115  // Look up whether the format is supported
116  if (not info.isFormatSupported(format))
117  {
118  LOGWARNING << "Audio format is not supported by backend,"
119  << "falling back to preferred format";
120  format = info.preferredFormat();
121  if (not info.isFormatSupported(format))
122  {
123  LOGWARNING << "Fallback failed. Probably there is"
124  << "no audio output device available.";
125  return false;
126  }
127  }
128 
129  LOGMESSAGE << "Format sample size and type" << format.sampleSize()
130  << format.sampleType() << format.sampleRate();
131  if ((format.sampleSize()!=16 and format.sampleSize()!=24)
132  or format.sampleType() != QAudioFormat::SignedInt)
133  {
134  LOGWARNING << "Fallback sample type and/or size are not supported by the application.";
135  return false;
136  }
137 
138  // If the wanted sample rate and channel count
139  //can not be met reset to the suggested values:
140  if (format.sampleRate() != realizedParameters.sampleRate)
141  {
142  LOGMESSAGE << "Resetting sample rate to " << format.sampleRate();
143  realizedParameters.sampleRate = format.sampleRate();
144  }
145  if (format.channelCount() != realizedParameters.channelCount)
146  {
147  LOGMESSAGE << "Resetting channel count to " << format.channelCount();
148  realizedParameters.channelCount = format.channelCount();
149  }
150 
151  mSampleSize = format.sampleSize();
152 
153  // ############### CREATE QT AUDIO OUTPUT STREAM ##################
154  pQtAudioOutputStream = new QAudioOutput(info, format);
155 
156  // Set audio buffer size
157  if (realizedParameters.bufferSize > 0)
158  {
159  LOGMESSAGE << "Set Qt audio buffer size to" << realizedParameters.bufferSize;
160  pQtAudioOutputStream->setBufferSize(realizedParameters.bufferSize);
161  }
162 
163  if (pQtAudioOutputStream->error() != QAudio::NoError)
164  {
165  LOGWARNING << "Error opening QAudioOutput with error " << pQtAudioOutputStream->error();
166  return false;
167  }
168 
169  // set volume to 100%
170  pQtAudioOutputStream->setVolume(1);
171 
172  // ################ OPEN QT AUDIO OUTPUT STREAM ##################
173  if (not open(QIODevice::ReadOnly))
174  {
175  LOGWARNING << "Audio output device could not be opened, aborting.";
176  return false;
177  }
178 
179  // ################ START QT AUDIO OUTPUT STREAM ##################
180  pQtAudioOutputStream->start(this);
181  if (pQtAudioOutputStream->error() != QAudio::NoError)
182  {
183  LOGWARNING << "Error opening QAudioOutput with error ",pQtAudioOutputStream->error();
184  return false;
185  }
186 
187  if (realizedParameters.deviceName != info.deviceName())
188  {
189  LOGERROR << "Consistency error";
190  return false;
191  }
192  // Update current device name
193  LOGMESSAGE << "Opened audio output device:" << realizedParameters.deviceName;
194  realizedParameters.supportedSampleRates = info.supportedSampleRates();
195  realizedParameters.active = true;
196  pAudioOutput->setActualParameters(realizedParameters);
197  return true;
198 }
199 
200 
201 //-----------------------------------------------------------------------------
202 // Stop the audio output device
203 //-----------------------------------------------------------------------------
207 
209 {
211  {
212  pQtAudioOutputStream->stop();
213  delete pQtAudioOutputStream;
214  pQtAudioOutputStream = nullptr;
215  }
216 }
217 
218 
219 //-----------------------------------------------------------------------------
220 // Set global volume
221 //-----------------------------------------------------------------------------
226 
227 void AudioOutputDevice::setVolume(double volume)
228 {
229  if (pQtAudioOutputStream) pQtAudioOutputStream->setVolume(volume);
230 }
231 
232 
233 //-----------------------------------------------------------------------------
234 // Read PCM data
235 //-----------------------------------------------------------------------------
244 
245 qint64 AudioOutputDevice::readData (char* data, qint64 maxSize)
246 {
247  return pAudioOutput->getAudioGenerator()->generateSound(data,maxSize);
248 }
249 
250 
251 //-----------------------------------------------------------------------------
252 // Empty implementation for writing
253 //-----------------------------------------------------------------------------
262 
263 qint64 AudioOutputDevice::writeData (const char * data, qint64 maxSize)
264 { (void)data; (void)maxSize; return 0; }
265 
void disconnect()
Stop the audio output device.
AudioOutputDevice(AudioOutput *audioplayerthread)
Constructor, resetting the member variables.
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.
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