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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 40 - (show annotations) (download)
Tue Mar 30 13:14:58 2004 UTC (20 years ago) by schoenebeck
File size: 23236 byte(s)
* added Envelope Generator 2 and 3 (filter cutoff EG and pitch EG) for
  accurate .gig playback
* fixed accuracy of pitch calculation
* changed filter cutoff range to 100Hz..10kHz with exponential curve, this
  value range can also be adjusted on compile time by setting
  FILTER_CUTOFF_MIN and FILTER_CUTOFF_MAX in src/voice.h to desired
  frequencies
* src/lfo.h: lfo is now generalized to a C++ template, which will be useful
  especially when we implement further engines

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.
329 *
330 * @param pPitchbendEvent - absolute pitch value and time stamp of the event
331 */
332 void AudioThread::ProcessPitchbend(ModulationSystem::Event* pPitchbendEvent) {
333 this->Pitch = pPitchbendEvent->Pitch; // store current pitch value
334 pEvents->move(pPitchbendEvent, pSynthesisEvents[ModulationSystem::destination_vco]);
335 }
336
337 /**
338 * Immediately kills the voice given with pVoice (no matter if sustain is
339 * pressed or not) and removes it from the MIDI key's list of active voice.
340 * This method will e.g. be called if a voice went inactive by itself.
341 *
342 * @param pVoice - points to the voice to be killed
343 */
344 void AudioThread::KillVoice(Voice* pVoice) {
345 if (pVoice) {
346 if (pVoice->IsActive()) pVoice->Kill();
347
348 midi_key_info_t* pKey = &pMIDIKeyInfo[pVoice->MIDIKey];
349
350 // free the voice object
351 pVoicePool->free(pVoice);
352
353 // check if there are no voices left on the MIDI key and update the key info if so
354 if (pKey->pActiveVoices->is_empty()) {
355 pKey->Active = false;
356 pActiveKeys->free(pKey->pSelf); // remove key from list of active keys
357 pKey->pSelf = NULL;
358 dmsg(3,("Key has no more voices now\n"));
359 }
360 }
361 else std::cerr << "Couldn't release voice! (pVoice == NULL)\n" << std::flush;
362 }
363
364 /**
365 * Reacts on supported control change commands (e.g. pitch bend wheel,
366 * modulation wheel, aftertouch).
367 *
368 * @param pControlChangeEvent - controller, value and time stamp of the event
369 */
370 void AudioThread::ProcessControlChange(ModulationSystem::Event* pControlChangeEvent) {
371 dmsg(4,("AudioThread::ContinuousController cc=%d v=%d\n", pControlChangeEvent->Controller, pControlChangeEvent->Value));
372
373 switch (pControlChangeEvent->Controller) {
374 case 64: {
375 if (pControlChangeEvent->Value >= 64 && !SustainPedal) {
376 dmsg(4,("PEDAL DOWN\n"));
377 SustainPedal = true;
378
379 // cancel release process of voices if necessary
380 uint* piKey = pActiveKeys->first();
381 if (piKey) {
382 pControlChangeEvent->Type = ModulationSystem::event_type_cancel_release; // transform event type
383 while (piKey) {
384 midi_key_info_t* pKey = &pMIDIKeyInfo[*piKey];
385 pActiveKeys->set_current(piKey);
386 piKey = pActiveKeys->next();
387 if (!pKey->KeyPressed) {
388 ModulationSystem::Event* pNewEvent = pKey->pEvents->alloc();
389 if (pNewEvent) *pNewEvent = *pControlChangeEvent; // copy event to the key's own event list
390 else dmsg(1,("Event pool emtpy!\n"));
391 }
392 }
393 }
394 }
395 if (pControlChangeEvent->Value < 64 && SustainPedal) {
396 dmsg(4,("PEDAL UP\n"));
397 SustainPedal = false;
398
399 // release voices if their respective key is not pressed
400 uint* piKey = pActiveKeys->first();
401 if (piKey) {
402 pControlChangeEvent->Type = ModulationSystem::event_type_release; // transform event type
403 while (piKey) {
404 midi_key_info_t* pKey = &pMIDIKeyInfo[*piKey];
405 pActiveKeys->set_current(piKey);
406 piKey = pActiveKeys->next();
407 if (!pKey->KeyPressed) {
408 ModulationSystem::Event* pNewEvent = pKey->pEvents->alloc();
409 if (pNewEvent) *pNewEvent = *pControlChangeEvent; // copy event to the key's own event list
410 else dmsg(1,("Event pool emtpy!\n"));
411 }
412 }
413 }
414 }
415 break;
416 }
417 }
418
419 // update controller value in the engine's controller table
420 ControllerTable[pControlChangeEvent->Controller] = pControlChangeEvent->Value;
421
422 // move event from the unsorted event list to the control change event list
423 pEvents->move(pControlChangeEvent, pCCEvents);
424 }
425
426 /**
427 * Caches a certain size at the beginning of the given sample in RAM. If the
428 * sample is very short, the whole sample will be loaded into RAM and thus
429 * no disk streaming is needed for this sample. Caching an initial part of
430 * samples is needed to compensate disk reading latency.
431 *
432 * @param pSample - points to the sample to be cached
433 */
434 void AudioThread::CacheInitialSamples(gig::Sample* pSample) {
435 if (!pSample || pSample->GetCache().Size) return;
436 if (pSample->SamplesTotal <= NUM_RAM_PRELOAD_SAMPLES) {
437 // Sample is too short for disk streaming, so we load the whole
438 // sample into RAM and place 'pAudioIO->FragmentSize << MAX_PITCH'
439 // number of '0' samples (silence samples) behind the official buffer
440 // border, to allow the interpolator do it's work even at the end of
441 // the sample.
442 gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension((pAudioIO->MaxSamplesPerCycle() << MAX_PITCH) + 3);
443 dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize));
444 }
445 else { // we only cache NUM_RAM_PRELOAD_SAMPLES and stream the other sample points from disk
446 pSample->LoadSampleData(NUM_RAM_PRELOAD_SAMPLES);
447 }
448
449 if (!pSample->GetCache().Size) std::cerr << "Unable to cache sample - maybe memory full!" << std::endl << std::flush;
450 }
451
452 /**
453 * Load an instrument from a .gig file.
454 *
455 * @param FileName - file name of the Gigasampler instrument file
456 * @param Instrument - index of the instrument in the .gig file
457 * @returns detailed description of the result of the method call
458 */
459 result_t AudioThread::LoadInstrument(const char* FileName, uint Instrument) {
460 result_t result;
461
462 if (pInstrument) { // if already running
463 // signal audio thread not to enter render part anymore
464 SuspensionRequested = true;
465 // sleep until wakened by audio thread
466 pthread_mutex_lock(&__render_state_mutex);
467 pthread_cond_wait(&__render_exit_condition, &__render_state_mutex);
468 pthread_mutex_unlock(&__render_state_mutex);
469
470 dmsg(1,("Freeing old instrument from memory..."));
471 delete pGig;
472 delete pRIFF;
473 pInstrument = NULL;
474 dmsg(1,("OK\n"));
475 }
476
477 // loading gig file
478 try {
479 dmsg(1,("Loading gig file..."));
480 pRIFF = new RIFF::File(FileName);
481 pGig = new gig::File(pRIFF);
482 pInstrument = pGig->GetInstrument(Instrument);
483 if (!pInstrument) {
484 std::stringstream msg;
485 msg << "There's no instrument with index " << Instrument << ".";
486 std::cerr << msg << std::endl;
487 result.type = result_type_error;
488 result.code = LSCP_ERR_UNKNOWN;
489 result.message = msg.str();
490 return result;
491 }
492 pGig->GetFirstSample(); // just to complete instrument loading before we enter the realtime part
493 dmsg(1,("OK\n"));
494 }
495 catch (RIFF::Exception e) {
496 e.PrintMessage();
497 result.type = result_type_error;
498 result.code = LSCP_ERR_UNKNOWN;
499 result.message = e.Message;
500 return result;
501 }
502 catch (...) {
503 dmsg(1,("Unknown exception while trying to parse gig file.\n"));
504 result.type = result_type_error;
505 result.code = LSCP_ERR_UNKNOWN;
506 result.message = "Unknown exception while trying to parse gig file.";
507 return result;
508 }
509
510 // cache initial samples points (for actually needed samples)
511 dmsg(1,("Caching initial samples..."));
512 gig::Region* pRgn = this->pInstrument->GetFirstRegion();
513 while (pRgn) {
514 if (!pRgn->GetSample()->GetCache().Size) {
515 dmsg(2,("C"));
516 CacheInitialSamples(pRgn->GetSample());
517 }
518 for (uint i = 0; i < pRgn->DimensionRegions; i++) {
519 CacheInitialSamples(pRgn->pDimensionRegions[i]->pSample);
520 }
521
522 pRgn = this->pInstrument->GetNextRegion();
523 }
524 dmsg(1,("OK\n"));
525
526 ResetInternal(); // reset engine
527
528 // signal audio thread to continue with rendering
529 SuspensionRequested = false;
530
531 // success
532 result.type = result_type_success;
533 return result;
534 }
535
536 /**
537 * Reset all voices and disk thread and clear input event queue and all
538 * control and status variables.
539 */
540 void AudioThread::Reset() {
541 if (pInstrument) { // if already running
542 // signal audio thread not to enter render part anymore
543 SuspensionRequested = true;
544 // sleep until wakened by audio thread
545 pthread_mutex_lock(&__render_state_mutex);
546 pthread_cond_wait(&__render_exit_condition, &__render_state_mutex);
547 pthread_mutex_unlock(&__render_state_mutex);
548 }
549
550 ResetInternal();
551
552 // signal audio thread to continue with rendering
553 SuspensionRequested = false;
554 }
555
556 /**
557 * Reset all voices and disk thread and clear input event queue and all
558 * control and status variables. This method is not thread safe!
559 */
560 void AudioThread::ResetInternal() {
561 this->Pitch = 0;
562 SustainPedal = 0;
563 ActiveVoiceCount = 0;
564 ActiveVoiceCountMax = 0;
565
566 // reset key info
567 for (uint i = 0; i < 128; i++) {
568 pMIDIKeyInfo[i].pActiveVoices->clear();
569 pMIDIKeyInfo[i].pEvents->clear();
570 pMIDIKeyInfo[i].KeyPressed = false;
571 pMIDIKeyInfo[i].Active = false;
572 pMIDIKeyInfo[i].pSelf = NULL;
573 }
574
575 // reset all voices
576 for (Voice* pVoice = pVoicePool->first(); pVoice; pVoice = pVoicePool->next()) {
577 pVoice->Reset();
578 }
579
580 // free all active keys
581 pActiveKeys->clear();
582
583 // reset disk thread
584 pDiskThread->Reset();
585
586 // delete all input events
587 pEventQueue->init();
588 }

  ViewVC Help
Powered by ViewVC