tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_ReWirePlugin.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14#if TRACKTION_ENABLE_REWIRE
15
16#include "ReWire/ReWireSDK/ReWire.h"
17#include "ReWire/ReWireSDK/ReWireAPI.h"
18#include "ReWire/ReWireSDK/ReWireMixerAPI.h"
19
20#if JUCE_WINDOWS
21 #pragma comment(lib, "version.lib")
22#endif
23
24using namespace ReWire;
25
26//==============================================================================
27static juce::String getReWireErrorMessage (ReWireError err)
28{
29 const char* e = "Unknown error";
30
31 switch (err)
32 {
33 case kReWireError_AccessDenied: e = "Access Denied"; break;
34 case kReWireError_ReWireOpenByOtherApplication: e = "ReWire in use by another application"; break;
35 case kReWireError_DLLNotFound: e = "ReWire DLL not found"; break;
36 case kReWireError_DLLTooOld: e = "ReWire DLL too old"; break;
37 case kReWireError_UnableToLoadDLL: e = "Unable to load ReWire DLL"; break;
38 case kReWireError_NotEnoughMemoryForDLL: e = "Not enough memory for ReWire DLL"; break;
39 case kReWireError_OutOfMemory: e = "Out of memory"; break;
40 case kReWireError_UnableToOpenDevice: e = "Unable to open device"; break;
41 case kReWireError_AlreadyExists: e = "Already exists"; break;
42 case kReWireError_NotFound: e = "Not found"; break;
43 case kReWireError_Busy: e = "Busy"; break;
44 case kReWireError_BufferFull: e = "Buffer full"; break;
45 case kReWireError_PortNotConnected: e = "Port not connected"; break;
46 case kReWireError_PortConnected: e = "Port connected"; break;
47 case kReWireError_PortStale: e = "Port stale"; break;
48 case kReWireError_ReadError: e = "Read error"; break;
49 case kReWireError_NoMoreMessages: e = "No more messages"; break;
50 case kReWireImplError_ReWireNotOpen: e = "ReWire not open"; break;
51 case kReWireImplError_ReWireAlreadyOpen: e = "ReWire already open"; break;
52 case kReWireImplError_DeviceNotOpen: e = "Device not open"; break;
53 case kReWireImplError_DeviceAlreadyOpen: e = "Device already open"; break;
54 case kReWireImplError_AudioInfoInvalid: e = "Audio info invalid"; break;
55 case kReWireImplError_InvalidParameter: e = "Invalid Parameter"; break;
56 case kReWireImplError_InvalidSignature: e = "Invalid Signature"; break;
57 case kReWireError_Undefined: e = "Undefined error"; break;
58 case kReWireError_NoError: e = "No Error"; break;
59 default: break;
60 }
61
62 return e;
63}
64
65static void logRewireError (ReWireError res)
66{
67 if (res != kReWireError_NoError)
68 TRACKTION_LOG_ERROR (getReWireErrorMessage (res));
69}
70
71const uint32_t inputEventBufferSize = 200;
72
73//==============================================================================
81class ReWireSystem::Device : private Timer
82{
83public:
84 Device (Engine& e, TRWMDeviceHandle h, const juce::String& name, int index)
85 : engine (e),
86 handle (h),
87 deviceName (name),
88 deviceIndex (index),
89 buffer (2, 128)
90 {
92 jassert (isReWireEnabled (engine));
93
94 std::memset (reWireToLocalChanMap, 0, sizeof (reWireToLocalChanMap));
95
96 ReWireDeviceInfo devInfo;
97 ReWirePrepareDeviceInfo (&devInfo);
98 auto res = RWMGetDeviceInfo (index, &devInfo);
99
100 if (res == kReWireError_NoError)
101 {
102 for (int i = 0; i < devInfo.fChannelCount; ++i)
103 channelNames.add (juce::CharPointer_UTF8 (devInfo.fChannelNames[i]));
104 }
105 else
106 {
107 logRewireError (res);
108 }
109
110 // prepare all this stuff in case the timer has to call DriveAudio before the
111 // real callback
112 ReWireDriveAudioInputParams& in = inputToDeviceParams;
113 ReWireDriveAudioOutputParams& out = outputFromDeviceParams;
114
115 ReWirePrepareDriveAudioInputParams (&in, inputEventBufferSize, inputToDeviceBuffer);
116
117 ReWireClearBitField (in.fRequestedChannelsBitField, kReWireAudioChannelCount);
118
119 outputEventBufferSize = (uint32_t) std::max (32, (int) devInfo.fMaxEventOutputBufferSize);
120 outputFromDeviceBuffer.calloc (outputEventBufferSize);
121
122 ReWirePrepareDriveAudioOutputParams (&out, (ReWire_uint32_t) outputEventBufferSize, outputFromDeviceBuffer);
123
124 in.fFramesToRender = 128;
125 in.fTempo = 1000 * 120;
126 in.fSignatureNumerator = 4;
127 in.fSignatureDenominator = 4;
128 in.fLoopStartPPQ15360Pos = rewireLoopStart;
129 in.fLoopEndPPQ15360Pos = rewireLoopEnd;
130 in.fLoopOn = rewireLooping;
131
132 ReWirePrepareEventTarget (&eventTarget, 0, 0);
133
134 startTimer (50);
135 }
136
137 bool closeIfPossible()
138 {
140
141 if (handle == nullptr)
142 return true;
143
144 char okFlag = 0;
145
146 if (RWMIsCloseDeviceOK (handle, &okFlag) == kReWireError_NoError && okFlag != 0)
147 {
148 if (RWMCloseDevice (handle) == kReWireError_NoError)
149 {
150 handle = nullptr;
151 return true;
152 }
153 }
154
155 return false;
156 }
157
158 void addReference()
159 {
160 jassert (isReWireEnabled (engine));
161 ++references;
162 }
163
164 void removeReference()
165 {
166 --references;
167 jassert (references >= 0);
168 }
169
170 void prepareToPlay (double sr, int blockSize, int leftChanIndex, int rightChanIndex, Edit* edit)
171 {
173 jassert (isReWireEnabled (engine));
174 jassert (edit != nullptr);
175
176 const juce::ScopedLock sl (lock);
177
178 buffer.clear();
179 storedMessages.clear();
180
181 sampleRate = sr;
182 editRef = edit;
183
184 bufferSourceChannels.setBit (leftChanIndex);
185 bufferSourceChannels.setBit (rightChanIndex);
186 buffer.setSize (bufferSourceChannels.countNumberOfSetBits(), blockSize);
187
188 std::memset (reWireToLocalChanMap, 0, sizeof (reWireToLocalChanMap));
189 int localChan = 0;
190
191 for (int i = 0; i < kReWireAudioChannelCount; ++i)
192 if (bufferSourceChannels[i])
193 reWireToLocalChanMap[i] = (short) localChan++;
194
195 ReWireDeviceInfo devInfo;
196 ReWirePrepareDeviceInfo (&devInfo);
197 auto res = RWMGetDeviceInfo (deviceIndex, &devInfo);
198 logRewireError (res);
199
200 ReWireAudioInfo info;
201 ReWirePrepareAudioInfo (&info, (ReWire_int32_t) sr, blockSize + 512);
202 res = RWMSetAudioInfo (&info);
203
204 if (res != kReWireError_NoError)
205 {
206 logRewireError (res);
207 engine.getUIBehaviour().showWarningMessage (TRANS("Couldn't start ReWire plugin")
208 + ": " + getReWireErrorMessage (res));
209 }
210 else
211 {
212 auto& in = inputToDeviceParams;
213 auto& out = outputFromDeviceParams;
214
215 // setup the input fields
216 ReWirePrepareDriveAudioInputParams (&in,
217 inputEventBufferSize,
218 inputToDeviceBuffer);
219
220 ReWireClearBitField (in.fRequestedChannelsBitField, kReWireAudioChannelCount);
221
222 for (int i = kReWireAudioChannelCount; --i >= 0;)
223 {
224 if (bufferSourceChannels[i])
225 {
226 ReWireSetBitInBitField (in.fRequestedChannelsBitField, (unsigned short)i);
227 in.fAudioBuffers[i] = buffer.getWritePointer (reWireToLocalChanMap[i], 0);
228 }
229 }
230
231 // setup the output fields
232 outputEventBufferSize = (uint32_t) std::max (32, (int) devInfo.fMaxEventOutputBufferSize);
233 outputFromDeviceBuffer.calloc (outputEventBufferSize);
234
235 ReWirePrepareDriveAudioOutputParams (&out, (ReWire_uint32_t) outputEventBufferSize, outputFromDeviceBuffer);
236
237 rewireLoopStart = 0;
238 rewireLoopEnd = 0;
239 rewireLooping = 0;
240
241 if (edit != nullptr)
242 {
243 auto markPos = createPosition (edit->tempoSequence);
244 const auto loopRange = edit->getTransport().getLoopRange();
245
246 markPos.set (loopRange.getStart());
247 rewireLoopStart = juce::roundToInt (markPos.getPPQTime() * kReWirePPQ);
248
249 markPos.set (loopRange.getEnd());
250 rewireLoopEnd = juce::roundToInt (markPos.getPPQTime() * kReWirePPQ);
251
252 rewireLooping = edit->getTransport().looping;
253 }
254
255 // time limit for guessing if we need to chase the time
256 timePerBlock = TimeDuration::fromSamples (0.060 + blockSize, sampleRate);
257
258 ReWirePrepareEventTarget (&eventTarget, 0, 0);
259
260 timeSigRequest = false;
261 requestTempo = false;
262 requestLoop = false;
263 requestedReposition = false;
264 requestedPlay = false;
265 requestedStop = false;
266 wasPlaying = false;
267 }
268 }
269
270 void deinitialise()
271 {
272 bufferSourceChannels.clear();
273 storedMessages.clear();
274 }
275
276 void updateTempoInfo (const tempo::Sequence::Position& position)
277 {
278 const auto bpm = position.getTempo();
279 const auto [numerator, denominator] = position.getTimeSignature();
280
281 inputToDeviceParams.fTempo = (bpm < 10) ? 120000 : (ReWire_uint32_t) (1000 * bpm);
282 inputToDeviceParams.fSignatureNumerator = (numerator <= 0) ? 4 : (ReWire_uint32_t) numerator;
283 inputToDeviceParams.fSignatureDenominator = (denominator == 0) ? 4 : (ReWire_uint32_t) denominator;
284
285 inputToDeviceParams.fPPQ15360TickOfBatchStart = juce::roundToInt (position.getPPQTime() * kReWirePPQ);
286
287 pluginsServedThisFrame = 0;
288 }
289
290 void getAudioOutput (const PluginRenderContext& fc,
291 int leftChannelIndex, int rightChannelIndex,
292 int bus, int channel)
293 {
294 const juce::ScopedLock sl (lock);
295
296 auto& in = inputToDeviceParams;
297 auto& out = outputFromDeviceParams;
298
299 if (fc.bufferForMidiMessages != nullptr && references <= 1)
300 {
301 fc.bufferForMidiMessages->sortByTimestamp();
302
303 auto num = std::min ((int) fc.bufferForMidiMessages->size(),
304 (int) inputEventBufferSize);
305
306 auto* event = &in.fEventInBuffer.fEventBuffer [in.fEventInBuffer.fCount];
307
308 for (int i = 0; i < num; ++i)
309 {
310 auto& m = (*fc.bufferForMidiMessages)[i];
311 const int type = *(m.getRawData());
312
313 if (type < 0xf0 && type >= 0x80)
314 {
315 setupMidiEvent (*ReWireConvertToMIDIEvent (event, &eventTarget),
316 type, m, fc.bufferNumSamples, bus, channel);
317
318 ++event;
319 in.fEventInBuffer.fCount++;
320 }
321 }
322
323 fc.bufferForMidiMessages->clear();
324 }
325
326 if (pluginsServedThisFrame == 0)
327 {
328 in.fFramesToRender = (ReWire_uint32_t) fc.bufferNumSamples;
329 bool isPlaying = this->isPlaying (fc, in);
330
331 in.fLoopStartPPQ15360Pos = rewireLoopStart;
332 in.fLoopEndPPQ15360Pos = rewireLoopEnd;
333 in.fLoopOn = rewireLooping && ! fc.isRendering;
334
335 out.fEventOutBuffer.fCount = 0;
336 ReWireClearBitField (out.fServedChannelsBitField, kReWireAudioChannelCount);
337
338 if (storedMessages.size() > 0)
339 {
340 std::sort (storedMessages.begin(), storedMessages.end(),
341 [] (ReWireMIDIEvent* a, ReWireMIDIEvent* b) { return a->fRelativeSamplePos < b->fRelativeSamplePos; });
342
343 auto* event = &in.fEventInBuffer.fEventBuffer [in.fEventInBuffer.fCount];
344 auto num = std::min (storedMessages.size(), (int) inputEventBufferSize);
345
346 for (int i = 1; i < num; ++i)
347 {
348 auto* e1 = storedMessages.getUnchecked (i - 1);
349 auto* e2 = storedMessages.getUnchecked (i);
350
351 if (e1->fData1 == e2->fData1
352 && e1->fRelativeSamplePos == e2->fRelativeSamplePos
353 && e1->fMIDIEventType == 0x90
354 && e2->fMIDIEventType == 0x80)
355 {
356 e1->fMIDIEventType = 0x80;
357 e2->fMIDIEventType = 0x90;
358 std::swap (e1->fData2, e2->fData2);
359 }
360 }
361
362 for (int i = 0; i < num; ++i)
363 {
364 auto* midiEvent = ReWireConvertToMIDIEvent (event, &eventTarget);
365 *midiEvent = *storedMessages.getUnchecked(i);
366 ++event;
367 in.fEventInBuffer.fCount++;
368 }
369
370 storedMessages.clear();
371 }
372
373 if (wasPlaying && ! isPlaying)
374 in.fEventInBuffer.fCount = 0;
375
376 wasPlaying = isPlaying;
377
378 if (handle != nullptr)
379 RWMDriveAudio (handle, &in, &out);
380
382
383 in.fEventInBuffer.fCount = 0;
384 }
385
386 if (references > 1 && fc.bufferForMidiMessages != nullptr)
387 {
388 for (auto& m : *fc.bufferForMidiMessages)
389 {
390 auto type = *(m.getRawData());
391
392 if (type < 0xf0 && type >= 0x80)
393 {
394 auto midiEvent = new ReWireMIDIEvent();
395
396 setupMidiEvent (*ReWireConvertToMIDIEvent ((ReWireEvent*) midiEvent, &eventTarget),
397 type, m, fc.bufferNumSamples, bus, channel);
398
399 storedMessages.add (midiEvent);
400 }
401 }
402
403 fc.bufferForMidiMessages->clear();
404 }
405
406 if (pluginsServedThisFrame++ == 0)
407 handleEvents (out, fc.bufferForMidiMessages);
408
409 if (fc.destBuffer != nullptr)
410 {
411 if (ReWireIsBitInBitFieldSet (out.fServedChannelsBitField, (unsigned short) leftChannelIndex))
412 juce::FloatVectorOperations::copy (fc.destBuffer->getWritePointer (0, fc.bufferStartSample),
413 in.fAudioBuffers[leftChannelIndex],
414 fc.bufferNumSamples);
415
416 if (ReWireIsBitInBitFieldSet (out.fServedChannelsBitField, (unsigned short) rightChannelIndex))
417 juce::FloatVectorOperations::copy (fc.destBuffer->getWritePointer (1, fc.bufferStartSample),
418 in.fAudioBuffers[rightChannelIndex],
419 fc.bufferNumSamples);
420 }
421 }
422
423 void setupMidiEvent (ReWireMIDIEvent& e, int type, const juce::MidiMessage& m,
424 int numSamples, int bus, int channel) const
425 {
426 e.fMIDIEventType = (unsigned short) (0xf0 & type);
427 e.fData1 = m.getRawData()[1];
428 e.fData2 = m.getRawData()[2];
429
430 // need to make sure note-ons with vel=0 are converted to note-offs
431 if (e.fData2 == 0 && (e.fMIDIEventType & 0xf0) == 0x90)
432 e.fMIDIEventType = 0x80;
433
434 e.fRelativeSamplePos = juce::jlimit (0, numSamples - 1, juce::roundToInt (m.getTimeStamp() * sampleRate));
435 e.fEventTarget.fMIDIBusIndex = (unsigned short) bus;
436 e.fEventTarget.fChannel = (unsigned short) channel;
437 }
438
439 bool isPlaying (const PluginRenderContext& fc, ReWireDriveAudioInputParams& in)
440 {
441 const auto playheadOutputTime = fc.editTime.getStart();
442
443 if ((fc.isPlaying && playheadOutputTime >= 0s) || fc.isRendering)
444 {
445 if (lastTime > playheadOutputTime || lastTime < playheadOutputTime - timePerBlock)
446 in.fPlayMode = kReWirePlayModeChaseAndPlay;
447 else
448 in.fPlayMode = kReWirePlayModeKeepPlaying;
449
450 lastTime = playheadOutputTime;
451 return true;
452 }
453
454 in.fPlayMode = kReWirePlayModeStop;
455 return false;
456 }
457
458 void handleEvents (ReWireDriveAudioOutputParams& out,
459 MidiMessageArray* bufferForMidiMessages)
460 {
461 auto numEventsOut = (int) out.fEventOutBuffer.fCount;
462
463 for (int i = 0; i < numEventsOut; ++i)
464 {
465 auto event = &out.fEventOutBuffer.fEventBuffer[i];
466
467 switch (event->fEventType)
468 {
469 case kReWireRequestSignatureEvent:
470 {
471 auto theEvent = ReWireCastToRequestSignatureEvent (event);
472 requestedTimeSigNum = std::max (1, (int) theEvent->fSignatureNumerator);
473 requestedTimeSigDenom = std::max (1, (int) theEvent->fSignatureDenominator);
474 timeSigRequest = true;
475 }
476 break;
477
478 case kReWireRequestTempoEvent:
479 {
480 auto theEvent = ReWireCastToRequestTempoEvent (event);
481 requestedTempo = (int) theEvent->fTempo;
482 requestTempo = true;
483 }
484 break;
485
486 case kReWireRequestLoopEvent:
487 {
488 auto theEvent = ReWireCastToRequestLoopEvent (event);
489 rewireLoopStart = theEvent->fLoopStartPPQ15360Pos;
490 rewireLoopEnd = theEvent->fLoopEndPPQ15360Pos;
491 rewireLooping = theEvent->fLoopOn != 0;
492 requestLoop = true;
493 }
494 break;
495
496 case kReWireRequestRepositionEvent:
497 {
498 auto theEvent = ReWireCastToRequestRepositionEvent (event);
499 requestedPosition = theEvent->fPPQ15360Pos / (double) kReWirePPQ;
500 requestedReposition = true;
501 }
502 break;
503
504 case kReWireRequestPlayEvent:
505 requestedPlay = true;
506 break;
507
508 case kReWireRequestStopEvent:
509 requestedStop = true;
510 break;
511
512 case kReWireMIDIEvent:
513 if (bufferForMidiMessages != nullptr)
514 {
515 auto m = ReWireCastToMIDIEvent (event);
516
517 bufferForMidiMessages->addMidiMessage (juce::MidiMessage (m->fMIDIEventType | (0xf & m->fEventTarget.fChannel),
518 m->fData1, m->fData2),
519 0, midiSourceID);
520 }
521 break;
522
523 default:
524 break;
525 }
526 }
527 }
528
529 void timerCallback() override
530 {
531 if (timeSigRequest)
532 {
534
535 if (auto edit = editRef.get())
536 if (edit->tempoSequence.getNumTempos() == 1)
537 edit->tempoSequence.getTimeSig(0)->setStringTimeSig (juce::String (requestedTimeSigNum)
538 + "/"
539 + juce::String (requestedTimeSigDenom));
540
541 timeSigRequest = false;
542 }
543
544 if (requestTempo)
545 {
547
548 if (auto edit = editRef.get())
549 if (edit->tempoSequence.getNumTempos() == 1)
550 edit->tempoSequence.getTempo(0)->setBpm (requestedTempo / 1000.0);
551
552 requestTempo = false;
553 }
554
555 if (requestLoop)
556 {
558
559 if (auto edit = editRef.get())
560 {
561 edit->getTransport().looping = rewireLooping;
562
563 auto markPos = createPosition (edit->tempoSequence);
564
565 markPos.setPPQTime (rewireLoopStart / (double) kReWirePPQ);
566 edit->getTransport().setLoopIn (markPos.getTime());
567
568 markPos.setPPQTime (rewireLoopEnd / (double) kReWirePPQ);
569 edit->getTransport().setLoopOut (markPos.getTime());
570 }
571
572 requestLoop = false;
573 }
574 else
575 {
576 if (auto edit = editRef.get())
577 {
579
580 auto markPos = createPosition (edit->tempoSequence);
581 const auto loopRange = edit->getTransport().getLoopRange();
582 markPos.set (loopRange.getStart());
583 rewireLoopStart = juce::roundToInt (markPos.getPPQTime() * kReWirePPQ);
584
585 markPos.set (loopRange.getEnd());
586 rewireLoopEnd = juce::roundToInt (markPos.getPPQTime() * kReWirePPQ);
587
588 rewireLooping = edit->getTransport().looping;
589 }
590 }
591
592 if (requestedReposition)
593 {
594 if (auto edit = editRef.get())
595 {
597
598 auto pos = createPosition (edit->tempoSequence);
599 pos.setPPQTime (requestedPosition);
600 edit->getTransport().setPosition (pos.getTime());
601 }
602
603 requestedReposition = false;
604 }
605
606 if (requestedPlay)
607 {
609 if (auto edit = editRef.get())
610 edit->getTransport().play (true);
611
612 requestedPlay = false;
613 }
614
615 if (requestedStop)
616 {
618 if (auto edit = editRef.get())
619 edit->getTransport().stop (false, false);
620
621 requestedStop = false;
622 }
623
624 if (juce::Time::getApproximateMillisecondCounter() - lastDriveAudioTime > 400)
625 {
626 wasPlaying = false;
627
628 ReWireDriveAudioInputParams& in = inputToDeviceParams;
629 ReWireDriveAudioOutputParams& out = outputFromDeviceParams;
630
631 const juce::ScopedLock sl (lock);
632
633 // might have been waiting for real callback, so check again..
634 if (juce::Time::getApproximateMillisecondCounter() - lastDriveAudioTime > 400)
635 {
637
638 in.fPlayMode = kReWirePlayModeStop;
639 in.fEventInBuffer.fCount = 0;
640
641 in.fLoopStartPPQ15360Pos = rewireLoopStart;
642 in.fLoopEndPPQ15360Pos = rewireLoopEnd;
643 in.fLoopOn = rewireLooping;
644
645 out.fEventOutBuffer.fCount = 0;
646 ReWireClearBitField (out.fServedChannelsBitField, kReWireAudioChannelCount);
647
648 if (handle != 0)
649 RWMDriveAudio (handle, &in, &out);
650
651 const int numEventsOut = (int) out.fEventOutBuffer.fCount;
652
653 for (int i = 0; i < numEventsOut; ++i)
654 {
655 auto event = &out.fEventOutBuffer.fEventBuffer[i];
656
657 switch (event->fEventType)
658 {
659 case kReWireRequestSignatureEvent:
660 {
661 auto theEvent = ReWireCastToRequestSignatureEvent (event);
662 requestedTimeSigNum = std::max (1, (int) theEvent->fSignatureNumerator);
663 requestedTimeSigDenom = std::max (1, (int) theEvent->fSignatureDenominator);
664 timeSigRequest = true;
665 }
666 break;
667
668 case kReWireRequestTempoEvent:
669 {
670 auto theEvent = ReWireCastToRequestTempoEvent (event);
671 requestedTempo = (int) theEvent->fTempo;
672 requestTempo = true;
673 }
674 break;
675
676 case kReWireRequestLoopEvent:
677 {
678 auto theEvent = ReWireCastToRequestLoopEvent (event);
679 rewireLoopStart = theEvent->fLoopStartPPQ15360Pos;
680 rewireLoopEnd = theEvent->fLoopEndPPQ15360Pos;
681 rewireLooping = theEvent->fLoopOn != 0;
682 requestLoop = true;
683 }
684 break;
685
686 case kReWireRequestRepositionEvent:
687 {
688 auto theEvent = ReWireCastToRequestRepositionEvent (event);
689 requestedPosition = theEvent->fPPQ15360Pos / (double)kReWirePPQ;
690 requestedReposition = true;
691 }
692 break;
693
694 case kReWireRequestPlayEvent:
695 requestedPlay = true;
696 break;
697
698 case kReWireRequestStopEvent:
699 requestedStop = true;
700 break;
701
702 default:
703 break;
704 }
705 }
706 }
707 }
708 }
709
710 Engine& engine;
711 TRWMDeviceHandle handle;
712 juce::String deviceName;
713 juce::StringArray channelNames;
714
715private:
716 const int deviceIndex;
717 ReWireDriveAudioInputParams inputToDeviceParams;
718 ReWireDriveAudioOutputParams outputFromDeviceParams;
719 ReWireEvent inputToDeviceBuffer [inputEventBufferSize];
720 juce::HeapBlock<ReWireEvent> outputFromDeviceBuffer;
721 uint32_t outputEventBufferSize = 0;
722 ReWireEventTarget eventTarget;
723
724 uint32_t lastDriveAudioTime = 0;
726 short reWireToLocalChanMap[kReWireAudioChannelCount];
727 juce::BigInteger bufferSourceChannels;
728
730 MidiMessageArray::MPESourceID midiSourceID = MidiMessageArray::createUniqueMPESourceID();
731
732 int references = 0, pluginsServedThisFrame = 0;
733 double sampleRate = 0;
734 TimePosition lastTime;
735 TimeDuration timePerBlock;
736 bool wasPlaying = false;
737 SafeSelectable<Edit> editRef;
738
739 double requestedPosition = 0;
740 int requestedTempo = 0, requestedTimeSigNum = 0, requestedTimeSigDenom = 0;
741 int rewireLoopStart = 0, rewireLoopEnd = 0;
742 bool timeSigRequest = false;
743 bool requestTempo = false;
744 bool rewireLooping = false;
745 bool requestLoop = false;
746 bool requestedReposition = false;
747 bool requestedPlay = false;
748 bool requestedStop = false;
749
751
753};
754
755//==============================================================================
756static ReWireSystem* rewireSystemInstance = nullptr;
757
758ReWireSystem::ReWireSystem (Engine& e) : engine (e)
759{
761 jassert (rewireSystemInstance == nullptr);
762 jassert (isReWireEnabled (engine));
763
764 TRACKTION_LOG ("Initialising ReWire...");
765
766 ReWireOpenInfo openInfo;
767 ReWirePrepareOpenInfo (&openInfo, 44100, 6400);
768
769 auto res = RWMOpen (&openInfo);
770
771 if (res != kReWireError_NoError)
772 {
773 openError = getReWireErrorMessage (res);
774 logRewireError (res);
775 }
776 else
777 {
778 isOpen = true;
779
780 ReWire_int32_t numDevs = 0;
781 res = RWMGetDeviceCount (&numDevs);
782
783 if (res == kReWireError_NoError)
784 {
785 for (int i = 0; i < numDevs; ++i)
786 {
787 ReWireDeviceInfo devInfo;
788 ReWirePrepareDeviceInfo (&devInfo);
789
790 res = RWMGetDeviceInfo (i, &devInfo);
791
792 if (res == kReWireError_NoError)
793 {
794 deviceNames.add (juce::CharPointer_UTF8 (devInfo.fName));
795 devices.add (nullptr);
796 }
797 else
798 {
799 logRewireError (res);
800 }
801 }
802
803 if (numDevs > 0)
804 startTimer (100); // for idle calls
805 }
806 else
807 {
808 logRewireError (res);
809 }
810
811 RWMIdle();
812 }
813}
814
815ReWireSystem::~ReWireSystem()
816{
817 jassert (rewireSystemInstance == this);
818 closeSystem();
819 rewireSystemInstance = nullptr;
820}
821
822bool ReWireSystem::isReWireEnabled (Engine& e, bool returnCurrentState)
823{
824 if (returnCurrentState)
825 {
826 static bool systemEnabled = isReWireEnabled (e, false);
827 return systemEnabled;
828 }
829
830 return e.getPropertyStorage().getProperty (SettingID::reWireEnabled, true);
831}
832
833void ReWireSystem::setReWireEnabled (Engine& e, bool b)
834{
835 e.getPropertyStorage().setProperty (SettingID::reWireEnabled, b);
836}
837
838ReWireSystem* ReWireSystem::getInstanceIfActive() { return rewireSystemInstance; }
839
840const char* ReWireSystem::getReWireLibraryName() { return ReWire::getReWireLibraryName(); }
841const char* ReWireSystem::getReWireFolderName() { return ReWire::getReWireFolderName(); }
842const char* ReWireSystem::getPropellerheadFolderName() { return ReWire::getPropellerheadFolderName(); }
843int ReWireSystem::getRequiredVersionNumMajor() { return ReWire::getRequiredVersionNumMajor(); }
844int ReWireSystem::getRequiredVersionNumMinor() { return ReWire::getRequiredVersionNumMinor(); }
845
846void ReWireSystem::initialise (Engine& e)
847{
849
850 if (rewireSystemInstance == nullptr && isReWireEnabled (e))
851 {
852 setReWireEnabled (e, false);
853
854 {
855 DeadMansPedalMessage dmp (e.getPropertyStorage(),
856 TRANS("The ReWire system failed to start up correctly last time "
857 "Tracktion ran - it has now been disabled (see the settings panel to re-enable it)")
858 .replace ("Tracktion", e.getPropertyStorage().getApplicationName()));
859
860 rewireSystemInstance = new ReWireSystem (e);
861 }
862
863 setReWireEnabled (e, true);
864 }
865}
866
867bool ReWireSystem::shutdown()
868{
870
871 if (rewireSystemInstance != nullptr)
872 {
873 if (rewireSystemInstance->tryToCloseAllOpenDevices())
874 {
875 delete rewireSystemInstance;
876 return true;
877 }
878
879 return false;
880 }
881
882 return true;
883}
884
885bool ReWireSystem::closeSystem()
886{
888 jassert (isReWireEnabled (engine));
889
890 if (isOpen)
891 {
892 isOpen = false;
893 char okFlag = 0;
894
895 if (RWMIsCloseOK (&okFlag) == kReWireError_NoError && okFlag)
896 {
897 auto res = RWMClose();
898
899 if (res == kReWireError_NoError)
900 return true;
901
903 logRewireError (res);
904 openError = getReWireErrorMessage (res);
905 }
906 }
907
908 return false;
909}
910
911ReWireSystem::Device* ReWireSystem::openDevice (const juce::String& devName, juce::String& error)
912{
914 jassert (isOpen);
915 jassert (isReWireEnabled (engine));
916 auto index = deviceNames.indexOf (devName);
917
918 if (index >= 0)
919 {
920 if (auto dev = devices[index])
921 {
922 dev->addReference();
923 return dev;
924 }
925
926 DeadMansPedalMessage dmp (engine.getPropertyStorage(),
927 "The ReWire device \"" + devName + "\" crashed while being initialised.\n\n"
928 "You may want to remove this device or disable ReWire (in Tracktion's settings panel).");
929
930 if (auto dev = createDevice (index, devName, error))
931 {
932 devices.set (index, dev);
933 dev->addReference();
934 return dev;
935 }
936 }
937
938 if (error.isEmpty())
939 error = TRANS("Unknown device");
940
941 return {};
942}
943
944ReWireSystem::Device* ReWireSystem::createDevice (int index, const juce::String& devName, juce::String& error)
945{
947 TRWMDeviceHandle handle = nullptr;
948 auto res = RWMOpenDevice (index, &handle);
949
950 if (res != kReWireError_NoError)
951 {
952 logRewireError (res);
953 error = getReWireErrorMessage (res);
954 return {};
955 }
956
957 return new Device (engine, handle, devName, index);
958}
959
960bool ReWireSystem::tryToCloseAllOpenDevices()
961{
962 if (! isOpen)
963 return true;
964
966
967 bool ok = true;
968 bool waitForDevices = false;
969
970 for (auto& dev : devices)
971 {
972 if (dev != nullptr)
973 {
974 char isRunningFlag = 0;
975
976 if (RWMIsPanelAppLaunched (dev->handle, &isRunningFlag) == kReWireError_NoError
977 && isRunningFlag != 0)
978 {
979 auto res = RWMQuitPanelApp (dev->handle);
980 jassert (res == kReWireError_NoError); (void) res;
981
982 RWMIdle();
983 }
984
985 ok = ok && dev->closeIfPossible();
986 waitForDevices = true;
987 }
988 }
989
990 TRACKTION_LOG ("ReWire - closing system");
991
992 if (ok && closeSystem())
993 return true;
994
995 juce::ignoreUnused (waitForDevices);
996
997 #if JUCE_WINDOWS
998 if (waitForDevices)
999 {
1000 for (int j = 20; --j >= 0;)
1001 {
1002 RWMIdle();
1004 }
1005 }
1006 #endif
1007
1008 TRACKTION_LOG ("ReWire - done");
1009 return false;
1010}
1011
1012void ReWireSystem::timerCallback()
1013{
1015 auto err = RWMIdle();
1016 jassert (err == kReWireError_NoError); (void) err;
1017}
1018
1019
1020//==============================================================================
1021const char* ReWirePlugin::xmlTypeName = "ReWire";
1022
1023ReWirePlugin::ReWirePlugin (PluginCreationInfo info) : Plugin (info)
1024{
1025 auto* um = getUndoManager();
1026
1027 currentDeviceName.referTo (state, IDs::device, um);
1028 currentChannelNameL.referTo (state, IDs::channelL, um);
1029 currentChannelNameR.referTo (state, IDs::channelR, um);
1030 currentBus.referTo (state, IDs::bus, um);
1031 currentChannel.referTo (state, IDs::channel, um);
1032
1033 if (ReWireSystem::isReWireEnabled (info.edit.engine))
1034 ReWireSystem::initialise (info.edit.engine);
1035}
1036
1037ReWirePlugin::~ReWirePlugin()
1038{
1039 if (device != nullptr)
1040 device->removeReference();
1041
1042 notifyListenersOfDeletion();
1043}
1044
1045void ReWirePlugin::initialiseFully()
1046{
1047 openDevice (currentDeviceName);
1048}
1049
1050void ReWirePlugin::valueTreeChanged()
1051{
1052 Plugin::valueTreeChanged();
1053 triggerAsyncUpdate();
1054}
1055
1056void ReWirePlugin::handleAsyncUpdate()
1057{
1058 initialiseFully();
1059}
1060
1061juce::String ReWirePlugin::getName() const
1062{
1063 if (device != nullptr)
1064 return currentDeviceName;
1065
1066 return TRANS("ReWire Device");
1067}
1068
1069void ReWirePlugin::getChannelNames (juce::StringArray* ins,
1070 juce::StringArray* outs)
1071{
1072 getLeftRightChannelNames (ins);
1073
1074 if (outs != nullptr)
1075 {
1076 outs->add (currentChannelNameL);
1077 outs->add (currentChannelNameR);
1078 }
1079}
1080
1081void ReWirePlugin::initialise (const PluginInitialisationInfo& info)
1082{
1083 if (device != nullptr)
1084 {
1085 channelIndexL = std::max (0, device->channelNames.indexOf (currentChannelNameL.get()));
1086 channelIndexR = std::max (0, device->channelNames.indexOf (currentChannelNameR.get()));
1087
1088 device->prepareToPlay (info.sampleRate, info.blockSizeSamples,
1089 channelIndexL, channelIndexR, &edit);
1090
1091 currentTempoPosition = std::make_unique<tempo::Sequence::Position> (createPosition (edit.tempoSequence));
1092 }
1093}
1094
1095void ReWirePlugin::deinitialise()
1096{
1097 if (device != nullptr)
1098 device->deinitialise();
1099}
1100
1101void ReWirePlugin::prepareForNextBlock (TimePosition editTime)
1102{
1103 if (currentTempoPosition != nullptr && device != nullptr)
1104 {
1105 currentTempoPosition->set (editTime);
1106 device->updateTempoInfo (*currentTempoPosition);
1107 }
1108}
1109
1110void ReWirePlugin::applyToBuffer (const PluginRenderContext& fc)
1111{
1112 if (fc.destBuffer != nullptr && device != nullptr)
1113 {
1114 SCOPED_REALTIME_CHECK
1115
1116 fc.destBuffer->setSize (2, fc.destBuffer->getNumSamples(), true);
1117
1118 device->getAudioOutput (fc, channelIndexL, channelIndexR,
1119 currentBus, currentChannel);
1120 }
1121}
1122
1123//==============================================================================
1124juce::String ReWirePlugin::openDevice (const juce::String& newDev)
1125{
1127 auto error = TRANS("ReWire is disabled");
1128
1129 if (ReWireSystem::isReWireEnabled (engine))
1130 {
1131 if (rewireSystemInstance != nullptr)
1132 {
1133 error = rewireSystemInstance->openError;
1134
1135 if (device == nullptr || newDev != device->deviceName)
1136 {
1137 edit.getTransport().stop (false, true);
1138 edit.getTransport().freePlaybackContext();
1139
1140 if (auto newDevice = rewireSystemInstance->openDevice (newDev, error))
1141 {
1142 if (device != nullptr)
1143 device->removeReference();
1144
1145 device = newDevice;
1146 currentDeviceName = newDev;
1147
1148 if (! device->channelNames.contains (currentChannelNameL.get()))
1149 currentChannelNameL = device->channelNames[0];
1150
1151 if (! device->channelNames.contains (currentChannelNameR.get()))
1152 currentChannelNameR = device->channelNames[std::min (1, device->channelNames.size() - 1)];
1153
1154 startTimer (2000);
1155 }
1156
1157 if (newDev.isNotEmpty() && error.isNotEmpty())
1158 engine.getUIBehaviour().showWarningMessage (TRANS("ReWire error - Couldn't open device")
1159 + ": " + error);
1160
1162 updateBusesAndChannels();
1163
1164 changed();
1165 propertiesChanged();
1166 }
1167 }
1168 }
1169
1170 return error;
1171}
1172
1173bool ReWirePlugin::updateBusesAndChannels()
1174{
1175 juce::StringArray newBuses, newChannels;
1176 bool hasChanged = false;
1177
1178 if (device != nullptr)
1179 {
1180 ReWireEventInfo eventInfo;
1181 ReWirePrepareEventInfo (&eventInfo);
1182
1183 auto err = RWMGetEventInfo (device->handle, &eventInfo);
1184
1185 if (err != kReWireError_NoError)
1186 {
1187 logRewireError (err);
1188 }
1189 else
1190 {
1191 for (int i = 0; i < kReWireReservedEventBusIndex; ++i)
1192 {
1193 if (ReWireIsBitInBitFieldSet (eventInfo.fUsedBusBitField, (unsigned short)i))
1194 {
1195 ReWireEventBusInfo eventBusInfo;
1196 ReWirePrepareEventBusInfo (&eventBusInfo);
1197
1198 err = RWMGetEventBusInfo (device->handle, (unsigned short)i, &eventBusInfo);
1199 jassert (err == kReWireError_NoError);
1200
1201 if (err == kReWireError_NoError)
1202 {
1203 juce::String busName (juce::CharPointer_UTF8 (eventBusInfo.fBusName));
1204
1205 if (busName.trim().isEmpty())
1206 busName = "(" + TRANS("Unnamed") + ")";
1207
1208 newBuses.add (juce::String (i + 1) + ". " + busName);
1209 }
1210 }
1211 }
1212 }
1213
1214 hasChanged = newBuses != buses;
1215 buses = newBuses;
1216
1217 ReWireEventBusInfo eventBusInfo;
1218 ReWirePrepareEventBusInfo (&eventBusInfo);
1219
1220 err = RWMGetEventBusInfo (device->handle, (unsigned short)currentBus, &eventBusInfo);
1221 jassert (err == kReWireError_NoError);
1222
1223 if (err == kReWireError_NoError)
1224 {
1225 for (int j = 0; j < 16; ++j)
1226 {
1227 if (ReWireIsBitInBitFieldSet (eventBusInfo.fUsedChannelBitField, (unsigned short)j))
1228 {
1229 ReWireEventChannelInfo eventChannelInfo;
1230 ReWirePrepareEventChannelInfo (&eventChannelInfo);
1231
1232 ReWireEventTarget eventTarget;
1233 ReWirePrepareEventTarget (&eventTarget, (unsigned short)currentBus, (unsigned short)j);
1234
1235 err = RWMGetEventChannelInfo (device->handle, &eventTarget, &eventChannelInfo);
1236
1237 jassert (err == kReWireError_NoError);
1238
1239 if (err == kReWireError_NoError)
1240 {
1241 juce::String chanName (juce::CharPointer_UTF8 (eventChannelInfo.fChannelName));
1242 newChannels.add (juce::String (j + 1) + ". " + chanName);
1243 }
1244 }
1245 }
1246 }
1247
1248 hasChanged = hasChanged || (channels != newChannels);
1249 channels = newChannels;
1250
1251 char isRunningFlag = 0;
1252 err = RWMIsPanelAppLaunched (device->handle, &isRunningFlag);
1253 bool nowRunning = (err == kReWireError_NoError && isRunningFlag != 0);
1254 hasChanged = hasChanged || (uiIsRunning != nowRunning);
1255 uiIsRunning = nowRunning;
1256 }
1257 else
1258 {
1259 uiIsRunning = false;
1260 }
1261
1262 return hasChanged;
1263}
1264
1265void ReWirePlugin::openExternalUI()
1266{
1267 if (device != nullptr)
1268 {
1269 auto err = RWMLaunchPanelApp (device->handle);
1270
1271 if (err != kReWireError_NoError)
1272 {
1273 logRewireError (err);
1274 engine.getUIBehaviour().showWarningMessage (TRANS("ReWire error opening interface")
1275 + ": " + getReWireErrorMessage (err));
1276 }
1277
1278 updateBusesAndChannels();
1279 }
1280}
1281
1282void ReWirePlugin::setLeftChannel (const juce::String& channelName)
1283{
1284 if (currentChannelNameL == channelName)
1285 return;
1286
1287 currentChannelNameL = channelName;
1288 changed();
1289 updateBusesAndChannels();
1290 TransportControl::restartAllTransports (engine, true);
1291}
1292
1293void ReWirePlugin::setRightChannel (const juce::String& channelName)
1294{
1295 if (currentChannelNameR == channelName)
1296 return;
1297
1298 currentChannelNameR = channelName;
1299 changed();
1300 updateBusesAndChannels();
1301 TransportControl::restartAllTransports (engine, true);
1302}
1303
1304void ReWirePlugin::setMidiBus (int busNum)
1305{
1306 unsigned short v = (unsigned short) (std::max (0, busNum));
1307
1308 if (currentBus != v)
1309 {
1310 currentBus = v;
1311
1312 if (updateBusesAndChannels())
1313 SelectionManager::refreshAllPropertyPanels();
1314 }
1315}
1316
1317void ReWirePlugin::setMidiChannel (int channel)
1318{
1319 unsigned short v = (unsigned short) (std::max (0, channel));
1320
1321 if (currentChannel != v)
1322 {
1323 currentChannel = v;
1324 changed();
1325 }
1326}
1327
1328bool ReWirePlugin::hasNameForMidiNoteNumber (int note, int /*midiChannel*/, juce::String& name)
1329{
1330 if (device != nullptr)
1331 {
1332 ReWireEventTarget eventTarget;
1333 ReWirePrepareEventTarget (&eventTarget, (unsigned short)currentBus, (unsigned short)currentChannel);
1334
1335 ReWireEventNoteInfo noteInfo;
1336 ReWirePrepareEventNoteInfo (&noteInfo);
1337
1338 if (RWMGetNoteInfo (device->handle, &eventTarget, (unsigned short)note, &noteInfo) == kReWireError_NoError
1339 && noteInfo.fType != kReWireEventNoteTypeUnused)
1340 {
1341 name = noteInfo.fKeyName;
1342 return name.isNotEmpty();
1343 }
1344 }
1345
1346 return false;
1347}
1348
1349void ReWirePlugin::timerCallback()
1350{
1351 if (updateBusesAndChannels())
1352 propertiesChanged();
1353}
1354
1355juce::StringArray ReWirePlugin::getDeviceChannelNames() const
1356{
1357 return device->channelNames;
1358}
1359
1360#endif
1361
1362}} // namespace tracktion { inline namespace engine
void add(String stringToAdd)
bool isEmpty() const noexcept
bool isNotEmpty() const noexcept
static void JUCE_CALLTYPE sleep(int milliseconds)
static uint32 getApproximateMillisecondCounter() noexcept
T is_pointer_v
#define TRANS(stringLiteral)
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define jassertfalse
typedef short
T lock(T... args)
typedef double
T max(T... args)
T memset(T... args)
T min(T... args)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
void ignoreUnused(Types &&...) noexcept
int roundToInt(const FloatType value) noexcept
tempo::Sequence::Position createPosition(const TempoSequence &s)
Creates a Position to iterate over the given TempoSequence.
T sort(T... args)
typedef uint32_t
T swap(T... args)
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.