/[svn]/linuxsampler/trunk/src/engines/gig/Engine.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/gig/Engine.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4020 - (hide annotations) (download)
Tue Jan 4 16:04:26 2022 UTC (2 years, 3 months ago) by schoenebeck
File size: 20181 byte(s)
* SFZ engine: Fixed wrong velocity for release triggered samples: if and
  only if note-off velocity is exactly zero (which means MIDI keyboard
  does not have release velocity sensors) then use previous note-on
  velocity as note-off velocity for release triggered samples instead
  (regression of SVN r4013).

* Gig engine: Fixed wrong velocity for release triggered samples: if and
  only if note-off velocity is exactly zero (which means MIDI keyboard
  does not have release velocity sensors) then use previous note-on
  velocity as note-off velocity for release triggered samples instead 
  (regression of SVN r2879).

* Bumped version (2.2.0.svn15).

1 schoenebeck 53 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 persson 1644 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 4020 * Copyright (C) 2005-2022 Christian Schoenebeck *
7     * Copyright (C) 2009-2010 Grigor Iliev *
8 schoenebeck 53 * *
9     * 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 *
11     * the Free Software Foundation; either version 2 of the License, or *
12     * (at your option) any later version. *
13     * *
14     * This program is distributed in the hope that it will be useful, *
15     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17     * GNU General Public License for more details. *
18     * *
19     * You should have received a copy of the GNU General Public License *
20     * along with this program; if not, write to the Free Software *
21     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
22     * MA 02111-1307 USA *
23     ***************************************************************************/
24    
25     #include "Engine.h"
26 iliev 2012 #include "EngineChannel.h"
27 schoenebeck 2600 #include "InstrumentScriptVM.h"
28 schoenebeck 53
29     namespace LinuxSampler { namespace gig {
30 iliev 2012 Engine::Format Engine::GetEngineFormat() { return GIG; }
31 schoenebeck 53
32 schoenebeck 2611 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 schoenebeck 2600 }
37    
38 schoenebeck 412 /**
39 iliev 2012 * Reacts on supported control change commands (e.g. pitch bend wheel,
40     * modulation wheel, aftertouch).
41 schoenebeck 412 *
42 iliev 2012 * @param pEngineChannel - engine channel on which this event occured on
43     * @param itControlChangeEvent - controller, value and time stamp of the event
44 schoenebeck 412 */
45 iliev 2012 void Engine::ProcessControlChange (
46     LinuxSampler::EngineChannel* pEngineChannel,
47     Pool<Event>::Iterator& itControlChangeEvent
48     ) {
49     dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));
50 persson 1039
51 iliev 2012 EngineChannel* pChannel = dynamic_cast<EngineChannel*>(pEngineChannel);
52     // handle the "control triggered" MIDI rule: a control change
53     // event can trigger a new note on or note off event
54     if (pChannel->pInstrument) {
55 schoenebeck 411
56 iliev 2012 ::gig::MidiRule* rule;
57     for (int i = 0 ; (rule = pChannel->pInstrument->GetMidiRule(i)) ; i++) {
58 schoenebeck 411
59 iliev 2012 if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
60     dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
61     if (itControlChangeEvent->Param.CC.Controller ==
62     ctrlTrigger->ControllerNumber) {
63 persson 1038
64 iliev 2012 uint8_t oldCCValue = pChannel->ControllerTable[
65     itControlChangeEvent->Param.CC.Controller];
66     uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
67 schoenebeck 53
68 iliev 2012 for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
69     ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
70     &ctrlTrigger->pTriggers[i];
71 schoenebeck 53
72 iliev 2012 // check if the controller has passed the
73     // trigger point in the right direction
74     if ((pTrigger->Descending &&
75     oldCCValue > pTrigger->TriggerPoint &&
76     newCCValue <= pTrigger->TriggerPoint) ||
77     (!pTrigger->Descending &&
78     oldCCValue < pTrigger->TriggerPoint &&
79     newCCValue >= pTrigger->TriggerPoint)) {
80 schoenebeck 53
81 iliev 2012 RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
82     if (itNewEvent) {
83     *itNewEvent = *itControlChangeEvent;
84     itNewEvent->Param.Note.Key = pTrigger->Key;
85 schoenebeck 53
86 iliev 2012 if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
87     itNewEvent->Type = Event::type_note_off;
88     itNewEvent->Param.Note.Velocity = 100;
89 schoenebeck 53
90 iliev 2012 ProcessNoteOff(pEngineChannel, itNewEvent);
91     } else {
92     itNewEvent->Type = Event::type_note_on;
93     //TODO: if Velocity is 255, the triggered velocity should
94     // depend on how fast the controller is moving
95     itNewEvent->Param.Note.Velocity =
96     pTrigger->Velocity == 255 ? 100 :
97     pTrigger->Velocity;
98 schoenebeck 53
99 iliev 2012 ProcessNoteOn(pEngineChannel, itNewEvent);
100     }
101     }
102     else dmsg(1,("Event pool emtpy!\n"));
103     }
104 schoenebeck 1321 }
105     }
106     }
107     }
108     }
109    
110 iliev 2012 // update controller value in the engine channel's controller table
111     pChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
112 schoenebeck 1321
113 iliev 2012 ProcessHardcodedControllers(pEngineChannel, itControlChangeEvent);
114 schoenebeck 1321
115 iliev 2012 // handle FX send controllers
116     ProcessFxSendControllers(pChannel, itControlChangeEvent);
117 schoenebeck 1321 }
118    
119 schoenebeck 2559 void Engine::ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) {
120 schoenebeck 3015 // 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 schoenebeck 2559 // 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 iliev 2012 DiskThread* Engine::CreateDiskThread() {
131     return new DiskThread (
132     iMaxDiskStreams,
133     ((pAudioOutputDevice->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
134     &instruments
135     );
136 schoenebeck 412 }
137    
138 iliev 2012 void Engine::TriggerNewVoices (
139     LinuxSampler::EngineChannel* pEngineChannel,
140     RTList<Event>::Iterator& itNoteOnEvent,
141     bool HandleKeyGroupConflicts
142     ) {
143 schoenebeck 3219 NoteIterator itNote = GetNotePool()->fromID(itNoteOnEvent->Param.Note.ID);
144     if (!itNote) {
145     dmsg(1,("gig::Engine: No Note object for triggering new voices!\n"));
146     return;
147     }
148    
149 iliev 2012 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
150 schoenebeck 3219
151 iliev 2012 // first, get total amount of required voices (dependant on amount of layers)
152 schoenebeck 3219 // (using the note's MIDI note number instead of the MIDI event's one,
153     // because an instrument script might have modified the note number)
154     ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNote->cause.Param.Note.Key);
155 schoenebeck 2879 if (!pRegion || RegionSuspended(pRegion))
156     return;
157     const int voicesRequired = pRegion->Layers;
158     if (voicesRequired <= 0)
159     return;
160    
161     // now launch the required amount of voices
162     for (int i = 0; i < voicesRequired; i++) {
163     VoiceIterator itNewVoice =
164 iliev 2012 LaunchVoice(pChannel, itNoteOnEvent, i, false, true, HandleKeyGroupConflicts);
165 schoenebeck 2879 if (!itNewVoice) continue;
166     itNewVoice.moveToEndOf(itNote->pActiveVoices);
167 schoenebeck 412 }
168 persson 438 }
169 schoenebeck 412
170 iliev 2012 void Engine::TriggerReleaseVoices (
171     LinuxSampler::EngineChannel* pEngineChannel,
172     RTList<Event>::Iterator& itNoteOffEvent
173     ) {
174 schoenebeck 3219 NoteIterator itNote = GetNotePool()->fromID(itNoteOffEvent->Param.Note.ID);
175     if (!itNote) {
176     dmsg(1,("gig::Engine: No Note object for triggering new release voices!\n"));
177     return;
178     }
179    
180 iliev 2012 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
181 schoenebeck 4020 MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNote->cause.Param.Note.Key];
182 iliev 2012 // first, get total amount of required voices (dependant on amount of layers)
183 schoenebeck 3219 // (using the note's MIDI note number instead of the MIDI event's one,
184     // because an instrument script might have modified the note number)
185     ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNote->cause.Param.Note.Key);
186 schoenebeck 2879 if (!pRegion)
187     return;
188     const int voicesRequired = pRegion->Layers;
189     if (voicesRequired <= 0)
190     return;
191 schoenebeck 53
192 schoenebeck 2879 // MIDI note-on velocity is used instead of note-off velocity
193 schoenebeck 4020 // if note-off velocity is exactly zero (which means missing sensors
194     // in keyboard), otherwise if note-off velocity is non-zero then use
195     // note object instead of MIDI note-off event, since note-off velocity
196     // might have been modified by RT instrument script
197     const uint8_t vel = itNote->cause.Param.Note.Velocity ?: pKey->Velocity;
198     itNote->cause.Param.Note.Velocity = vel;
199 schoenebeck 2879
200     // now launch the required amount of voices
201     for (int i = 0; i < voicesRequired; i++) {
202     VoiceIterator itNewVoice =
203 iliev 2012 LaunchVoice(pChannel, itNoteOffEvent, i, true, false, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples
204 schoenebeck 2879 if (!itNewVoice) continue;
205     itNewVoice.moveToEndOf(itNote->pActiveVoices);
206 schoenebeck 53 }
207 schoenebeck 412 }
208    
209 iliev 2012 Pool<Voice>::Iterator Engine::LaunchVoice (
210     LinuxSampler::EngineChannel* pEngineChannel,
211     Pool<Event>::Iterator& itNoteOnEvent,
212     int iLayer,
213     bool ReleaseTriggerVoice,
214     bool VoiceStealing,
215     bool HandleKeyGroupConflicts
216     ) {
217 schoenebeck 3219 NoteIterator itNote = GetNotePool()->fromID(itNoteOnEvent->Param.Note.ID);
218     if (!itNote) {
219     dmsg(1,("gig::Engine: No Note object for launching voices!\n"));
220     return Pool<Voice>::Iterator();
221     }
222    
223 iliev 2012 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
224 schoenebeck 3219 // the note's MIDI note number might differ from the event's note number
225     // because a script might have modified the note's note number
226 iliev 2012 int MIDIKey = itNoteOnEvent->Param.Note.Key;
227 schoenebeck 3219 int NoteKey = itNote->cause.Param.Note.Key;
228 schoenebeck 2879 //EngineChannel::MidiKey* pKey = &pChannel->pMIDIKeyInfo[MIDIKey];
229 schoenebeck 3219 ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(NoteKey);
230 schoenebeck 53
231 schoenebeck 668 // if nothing defined for this key
232     if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do
233    
234 persson 2114 int iKeyGroup = pRegion->KeyGroup;
235     // only need to send a group event from the first voice in a layered region,
236     // as all layers in a region always belongs to the same key group
237     if (HandleKeyGroupConflicts && iLayer == 0) pChannel->HandleKeyGroupConflicts(iKeyGroup, itNoteOnEvent);
238 schoenebeck 669
239     Voice::type_t VoiceType = Voice::type_normal;
240    
241     // get current dimension values to select the right dimension region
242     //TODO: for stolen voices this dimension region selection block is processed twice, this should be changed
243     //FIXME: controller values for selecting the dimension region here are currently not sample accurate
244     uint DimValues[8] = { 0 };
245     for (int i = pRegion->Dimensions - 1; i >= 0; i--) {
246     switch (pRegion->pDimensionDefinitions[i].dimension) {
247     case ::gig::dimension_samplechannel:
248     DimValues[i] = 0; //TODO: we currently ignore this dimension
249     break;
250     case ::gig::dimension_layer:
251     DimValues[i] = iLayer;
252     break;
253     case ::gig::dimension_velocity:
254 schoenebeck 3219 DimValues[i] = itNote->cause.Param.Note.Velocity;
255 schoenebeck 669 break;
256     case ::gig::dimension_channelaftertouch:
257 iliev 2012 DimValues[i] = pChannel->ControllerTable[128];
258 schoenebeck 669 break;
259     case ::gig::dimension_releasetrigger:
260     VoiceType = (ReleaseTriggerVoice) ? Voice::type_release_trigger : (!iLayer) ? Voice::type_release_trigger_required : Voice::type_normal;
261     DimValues[i] = (uint) ReleaseTriggerVoice;
262     break;
263     case ::gig::dimension_keyboard:
264 iliev 2012 DimValues[i] = (uint) (pChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);
265 schoenebeck 669 break;
266     case ::gig::dimension_roundrobin:
267 persson 2043 DimValues[i] = uint(*pChannel->pMIDIKeyInfo[MIDIKey].pRoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on in this Region
268 schoenebeck 669 break;
269 persson 2043 case ::gig::dimension_roundrobinkeyboard:
270     DimValues[i] = uint(pChannel->RoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on
271     break;
272 schoenebeck 669 case ::gig::dimension_random:
273 persson 2072 DimValues[i] = uint(Random() * pRegion->pDimensionDefinitions[i].zones);
274 schoenebeck 669 break;
275 schoenebeck 2611 case ::gig::dimension_smartmidi:
276     DimValues[i] = 0;
277     break;
278 schoenebeck 669 case ::gig::dimension_modwheel:
279 iliev 2012 DimValues[i] = pChannel->ControllerTable[1];
280 schoenebeck 669 break;
281     case ::gig::dimension_breath:
282 iliev 2012 DimValues[i] = pChannel->ControllerTable[2];
283 schoenebeck 669 break;
284     case ::gig::dimension_foot:
285 iliev 2012 DimValues[i] = pChannel->ControllerTable[4];
286 schoenebeck 669 break;
287     case ::gig::dimension_portamentotime:
288 iliev 2012 DimValues[i] = pChannel->ControllerTable[5];
289 schoenebeck 669 break;
290     case ::gig::dimension_effect1:
291 iliev 2012 DimValues[i] = pChannel->ControllerTable[12];
292 schoenebeck 669 break;
293     case ::gig::dimension_effect2:
294 iliev 2012 DimValues[i] = pChannel->ControllerTable[13];
295 schoenebeck 669 break;
296     case ::gig::dimension_genpurpose1:
297 iliev 2012 DimValues[i] = pChannel->ControllerTable[16];
298 schoenebeck 669 break;
299     case ::gig::dimension_genpurpose2:
300 iliev 2012 DimValues[i] = pChannel->ControllerTable[17];
301 schoenebeck 669 break;
302     case ::gig::dimension_genpurpose3:
303 iliev 2012 DimValues[i] = pChannel->ControllerTable[18];
304 schoenebeck 669 break;
305     case ::gig::dimension_genpurpose4:
306 iliev 2012 DimValues[i] = pChannel->ControllerTable[19];
307 schoenebeck 669 break;
308     case ::gig::dimension_sustainpedal:
309 iliev 2012 DimValues[i] = pChannel->ControllerTable[64];
310 schoenebeck 669 break;
311     case ::gig::dimension_portamento:
312 iliev 2012 DimValues[i] = pChannel->ControllerTable[65];
313 schoenebeck 669 break;
314     case ::gig::dimension_sostenutopedal:
315 iliev 2012 DimValues[i] = pChannel->ControllerTable[66];
316 schoenebeck 669 break;
317     case ::gig::dimension_softpedal:
318 iliev 2012 DimValues[i] = pChannel->ControllerTable[67];
319 schoenebeck 669 break;
320     case ::gig::dimension_genpurpose5:
321 iliev 2012 DimValues[i] = pChannel->ControllerTable[80];
322 schoenebeck 669 break;
323     case ::gig::dimension_genpurpose6:
324 iliev 2012 DimValues[i] = pChannel->ControllerTable[81];
325 schoenebeck 669 break;
326     case ::gig::dimension_genpurpose7:
327 iliev 2012 DimValues[i] = pChannel->ControllerTable[82];
328 schoenebeck 669 break;
329     case ::gig::dimension_genpurpose8:
330 iliev 2012 DimValues[i] = pChannel->ControllerTable[83];
331 schoenebeck 669 break;
332     case ::gig::dimension_effect1depth:
333 iliev 2012 DimValues[i] = pChannel->ControllerTable[91];
334 schoenebeck 669 break;
335     case ::gig::dimension_effect2depth:
336 iliev 2012 DimValues[i] = pChannel->ControllerTable[92];
337 schoenebeck 669 break;
338     case ::gig::dimension_effect3depth:
339 iliev 2012 DimValues[i] = pChannel->ControllerTable[93];
340 schoenebeck 669 break;
341     case ::gig::dimension_effect4depth:
342 iliev 2012 DimValues[i] = pChannel->ControllerTable[94];
343 schoenebeck 669 break;
344     case ::gig::dimension_effect5depth:
345 iliev 2012 DimValues[i] = pChannel->ControllerTable[95];
346 schoenebeck 669 break;
347     case ::gig::dimension_none:
348     std::cerr << "gig::Engine::LaunchVoice() Error: dimension=none\n" << std::flush;
349     break;
350     default:
351     std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;
352     }
353     }
354 persson 1038
355     // return if this is a release triggered voice and there is no
356     // releasetrigger dimension (could happen if an instrument
357     // change has occured between note on and off)
358 persson 2115 if (ReleaseTriggerVoice && !(VoiceType & Voice::type_release_trigger)) return Pool<Voice>::Iterator();
359 persson 1038
360 schoenebeck 2600 ::gig::DimensionRegion* pDimRgn;
361 schoenebeck 2879 if (!itNote->Format.Gig.DimMask) { // normal case ...
362 schoenebeck 2600 pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);
363     } else { // some dimension zones were overridden (i.e. by instrument script) ...
364 schoenebeck 2879 dmsg(3,("trigger with dim mask=%d val=%d\n", itNote->Format.Gig.DimMask, itNote->Format.Gig.DimBits));
365 schoenebeck 2600 int index = pRegion->GetDimensionRegionIndexByValue(DimValues);
366 schoenebeck 2879 index &= ~itNote->Format.Gig.DimMask;
367     index |= itNote->Format.Gig.DimBits & itNote->Format.Gig.DimMask;
368 schoenebeck 2600 pDimRgn = pRegion->pDimensionRegions[index & 255];
369     }
370 schoenebeck 2565 if (!pDimRgn) return Pool<Voice>::Iterator(); // error (could not resolve dimension region)
371 schoenebeck 669
372     // no need to continue if sample is silent
373     if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator();
374 schoenebeck 2927
375     dmsg(2,("sample -> \"%s\"\n", pDimRgn->pSample->pInfo->Name.c_str()));
376 schoenebeck 669
377 schoenebeck 233 // allocate a new voice for the key
378 schoenebeck 2879 Pool<Voice>::Iterator itNewVoice = GetVoicePool()->allocAppend();
379 schoenebeck 285
380 iliev 2027 int res = InitNewVoice (
381     pChannel, pDimRgn, itNoteOnEvent, VoiceType, iLayer,
382     iKeyGroup, ReleaseTriggerVoice, VoiceStealing, itNewVoice
383     );
384     if (!res) return itNewVoice;
385    
386 schoenebeck 271 return Pool<Voice>::Iterator(); // no free voice or error
387 schoenebeck 233 }
388    
389 schoenebeck 53 bool Engine::DiskStreamSupported() {
390     return true;
391     }
392    
393     String Engine::Description() {
394 iliev 2012 return "GigaSampler Format Engine";
395 schoenebeck 53 }
396    
397     String Engine::Version() {
398 schoenebeck 2494 String s = "$Revision$";
399 schoenebeck 123 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
400 schoenebeck 53 }
401    
402     }} // namespace LinuxSampler::gig

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC