--- linuxsampler/trunk/src/engines/gig/Voice.cpp 2007/01/06 11:02:58 1010 +++ linuxsampler/trunk/src/engines/gig/Voice.cpp 2009/03/08 09:57:19 1858 @@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005, 2006 Christian Schoenebeck * + * Copyright (C) 2005 - 2009 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -38,8 +38,8 @@ pLFO3 = new LFOSigned(1200.0f); // pitch EG (-1200..+1200 range) KeyGroup = 0; SynthesisMode = 0; // set all mode bits to 0 first - // select synthesis implementation (currently either pure C++ or MMX+SSE(1)) - #if CONFIG_ASM && ARCH_X86 + // select synthesis implementation (asm core is not supported ATM) + #if 0 // CONFIG_ASM && ARCH_X86 SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, Features::supportsMMX() && Features::supportsSSE()); #else SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, false); @@ -79,6 +79,7 @@ int Voice::Trigger(EngineChannel* pEngineChannel, Pool::Iterator& itNoteOnEvent, int PitchBend, ::gig::DimensionRegion* pDimRgn, type_t VoiceType, int iKeyGroup) { this->pEngineChannel = pEngineChannel; this->pDimRgn = pDimRgn; + Orphan = false; #if CONFIG_DEVMODE if (itNoteOnEvent->FragmentPos() > pEngine->MaxSamplesPerCycle) { // just a sanity check for debugging @@ -182,10 +183,15 @@ // calculate initial pitch value { - double pitchbasecents = pDimRgn->FineTune + (int) pEngine->ScaleTuning[MIDIKey % 12]; - if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100; + double pitchbasecents = pEngineChannel->pInstrument->FineTune + pDimRgn->FineTune + pEngine->ScaleTuning[MIDIKey % 12]; + + // GSt behaviour: maximum transpose up is 40 semitones. If + // MIDI key is more than 40 semitones above unity note, + // the transpose is not done. + if (pDimRgn->PitchTrack && (MIDIKey - (int) pDimRgn->UnityNote) < 40) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100; + this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents) * (double(pSample->SamplesPerSecond) / double(pEngine->SampleRate)); - this->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents + this->PitchBend = RTMath::CentsToFreqRatio(PitchBend / 8192.0 * 100.0 * pEngineChannel->pInstrument->PitchbendRange); } // the length of the decay and release curves are dependent on the velocity @@ -514,8 +520,8 @@ finalSynthesisParameters.filterLeft.SetType(pDimRgn->VCFType); finalSynthesisParameters.filterRight.SetType(pDimRgn->VCFType); #else // override filter type - FilterLeft.SetType(CONFIG_OVERRIDE_FILTER_TYPE); - FilterRight.SetType(CONFIG_OVERRIDE_FILTER_TYPE); + finalSynthesisParameters.filterLeft.SetType(CONFIG_OVERRIDE_FILTER_TYPE); + finalSynthesisParameters.filterRight.SetType(CONFIG_OVERRIDE_FILTER_TYPE); #endif // CONFIG_OVERRIDE_FILTER_TYPE VCFCutoffCtrl.value = pEngineChannel->ControllerTable[VCFCutoffCtrl.controller]; @@ -731,9 +737,7 @@ } void Voice::processPitchEvent(RTList::Iterator& itEvent) { - const float pitch = RTMath::CentsToFreqRatio(((double) itEvent->Param.Pitch.Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents - finalSynthesisParameters.fFinalPitch *= pitch; - PitchBend = pitch; + PitchBend = RTMath::CentsToFreqRatio(itEvent->Param.Pitch.Pitch / 8192.0 * 100.0 * pEngineChannel->pInstrument->PitchbendRange); } void Voice::processCutoffEvent(RTList::Iterator& itEvent) { @@ -775,26 +779,47 @@ RTList::Iterator itCCEvent = pEngineChannel->pEvents->first(); RTList::Iterator itNoteEvent = pEngineChannel->pMIDIKeyInfo[MIDIKey].pEvents->first(); - if (Skip) { // skip events that happened before this voice was triggered + if (itTriggerEvent) { // skip events that happened before this voice was triggered while (itCCEvent && itCCEvent->FragmentPos() <= Skip) ++itCCEvent; - while (itNoteEvent && itNoteEvent->FragmentPos() <= Skip) ++itNoteEvent; + // we can't simply compare the timestamp here, because note events + // might happen on the same time stamp, so we have to deal on the + // actual sequence the note events arrived instead (see bug #112) + for (; itNoteEvent; ++itNoteEvent) { + if (itTriggerEvent == itNoteEvent) { + ++itNoteEvent; + break; + } + } } uint killPos; - if (itKillEvent) killPos = RTMath::Min(itKillEvent->FragmentPos(), pEngine->MaxFadeOutPos); + if (itKillEvent) { + int maxFadeOutPos = Samples - pEngine->MinFadeOutSamples; + if (maxFadeOutPos < 0) { + // There's not enough space in buffer to do a fade out + // from max volume (this can only happen for audio + // drivers that use Samples < MaxSamplesPerCycle). + // End the EG1 here, at pos 0, with a shorter max fade + // out time. + EG1.enterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); + itKillEvent = Pool::Iterator(); + } else { + killPos = RTMath::Min(itKillEvent->FragmentPos(), maxFadeOutPos); + } + } uint i = Skip; while (i < Samples) { int iSubFragmentEnd = RTMath::Min(i + CONFIG_DEFAULT_SUBFRAGMENT_SIZE, Samples); // initialize all final synthesis parameters - finalSynthesisParameters.fFinalPitch = PitchBase * PitchBend; fFinalCutoff = VCFCutoffCtrl.fvalue; fFinalResonance = VCFResonanceCtrl.fvalue; // process MIDI control change and pitchbend events for this subfragment processCCEvents(itCCEvent, iSubFragmentEnd); + finalSynthesisParameters.fFinalPitch = PitchBase * PitchBend; float fFinalVolume = VolumeSmoother.render() * CrossfadeSmoother.render(); #ifdef CONFIG_PROCESS_MUTED_CHANNELS if (pEngineChannel->GetMute()) fFinalVolume = 0; @@ -920,13 +945,30 @@ * fading down the volume level to avoid clicks and regular processing * until the kill event actually occured! * - * @see Kill() + * If it's necessary to know when the voice's disk stream was actually + * deleted, then one can set the optional @a bRequestNotification + * parameter and this method will then return the handle of the disk + * stream (unique identifier) and one can use this handle to poll the + * disk thread if this stream has been deleted. In any case this method + * will return immediately and will not block until the stream actually + * was deleted. + * + * @param bRequestNotification - (optional) whether the disk thread shall + * provide a notification once it deleted + * the respective disk stream + * (default=false) + * @returns handle to the voice's disk stream or @c Stream::INVALID_HANDLE + * if the voice did not use a disk stream at all + * @see Kill() */ - void Voice::KillImmediately() { + Stream::Handle Voice::KillImmediately(bool bRequestNotification) { + Stream::Handle hStream = Stream::INVALID_HANDLE; if (DiskVoice && DiskStreamRef.State != Stream::state_unused) { - pDiskThread->OrderDeletionOfStream(&DiskStreamRef); + pDiskThread->OrderDeletionOfStream(&DiskStreamRef, bRequestNotification); + hStream = DiskStreamRef.hStream; } Reset(); + return hStream; } /**