/[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 319 - (show annotations) (download)
Mon Dec 13 00:46:42 2004 UTC (14 years ago) by schoenebeck
File size: 50284 byte(s)
* introduced 'synthesis mode' to reduce the amount of code and conditionals
  for the current synthesis case in the main synthesis loop
* support for MMX and SSE(1) in the core synthesis algorithms (CPU feature
  detection at runtime, only x86 so far)

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

  ViewVC Help
Powered by ViewVC