/[svn]/linuxsampler/trunk/src/audiothread.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/audiothread.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 37 - (show annotations) (download)
Wed Mar 10 22:01:36 2004 UTC (20 years, 1 month ago) by schoenebeck
File size: 23420 byte(s)
* src/eg_vca.cpp: added following transitions to the envelope generator:
  'Attack_Hold' -> 'Release', 'Decay_1' -> 'Release' in case of a release
  event
* EG1 parameters 'Attack Time', 'Release Time' and 'Sustain Time' are now
  controllable by a MIDI controller defined in the .gig file
* src/voice.cpp: fixed bug in pitch calculation which caused new triggered
  voices to be played back without honoring the current pitch bend value

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003 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 "audiothread.h"
24
25 AudioThread::AudioThread(AudioIO* pAudioIO) {
26 this->pAudioIO = pAudioIO;
27 this->pDiskThread = new DiskThread(((pAudioIO->MaxSamplesPerCycle() << MAX_PITCH) << 1) + 6); //FIXME: assuming stereo
28 this->pInstrument = NULL;
29 this->Pitch = 0;
30 this->SustainPedal = 0;
31 Voice::pDiskThread = this->pDiskThread;
32 Voice::pEngine = this;
33 pEventQueue = new RingBuffer<ModulationSystem::Event>(MAX_EVENTS_PER_FRAGMENT);
34 pEventPool = new RTELMemoryPool<ModulationSystem::Event>(MAX_EVENTS_PER_FRAGMENT);
35 pVoicePool = new RTELMemoryPool<Voice>(MAX_AUDIO_VOICES);
36 pActiveKeys = new RTELMemoryPool<uint>(128);
37 pEvents = new RTEList<ModulationSystem::Event>(pEventPool);
38 pCCEvents = new RTEList<ModulationSystem::Event>(pEventPool);
39 for (uint i = 0; i < ModulationSystem::destination_count; i++) {
40 pSynthesisEvents[i] = new RTEList<ModulationSystem::Event>(pEventPool);
41 }
42 for (uint i = 0; i < 128; i++) {
43 pMIDIKeyInfo[i].pActiveVoices = new RTEList<Voice>(pVoicePool);
44 pMIDIKeyInfo[i].KeyPressed = false;
45 pMIDIKeyInfo[i].Active = false;
46 pMIDIKeyInfo[i].pSelf = NULL;
47 pMIDIKeyInfo[i].pEvents = new RTEList<ModulationSystem::Event>(pEventPool);
48 }
49
50 // FIXME: assuming stereo output
51 pAudioSumBuffer[0] = new float[pAudioIO->MaxSamplesPerCycle() * pAudioIO->Channels()];
52 pAudioSumBuffer[1] = &pAudioSumBuffer[0][pAudioIO->MaxSamplesPerCycle()];
53
54 // set all voice outputs to the AudioSumBuffer
55 for (Voice* pVoice = pVoicePool->alloc(); pVoice; pVoice = pVoicePool->alloc()) { //FIXME: assuming stereo
56 pVoice->SetOutputLeft(pAudioSumBuffer[0], pAudioIO->MaxSamplesPerCycle());
57 pVoice->SetOutputRight(pAudioSumBuffer[1], pAudioIO->MaxSamplesPerCycle());
58 }
59 pVoicePool->clear();
60
61 pRIFF = NULL;
62 pGig = NULL;
63 pInstrument = NULL;
64
65 // initialize modulation system
66 ModulationSystem::Initialize(pAudioIO->SampleRate(), pAudioIO->MaxSamplesPerCycle());
67
68 // set all MIDI controller values to zero
69 memset(ControllerTable, 0x00, 128);
70
71 SuspensionRequested = false;
72 pthread_mutex_init(&__render_state_mutex, NULL);
73 pthread_cond_init(&__render_exit_condition, NULL);
74
75 dmsg(1,("Starting disk thread..."));
76 pDiskThread->StartThread();
77 dmsg(1,("OK\n"));
78 }
79
80 AudioThread::~AudioThread() {
81 if (pDiskThread) {
82 pDiskThread->StopThread();
83 delete pDiskThread;
84 }
85 if (pGig) delete pGig;
86 if (pRIFF) delete pRIFF;
87 ModulationSystem::Close();
88 for (uint i = 0; i < 128; i++) {
89 if (pMIDIKeyInfo[i].pActiveVoices) delete pMIDIKeyInfo[i].pActiveVoices;
90 if (pMIDIKeyInfo[i].pEvents) delete pMIDIKeyInfo[i].pEvents;
91 }
92 for (uint i = 0; i < ModulationSystem::destination_count; i++) {
93 if (pSynthesisEvents[i]) delete pSynthesisEvents[i];
94 }
95 delete[] pSynthesisEvents;
96 if (pEvents) delete pEvents;
97 if (pCCEvents) delete pCCEvents;
98 if (pEventQueue) delete pEventQueue;
99 if (pEventPool) delete pEventPool;
100 if (pVoicePool) delete pVoicePool;
101 if (pActiveKeys) delete pActiveKeys;
102 delete[] pAudioSumBuffer[0]; // this also frees the right channel buffer
103 pthread_cond_destroy(&__render_exit_condition);
104 pthread_mutex_destroy(&__render_state_mutex);
105 }
106
107 /**
108 * Let this engine proceed to render the given amount of sample points. The
109 * calculated audio data of all voices of this engine will be placed into
110 * the engine's audio sum buffer which has to be copied and eventually be
111 * converted to the appropriate value range by the audio output class (e.g.
112 * AlsaIO or JackIO) right after.
113 *
114 * @param Samples - number of sample points to be rendered
115 * @returns 0 on success
116 */
117 int AudioThread::RenderAudio(uint Samples) {
118
119 // zero out the output sum buffer (left and right channel)
120 memset(pAudioSumBuffer[0], 0, Samples * pAudioIO->Channels() * sizeof(float));
121
122
123 // check if rendering process was requested to be interrupted (e.g. to load another instrument)
124 if (SuspensionRequested) {
125 pthread_cond_broadcast(&__render_exit_condition); // wake up anybody waiting for us
126 return 0;
127 }
128
129
130 // empty the event lists for the new fragment
131 pEvents->clear();
132 pCCEvents->clear();
133 for (uint i = 0; i < ModulationSystem::destination_count; i++) {
134 pSynthesisEvents[i]->clear();
135 }
136
137 // read and copy events from input queue
138 ModulationSystem::Event Event;
139 while (true) {
140 if (!pEventQueue->pop(&Event)) break;
141 pEvents->alloc_assign(Event);
142 }
143
144
145 // update time of start and end of this audio fragment (as events' time stamps relate to this)
146 ModulationSystem::UpdateFragmentTime();
147
148
149 // process events
150 ModulationSystem::Event* pNextEvent = pEvents->first();
151 while (pNextEvent) {
152 ModulationSystem::Event* pEvent = pNextEvent;
153 pEvents->set_current(pEvent);
154 pNextEvent = pEvents->next();
155 switch (pEvent->Type) {
156 case ModulationSystem::event_type_note_on:
157 dmsg(5,("Audio Thread: Note on received\n"));
158 ProcessNoteOn(pEvent);
159 break;
160 case ModulationSystem::event_type_note_off:
161 dmsg(5,("Audio Thread: Note off received\n"));
162 ProcessNoteOff(pEvent);
163 break;
164 case ModulationSystem::event_type_control_change:
165 dmsg(5,("Audio Thread: MIDI CC received\n"));
166 ProcessControlChange(pEvent);
167 break;
168 case ModulationSystem::event_type_pitchbend:
169 dmsg(5,("Audio Thread: Pitchbend received\n"));
170 ProcessPitchbend(pEvent);
171 break;
172 }
173 }
174
175
176 // render audio from all active voices
177 int active_voices = 0;
178 uint* piKey = pActiveKeys->first();
179 while (piKey) { // iterate through all active keys
180 midi_key_info_t* pKey = &pMIDIKeyInfo[*piKey];
181 pActiveKeys->set_current(piKey);
182 piKey = pActiveKeys->next();
183
184 Voice* pVoiceNext = pKey->pActiveVoices->first();
185 while (pVoiceNext) { // iterate through all voices on this key
186 // already get next voice on key
187 Voice* pVoice = pVoiceNext;
188 pKey->pActiveVoices->set_current(pVoice);
189 pVoiceNext = pKey->pActiveVoices->next();
190
191 // now render current voice
192 pVoice->Render(Samples);
193 if (pVoice->IsActive()) active_voices++; // still active
194 else { // voice reached end, is now inactive
195 KillVoice(pVoice); // remove voice from the list of active voices
196 }
197 }
198 pKey->pEvents->clear(); // free all events on the key
199 }
200
201
202 // write that to the disk thread class so that it can print it
203 // on the console for debugging purposes
204 ActiveVoiceCount = active_voices;
205 if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;
206
207
208 return 0;
209 }
210
211 /**
212 * Will be called by the MIDIIn Thread to let the audio thread trigger a new
213 * voice for the given key.
214 *
215 * @param Key - MIDI key number of the triggered key
216 * @param Velocity - MIDI velocity value of the triggered key
217 */
218 void AudioThread::SendNoteOn(uint8_t Key, uint8_t Velocity) {
219 ModulationSystem::Event Event;
220 Event.Type = ModulationSystem::event_type_note_on;
221 Event.Key = Key;
222 Event.Velocity = Velocity;
223 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&Event);
224 else dmsg(1,("AudioThread: Input event queue full!"));
225 }
226
227 /**
228 * Will be called by the MIDIIn Thread to signal the audio thread to release
229 * voice(s) on the given key.
230 *
231 * @param Key - MIDI key number of the released key
232 * @param Velocity - MIDI release velocity value of the released key
233 */
234 void AudioThread::SendNoteOff(uint8_t Key, uint8_t Velocity) {
235 ModulationSystem::Event Event;
236 Event.Type = ModulationSystem::event_type_note_off;
237 Event.Key = Key;
238 Event.Velocity = Velocity;
239 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&Event);
240 else dmsg(1,("AudioThread: Input event queue full!"));
241 }
242
243 /**
244 * Will be called by the MIDIIn Thread to signal the audio thread to change
245 * the pitch value for all voices.
246 *
247 * @param Pitch - MIDI pitch value (-8192 ... +8191)
248 */
249 void AudioThread::SendPitchbend(int Pitch) {
250 ModulationSystem::Event Event;
251 Event.Type = ModulationSystem::event_type_pitchbend;
252 Event.Pitch = Pitch;
253 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&Event);
254 else dmsg(1,("AudioThread: Input event queue full!"));
255 }
256
257 /**
258 * Will be called by the MIDIIn Thread to signal the audio thread that a
259 * continuous controller value has changed.
260 *
261 * @param Controller - MIDI controller number of the occured control change
262 * @param Value - value of the control change
263 */
264 void AudioThread::SendControlChange(uint8_t Controller, uint8_t Value) {
265 ModulationSystem::Event Event;
266 Event.Type = ModulationSystem::event_type_control_change;
267 Event.Controller = Controller;
268 Event.Value = Value;
269 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&Event);
270 else dmsg(1,("AudioThread: Input event queue full!"));
271 }
272
273 /**
274 * Assigns and triggers a new voice for the respective MIDI key.
275 *
276 * @param pNoteOnEvent - key, velocity and time stamp of the event
277 */
278 void AudioThread::ProcessNoteOn(ModulationSystem::Event* pNoteOnEvent) {
279 midi_key_info_t* pKey = &pMIDIKeyInfo[pNoteOnEvent->Key];
280
281 pKey->KeyPressed = true; // the MIDI key was now pressed down
282
283 // cancel release process of voices on this key if needed
284 if (pKey->Active && !SustainPedal) {
285 pNoteOnEvent->Type = ModulationSystem::event_type_cancel_release; // transform event type
286 pEvents->move(pNoteOnEvent, pKey->pEvents); // move event to the key's own event list
287 }
288
289 // allocate a new voice for the key
290 Voice* pNewVoice = pKey->pActiveVoices->alloc();
291 if (pNewVoice) {
292 // launch the new voice
293 if (pNewVoice->Trigger(pNoteOnEvent, this->Pitch, this->pInstrument) < 0) {
294 dmsg(1,("Triggering new voice failed!\n"));
295 pKey->pActiveVoices->free(pNewVoice);
296 }
297 else if (!pKey->Active) { // mark as active key
298 pKey->Active = true;
299 pKey->pSelf = pActiveKeys->alloc();
300 *pKey->pSelf = pNoteOnEvent->Key;
301 }
302 }
303 else std::cerr << "No free voice!" << std::endl << std::flush;
304 }
305
306 /**
307 * Releases the voices on the given key if sustain pedal is not pressed.
308 * If sustain is pressed, the release of the note will be postponed until
309 * sustain pedal will be released or voice turned inactive by itself (e.g.
310 * due to completion of sample playback).
311 *
312 * @param pNoteOffEvent - key, velocity and time stamp of the event
313 */
314 void AudioThread::ProcessNoteOff(ModulationSystem::Event* pNoteOffEvent) {
315 midi_key_info_t* pKey = &pMIDIKeyInfo[pNoteOffEvent->Key];
316
317 pKey->KeyPressed = false; // the MIDI key was now released
318
319 // release voices on this key if needed
320 if (pKey->Active && !SustainPedal) {
321 pNoteOffEvent->Type = ModulationSystem::event_type_release; // transform event type
322 pEvents->move(pNoteOffEvent, pKey->pEvents); // move event to the key's own event list
323 }
324 }
325
326 /**
327 * Moves pitchbend event from the general (input) event list to the pitch
328 * event list and converts absolute pitch value to delta pitch value.
329 *
330 * @param pPitchbendEvent - absolute pitch value and time stamp of the event
331 */
332 void AudioThread::ProcessPitchbend(ModulationSystem::Event* pPitchbendEvent) {
333 int currentPitch = pPitchbendEvent->Pitch;
334 pPitchbendEvent->Pitch -= this->Pitch; // convert to delta
335 this->Pitch = currentPitch; // store current absolute pitch value
336 pEvents->move(pPitchbendEvent, pSynthesisEvents[ModulationSystem::destination_vco]);
337 }
338
339 /**
340 * Immediately kills the voice given with pVoice (no matter if sustain is
341 * pressed or not) and removes it from the MIDI key's list of active voice.
342 * This method will e.g. be called if a voice went inactive by itself.
343 *
344 * @param pVoice - points to the voice to be killed
345 */
346 void AudioThread::KillVoice(Voice* pVoice) {
347 if (pVoice) {
348 if (pVoice->IsActive()) pVoice->Kill();
349
350 midi_key_info_t* pKey = &pMIDIKeyInfo[pVoice->MIDIKey];
351
352 // free the voice object
353 pVoicePool->free(pVoice);
354
355 // check if there are no voices left on the MIDI key and update the key info if so
356 if (pKey->pActiveVoices->is_empty()) {
357 pKey->Active = false;
358 pActiveKeys->free(pKey->pSelf); // remove key from list of active keys
359 pKey->pSelf = NULL;
360 dmsg(3,("Key has no more voices now\n"));
361 }
362 }
363 else std::cerr << "Couldn't release voice! (pVoice == NULL)\n" << std::flush;
364 }
365
366 /**
367 * Reacts on supported control change commands (e.g. pitch bend wheel,
368 * modulation wheel, aftertouch).
369 *
370 * @param pControlChangeEvent - controller, value and time stamp of the event
371 */
372 void AudioThread::ProcessControlChange(ModulationSystem::Event* pControlChangeEvent) {
373 dmsg(4,("AudioThread::ContinuousController cc=%d v=%d\n", pControlChangeEvent->Controller, pControlChangeEvent->Value));
374
375 switch (pControlChangeEvent->Controller) {
376 case 64: {
377 if (pControlChangeEvent->Value >= 64 && !SustainPedal) {
378 dmsg(4,("PEDAL DOWN\n"));
379 SustainPedal = true;
380
381 // cancel release process of voices if necessary
382 uint* piKey = pActiveKeys->first();
383 if (piKey) {
384 pControlChangeEvent->Type = ModulationSystem::event_type_cancel_release; // transform event type
385 while (piKey) {
386 midi_key_info_t* pKey = &pMIDIKeyInfo[*piKey];
387 pActiveKeys->set_current(piKey);
388 piKey = pActiveKeys->next();
389 if (!pKey->KeyPressed) {
390 ModulationSystem::Event* pNewEvent = pKey->pEvents->alloc();
391 if (pNewEvent) *pNewEvent = *pControlChangeEvent; // copy event to the key's own event list
392 else dmsg(1,("Event pool emtpy!\n"));
393 }
394 }
395 }
396 }
397 if (pControlChangeEvent->Value < 64 && SustainPedal) {
398 dmsg(4,("PEDAL UP\n"));
399 SustainPedal = false;
400
401 // release voices if their respective key is not pressed
402 uint* piKey = pActiveKeys->first();
403 if (piKey) {
404 pControlChangeEvent->Type = ModulationSystem::event_type_release; // transform event type
405 while (piKey) {
406 midi_key_info_t* pKey = &pMIDIKeyInfo[*piKey];
407 pActiveKeys->set_current(piKey);
408 piKey = pActiveKeys->next();
409 if (!pKey->KeyPressed) {
410 ModulationSystem::Event* pNewEvent = pKey->pEvents->alloc();
411 if (pNewEvent) *pNewEvent = *pControlChangeEvent; // copy event to the key's own event list
412 else dmsg(1,("Event pool emtpy!\n"));
413 }
414 }
415 }
416 }
417 break;
418 }
419 }
420
421 // update controller value in the engine's controller table
422 ControllerTable[pControlChangeEvent->Controller] = pControlChangeEvent->Value;
423
424 // move event from the unsorted event list to the control change event list
425 pEvents->move(pControlChangeEvent, pCCEvents);
426 }
427
428 /**
429 * Caches a certain size at the beginning of the given sample in RAM. If the
430 * sample is very short, the whole sample will be loaded into RAM and thus
431 * no disk streaming is needed for this sample. Caching an initial part of
432 * samples is needed to compensate disk reading latency.
433 *
434 * @param pSample - points to the sample to be cached
435 */
436 void AudioThread::CacheInitialSamples(gig::Sample* pSample) {
437 if (!pSample || pSample->GetCache().Size) return;
438 if (pSample->SamplesTotal <= NUM_RAM_PRELOAD_SAMPLES) {
439 // Sample is too short for disk streaming, so we load the whole
440 // sample into RAM and place 'pAudioIO->FragmentSize << MAX_PITCH'
441 // number of '0' samples (silence samples) behind the official buffer
442 // border, to allow the interpolator do it's work even at the end of
443 // the sample.
444 gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension((pAudioIO->MaxSamplesPerCycle() << MAX_PITCH) + 3);
445 dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
446 }
447 else { // we only cache NUM_RAM_PRELOAD_SAMPLES and stream the other sample points from disk
448 pSample->LoadSampleData(NUM_RAM_PRELOAD_SAMPLES);
449 }
450
451 if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
452 }
453
454 /**
455 * Load an instrument from a .gig file.
456 *
457 * @param FileName - file name of the Gigasampler instrument file
458 * @param Instrument - index of the instrument in the .gig file
459 * @returns detailed description of the result of the method call
460 */
461 result_t AudioThread::LoadInstrument(const char* FileName, uint Instrument) {
462 result_t result;
463
464 if (pInstrument) { // if already running
465 // signal audio thread not to enter render part anymore
466 SuspensionRequested = true;
467 // sleep until wakened by audio thread
468 pthread_mutex_lock(&__render_state_mutex);
469 pthread_cond_wait(&__render_exit_condition, &__render_state_mutex);
470 pthread_mutex_unlock(&__render_state_mutex);
471
472 dmsg(1,("Freeing old instrument from memory..."));
473 delete pGig;
474 delete pRIFF;
475 pInstrument = NULL;
476 dmsg(1,("OK\n"));
477 }
478
479 // loading gig file
480 try {
481 dmsg(1,("Loading gig file..."));
482 pRIFF = new RIFF::File(FileName);
483 pGig = new gig::File(pRIFF);
484 pInstrument = pGig->GetInstrument(Instrument);
485 if (!pInstrument) {
486 std::stringstream msg;
487 msg << "There's no instrument with index " << Instrument << ".";
488 std::cerr << msg << std::endl;
489 result.type = result_type_error;
490 result.code = LSCP_ERR_UNKNOWN;
491 result.message = msg.str();
492 return result;
493 }
494 pGig->GetFirstSample(); // just to complete instrument loading before we enter the realtime part
495 dmsg(1,("OK\n"));
496 }
497 catch (RIFF::Exception e) {
498 e.PrintMessage();
499 result.type = result_type_error;
500 result.code = LSCP_ERR_UNKNOWN;
501 result.message = e.Message;
502 return result;
503 }
504 catch (...) {
505 dmsg(1,("Unknown exception while trying to parse gig file.\n"));
506 result.type = result_type_error;
507 result.code = LSCP_ERR_UNKNOWN;
508 result.message = "Unknown exception while trying to parse gig file.";
509 return result;
510 }
511
512 // cache initial samples points (for actually needed samples)
513 dmsg(1,("Caching initial samples..."));
514 gig::Region* pRgn = this->pInstrument->GetFirstRegion();
515 while (pRgn) {
516 if (!pRgn->GetSample()->GetCache().Size) {
517 dmsg(2,("C"));
518 CacheInitialSamples(pRgn->GetSample());
519 }
520 for (uint i = 0; i < pRgn->DimensionRegions; i++) {
521 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample);
522 }
523
524 pRgn = this->pInstrument->GetNextRegion();
525 }
526 dmsg(1,("OK\n"));
527
528 ResetInternal(); // reset engine
529
530 // signal audio thread to continue with rendering
531 SuspensionRequested = false;
532
533 // success
534 result.type = result_type_success;
535 return result;
536 }
537
538 /**
539 * Reset all voices and disk thread and clear input event queue and all
540 * control and status variables.
541 */
542 void AudioThread::Reset() {
543 if (pInstrument) { // if already running
544 // signal audio thread not to enter render part anymore
545 SuspensionRequested = true;
546 // sleep until wakened by audio thread
547 pthread_mutex_lock(&__render_state_mutex);
548 pthread_cond_wait(&__render_exit_condition, &__render_state_mutex);
549 pthread_mutex_unlock(&__render_state_mutex);
550 }
551
552 ResetInternal();
553
554 // signal audio thread to continue with rendering
555 SuspensionRequested = false;
556 }
557
558 /**
559 * Reset all voices and disk thread and clear input event queue and all
560 * control and status variables. This method is not thread safe!
561 */
562 void AudioThread::ResetInternal() {
563 this->Pitch = 0;
564 SustainPedal = 0;
565 ActiveVoiceCount = 0;
566 ActiveVoiceCountMax = 0;
567
568 // reset key info
569 for (uint i = 0; i < 128; i++) {
570 pMIDIKeyInfo[i].pActiveVoices->clear();
571 pMIDIKeyInfo[i].pEvents->clear();
572 pMIDIKeyInfo[i].KeyPressed = false;
573 pMIDIKeyInfo[i].Active = false;
574 pMIDIKeyInfo[i].pSelf = NULL;
575 }
576
577 // reset all voices
578 for (Voice* pVoice = pVoicePool->first(); pVoice; pVoice = pVoicePool->next()) {
579 pVoice->Reset();
580 }
581
582 // free all active keys
583 pActiveKeys->clear();
584
585 // reset disk thread
586 pDiskThread->Reset();
587
588 // delete all input events
589 pEventQueue->init();
590 }

  ViewVC Help
Powered by ViewVC