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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 35 - (show annotations) (download)
Fri Mar 5 13:46:15 2004 UTC (20 years, 1 month ago) by schoenebeck
File size: 23204 byte(s)
* implemented parser for the LinuxSampler control protocol (LSCP) by using
  flex / bison (where src/network/lscp.l is the input file for lex / flex
  and src/network/lscp.y is the input file for yacc / bison), parser and
  scanner can be regenerated by 'make parser'
* implemented LSCP network server (only single threaded so far), LSCP
  server will be launched if LinuxSampler was started with "--server" flag,
  implemented the following LSCP commands so far: "LOAD INSTRUMENT", "GET
  CHANNEL VOICE_COUNT", "GET CHANNEL STREAM_COUNT", "GET CHANNEL
  BUFFER_FILL", "SET CHANNEL VOLUME" and "RESET CHANNEL"
* disk thread now started within the engine

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

  ViewVC Help
Powered by ViewVC