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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 411 - (hide annotations) (download)
Sat Feb 26 02:01:14 2005 UTC (19 years, 1 month ago) by schoenebeck
File size: 42975 byte(s)
* design change: using now one sampler engine instance and one disk thread
  instance for all sampler channels that are connected to the same audio
  output device (and are using the same engine type of course)
* added EngineFactory / EngineChannelFactory to remove the annoying build
  dependencies e.g. of the lscpserver to the actual sampler engine
  implementations
* bumped version to 0.3.0 (current CVS state is still quite broken,
  previous, stable CVS version was tagged as "v0_2_0" and is also available
  as source tarball)

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

  ViewVC Help
Powered by ViewVC