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