/[svn]/linuxsampler/trunk/src/engines/gig/Engine.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/gig/Engine.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 361 - (show annotations) (download)
Wed Feb 9 01:22:18 2005 UTC (19 years, 1 month ago) by schoenebeck
File size: 51202 byte(s)
* bunch of fixes for OSX (patch by Stephane Letz)

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the Free Software *
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
20 * MA 02111-1307 USA *
21 ***************************************************************************/
22
23 #include <sstream>
24 #include "DiskThread.h"
25 #include "Voice.h"
26 #include "EGADSR.h"
27
28 #include "Engine.h"
29
30 #if defined(__APPLE__)
31 # include <stdlib.h>
32 #else
33 # include <malloc.h>
34 #endif
35
36 namespace LinuxSampler { namespace gig {
37
38 InstrumentResourceManager Engine::Instruments;
39
40 Engine::Engine() {
41 pRIFF = NULL;
42 pGig = NULL;
43 pInstrument = NULL;
44 pAudioOutputDevice = NULL;
45 pDiskThread = NULL;
46 pEventGenerator = NULL;
47 pSysexBuffer = new RingBuffer<uint8_t>(SYSEX_BUFFER_SIZE, 0);
48 pEventQueue = new RingBuffer<Event>(MAX_EVENTS_PER_FRAGMENT, 0);
49 pEventPool = new Pool<Event>(MAX_EVENTS_PER_FRAGMENT);
50 pVoicePool = new Pool<Voice>(MAX_AUDIO_VOICES);
51 pActiveKeys = new Pool<uint>(128);
52 pVoiceStealingQueue = new RTList<Event>(pEventPool);
53 pEvents = new RTList<Event>(pEventPool);
54 pCCEvents = new RTList<Event>(pEventPool);
55 for (uint i = 0; i < Event::destination_count; i++) {
56 pSynthesisEvents[i] = new RTList<Event>(pEventPool);
57 }
58 for (uint i = 0; i < 128; i++) {
59 pMIDIKeyInfo[i].pActiveVoices = new RTList<Voice>(pVoicePool);
60 pMIDIKeyInfo[i].KeyPressed = false;
61 pMIDIKeyInfo[i].Active = false;
62 pMIDIKeyInfo[i].ReleaseTrigger = false;
63 pMIDIKeyInfo[i].pEvents = new RTList<Event>(pEventPool);
64 }
65 for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
66 iterVoice->SetEngine(this);
67 }
68 pVoicePool->clear();
69
70 pSynthesisParameters[0] = NULL; // we allocate when an audio device is connected
71 pBasicFilterParameters = NULL;
72 pMainFilterParameters = NULL;
73
74 InstrumentIdx = -1;
75 InstrumentStat = -1;
76
77 AudioDeviceChannelLeft = -1;
78 AudioDeviceChannelRight = -1;
79
80 ResetInternal();
81 }
82
83 Engine::~Engine() {
84 if (pDiskThread) {
85 dmsg(1,("Stopping disk thread..."));
86 pDiskThread->StopThread();
87 delete pDiskThread;
88 dmsg(1,("OK\n"));
89 }
90
91 if (pInstrument) Instruments.HandBack(pInstrument, this);
92
93 if (pGig) delete pGig;
94 if (pRIFF) delete pRIFF;
95 for (uint i = 0; i < 128; i++) {
96 if (pMIDIKeyInfo[i].pActiveVoices) delete pMIDIKeyInfo[i].pActiveVoices;
97 if (pMIDIKeyInfo[i].pEvents) delete pMIDIKeyInfo[i].pEvents;
98 }
99 for (uint i = 0; i < Event::destination_count; i++) {
100 if (pSynthesisEvents[i]) delete pSynthesisEvents[i];
101 }
102 if (pEvents) delete pEvents;
103 if (pCCEvents) delete pCCEvents;
104 if (pEventQueue) delete pEventQueue;
105 if (pEventPool) delete pEventPool;
106 if (pVoicePool) {
107 pVoicePool->clear();
108 delete pVoicePool;
109 }
110 if (pActiveKeys) delete pActiveKeys;
111 if (pSysexBuffer) delete pSysexBuffer;
112 if (pEventGenerator) delete pEventGenerator;
113 if (pMainFilterParameters) delete[] pMainFilterParameters;
114 if (pBasicFilterParameters) delete[] pBasicFilterParameters;
115 if (pSynthesisParameters[0]) free(pSynthesisParameters[0]);
116 if (pVoiceStealingQueue) delete pVoiceStealingQueue;
117 }
118
119 void Engine::Enable() {
120 dmsg(3,("gig::Engine: enabling\n"));
121 EngineDisabled.PushAndUnlock(false, 2); // set condition object 'EngineDisabled' to false (wait max. 2s)
122 dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));
123 }
124
125 void Engine::Disable() {
126 dmsg(3,("gig::Engine: disabling\n"));
127 bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s
128 if (!pWasDisabled) dmsg(3,("gig::Engine warning: Timeout waiting to disable engine.\n"));
129 }
130
131 void Engine::DisableAndLock() {
132 dmsg(3,("gig::Engine: disabling\n"));
133 bool* pWasDisabled = EngineDisabled.Push(true, 2); // wait max. 2s
134 if (!pWasDisabled) dmsg(3,("gig::Engine warning: Timeout waiting to disable engine.\n"));
135 }
136
137 /**
138 * Reset all voices and disk thread and clear input event queue and all
139 * control and status variables.
140 */
141 void Engine::Reset() {
142 DisableAndLock();
143
144 //if (pAudioOutputDevice->IsPlaying()) { // if already running
145 /*
146 // signal audio thread not to enter render part anymore
147 SuspensionRequested = true;
148 // sleep until wakened by audio thread
149 pthread_mutex_lock(&__render_state_mutex);
150 pthread_cond_wait(&__render_exit_condition, &__render_state_mutex);
151 pthread_mutex_unlock(&__render_state_mutex);
152 */
153 //}
154
155 //if (wasplaying) pAudioOutputDevice->Stop();
156
157 ResetInternal();
158
159 // signal audio thread to continue with rendering
160 //SuspensionRequested = false;
161 Enable();
162 }
163
164 /**
165 * Reset all voices and disk thread and clear input event queue and all
166 * control and status variables. This method is not thread safe!
167 */
168 void Engine::ResetInternal() {
169 Pitch = 0;
170 SustainPedal = false;
171 ActiveVoiceCount = 0;
172 ActiveVoiceCountMax = 0;
173 GlobalVolume = 1.0;
174 CurrentKeyDimension = 0;
175
176 // reset voice stealing parameters
177 itLastStolenVoice = RTList<Voice>::Iterator();
178 iuiLastStolenKey = RTList<uint>::Iterator();
179 pVoiceStealingQueue->clear();
180
181 // reset to normal chromatic scale (means equal temper)
182 memset(&ScaleTuning[0], 0x00, 12);
183
184 // set all MIDI controller values to zero
185 memset(ControllerTable, 0x00, 128);
186
187 // reset key info
188 for (uint i = 0; i < 128; i++) {
189 pMIDIKeyInfo[i].pActiveVoices->clear();
190 pMIDIKeyInfo[i].pEvents->clear();
191 pMIDIKeyInfo[i].KeyPressed = false;
192 pMIDIKeyInfo[i].Active = false;
193 pMIDIKeyInfo[i].ReleaseTrigger = false;
194 pMIDIKeyInfo[i].itSelf = Pool<uint>::Iterator();
195 }
196
197 // reset all key groups
198 map<uint,uint*>::iterator iter = ActiveKeyGroups.begin();
199 for (; iter != ActiveKeyGroups.end(); iter++) iter->second = NULL;
200
201 // reset all voices
202 for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
203 iterVoice->Reset();
204 }
205 pVoicePool->clear();
206
207 // free all active keys
208 pActiveKeys->clear();
209
210 // reset disk thread
211 if (pDiskThread) pDiskThread->Reset();
212
213 // delete all input events
214 pEventQueue->init();
215 }
216
217 /**
218 * Load an instrument from a .gig file.
219 *
220 * @param FileName - file name of the Gigasampler instrument file
221 * @param Instrument - index of the instrument in the .gig file
222 * @throws LinuxSamplerException on error
223 * @returns detailed description of the method call result
224 */
225 void Engine::LoadInstrument(const char* FileName, uint Instrument) {
226
227 DisableAndLock();
228
229 ResetInternal(); // reset engine
230
231 // free old instrument
232 if (pInstrument) {
233 // give old instrument back to instrument manager
234 Instruments.HandBack(pInstrument, this);
235 }
236
237 InstrumentFile = FileName;
238 InstrumentIdx = Instrument;
239 InstrumentStat = 0;
240
241 // delete all key groups
242 ActiveKeyGroups.clear();
243
244 // request gig instrument from instrument manager
245 try {
246 instrument_id_t instrid;
247 instrid.FileName = FileName;
248 instrid.iInstrument = Instrument;
249 pInstrument = Instruments.Borrow(instrid, this);
250 if (!pInstrument) {
251 InstrumentStat = -1;
252 dmsg(1,("no instrument loaded!!!\n"));
253 exit(EXIT_FAILURE);
254 }
255 }
256 catch (RIFF::Exception e) {
257 InstrumentStat = -2;
258 String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message;
259 throw LinuxSamplerException(msg);
260 }
261 catch (InstrumentResourceManagerException e) {
262 InstrumentStat = -3;
263 String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message();
264 throw LinuxSamplerException(msg);
265 }
266 catch (...) {
267 InstrumentStat = -4;
268 throw LinuxSamplerException("gig::Engine error: Failed to load instrument, cause: Unknown exception while trying to parse gig file.");
269 }
270
271 // rebuild ActiveKeyGroups map with key groups of current instrument
272 for (::gig::Region* pRegion = pInstrument->GetFirstRegion(); pRegion; pRegion = pInstrument->GetNextRegion())
273 if (pRegion->KeyGroup) ActiveKeyGroups[pRegion->KeyGroup] = NULL;
274
275 InstrumentStat = 100;
276
277 // inform audio driver for the need of two channels
278 try {
279 if (pAudioOutputDevice) pAudioOutputDevice->AcquireChannels(2); // gig Engine only stereo
280 }
281 catch (AudioOutputException e) {
282 String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();
283 throw LinuxSamplerException(msg);
284 }
285
286 Enable();
287 }
288
289 /**
290 * Will be called by the InstrumentResourceManager when the instrument
291 * we are currently using in this engine is going to be updated, so we
292 * can stop playback before that happens.
293 */
294 void Engine::ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
295 dmsg(3,("gig::Engine: Received instrument update message.\n"));
296 DisableAndLock();
297 ResetInternal();
298 this->pInstrument = NULL;
299 }
300
301 /**
302 * Will be called by the InstrumentResourceManager when the instrument
303 * update process was completed, so we can continue with playback.
304 */
305 void Engine::ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
306 this->pInstrument = pNewResource; //TODO: there are couple of engine parameters we should update here as well if the instrument was updated (see LoadInstrument())
307 Enable();
308 }
309
310 void Engine::Connect(AudioOutputDevice* pAudioOut) {
311 pAudioOutputDevice = pAudioOut;
312
313 ResetInternal();
314
315 // inform audio driver for the need of two channels
316 try {
317 pAudioOutputDevice->AcquireChannels(2); // gig engine only stereo
318 }
319 catch (AudioOutputException e) {
320 String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();
321 throw LinuxSamplerException(msg);
322 }
323
324 this->AudioDeviceChannelLeft = 0;
325 this->AudioDeviceChannelRight = 1;
326 this->pOutputLeft = pAudioOutputDevice->Channel(0)->Buffer();
327 this->pOutputRight = pAudioOutputDevice->Channel(1)->Buffer();
328 this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();
329 this->SampleRate = pAudioOutputDevice->SampleRate();
330
331 // FIXME: audio drivers with varying fragment sizes might be a problem here
332 MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * EG_MIN_RELEASE_TIME) - 1;
333 if (MaxFadeOutPos < 0)
334 throw LinuxSamplerException("EG_MIN_RELEASE_TIME in EGADSR.h to big for current audio fragment size / sampling rate!");
335
336 // (re)create disk thread
337 if (this->pDiskThread) {
338 dmsg(1,("Stopping disk thread..."));
339 this->pDiskThread->StopThread();
340 delete this->pDiskThread;
341 dmsg(1,("OK\n"));
342 }
343 this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << MAX_PITCH) << 1) + 6); //FIXME: assuming stereo
344 if (!pDiskThread) {
345 dmsg(0,("gig::Engine new diskthread = NULL\n"));
346 exit(EXIT_FAILURE);
347 }
348
349 for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
350 iterVoice->pDiskThread = this->pDiskThread;
351 dmsg(3,("d"));
352 }
353 pVoicePool->clear();
354
355 // (re)create event generator
356 if (pEventGenerator) delete pEventGenerator;
357 pEventGenerator = new EventGenerator(pAudioOut->SampleRate());
358
359 // (re)allocate synthesis parameter matrix
360 if (pSynthesisParameters[0]) free(pSynthesisParameters[0]);
361
362 #if defined(__APPLE__)
363 pSynthesisParameters[0] = (float *) malloc(Event::destination_count * sizeof(float) * pAudioOut->MaxSamplesPerCycle());
364 #else
365 pSynthesisParameters[0] = (float *) memalign(16,(Event::destination_count * sizeof(float) * pAudioOut->MaxSamplesPerCycle()));
366 #endif
367 for (int dst = 1; dst < Event::destination_count; dst++)
368 pSynthesisParameters[dst] = pSynthesisParameters[dst - 1] + pAudioOut->MaxSamplesPerCycle();
369
370 // (re)allocate biquad filter parameter sequence
371 if (pBasicFilterParameters) delete[] pBasicFilterParameters;
372 if (pMainFilterParameters) delete[] pMainFilterParameters;
373 pBasicFilterParameters = new biquad_param_t[pAudioOut->MaxSamplesPerCycle()];
374 pMainFilterParameters = new biquad_param_t[pAudioOut->MaxSamplesPerCycle()];
375
376 dmsg(1,("Starting disk thread..."));
377 pDiskThread->StartThread();
378 dmsg(1,("OK\n"));
379
380 for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
381 if (!iterVoice->pDiskThread) {
382 dmsg(0,("Engine -> voice::trigger: !pDiskThread\n"));
383 exit(EXIT_FAILURE);
384 }
385 }
386 }
387
388 void Engine::DisconnectAudioOutputDevice() {
389 if (pAudioOutputDevice) { // if clause to prevent disconnect loops
390 AudioOutputDevice* olddevice = pAudioOutputDevice;
391 pAudioOutputDevice = NULL;
392 olddevice->Disconnect(this);
393 AudioDeviceChannelLeft = -1;
394 AudioDeviceChannelRight = -1;
395 }
396 }
397
398 /**
399 * Let this engine proceed to render the given amount of sample points. The
400 * calculated audio data of all voices of this engine will be placed into
401 * the engine's audio sum buffer which has to be copied and eventually be
402 * converted to the appropriate value range by the audio output class (e.g.
403 * AlsaIO or JackIO) right after.
404 *
405 * @param Samples - number of sample points to be rendered
406 * @returns 0 on success
407 */
408 int Engine::RenderAudio(uint Samples) {
409 dmsg(5,("RenderAudio(Samples=%d)\n", Samples));
410
411 // return if no instrument loaded or engine disabled
412 if (EngineDisabled.Pop()) {
413 dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));
414 return 0;
415 }
416 if (!pInstrument) {
417 dmsg(5,("gig::Engine: no instrument loaded\n"));
418 return 0;
419 }
420
421
422 // update time of start and end of this audio fragment (as events' time stamps relate to this)
423 pEventGenerator->UpdateFragmentTime(Samples);
424
425
426 // empty the event lists for the new fragment
427 pEvents->clear();
428 pCCEvents->clear();
429 for (uint i = 0; i < Event::destination_count; i++) {
430 pSynthesisEvents[i]->clear();
431 }
432 {
433 RTList<uint>::Iterator iuiKey = pActiveKeys->first();
434 RTList<uint>::Iterator end = pActiveKeys->end();
435 for(; iuiKey != end; ++iuiKey) {
436 pMIDIKeyInfo[*iuiKey].pEvents->clear(); // free all events on the key
437 }
438 }
439
440
441 // get all events from the input event queue which belong to the current fragment
442 {
443 RingBuffer<Event>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
444 Event* pEvent;
445 while (true) {
446 // get next event from input event queue
447 if (!(pEvent = eventQueueReader.pop())) break;
448 // if younger event reached, ignore that and all subsequent ones for now
449 if (pEvent->FragmentPos() >= Samples) {
450 eventQueueReader--;
451 dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
452 pEvent->ResetFragmentPos();
453 break;
454 }
455 // copy event to internal event list
456 if (pEvents->poolIsEmpty()) {
457 dmsg(1,("Event pool emtpy!\n"));
458 break;
459 }
460 *pEvents->allocAppend() = *pEvent;
461 }
462 eventQueueReader.free(); // free all copied events from input queue
463 }
464
465
466 // process events
467 {
468 RTList<Event>::Iterator itEvent = pEvents->first();
469 RTList<Event>::Iterator end = pEvents->end();
470 for (; itEvent != end; ++itEvent) {
471 switch (itEvent->Type) {
472 case Event::type_note_on:
473 dmsg(5,("Engine: Note on received\n"));
474 ProcessNoteOn(itEvent);
475 break;
476 case Event::type_note_off:
477 dmsg(5,("Engine: Note off received\n"));
478 ProcessNoteOff(itEvent);
479 break;
480 case Event::type_control_change:
481 dmsg(5,("Engine: MIDI CC received\n"));
482 ProcessControlChange(itEvent);
483 break;
484 case Event::type_pitchbend:
485 dmsg(5,("Engine: Pitchbend received\n"));
486 ProcessPitchbend(itEvent);
487 break;
488 case Event::type_sysex:
489 dmsg(5,("Engine: Sysex received\n"));
490 ProcessSysex(itEvent);
491 break;
492 }
493 }
494 }
495
496
497 int active_voices = 0;
498
499 // render audio from all active voices
500 {
501 RTList<uint>::Iterator iuiKey = pActiveKeys->first();
502 RTList<uint>::Iterator end = pActiveKeys->end();
503 while (iuiKey != end) { // iterate through all active keys
504 midi_key_info_t* pKey = &pMIDIKeyInfo[*iuiKey];
505 ++iuiKey;
506
507 RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
508 RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
509 for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
510 // now render current voice
511 itVoice->Render(Samples);
512 if (itVoice->IsActive()) active_voices++; // still active
513 else { // voice reached end, is now inactive
514 FreeVoice(itVoice); // remove voice from the list of active voices
515 }
516 }
517 }
518 }
519
520
521 // now render all postponed voices from voice stealing
522 {
523 RTList<Event>::Iterator itVoiceStealEvent = pVoiceStealingQueue->first();
524 RTList<Event>::Iterator end = pVoiceStealingQueue->end();
525 for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {
526 Pool<Voice>::Iterator itNewVoice = LaunchVoice(itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false);
527 if (itNewVoice) {
528 for (; itNewVoice; itNewVoice = itNewVoice->itChildVoice) {
529 itNewVoice->Render(Samples);
530 if (itNewVoice->IsActive()) active_voices++; // still active
531 else { // voice reached end, is now inactive
532 FreeVoice(itNewVoice); // remove voice from the list of active voices
533 }
534 }
535 }
536 else dmsg(1,("gig::Engine: ERROR, voice stealing didn't work out!\n"));
537 }
538 }
539 // reset voice stealing for the new fragment
540 pVoiceStealingQueue->clear();
541 itLastStolenVoice = RTList<Voice>::Iterator();
542 iuiLastStolenKey = RTList<uint>::Iterator();
543
544
545 // free all keys which have no active voices left
546 {
547 RTList<uint>::Iterator iuiKey = pActiveKeys->first();
548 RTList<uint>::Iterator end = pActiveKeys->end();
549 while (iuiKey != end) { // iterate through all active keys
550 midi_key_info_t* pKey = &pMIDIKeyInfo[*iuiKey];
551 ++iuiKey;
552 if (pKey->pActiveVoices->isEmpty()) FreeKey(pKey);
553 #if DEVMODE
554 else { // FIXME: should be removed before the final release (purpose: just a sanity check for debugging)
555 RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
556 RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
557 for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
558 if (itVoice->itKillEvent) {
559 dmsg(1,("gig::Engine: ERROR, killed voice survived !!!\n"));
560 }
561 }
562 }
563 #endif // DEVMODE
564 }
565 }
566
567
568 // write that to the disk thread class so that it can print it
569 // on the console for debugging purposes
570 ActiveVoiceCount = active_voices;
571 if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;
572
573
574 return 0;
575 }
576
577 /**
578 * Will be called by the MIDIIn Thread to let the audio thread trigger a new
579 * voice for the given key.
580 *
581 * @param Key - MIDI key number of the triggered key
582 * @param Velocity - MIDI velocity value of the triggered key
583 */
584 void Engine::SendNoteOn(uint8_t Key, uint8_t Velocity) {
585 Event event = pEventGenerator->CreateEvent();
586 event.Type = Event::type_note_on;
587 event.Param.Note.Key = Key;
588 event.Param.Note.Velocity = Velocity;
589 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
590 else dmsg(1,("Engine: Input event queue full!"));
591 }
592
593 /**
594 * Will be called by the MIDIIn Thread to signal the audio thread to release
595 * voice(s) on the given key.
596 *
597 * @param Key - MIDI key number of the released key
598 * @param Velocity - MIDI release velocity value of the released key
599 */
600 void Engine::SendNoteOff(uint8_t Key, uint8_t Velocity) {
601 Event event = pEventGenerator->CreateEvent();
602 event.Type = Event::type_note_off;
603 event.Param.Note.Key = Key;
604 event.Param.Note.Velocity = Velocity;
605 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
606 else dmsg(1,("Engine: Input event queue full!"));
607 }
608
609 /**
610 * Will be called by the MIDIIn Thread to signal the audio thread to change
611 * the pitch value for all voices.
612 *
613 * @param Pitch - MIDI pitch value (-8192 ... +8191)
614 */
615 void Engine::SendPitchbend(int Pitch) {
616 Event event = pEventGenerator->CreateEvent();
617 event.Type = Event::type_pitchbend;
618 event.Param.Pitch.Pitch = Pitch;
619 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
620 else dmsg(1,("Engine: Input event queue full!"));
621 }
622
623 /**
624 * Will be called by the MIDIIn Thread to signal the audio thread that a
625 * continuous controller value has changed.
626 *
627 * @param Controller - MIDI controller number of the occured control change
628 * @param Value - value of the control change
629 */
630 void Engine::SendControlChange(uint8_t Controller, uint8_t Value) {
631 Event event = pEventGenerator->CreateEvent();
632 event.Type = Event::type_control_change;
633 event.Param.CC.Controller = Controller;
634 event.Param.CC.Value = Value;
635 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
636 else dmsg(1,("Engine: Input event queue full!"));
637 }
638
639 /**
640 * Will be called by the MIDI input device whenever a MIDI system
641 * exclusive message has arrived.
642 *
643 * @param pData - pointer to sysex data
644 * @param Size - lenght of sysex data (in bytes)
645 */
646 void Engine::SendSysex(void* pData, uint Size) {
647 Event event = pEventGenerator->CreateEvent();
648 event.Type = Event::type_sysex;
649 event.Param.Sysex.Size = Size;
650 if (pEventQueue->write_space() > 0) {
651 if (pSysexBuffer->write_space() >= Size) {
652 // copy sysex data to input buffer
653 uint toWrite = Size;
654 uint8_t* pPos = (uint8_t*) pData;
655 while (toWrite) {
656 const uint writeNow = RTMath::Min(toWrite, pSysexBuffer->write_space_to_end());
657 pSysexBuffer->write(pPos, writeNow);
658 toWrite -= writeNow;
659 pPos += writeNow;
660
661 }
662 // finally place sysex event into input event queue
663 pEventQueue->push(&event);
664 }
665 else dmsg(1,("Engine: Sysex message too large (%d byte) for input buffer (%d byte)!",Size,SYSEX_BUFFER_SIZE));
666 }
667 else dmsg(1,("Engine: Input event queue full!"));
668 }
669
670 /**
671 * Assigns and triggers a new voice for the respective MIDI key.
672 *
673 * @param itNoteOnEvent - key, velocity and time stamp of the event
674 */
675 void Engine::ProcessNoteOn(Pool<Event>::Iterator& itNoteOnEvent) {
676
677 const int key = itNoteOnEvent->Param.Note.Key;
678
679 // Change key dimension value if key is in keyswitching area
680 if (key >= pInstrument->DimensionKeyRange.low && key <= pInstrument->DimensionKeyRange.high)
681 CurrentKeyDimension = ((key - pInstrument->DimensionKeyRange.low) * 128) /
682 (pInstrument->DimensionKeyRange.high - pInstrument->DimensionKeyRange.low + 1);
683
684 midi_key_info_t* pKey = &pMIDIKeyInfo[key];
685
686 pKey->KeyPressed = true; // the MIDI key was now pressed down
687
688 // cancel release process of voices on this key if needed
689 if (pKey->Active && !SustainPedal) {
690 RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();
691 if (itCancelReleaseEvent) {
692 *itCancelReleaseEvent = *itNoteOnEvent; // copy event
693 itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type
694 }
695 else dmsg(1,("Event pool emtpy!\n"));
696 }
697
698 // move note on event to the key's own event list
699 RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);
700
701 // allocate and trigger a new voice for the key
702 LaunchVoice(itNoteOnEventOnKeyList, 0, false, true);
703 }
704
705 /**
706 * Releases the voices on the given key if sustain pedal is not pressed.
707 * If sustain is pressed, the release of the note will be postponed until
708 * sustain pedal will be released or voice turned inactive by itself (e.g.
709 * due to completion of sample playback).
710 *
711 * @param itNoteOffEvent - key, velocity and time stamp of the event
712 */
713 void Engine::ProcessNoteOff(Pool<Event>::Iterator& itNoteOffEvent) {
714 midi_key_info_t* pKey = &pMIDIKeyInfo[itNoteOffEvent->Param.Note.Key];
715
716 pKey->KeyPressed = false; // the MIDI key was now released
717
718 // release voices on this key if needed
719 if (pKey->Active && !SustainPedal) {
720 itNoteOffEvent->Type = Event::type_release; // transform event type
721 }
722
723 // move event to the key's own event list
724 RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);
725
726 // spawn release triggered voice(s) if needed
727 if (pKey->ReleaseTrigger) {
728 LaunchVoice(itNoteOffEventOnKeyList, 0, true, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples
729 pKey->ReleaseTrigger = false;
730 }
731 }
732
733 /**
734 * Moves pitchbend event from the general (input) event list to the pitch
735 * event list.
736 *
737 * @param itPitchbendEvent - absolute pitch value and time stamp of the event
738 */
739 void Engine::ProcessPitchbend(Pool<Event>::Iterator& itPitchbendEvent) {
740 this->Pitch = itPitchbendEvent->Param.Pitch.Pitch; // store current pitch value
741 itPitchbendEvent.moveToEndOf(pSynthesisEvents[Event::destination_vco]);
742 }
743
744 /**
745 * Allocates and triggers a new voice. This method will usually be
746 * called by the ProcessNoteOn() method and by the voices itself
747 * (e.g. to spawn further voices on the same key for layered sounds).
748 *
749 * @param itNoteOnEvent - key, velocity and time stamp of the event
750 * @param iLayer - layer index for the new voice (optional - only
751 * in case of layered sounds of course)
752 * @param ReleaseTriggerVoice - if new voice is a release triggered voice
753 * (optional, default = false)
754 * @param VoiceStealing - if voice stealing should be performed
755 * when there is no free voice
756 * (optional, default = true)
757 * @returns pointer to new voice or NULL if there was no free voice or
758 * if the voice wasn't triggered (for example when no region is
759 * defined for the given key).
760 */
761 Pool<Voice>::Iterator Engine::LaunchVoice(Pool<Event>::Iterator& itNoteOnEvent, int iLayer, bool ReleaseTriggerVoice, bool VoiceStealing) {
762 midi_key_info_t* pKey = &pMIDIKeyInfo[itNoteOnEvent->Param.Note.Key];
763
764 // allocate a new voice for the key
765 Pool<Voice>::Iterator itNewVoice = pKey->pActiveVoices->allocAppend();
766 if (itNewVoice) {
767 // launch the new voice
768 if (itNewVoice->Trigger(itNoteOnEvent, this->Pitch, this->pInstrument, iLayer, ReleaseTriggerVoice, VoiceStealing) < 0) {
769 dmsg(4,("Voice not triggered\n"));
770 pKey->pActiveVoices->free(itNewVoice);
771 }
772 else { // on success
773 uint** ppKeyGroup = NULL;
774 if (itNewVoice->KeyGroup) { // if this voice / key belongs to a key group
775 ppKeyGroup = &ActiveKeyGroups[itNewVoice->KeyGroup];
776 if (*ppKeyGroup) { // if there's already an active key in that key group
777 midi_key_info_t* pOtherKey = &pMIDIKeyInfo[**ppKeyGroup];
778 // kill all voices on the (other) key
779 RTList<Voice>::Iterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();
780 RTList<Voice>::Iterator end = pOtherKey->pActiveVoices->end();
781 for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
782 if (itVoiceToBeKilled->Type != Voice::type_release_trigger) itVoiceToBeKilled->Kill(itNoteOnEvent);
783 }
784 }
785 }
786 if (!pKey->Active) { // mark as active key
787 pKey->Active = true;
788 pKey->itSelf = pActiveKeys->allocAppend();
789 *pKey->itSelf = itNoteOnEvent->Param.Note.Key;
790 }
791 if (itNewVoice->KeyGroup) {
792 *ppKeyGroup = &*pKey->itSelf; // put key as the (new) active key to its key group
793 }
794 if (itNewVoice->Type == Voice::type_release_trigger_required) pKey->ReleaseTrigger = true; // mark key for the need of release triggered voice(s)
795 return itNewVoice; // success
796 }
797 }
798 else if (VoiceStealing) {
799 // first, get total amount of required voices (dependant on amount of layers)
800 ::gig::Region* pRegion = pInstrument->GetRegion(itNoteOnEvent->Param.Note.Key);
801 if (!pRegion) return Pool<Voice>::Iterator(); // nothing defined for this MIDI key, so no voice needed
802 int voicesRequired = pRegion->Layers;
803
804 // now steal the (remaining) amount of voices
805 for (int i = iLayer; i < voicesRequired; i++)
806 StealVoice(itNoteOnEvent);
807
808 // put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died
809 RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend();
810 if (itStealEvent) {
811 *itStealEvent = *itNoteOnEvent; // copy event
812 itStealEvent->Param.Note.Layer = iLayer;
813 itStealEvent->Param.Note.ReleaseTrigger = ReleaseTriggerVoice;
814 }
815 else dmsg(1,("Voice stealing queue full!\n"));
816 }
817
818 return Pool<Voice>::Iterator(); // no free voice or error
819 }
820
821 /**
822 * Will be called by LaunchVoice() method in case there are no free
823 * voices left. This method will select and kill one old voice for
824 * voice stealing and postpone the note-on event until the selected
825 * voice actually died.
826 *
827 * @param itNoteOnEvent - key, velocity and time stamp of the event
828 */
829 void Engine::StealVoice(Pool<Event>::Iterator& itNoteOnEvent) {
830 if (!pEventPool->poolIsEmpty()) {
831
832 RTList<uint>::Iterator iuiOldestKey;
833 RTList<Voice>::Iterator itOldestVoice;
834
835 // Select one voice for voice stealing
836 switch (VOICE_STEAL_ALGORITHM) {
837
838 // try to pick the oldest voice on the key where the new
839 // voice should be spawned, if there is no voice on that
840 // key, or no voice left to kill there, then procceed with
841 // 'oldestkey' algorithm
842 case voice_steal_algo_keymask: {
843 midi_key_info_t* pOldestKey = &pMIDIKeyInfo[itNoteOnEvent->Param.Note.Key];
844 if (itLastStolenVoice) {
845 itOldestVoice = itLastStolenVoice;
846 ++itOldestVoice;
847 }
848 else { // no voice stolen in this audio fragment cycle yet
849 itOldestVoice = pOldestKey->pActiveVoices->first();
850 }
851 if (itOldestVoice) {
852 iuiOldestKey = pOldestKey->itSelf;
853 break; // selection succeeded
854 }
855 } // no break - intentional !
856
857 // try to pick the oldest voice on the oldest active key
858 // (caution: must stay after 'keymask' algorithm !)
859 case voice_steal_algo_oldestkey: {
860 if (itLastStolenVoice) {
861 midi_key_info_t* pOldestKey = &pMIDIKeyInfo[*iuiLastStolenKey];
862 itOldestVoice = itLastStolenVoice;
863 ++itOldestVoice;
864 if (!itOldestVoice) {
865 iuiOldestKey = iuiLastStolenKey;
866 ++iuiOldestKey;
867 if (iuiOldestKey) {
868 midi_key_info_t* pOldestKey = &pMIDIKeyInfo[*iuiOldestKey];
869 itOldestVoice = pOldestKey->pActiveVoices->first();
870 }
871 else {
872 dmsg(1,("gig::Engine: Warning, too less voices, even for voice stealing! - Better recompile with higher MAX_AUDIO_VOICES.\n"));
873 return;
874 }
875 }
876 else iuiOldestKey = iuiLastStolenKey;
877 }
878 else { // no voice stolen in this audio fragment cycle yet
879 iuiOldestKey = pActiveKeys->first();
880 midi_key_info_t* pOldestKey = &pMIDIKeyInfo[*iuiOldestKey];
881 itOldestVoice = pOldestKey->pActiveVoices->first();
882 }
883 break;
884 }
885
886 // don't steal anything
887 case voice_steal_algo_none:
888 default: {
889 dmsg(1,("No free voice (voice stealing disabled)!\n"));
890 return;
891 }
892 }
893
894 //FIXME: can be removed, just a sanity check for debugging
895 if (!itOldestVoice->IsActive()) dmsg(1,("gig::Engine: ERROR, tried to steal a voice which was not active !!!\n"));
896
897 // now kill the selected voice
898 itOldestVoice->Kill(itNoteOnEvent);
899 // remember which voice on which key we stole, so we can simply proceed for the next voice stealing
900 this->itLastStolenVoice = itOldestVoice;
901 this->iuiLastStolenKey = iuiOldestKey;
902 }
903 else dmsg(1,("Event pool emtpy!\n"));
904 }
905
906 /**
907 * Removes the given voice from the MIDI key's list of active voices.
908 * This method will be called when a voice went inactive, e.g. because
909 * it finished to playback its sample, finished its release stage or
910 * just was killed.
911 *
912 * @param itVoice - points to the voice to be freed
913 */
914 void Engine::FreeVoice(Pool<Voice>::Iterator& itVoice) {
915 if (itVoice) {
916 midi_key_info_t* pKey = &pMIDIKeyInfo[itVoice->MIDIKey];
917
918 uint keygroup = itVoice->KeyGroup;
919
920 // free the voice object
921 pVoicePool->free(itVoice);
922
923 // if no other voices left and member of a key group, remove from key group
924 if (pKey->pActiveVoices->isEmpty() && keygroup) {
925 uint** ppKeyGroup = &ActiveKeyGroups[keygroup];
926 if (*ppKeyGroup == &*pKey->itSelf) *ppKeyGroup = NULL; // remove key from key group
927 }
928 }
929 else std::cerr << "Couldn't release voice! (!itVoice)\n" << std::flush;
930 }
931
932 /**
933 * Called when there's no more voice left on a key, this call will
934 * update the key info respectively.
935 *
936 * @param pKey - key which is now inactive
937 */
938 void Engine::FreeKey(midi_key_info_t* pKey) {
939 if (pKey->pActiveVoices->isEmpty()) {
940 pKey->Active = false;
941 pActiveKeys->free(pKey->itSelf); // remove key from list of active keys
942 pKey->itSelf = RTList<uint>::Iterator();
943 pKey->ReleaseTrigger = false;
944 pKey->pEvents->clear();
945 dmsg(3,("Key has no more voices now\n"));
946 }
947 else dmsg(1,("gig::Engine: Oops, tried to free a key which contains voices.\n"));
948 }
949
950 /**
951 * Reacts on supported control change commands (e.g. pitch bend wheel,
952 * modulation wheel, aftertouch).
953 *
954 * @param itControlChangeEvent - controller, value and time stamp of the event
955 */
956 void Engine::ProcessControlChange(Pool<Event>::Iterator& itControlChangeEvent) {
957 dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));
958
959 switch (itControlChangeEvent->Param.CC.Controller) {
960 case 64: {
961 if (itControlChangeEvent->Param.CC.Value >= 64 && !SustainPedal) {
962 dmsg(4,("PEDAL DOWN\n"));
963 SustainPedal = true;
964
965 // cancel release process of voices if necessary
966 RTList<uint>::Iterator iuiKey = pActiveKeys->first();
967 if (iuiKey) {
968 itControlChangeEvent->Type = Event::type_cancel_release; // transform event type
969 while (iuiKey) {
970 midi_key_info_t* pKey = &pMIDIKeyInfo[*iuiKey];
971 ++iuiKey;
972 if (!pKey->KeyPressed) {
973 RTList<Event>::Iterator itNewEvent = pKey->pEvents->allocAppend();
974 if (itNewEvent) *itNewEvent = *itControlChangeEvent; // copy event to the key's own event list
975 else dmsg(1,("Event pool emtpy!\n"));
976 }
977 }
978 }
979 }
980 if (itControlChangeEvent->Param.CC.Value < 64 && SustainPedal) {
981 dmsg(4,("PEDAL UP\n"));
982 SustainPedal = false;
983
984 // release voices if their respective key is not pressed
985 RTList<uint>::Iterator iuiKey = pActiveKeys->first();
986 if (iuiKey) {
987 itControlChangeEvent->Type = Event::type_release; // transform event type
988 while (iuiKey) {
989 midi_key_info_t* pKey = &pMIDIKeyInfo[*iuiKey];
990 ++iuiKey;
991 if (!pKey->KeyPressed) {
992 RTList<Event>::Iterator itNewEvent = pKey->pEvents->allocAppend();
993 if (itNewEvent) *itNewEvent = *itControlChangeEvent; // copy event to the key's own event list
994 else dmsg(1,("Event pool emtpy!\n"));
995 }
996 }
997 }
998 }
999 break;
1000 }
1001 }
1002
1003 // update controller value in the engine's controller table
1004 ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1005
1006 // move event from the unsorted event list to the control change event list
1007 itControlChangeEvent.moveToEndOf(pCCEvents);
1008 }
1009
1010 /**
1011 * Reacts on MIDI system exclusive messages.
1012 *
1013 * @param itSysexEvent - sysex data size and time stamp of the sysex event
1014 */
1015 void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {
1016 RingBuffer<uint8_t>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();
1017
1018 uint8_t exclusive_status, id;
1019 if (!reader.pop(&exclusive_status)) goto free_sysex_data;
1020 if (!reader.pop(&id)) goto free_sysex_data;
1021 if (exclusive_status != 0xF0) goto free_sysex_data;
1022
1023 switch (id) {
1024 case 0x41: { // Roland
1025 uint8_t device_id, model_id, cmd_id;
1026 if (!reader.pop(&device_id)) goto free_sysex_data;
1027 if (!reader.pop(&model_id)) goto free_sysex_data;
1028 if (!reader.pop(&cmd_id)) goto free_sysex_data;
1029 if (model_id != 0x42 /*GS*/) goto free_sysex_data;
1030 if (cmd_id != 0x12 /*DT1*/) goto free_sysex_data;
1031
1032 // command address
1033 uint8_t addr[3]; // 2 byte addr MSB, followed by 1 byte addr LSB)
1034 const RingBuffer<uint8_t>::NonVolatileReader checksum_reader = reader; // so we can calculate the check sum later
1035 if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;
1036 if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters
1037 }
1038 else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters
1039 }
1040 else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x10) { // Part Parameters (1)
1041 switch (addr[3]) {
1042 case 0x40: { // scale tuning
1043 uint8_t scale_tunes[12]; // detuning of all 12 semitones of an octave
1044 if (reader.read(&scale_tunes[0], 12) != 12) goto free_sysex_data;
1045 uint8_t checksum;
1046 if (!reader.pop(&checksum)) goto free_sysex_data;
1047 if (GSCheckSum(checksum_reader, 12) != checksum) goto free_sysex_data;
1048 for (int i = 0; i < 12; i++) scale_tunes[i] -= 64;
1049 AdjustScale((int8_t*) scale_tunes);
1050 break;
1051 }
1052 }
1053 }
1054 else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)
1055 }
1056 else if (addr[0] == 0x41) { // Drum Setup Parameters
1057 }
1058 break;
1059 }
1060 }
1061
1062 free_sysex_data: // finally free sysex data
1063 pSysexBuffer->increment_read_ptr(itSysexEvent->Param.Sysex.Size);
1064 }
1065
1066 /**
1067 * Calculates the Roland GS sysex check sum.
1068 *
1069 * @param AddrReader - reader which currently points to the first GS
1070 * command address byte of the GS sysex message in
1071 * question
1072 * @param DataSize - size of the GS message data (in bytes)
1073 */
1074 uint8_t Engine::GSCheckSum(const RingBuffer<uint8_t>::NonVolatileReader AddrReader, uint DataSize) {
1075 RingBuffer<uint8_t>::NonVolatileReader reader = AddrReader;
1076 uint bytes = 3 /*addr*/ + DataSize;
1077 uint8_t addr_and_data[bytes];
1078 reader.read(&addr_and_data[0], bytes);
1079 uint8_t sum = 0;
1080 for (uint i = 0; i < bytes; i++) sum += addr_and_data[i];
1081 return 128 - sum % 128;
1082 }
1083
1084 /**
1085 * Allows to tune each of the twelve semitones of an octave.
1086 *
1087 * @param ScaleTunes - detuning of all twelve semitones (in cents)
1088 */
1089 void Engine::AdjustScale(int8_t ScaleTunes[12]) {
1090 memcpy(&this->ScaleTuning[0], &ScaleTunes[0], 12); //TODO: currently not sample accurate
1091 }
1092
1093 /**
1094 * Initialize the parameter sequence for the modulation destination given by
1095 * by 'dst' with the constant value given by val.
1096 */
1097 void Engine::ResetSynthesisParameters(Event::destination_t dst, float val) {
1098 int maxsamples = pAudioOutputDevice->MaxSamplesPerCycle();
1099 float* m = &pSynthesisParameters[dst][0];
1100 for (int i = 0; i < maxsamples; i += 4) {
1101 m[i] = val;
1102 m[i+1] = val;
1103 m[i+2] = val;
1104 m[i+3] = val;
1105 }
1106 }
1107
1108 float Engine::Volume() {
1109 return GlobalVolume;
1110 }
1111
1112 void Engine::Volume(float f) {
1113 GlobalVolume = f;
1114 }
1115
1116 uint Engine::Channels() {
1117 return 2;
1118 }
1119
1120 void Engine::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
1121 AudioChannel* pChannel = pAudioOutputDevice->Channel(AudioDeviceChannel);
1122 if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
1123 switch (EngineAudioChannel) {
1124 case 0: // left output channel
1125 pOutputLeft = pChannel->Buffer();
1126 AudioDeviceChannelLeft = AudioDeviceChannel;
1127 break;
1128 case 1: // right output channel
1129 pOutputRight = pChannel->Buffer();
1130 AudioDeviceChannelRight = AudioDeviceChannel;
1131 break;
1132 default:
1133 throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
1134 }
1135 }
1136
1137 int Engine::OutputChannel(uint EngineAudioChannel) {
1138 switch (EngineAudioChannel) {
1139 case 0: // left channel
1140 return AudioDeviceChannelLeft;
1141 case 1: // right channel
1142 return AudioDeviceChannelRight;
1143 default:
1144 throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
1145 }
1146 }
1147
1148 uint Engine::VoiceCount() {
1149 return ActiveVoiceCount;
1150 }
1151
1152 uint Engine::VoiceCountMax() {
1153 return ActiveVoiceCountMax;
1154 }
1155
1156 bool Engine::DiskStreamSupported() {
1157 return true;
1158 }
1159
1160 uint Engine::DiskStreamCount() {
1161 return (pDiskThread) ? pDiskThread->ActiveStreamCount : 0;
1162 }
1163
1164 uint Engine::DiskStreamCountMax() {
1165 return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;
1166 }
1167
1168 String Engine::DiskStreamBufferFillBytes() {
1169 return pDiskThread->GetBufferFillBytes();
1170 }
1171
1172 String Engine::DiskStreamBufferFillPercentage() {
1173 return pDiskThread->GetBufferFillPercentage();
1174 }
1175
1176 String Engine::EngineName() {
1177 return "GigEngine";
1178 }
1179
1180 String Engine::InstrumentFileName() {
1181 return InstrumentFile;
1182 }
1183
1184 int Engine::InstrumentIndex() {
1185 return InstrumentIdx;
1186 }
1187
1188 int Engine::InstrumentStatus() {
1189 return InstrumentStat;
1190 }
1191
1192 String Engine::Description() {
1193 return "Gigasampler Engine";
1194 }
1195
1196 String Engine::Version() {
1197 String s = "$Revision: 1.23 $";
1198 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
1199 }
1200
1201 }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC