--- linuxsampler/trunk/src/engines/gig/Engine.cpp 2007/01/23 20:03:22 1037 +++ linuxsampler/trunk/src/engines/gig/Engine.cpp 2007/08/16 15:55:21 1297 @@ -51,6 +51,11 @@ if (engines.count(pDevice)) { dmsg(4,("Using existing gig::Engine.\n")); pEngine = engines[pDevice]; + + // Disable the engine while the new engine channel is + // added and initialized. The engine will be enabled again + // in EngineChannel::Connect. + pEngine->DisableAndLock(); } else { // create a new engine (and disk thread) instance for the given audio output device dmsg(4,("Creating new gig::Engine.\n")); pEngine = (Engine*) EngineFactory::Create("gig"); @@ -101,8 +106,12 @@ pEventQueue = new RingBuffer(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); pEventPool = new Pool(CONFIG_MAX_EVENTS_PER_FRAGMENT); pVoicePool = new Pool(CONFIG_MAX_VOICES); + pDimRegionsInUse = new ::gig::DimensionRegion*[CONFIG_MAX_VOICES + 1]; pVoiceStealingQueue = new RTList(pEventPool); pGlobalEvents = new RTList(pEventPool); + InstrumentChangeQueue = new RingBuffer(1, 0); + InstrumentChangeReplyQueue = new RingBuffer(1, 0); + for (RTList::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { iterVoice->SetEngine(this); } @@ -132,6 +141,10 @@ if (pEventGenerator) delete pEventGenerator; if (pVoiceStealingQueue) delete pVoiceStealingQueue; if (pSysexBuffer) delete pSysexBuffer; + if (pGlobalEvents) delete pGlobalEvents; + if (InstrumentChangeQueue) delete InstrumentChangeQueue; + if (InstrumentChangeReplyQueue) delete InstrumentChangeReplyQueue; + if (pDimRegionsInUse) delete[] pDimRegionsInUse; Unregister(); } @@ -257,7 +270,8 @@ delete this->pDiskThread; dmsg(1,("OK\n")); } - this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6); //FIXME: assuming stereo + this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo + &instruments); if (!pDiskThread) { dmsg(0,("gig::Engine new diskthread = NULL\n")); exit(EXIT_FAILURE); @@ -377,15 +391,46 @@ // reset internal voice counter (just for statistic of active voices) ActiveVoiceCountTemp = 0; + // handle instrument change commands + instrument_change_command_t command; + if (InstrumentChangeQueue->pop(&command) > 0) { + EngineChannel* pEngineChannel = command.pEngineChannel; + pEngineChannel->pInstrument = command.pInstrument; + + // iterate through all active voices and mark their + // dimension regions as "in use". The instrument resource + // manager may delete all of the instrument except the + // dimension regions and samples that are in use. + int i = 0; + RTList::Iterator iuiKey = pEngineChannel->pActiveKeys->first(); + RTList::Iterator end = pEngineChannel->pActiveKeys->end(); + while (iuiKey != end) { // iterate through all active keys + midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey]; + ++iuiKey; + + RTList::Iterator itVoice = pKey->pActiveVoices->first(); + RTList::Iterator itVoicesEnd = pKey->pActiveVoices->end(); + for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key + if (!itVoice->Orphan) { + itVoice->Orphan = true; + pDimRegionsInUse[i++] = itVoice->pDimRgn; + } + } + } + pDimRegionsInUse[i] = 0; // end of list + + // send a reply to the calling thread, which is waiting + instrument_change_reply_t reply; + InstrumentChangeReplyQueue->push(&reply); + } + // handle events on all engine channels for (int i = 0; i < engineChannels.size(); i++) { - if (!engineChannels[i]->pInstrument) continue; // ignore if no instrument loaded ProcessEvents(engineChannels[i], Samples); } // render all 'normal', active voices on all engine channels for (int i = 0; i < engineChannels.size(); i++) { - if (!engineChannels[i]->pInstrument) continue; // ignore if no instrument loaded RenderActiveVoices(engineChannels[i], Samples); } @@ -400,7 +445,6 @@ // handle cleanup on all engine channels for the next audio fragment for (int i = 0; i < engineChannels.size(); i++) { - if (!engineChannels[i]->pInstrument) continue; // ignore if no instrument loaded PostProcess(engineChannels[i]); } @@ -482,6 +526,8 @@ if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted #endif + uint vc = 0; + uint sc = 0; RTList::Iterator iuiKey = pEngineChannel->pActiveKeys->first(); RTList::Iterator end = pEngineChannel->pActiveKeys->end(); while (iuiKey != end) { // iterate through all active keys @@ -493,12 +539,21 @@ for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key // now render current voice itVoice->Render(Samples); - if (itVoice->IsActive()) ActiveVoiceCountTemp++; // still active - else { // voice reached end, is now inactive + if (itVoice->IsActive()) { // still active + ActiveVoiceCountTemp++; + vc++; + + if (itVoice->PlaybackState == Voice::playback_state_disk) { + if ((itVoice->DiskStreamRef).State == Stream::state_active) sc++; + } + } else { // voice reached end, is now inactive FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices } } } + + pEngineChannel->SetVoiceCount(vc); + pEngineChannel->SetDiskStreamCount(sc); } /** @@ -519,12 +574,21 @@ RTList::Iterator end = pVoiceStealingQueue->end(); for (; itVoiceStealEvent != end; ++itVoiceStealEvent) { EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel; + if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded Pool::Iterator itNewVoice = LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false); if (itNewVoice) { itNewVoice->Render(Samples); - if (itNewVoice->IsActive()) ActiveVoiceCountTemp++; // still active - else { // voice reached end, is now inactive + if (itNewVoice->IsActive()) { // still active + ActiveVoiceCountTemp++; + pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1); + + if (itNewVoice->PlaybackState == Voice::playback_state_disk) { + if (itNewVoice->DiskStreamRef.State == Stream::state_active) { + pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1); + } + } + } else { // voice reached end, is now inactive FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices } } @@ -666,6 +730,11 @@ if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted #endif + if (!pEngineChannel->pInstrument) return; // ignore if no instrument loaded + + //HACK: we should better add the transpose value only to the most mandatory places (like for retrieving the region and calculating the tuning), because otherwise voices will unintendedly survive when changing transpose while playing + itNoteOnEvent->Param.Note.Key += pEngineChannel->GlobalTranspose; + const int key = itNoteOnEvent->Param.Note.Key; midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key]; @@ -753,6 +822,9 @@ if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted #endif + //HACK: we should better add the transpose value only to the most mandatory places (like for retrieving the region and calculating the tuning), because otherwise voices will unintendedly survive when changing transpose while playing + itNoteOffEvent->Param.Note.Key += pEngineChannel->GlobalTranspose; + const int iKey = itNoteOffEvent->Param.Note.Key; midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey]; pKey->KeyPressed = false; // the MIDI key was now released @@ -763,7 +835,7 @@ bool bShouldRelease = pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEventOnKeyList->Param.Note.Key); // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any) - if (pEngineChannel->SoloMode) { //TODO: this feels like too much code just for handling solo mode :P + if (pEngineChannel->SoloMode && pEngineChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P bool bOtherKeysPressed = false; if (iKey == pEngineChannel->SoloKey) { pEngineChannel->SoloKey = -1; @@ -826,7 +898,7 @@ itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type // spawn release triggered voice(s) if needed - if (pKey->ReleaseTrigger) { + if (pKey->ReleaseTrigger && pEngineChannel->pInstrument) { // first, get total amount of required voices (dependant on amount of layers) ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key); if (pRegion) { @@ -1021,6 +1093,12 @@ std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush; } } + + // return if this is a release triggered voice and there is no + // releasetrigger dimension (could happen if an instrument + // change has occured between note on and off) + if (ReleaseTriggerVoice && VoiceType != Voice::type_release_trigger) return Pool::Iterator(); + ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues); // no need to continue if sample is silent @@ -1250,6 +1328,13 @@ uint keygroup = itVoice->KeyGroup; + // if the sample and dimension region belong to an + // instrument that is unloaded, tell the disk thread to + // release them + if (itVoice->Orphan) { + pDiskThread->OrderDeletionOfDimreg(itVoice->pDimRgn); + } + // free the voice object pVoicePool->free(itVoice); @@ -1300,6 +1385,20 @@ pEngineChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN; break; } + case 6: { // data entry (currently only used for RPN controllers) + if (pEngineChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones + int transpose = (int) itControlChangeEvent->Param.CC.Value - 64; + // limit to +- two octaves for now + transpose = RTMath::Min(transpose, 24); + transpose = RTMath::Max(transpose, -24); + pEngineChannel->GlobalTranspose = transpose; + // workaround, so we won't have hanging notes + ReleaseAllVoices(pEngineChannel, itControlChangeEvent); + } + // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data + pEngineChannel->ResetMidiRpnController(); + break; + } case 7: { // volume //TODO: not sample accurate yet pEngineChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value]; @@ -1404,6 +1503,14 @@ } break; } + case 100: { // RPN controller LSB + pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value); + break; + } + case 101: { // RPN controller MSB + pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value); + break; + } // Channel Mode Messages @@ -1440,6 +1547,7 @@ FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value); + pFxSend->SetInfoChanged(true); } } } @@ -1636,7 +1744,7 @@ } String Engine::Version() { - String s = "$Revision: 1.70 $"; + String s = "$Revision: 1.78 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } @@ -1686,4 +1794,28 @@ return y; } + /** + * Changes the instrument for an engine channel. + * + * @param pEngineChannel - engine channel on which the instrument + * should be changed + * @param pInstrument - new instrument + * @returns a list of dimension regions from the old instrument + * that are still in use + */ + ::gig::DimensionRegion** Engine::ChangeInstrument(EngineChannel* pEngineChannel, ::gig::Instrument* pInstrument) { + instrument_change_command_t command; + command.pEngineChannel = pEngineChannel; + command.pInstrument = pInstrument; + InstrumentChangeQueue->push(&command); + + // wait for the audio thread to confirm that the instrument + // change has been done + instrument_change_reply_t reply; + while (InstrumentChangeReplyQueue->pop(&reply) == 0) { + usleep(10000); + } + return pDimRegionsInUse; + } + }} // namespace LinuxSampler::gig