1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; version 2 of
8 //  the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //
19 //=========================================================
20 
21 #include "audiodev.h"
22 #include "large_int.h"
23 #include <rtaudio/RtAudio.h>
24 
25 
26 // For debugging output: Uncomment the fprintf section.
27 #define DEBUG_RTAUDIO(dev, format, args...) // fprintf(dev, format, ##args);
28 
29 namespace MusECore {
30 
31 //---------------------------------------------------------
32 //   RtAudioDevice
33 //---------------------------------------------------------
34 
35 struct MuseRtAudioPort {
36   QString name;
37   float* buffer;
38 };
39 
40 class RtAudioDevice : public AudioDevice {
41   private:
42       RtAudio *dac;
43 
44       // Critical variables that need to all update at once.
45       // We employ a 'flipping' technique.
46       unsigned _framesAtCycleStart[2];
47       uint64_t _timeUSAtCycleStart[2];
48       unsigned _frameCounter[2];
49       unsigned _criticalVariablesIdx;
50 
51    public:
52       // Time in microseconds at which the driver was created.
53       uint64_t _start_timeUS;
54 
55       QList<MuseRtAudioPort*> outputPortsList;
56       QList<MuseRtAudioPort*> inputPortsList;
57 
driverName()58       virtual const char* driverName() const { return "RtAudioDevice"; }
59 
driverBackendName()60       virtual QString driverBackendName() {
61 
62         auto api = dac->getCurrentApi();
63         switch (api) {
64           case RtAudio::RTAUDIO_DUMMY:
65             return "RTAUDIO_DUMMY";
66             break;
67           case RtAudio::UNSPECIFIED:
68               return "UNSPECIFIED";
69               break;
70           case RtAudio::LINUX_ALSA:
71               return "LINUX_ALSA";
72               break;
73           case RtAudio::LINUX_PULSE:
74               return "LINUX_PULSE";
75               break;
76           case RtAudio::LINUX_OSS:
77               return "LINUX_OSS";
78               break;
79           case RtAudio::UNIX_JACK:
80               return "UNIX_JACK";
81               break;
82           case RtAudio::MACOSX_CORE:
83               return "MACOSX_CORE";
84               break;
85           case RtAudio::WINDOWS_WASAPI:
86               return "WINDOWS_WASAPI";
87               break;
88           case RtAudio::WINDOWS_ASIO:
89               return "WINDOWS_ASIO";
90               break;
91           case RtAudio::WINDOWS_DS:
92               return "WINDOWS_DS";
93               break;
94           default:
95               return "UNKNOWN";
96         }
97 
98       }
99 
100       // For callback usage only.
setCriticalVariables(unsigned segmentSize)101       void setCriticalVariables(unsigned segmentSize)
102       {
103         static bool _firstTime = true;
104         const unsigned idx = (_criticalVariablesIdx + 1) % 2;
105         _timeUSAtCycleStart[idx] = systemTimeUS();
106         // Let these start at zero and only increment on subsequent callbacks.
107         if(!_firstTime)
108         {
109           _framesAtCycleStart[idx] = _framesAtCycleStart[_criticalVariablesIdx] + segmentSize;
110           _frameCounter[idx] = _frameCounter[_criticalVariablesIdx] + segmentSize;
111         }
112         _firstTime = false;
113         // Now 'flip' the variables all at once.
114         _criticalVariablesIdx = idx;
115       }
116 
117       RtAudioDevice(bool forceDefault);
~RtAudioDevice()118       virtual ~RtAudioDevice()
119       {
120 
121         while (outputPortsList.size() > 0) {
122           MuseRtAudioPort *port = outputPortsList.takeFirst();
123           free (port->buffer);
124           free (port);
125         }
126 
127         while (inputPortsList.size() > 0) {
128           MuseRtAudioPort *port = inputPortsList.takeFirst();
129           free (port->buffer);
130           free (port);
131         }
132 
133       }
134 
deviceType()135       virtual inline int deviceType() const { return RTAUDIO_AUDIO; }
136 
137       virtual bool start(int);
138 
139       virtual void stop ();
framePos()140       virtual unsigned  framePos() const {
141             // Not much choice but to do this:
142             const unsigned int facs = framesAtCycleStart();
143             const unsigned int fscs = framesSinceCycleStart();
144             DEBUG_RTAUDIO(stderr, "RtAudioDevice::framePos framesAtCycleStart:%u framesSinceCycleStart:%u\n", facs, fscs);
145             return facs + fscs;
146             }
147 
148       // These are meant to be called from inside process thread only.
framesAtCycleStart()149       virtual unsigned framesAtCycleStart() const { return _framesAtCycleStart[_criticalVariablesIdx]; }
framesSinceCycleStart()150       virtual unsigned framesSinceCycleStart() const
151       {
152         const uint64_t ct = systemTimeUS();
153         DEBUG_RTAUDIO(stderr, "RtAudioDevice::framesSinceCycleStart systemTimeUS:%lu timeUSAtCycleStart:%lu\n",
154                       ct, _timeUSAtCycleStart[_criticalVariablesIdx]);
155         // Do not round up here since time resolution is higher than (audio) frame resolution.
156         unsigned f = muse_multiply_64_div_64_to_64(ct - _timeUSAtCycleStart[_criticalVariablesIdx], MusEGlobal::sampleRate, 1000000UL);
157 
158         // Safety due to inaccuracies. It cannot be after the segment, right?
159         if(f >= MusEGlobal::segmentSize)
160           f = MusEGlobal::segmentSize - 1;
161         return f;
162       }
163 
getBuffer(void * port,unsigned long nframes)164       virtual float* getBuffer(void* port, unsigned long nframes)
165       {
166         if (nframes > MusEGlobal::segmentSize) {
167 
168           fprintf(stderr, "RtAudioDevice::getBuffer nframes > segment size\n");
169 
170           exit(-1);
171         }
172 
173         return ((MuseRtAudioPort*)port)->buffer;
174       }
175 
outputPorts(bool,int)176       virtual std::list<QString> outputPorts(bool, int) {
177         std::list<QString> outlist;
178 
179         foreach (MuseRtAudioPort *port, outputPortsList) {
180 
181           outlist.push_back(port->name);
182         }
183 
184         return outlist;
185       }
186 
inputPorts(bool,int)187       virtual std::list<QString> inputPorts(bool, int) {
188         std::list<QString> inlist;
189 
190         foreach (MuseRtAudioPort *port, inputPortsList) {
191 
192           inlist.push_back(port->name);
193         }
194 
195         return inlist;
196       }
197 
registerClient()198       virtual void registerClient() {}
199 
clientName()200       virtual const char* clientName() { return "RtAudio"; }
201 
registerOutPort(const char * name,bool)202       virtual void* registerOutPort(const char* name, bool) {
203 
204         fprintf(stderr, "register output port [%s] length %d char %c\n", name, int(strlen(name)), name[strlen(name)-1] );
205 
206         foreach (MuseRtAudioPort *port, outputPortsList) {
207           if (port->name == name) {
208             fprintf(stderr, "RtAudioDevice::registerOutPort - port [%s] already exists, return existing.", name);
209             return port;
210           }
211         }
212 
213         MuseRtAudioPort *port = new MuseRtAudioPort();
214         port->name = name;
215         port->buffer = new float[MusEGlobal::segmentSize];
216         memset(port->buffer, 0, MusEGlobal::segmentSize * sizeof(float));
217 
218         outputPortsList.push_back(port);
219         return port;
220       }
221 
registerInPort(const char * name,bool)222       virtual void* registerInPort(const char* name, bool) {
223 
224         fprintf(stderr, "register input port [%s] length %d char %c\n", name, int(strlen(name)), name[strlen(name)-1] );
225 
226         foreach (MuseRtAudioPort *port, inputPortsList) {
227           if (port->name == name) {
228             fprintf(stderr, "RtAudioDevice::registerInPort - port [%s] already exists, return existing.", name);
229             return port;
230           }
231         }
232 
233         MuseRtAudioPort *port = new MuseRtAudioPort();
234         port->name = name;
235         port->buffer = new float[MusEGlobal::segmentSize];
236         memset(port->buffer, 0, MusEGlobal::segmentSize * sizeof(float));
237 
238         inputPortsList.push_back(port);
239         return port;
240       }
241 
portType(void *)242       virtual AudioDevice::PortType portType(void*) const { return AudioPort; }
portDirection(void *)243       virtual AudioDevice::PortDirection portDirection(void*) const { return OutputPort; }
unregisterPort(void *)244       virtual void unregisterPort(void*) {}
connect(void *,void *)245       virtual bool connect(void* /*src*/, void* /*dst*/)  {
246         DEBUG_RTAUDIO(stderr, "RtAudio::connect\n");
247         return false;
248       }
connect(const char *,const char *)249       virtual bool connect(const char* /*src*/, const char* /*dst*/)  {
250         DEBUG_RTAUDIO(stderr, "RtAudio::connect2\n");
251         return false;
252       }
disconnect(void *,void *)253       virtual bool disconnect(void* /*src*/, void* /*dst*/)  { return false; }
disconnect(const char *,const char *)254       virtual bool disconnect(const char* /*src*/, const char* /*dst*/)  { return false; }
connections(void *)255       virtual int connections(void* /* clientPort */) {
256         DEBUG_RTAUDIO(stderr, "RtAudio::connections\n");
257         return 1; // always return nonzero, for now
258       }
portConnectedTo(void *,const char *)259       virtual bool portConnectedTo(void*, const char*) {
260         DEBUG_RTAUDIO(stderr, "RtAudio::portConnectedTo\n");
261         return false;
262       }
portsCanDisconnect(void *,void *)263       virtual bool portsCanDisconnect(void* /*src*/, void* /*dst*/) const {
264         DEBUG_RTAUDIO(stderr, "RtAudio::portCanDisconnect\n");
265         return false;
266       }
portsCanDisconnect(const char *,const char *)267       virtual bool portsCanDisconnect(const char* /*src*/, const char* /*dst*/) const {
268         DEBUG_RTAUDIO(stderr, "RtAudio::portCanDisconnect2\n");
269         return false;
270       }
portsCanConnect(void *,void *)271       virtual bool portsCanConnect(void* /*src*/, void* /*dst*/) const {
272         DEBUG_RTAUDIO(stderr, "RtAudio::portCanConnect\n");
273         return false;
274       }
portsCanConnect(const char *,const char *)275       virtual bool portsCanConnect(const char* /*src*/, const char* /*dst*/) const {
276         DEBUG_RTAUDIO(stderr, "RtAudio::portCanConnect\n");
277         return false;
278       }
portsCompatible(void *,void *)279       virtual bool portsCompatible(void* /*src*/, void* /*dst*/) const {
280         DEBUG_RTAUDIO(stderr, "RtAudio::portCompatible\n");
281         return false;
282       }
portsCompatible(const char *,const char *)283       virtual bool portsCompatible(const char* /*src*/, const char* /*dst*/) const {
284         DEBUG_RTAUDIO(stderr, "RtAudio::portCompatible2\n");
285         return false;
286       }
setPortName(void *,const char *)287       virtual void setPortName(void*, const char*) {
288         DEBUG_RTAUDIO(stderr, "RtAudio::setPortName\n");
289       }
findPort(const char *)290       virtual void* findPort(const char*) {
291         DEBUG_RTAUDIO(stderr, "RtAudio::findPort\n");
292         return 0;
293       }
294       // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias.
295       virtual char*  portName(void*, char* str, int str_size, int /*preferred_name_or_alias*/ = -1) {
296         DEBUG_RTAUDIO(stderr, "RtAudio::portName %s\n", str);
297         if(str_size == 0) {
298           return 0;
299         }
300         str[0] = '\0';
301         return str;
302       }
canonicalPortName(void *)303       virtual const char* canonicalPortName(void*) {
304         DEBUG_RTAUDIO(stderr, "RtAudio::canonicalPortName\n");
305         return 0;
306       }
portLatency(void *,bool)307       virtual unsigned int portLatency(void* /*port*/, bool /*capture*/) const {
308         DEBUG_RTAUDIO(stderr, "RtAudio::portLatency\n");
309         return 0;
310       }
311 
frameTime()312       virtual unsigned frameTime() const {
313         return _frameCounter[_criticalVariablesIdx];
314       }
315 
isRealtime()316       virtual bool isRealtime() { return MusEGlobal::realTimeScheduling; }
realtimePriority()317       virtual int realtimePriority() const { return 40; }
318 
setFreewheel(bool)319       virtual void setFreewheel(bool) {}
320       virtual int setMaster(bool, bool /*unconditional*/ = false) { return 1; }
321 };
322 
323 } // namespace MusECore
324