--- linuxsampler/trunk/src/audiothread.cpp 2003/11/05 14:47:10 9 +++ linuxsampler/trunk/src/audiothread.cpp 2003/11/21 15:40:40 14 @@ -28,12 +28,16 @@ this->pInstrument = pInstrument; pCommandQueue = new RingBuffer(1024); pVoices = new Voice*[MAX_AUDIO_VOICES]; + // allocate the ActiveVoicePool (for each midi key there is a variable size linked list + // of pointers to Voice objects) + ActiveVoicePool = new RTELMemoryPool(MAX_AUDIO_VOICES); for (uint i = 0; i < MAX_AUDIO_VOICES; i++) { pVoices[i] = new Voice(pDiskThread); } for (uint i = 0; i < 128; i++) { - ActiveVoices[i] = NULL; + pActiveVoices[i] = new RTEList; } + SustainedKeyPool = new RTELMemoryPool(MAX_AUDIO_VOICES); pAudioSumBuffer = new float[pAudioIO->FragmentSize * pAudioIO->Channels]; @@ -43,10 +47,11 @@ } // cache initial samples points (for actually needed samples) - dmsg(("Caching initial samples...")); + dmsg(1,("Caching initial samples...")); gig::Region* pRgn = this->pInstrument->GetFirstRegion(); while (pRgn) { if (!pRgn->GetSample()->GetCache().Size) { + dmsg(2,("C")); CacheInitialSamples(pRgn->GetSample()); } for (uint i = 0; i < pRgn->DimensionRegions; i++) { @@ -55,7 +60,12 @@ pRgn = this->pInstrument->GetNextRegion(); } - dmsg(("OK\n")); + + // sustain pedal value + PrevHoldCCValue = 0; + SustainPedal = 0; + + dmsg(1,("OK\n")); } AudioThread::~AudioThread() { @@ -69,7 +79,7 @@ } int AudioThread::Main() { - dmsg(("Audio thread running\n")); + dmsg(2,("Audio thread running\n")); while (true) { @@ -80,13 +90,17 @@ switch (command.type) { case command_type_note_on: - dmsg(("Audio Thread: Note on received\n")); + dmsg(5,("Audio Thread: Note on received\n")); ActivateVoice(command.pitch, command.velocity); break; case command_type_note_off: - dmsg(("Audio Thread: Note off received\n")); + dmsg(5,("Audio Thread: Note off received\n")); ReleaseVoice(command.pitch, command.velocity); break; + case command_type_continuous_controller: + dmsg(5,("Audio Thread: MIDI CC received\n")); + ContinuousController(command.channel, command.number, command.value); + break; } } @@ -98,11 +112,20 @@ // render audio from all active voices + int active_voices = 0; for (uint i = 0; i < MAX_AUDIO_VOICES; i++) { if (pVoices[i]->IsActive()) { pVoices[i]->RenderAudio(); + if (pVoices[i]->IsActive()) active_voices++; // still active + else { // voice reached end, is now inactive + ReleaseVoice(pVoices[i]); // remove voice from the list of active voices + } } } + // write that to the disk thread class so that it can print it + // on the console for debugging purposes + ActiveVoiceCount = active_voices; + if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount; // check clipping in the audio sum, convert to sample_type @@ -140,23 +163,74 @@ this->pCommandQueue->write(&cmd, 1); } +// Will be called by the MIDIIn Thead to send MIDI continuos controller events +void AudioThread::ProcessContinuousController(uint8_t Channel, uint8_t Number, uint8_t Value) { + command_t cmd; + cmd.type = command_type_continuous_controller; + cmd.channel = Channel; + cmd.number = Number; + cmd.value = Value; + this->pCommandQueue->write(&cmd, 1); +} + void AudioThread::ActivateVoice(uint8_t MIDIKey, uint8_t Velocity) { for (int i = 0; i < MAX_AUDIO_VOICES; i++) { if (pVoices[i]->IsActive()) continue; pVoices[i]->Trigger(MIDIKey, Velocity, this->pInstrument); - ActiveVoices[MIDIKey] = pVoices[i]; + // add (append) a new voice to the corresponding MIDIKey active voices list + Voice** new_voice_ptr = ActiveVoicePool->alloc_append(pActiveVoices[MIDIKey]); + *new_voice_ptr = pVoices[i]; + pVoices[i]->pSelfPtr = new_voice_ptr; // FIXME: hack to allow fast deallocation return; } std::cerr << "No free voice!" << std::endl << std::flush; } void AudioThread::ReleaseVoice(uint8_t MIDIKey, uint8_t Velocity) { - Voice* pVoice = ActiveVoices[MIDIKey]; + // if sustain pedal is pressed postpone the Note-Off + if (SustainPedal) { + // alloc an element in the SustainedKeyPool and add the current midikey to it + sustained_key_t* key = SustainedKeyPool->alloc(); + if (key == NULL) printf("ERROR: SustainedKeyPool FULL ! exiting\n"); // FIXME + key->midikey = MIDIKey; + key->velocity = Velocity; + } + else { + // get the first voice in the list of active voices on the MIDI Key + Voice** pVoicePtr = pActiveVoices[MIDIKey]->first(); + if (pVoicePtr) ReleaseVoice(*pVoicePtr); + else std::cerr << "Couldn't find active voice for note off command!" << std::endl << std::flush; + } +} + +void AudioThread::ReleaseVoice(Voice* pVoice) { if (pVoice) { - pVoice->Kill(); //TODO: for now we're rude and just kill the poor, poor voice immediately :), later we add a Release() method to the Voice class and call it here to let the voice go through it's release phase - ActiveVoices[MIDIKey] = NULL; + if (pVoice->IsActive()) pVoice->Kill(); //TODO: for now we're rude and just kill the poor, poor voice immediately :), later we add a Release() method to the Voice class and call it here to let the voice go through it's release phase + + // remove the voice from the list associated to this MIDI key + ActiveVoicePool->free(pVoice->pSelfPtr); + } + else std::cerr << "Couldn't find active voice to release!" << std::endl << std::flush; +} + +void AudioThread::ContinuousController(uint8_t Channel, uint8_t Number, uint8_t Value) { + dmsg(4,("AudioThread::ContinuousController c=%d n=%d v=%d\n", Channel, Number, Value)); + if (Number == 64) { + if (Value >= 64 && PrevHoldCCValue < 64) { + dmsg(4,("PEDAL DOWN\n")); + SustainPedal = true; + } + if (Value < 64 && PrevHoldCCValue >= 64) { + dmsg(4,("PEDAL UP\n")); + SustainPedal = false; + for (sustained_key_t* key = SustainedKeyPool->first(); key; key = SustainedKeyPool->next()) { + ReleaseVoice(key->midikey, key->velocity); + } + // empty the SustainedKeyPool (free all the elements) + SustainedKeyPool->empty(); + } + PrevHoldCCValue = Value; } - else std::cerr << "Couldn't find active voice for note off command!" << std::endl << std::flush; } void AudioThread::CacheInitialSamples(gig::Sample* pSample) { @@ -168,7 +242,7 @@ // border, to allow the interpolator do it's work even at the end of // the sample. gig::buffer_t buf = pSample->LoadSampleDataWithNullSamplesExtension(pAudioIO->FragmentSize << MAX_PITCH); - dmsg(("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize)); + dmsg(4,("Cached %d Bytes, %d silence bytes.\n", buf.Size, buf.NullExtensionSize)); } else { // we only cache NUM_RAM_PRELOAD_SAMPLES and stream the other sample points from disk pSample->LoadSampleData(NUM_RAM_PRELOAD_SAMPLES);