3 |
* LinuxSampler - modular, streaming capable sampler * |
* LinuxSampler - modular, streaming capable sampler * |
4 |
* * |
* * |
5 |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005-2009 Christian Schoenebeck * |
* Copyright (C) 2005-2008 Christian Schoenebeck * |
7 |
* Copyright (C) 2009 Grigor Iliev * |
* Copyright (C) 2009-2010 Christian Schoenebeck and Grigor Iliev * |
8 |
* * |
* * |
9 |
* This program is free software; you can redistribute it and/or modify * |
* This program is free software; you can redistribute it and/or modify * |
10 |
* it under the terms of the GNU General Public License as published by * |
* it under the terms of the GNU General Public License as published by * |
24 |
|
|
25 |
#include "Engine.h" |
#include "Engine.h" |
26 |
#include "EngineChannel.h" |
#include "EngineChannel.h" |
27 |
|
#include "InstrumentScriptVM.h" |
28 |
|
|
29 |
namespace LinuxSampler { namespace gig { |
namespace LinuxSampler { namespace gig { |
30 |
Engine::Format Engine::GetEngineFormat() { return GIG; } |
Engine::Format Engine::GetEngineFormat() { return GIG; } |
31 |
|
|
32 |
|
void Engine::CreateInstrumentScriptVM() { |
33 |
|
dmsg(2,("gig::Engine created Giga format scriptvm\n")); |
34 |
|
if (pScriptVM) return; |
35 |
|
pScriptVM = new InstrumentScriptVM; // gig format specific extended script runner |
36 |
|
} |
37 |
|
|
38 |
/** |
/** |
39 |
* Reacts on supported control change commands (e.g. pitch bend wheel, |
* Reacts on supported control change commands (e.g. pitch bend wheel, |
40 |
* modulation wheel, aftertouch). |
* modulation wheel, aftertouch). |
116 |
ProcessFxSendControllers(pChannel, itControlChangeEvent); |
ProcessFxSendControllers(pChannel, itControlChangeEvent); |
117 |
} |
} |
118 |
|
|
119 |
|
void Engine::ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) { |
120 |
|
// forward this to the CC routine, so it updates the current aftertouch value and may handle aftertouch trigger rules |
121 |
|
ProcessControlChange(pEngineChannel, itChannelPressureEvent); |
122 |
|
|
123 |
|
// if required: engine global aftertouch handling (apart from the per voice handling) |
124 |
|
} |
125 |
|
|
126 |
|
void Engine::ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) { |
127 |
|
// if required: engine global aftertouch handling (apart from the per voice handling) |
128 |
|
} |
129 |
|
|
130 |
DiskThread* Engine::CreateDiskThread() { |
DiskThread* Engine::CreateDiskThread() { |
131 |
return new DiskThread ( |
return new DiskThread ( |
132 |
iMaxDiskStreams, |
iMaxDiskStreams, |
143 |
EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel); |
EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel); |
144 |
// first, get total amount of required voices (dependant on amount of layers) |
// first, get total amount of required voices (dependant on amount of layers) |
145 |
::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOnEvent->Param.Note.Key); |
::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOnEvent->Param.Note.Key); |
146 |
if (pRegion && !RegionSuspended(pRegion)) { |
if (!pRegion || RegionSuspended(pRegion)) |
147 |
int voicesRequired = pRegion->Layers; |
return; |
148 |
// now launch the required amount of voices |
const int voicesRequired = pRegion->Layers; |
149 |
for (int i = 0; i < voicesRequired; i++) |
if (voicesRequired <= 0) |
150 |
|
return; |
151 |
|
|
152 |
|
NoteIterator itNote = GetNotePool()->fromID(itNoteOnEvent->Param.Note.ID); |
153 |
|
if (!itNote) { |
154 |
|
dmsg(1,("gig::Engine: No Note object for triggering new voices!\n")); |
155 |
|
return; |
156 |
|
} |
157 |
|
|
158 |
|
// now launch the required amount of voices |
159 |
|
for (int i = 0; i < voicesRequired; i++) { |
160 |
|
VoiceIterator itNewVoice = |
161 |
LaunchVoice(pChannel, itNoteOnEvent, i, false, true, HandleKeyGroupConflicts); |
LaunchVoice(pChannel, itNoteOnEvent, i, false, true, HandleKeyGroupConflicts); |
162 |
|
if (!itNewVoice) continue; |
163 |
|
itNewVoice.moveToEndOf(itNote->pActiveVoices); |
164 |
} |
} |
165 |
} |
} |
166 |
|
|
172 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNoteOffEvent->Param.Note.Key]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNoteOffEvent->Param.Note.Key]; |
173 |
// first, get total amount of required voices (dependant on amount of layers) |
// first, get total amount of required voices (dependant on amount of layers) |
174 |
::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOffEvent->Param.Note.Key); |
::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOffEvent->Param.Note.Key); |
175 |
if (pRegion) { |
if (!pRegion) |
176 |
int voicesRequired = pRegion->Layers; |
return; |
177 |
|
const int voicesRequired = pRegion->Layers; |
178 |
|
if (voicesRequired <= 0) |
179 |
|
return; |
180 |
|
|
181 |
|
NoteIterator itNote = GetNotePool()->fromID(itNoteOffEvent->Param.Note.ID); |
182 |
|
if (!itNote) { |
183 |
|
dmsg(1,("gig::Engine: No Note object for triggering new release voices!\n")); |
184 |
|
return; |
185 |
|
} |
186 |
|
|
187 |
// MIDI note-on velocity is used instead of note-off velocity |
// MIDI note-on velocity is used instead of note-off velocity |
188 |
itNoteOffEvent->Param.Note.Velocity = pKey->Velocity; |
itNoteOffEvent->Param.Note.Velocity = pKey->Velocity; |
189 |
|
|
190 |
// now launch the required amount of voices |
// now launch the required amount of voices |
191 |
for (int i = 0; i < voicesRequired; i++) |
for (int i = 0; i < voicesRequired; i++) { |
192 |
|
VoiceIterator itNewVoice = |
193 |
LaunchVoice(pChannel, itNoteOffEvent, i, true, false, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples |
LaunchVoice(pChannel, itNoteOffEvent, i, true, false, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples |
194 |
|
if (!itNewVoice) continue; |
195 |
|
itNewVoice.moveToEndOf(itNote->pActiveVoices); |
196 |
} |
} |
197 |
} |
} |
198 |
|
|
206 |
) { |
) { |
207 |
EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel); |
EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel); |
208 |
int MIDIKey = itNoteOnEvent->Param.Note.Key; |
int MIDIKey = itNoteOnEvent->Param.Note.Key; |
209 |
EngineChannel::MidiKey* pKey = &pChannel->pMIDIKeyInfo[MIDIKey]; |
//EngineChannel::MidiKey* pKey = &pChannel->pMIDIKeyInfo[MIDIKey]; |
210 |
::gig::Region* pRegion = pChannel->pInstrument->GetRegion(MIDIKey); |
::gig::Region* pRegion = pChannel->pInstrument->GetRegion(MIDIKey); |
211 |
|
|
212 |
// if nothing defined for this key |
// if nothing defined for this key |
213 |
if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do |
if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do |
214 |
|
|
215 |
// only mark the first voice of a layered voice (group) to be in a |
int iKeyGroup = pRegion->KeyGroup; |
216 |
// key group, so the layered voices won't kill each other |
// only need to send a group event from the first voice in a layered region, |
217 |
int iKeyGroup = (iLayer == 0 && !ReleaseTriggerVoice) ? pRegion->KeyGroup : 0; |
// as all layers in a region always belongs to the same key group |
218 |
|
if (HandleKeyGroupConflicts && iLayer == 0) pChannel->HandleKeyGroupConflicts(iKeyGroup, itNoteOnEvent); |
|
// handle key group (a.k.a. exclusive group) conflicts |
|
|
if (HandleKeyGroupConflicts) { |
|
|
if (iKeyGroup) { // if this voice / key belongs to a key group |
|
|
uint** ppKeyGroup = &pChannel->ActiveKeyGroups[iKeyGroup]; |
|
|
if (*ppKeyGroup) { // if there's already an active key in that key group |
|
|
EngineChannel::MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[**ppKeyGroup]; |
|
|
// kill all voices on the (other) key |
|
|
RTList<Voice>::Iterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first(); |
|
|
RTList<Voice>::Iterator end = pOtherKey->pActiveVoices->end(); |
|
|
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
|
|
if (itVoiceToBeKilled->Type != Voice::type_release_trigger) { |
|
|
itVoiceToBeKilled->Kill(itNoteOnEvent); |
|
|
--VoiceSpawnsLeft; //FIXME: just a hack, we should better check in StealVoice() if the voice was killed due to key conflict |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
219 |
|
|
220 |
Voice::type_t VoiceType = Voice::type_normal; |
Voice::type_t VoiceType = Voice::type_normal; |
221 |
|
|
245 |
DimValues[i] = (uint) (pChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones); |
DimValues[i] = (uint) (pChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones); |
246 |
break; |
break; |
247 |
case ::gig::dimension_roundrobin: |
case ::gig::dimension_roundrobin: |
248 |
DimValues[i] = (uint) pChannel->pMIDIKeyInfo[MIDIKey].RoundRobinIndex; // incremented for each note on |
DimValues[i] = uint(*pChannel->pMIDIKeyInfo[MIDIKey].pRoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on in this Region |
249 |
|
break; |
250 |
|
case ::gig::dimension_roundrobinkeyboard: |
251 |
|
DimValues[i] = uint(pChannel->RoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on |
252 |
break; |
break; |
253 |
case ::gig::dimension_random: |
case ::gig::dimension_random: |
254 |
RandomSeed = RandomSeed * 1103515245 + 12345; // classic pseudo random number generator |
DimValues[i] = uint(Random() * pRegion->pDimensionDefinitions[i].zones); |
255 |
DimValues[i] = (uint) RandomSeed >> (32 - pRegion->pDimensionDefinitions[i].bits); // highest bits are most random |
break; |
256 |
|
case ::gig::dimension_smartmidi: |
257 |
|
DimValues[i] = 0; |
258 |
break; |
break; |
259 |
case ::gig::dimension_modwheel: |
case ::gig::dimension_modwheel: |
260 |
DimValues[i] = pChannel->ControllerTable[1]; |
DimValues[i] = pChannel->ControllerTable[1]; |
336 |
// return if this is a release triggered voice and there is no |
// return if this is a release triggered voice and there is no |
337 |
// releasetrigger dimension (could happen if an instrument |
// releasetrigger dimension (could happen if an instrument |
338 |
// change has occured between note on and off) |
// change has occured between note on and off) |
339 |
if (ReleaseTriggerVoice && VoiceType != Voice::type_release_trigger) return Pool<Voice>::Iterator(); |
if (ReleaseTriggerVoice && !(VoiceType & Voice::type_release_trigger)) return Pool<Voice>::Iterator(); |
340 |
|
|
341 |
::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues); |
NoteIterator itNote = GetNotePool()->fromID(itNoteOnEvent->Param.Note.ID); |
342 |
|
|
343 |
|
::gig::DimensionRegion* pDimRgn; |
344 |
|
if (!itNote->Format.Gig.DimMask) { // normal case ... |
345 |
|
pDimRgn = pRegion->GetDimensionRegionByValue(DimValues); |
346 |
|
} else { // some dimension zones were overridden (i.e. by instrument script) ... |
347 |
|
dmsg(3,("trigger with dim mask=%d val=%d\n", itNote->Format.Gig.DimMask, itNote->Format.Gig.DimBits)); |
348 |
|
int index = pRegion->GetDimensionRegionIndexByValue(DimValues); |
349 |
|
index &= ~itNote->Format.Gig.DimMask; |
350 |
|
index |= itNote->Format.Gig.DimBits & itNote->Format.Gig.DimMask; |
351 |
|
pDimRgn = pRegion->pDimensionRegions[index & 255]; |
352 |
|
} |
353 |
|
if (!pDimRgn) return Pool<Voice>::Iterator(); // error (could not resolve dimension region) |
354 |
|
|
355 |
// no need to continue if sample is silent |
// no need to continue if sample is silent |
356 |
if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator(); |
if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator(); |
357 |
|
|
358 |
|
dmsg(2,("sample -> \"%s\"\n", pDimRgn->pSample->pInfo->Name.c_str())); |
359 |
|
|
360 |
// allocate a new voice for the key |
// allocate a new voice for the key |
361 |
Pool<Voice>::Iterator itNewVoice = pKey->pActiveVoices->allocAppend(); |
Pool<Voice>::Iterator itNewVoice = GetVoicePool()->allocAppend(); |
362 |
if (itNewVoice) { |
|
363 |
// launch the new voice |
int res = InitNewVoice ( |
364 |
if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pDimRgn, VoiceType, iKeyGroup) < 0) { |
pChannel, pDimRgn, itNoteOnEvent, VoiceType, iLayer, |
365 |
dmsg(4,("Voice not triggered\n")); |
iKeyGroup, ReleaseTriggerVoice, VoiceStealing, itNewVoice |
366 |
pKey->pActiveVoices->free(itNewVoice); |
); |
367 |
} |
if (!res) return itNewVoice; |
|
else { // on success |
|
|
--VoiceSpawnsLeft; |
|
|
if (!pKey->Active) { // mark as active key |
|
|
pKey->Active = true; |
|
|
pKey->itSelf = pChannel->pActiveKeys->allocAppend(); |
|
|
*pKey->itSelf = itNoteOnEvent->Param.Note.Key; |
|
|
} |
|
|
if (itNewVoice->KeyGroup) { |
|
|
uint** ppKeyGroup = &pChannel->ActiveKeyGroups[itNewVoice->KeyGroup]; |
|
|
*ppKeyGroup = &*pKey->itSelf; // put key as the (new) active key to its key group |
|
|
} |
|
|
if (itNewVoice->Type == Voice::type_release_trigger_required) pKey->ReleaseTrigger = true; // mark key for the need of release triggered voice(s) |
|
|
return itNewVoice; // success |
|
|
} |
|
|
} |
|
|
else if (VoiceStealing) { |
|
|
// try to steal one voice |
|
|
int result = StealVoice(pChannel, itNoteOnEvent); |
|
|
if (!result) { // voice stolen successfully |
|
|
// put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died |
|
|
RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend(); |
|
|
if (itStealEvent) { |
|
|
*itStealEvent = *itNoteOnEvent; // copy event |
|
|
itStealEvent->Param.Note.Layer = iLayer; |
|
|
itStealEvent->Param.Note.ReleaseTrigger = ReleaseTriggerVoice; |
|
|
pKey->VoiceTheftsQueued++; |
|
|
} |
|
|
else dmsg(1,("Voice stealing queue full!\n")); |
|
|
} |
|
|
} |
|
368 |
|
|
369 |
return Pool<Voice>::Iterator(); // no free voice or error |
return Pool<Voice>::Iterator(); // no free voice or error |
370 |
} |
} |
378 |
} |
} |
379 |
|
|
380 |
String Engine::Version() { |
String Engine::Version() { |
381 |
String s = "$Revision: 1.105 $"; |
String s = "$Revision$"; |
382 |
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword |
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword |
383 |
} |
} |
384 |
|
|