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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4020 - (show 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 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005-2022 Christian Schoenebeck *
7 * Copyright (C) 2009-2010 Grigor Iliev *
8 * *
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 #include "EngineChannel.h"
27 #include "InstrumentScriptVM.h"
28
29 namespace LinuxSampler { namespace gig {
30 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,
40 * modulation wheel, aftertouch).
41 *
42 * @param pEngineChannel - engine channel on which this event occured on
43 * @param itControlChangeEvent - controller, value and time stamp of the event
44 */
45 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
51 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
56 ::gig::MidiRule* rule;
57 for (int i = 0 ; (rule = pChannel->pInstrument->GetMidiRule(i)) ; i++) {
58
59 if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
60 dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
61 if (itControlChangeEvent->Param.CC.Controller ==
62 ctrlTrigger->ControllerNumber) {
63
64 uint8_t oldCCValue = pChannel->ControllerTable[
65 itControlChangeEvent->Param.CC.Controller];
66 uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
67
68 for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
69 ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
70 &ctrlTrigger->pTriggers[i];
71
72 // 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
81 RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
82 if (itNewEvent) {
83 *itNewEvent = *itControlChangeEvent;
84 itNewEvent->Param.Note.Key = pTrigger->Key;
85
86 if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
87 itNewEvent->Type = Event::type_note_off;
88 itNewEvent->Param.Note.Velocity = 100;
89
90 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
99 ProcessNoteOn(pEngineChannel, itNewEvent);
100 }
101 }
102 else dmsg(1,("Event pool emtpy!\n"));
103 }
104 }
105 }
106 }
107 }
108 }
109
110 // update controller value in the engine channel's controller table
111 pChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
112
113 ProcessHardcodedControllers(pEngineChannel, itControlChangeEvent);
114
115 // handle FX send controllers
116 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() {
131 return new DiskThread (
132 iMaxDiskStreams,
133 ((pAudioOutputDevice->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
134 &instruments
135 );
136 }
137
138 void Engine::TriggerNewVoices (
139 LinuxSampler::EngineChannel* pEngineChannel,
140 RTList<Event>::Iterator& itNoteOnEvent,
141 bool HandleKeyGroupConflicts
142 ) {
143 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 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
150
151 // first, get total amount of required voices (dependant on amount of layers)
152 // (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 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 LaunchVoice(pChannel, itNoteOnEvent, i, false, true, HandleKeyGroupConflicts);
165 if (!itNewVoice) continue;
166 itNewVoice.moveToEndOf(itNote->pActiveVoices);
167 }
168 }
169
170 void Engine::TriggerReleaseVoices (
171 LinuxSampler::EngineChannel* pEngineChannel,
172 RTList<Event>::Iterator& itNoteOffEvent
173 ) {
174 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 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
181 MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNote->cause.Param.Note.Key];
182 // first, get total amount of required voices (dependant on amount of layers)
183 // (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 if (!pRegion)
187 return;
188 const int voicesRequired = pRegion->Layers;
189 if (voicesRequired <= 0)
190 return;
191
192 // MIDI note-on velocity is used instead of note-off velocity
193 // 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
200 // now launch the required amount of voices
201 for (int i = 0; i < voicesRequired; i++) {
202 VoiceIterator itNewVoice =
203 LaunchVoice(pChannel, itNoteOffEvent, i, true, false, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples
204 if (!itNewVoice) continue;
205 itNewVoice.moveToEndOf(itNote->pActiveVoices);
206 }
207 }
208
209 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 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 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
224 // 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 int MIDIKey = itNoteOnEvent->Param.Note.Key;
227 int NoteKey = itNote->cause.Param.Note.Key;
228 //EngineChannel::MidiKey* pKey = &pChannel->pMIDIKeyInfo[MIDIKey];
229 ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(NoteKey);
230
231 // if nothing defined for this key
232 if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do
233
234 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
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 DimValues[i] = itNote->cause.Param.Note.Velocity;
255 break;
256 case ::gig::dimension_channelaftertouch:
257 DimValues[i] = pChannel->ControllerTable[128];
258 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 DimValues[i] = (uint) (pChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);
265 break;
266 case ::gig::dimension_roundrobin:
267 DimValues[i] = uint(*pChannel->pMIDIKeyInfo[MIDIKey].pRoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on in this Region
268 break;
269 case ::gig::dimension_roundrobinkeyboard:
270 DimValues[i] = uint(pChannel->RoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on
271 break;
272 case ::gig::dimension_random:
273 DimValues[i] = uint(Random() * pRegion->pDimensionDefinitions[i].zones);
274 break;
275 case ::gig::dimension_smartmidi:
276 DimValues[i] = 0;
277 break;
278 case ::gig::dimension_modwheel:
279 DimValues[i] = pChannel->ControllerTable[1];
280 break;
281 case ::gig::dimension_breath:
282 DimValues[i] = pChannel->ControllerTable[2];
283 break;
284 case ::gig::dimension_foot:
285 DimValues[i] = pChannel->ControllerTable[4];
286 break;
287 case ::gig::dimension_portamentotime:
288 DimValues[i] = pChannel->ControllerTable[5];
289 break;
290 case ::gig::dimension_effect1:
291 DimValues[i] = pChannel->ControllerTable[12];
292 break;
293 case ::gig::dimension_effect2:
294 DimValues[i] = pChannel->ControllerTable[13];
295 break;
296 case ::gig::dimension_genpurpose1:
297 DimValues[i] = pChannel->ControllerTable[16];
298 break;
299 case ::gig::dimension_genpurpose2:
300 DimValues[i] = pChannel->ControllerTable[17];
301 break;
302 case ::gig::dimension_genpurpose3:
303 DimValues[i] = pChannel->ControllerTable[18];
304 break;
305 case ::gig::dimension_genpurpose4:
306 DimValues[i] = pChannel->ControllerTable[19];
307 break;
308 case ::gig::dimension_sustainpedal:
309 DimValues[i] = pChannel->ControllerTable[64];
310 break;
311 case ::gig::dimension_portamento:
312 DimValues[i] = pChannel->ControllerTable[65];
313 break;
314 case ::gig::dimension_sostenutopedal:
315 DimValues[i] = pChannel->ControllerTable[66];
316 break;
317 case ::gig::dimension_softpedal:
318 DimValues[i] = pChannel->ControllerTable[67];
319 break;
320 case ::gig::dimension_genpurpose5:
321 DimValues[i] = pChannel->ControllerTable[80];
322 break;
323 case ::gig::dimension_genpurpose6:
324 DimValues[i] = pChannel->ControllerTable[81];
325 break;
326 case ::gig::dimension_genpurpose7:
327 DimValues[i] = pChannel->ControllerTable[82];
328 break;
329 case ::gig::dimension_genpurpose8:
330 DimValues[i] = pChannel->ControllerTable[83];
331 break;
332 case ::gig::dimension_effect1depth:
333 DimValues[i] = pChannel->ControllerTable[91];
334 break;
335 case ::gig::dimension_effect2depth:
336 DimValues[i] = pChannel->ControllerTable[92];
337 break;
338 case ::gig::dimension_effect3depth:
339 DimValues[i] = pChannel->ControllerTable[93];
340 break;
341 case ::gig::dimension_effect4depth:
342 DimValues[i] = pChannel->ControllerTable[94];
343 break;
344 case ::gig::dimension_effect5depth:
345 DimValues[i] = pChannel->ControllerTable[95];
346 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
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 if (ReleaseTriggerVoice && !(VoiceType & Voice::type_release_trigger)) return Pool<Voice>::Iterator();
359
360 ::gig::DimensionRegion* pDimRgn;
361 if (!itNote->Format.Gig.DimMask) { // normal case ...
362 pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);
363 } else { // some dimension zones were overridden (i.e. by instrument script) ...
364 dmsg(3,("trigger with dim mask=%d val=%d\n", itNote->Format.Gig.DimMask, itNote->Format.Gig.DimBits));
365 int index = pRegion->GetDimensionRegionIndexByValue(DimValues);
366 index &= ~itNote->Format.Gig.DimMask;
367 index |= itNote->Format.Gig.DimBits & itNote->Format.Gig.DimMask;
368 pDimRgn = pRegion->pDimensionRegions[index & 255];
369 }
370 if (!pDimRgn) return Pool<Voice>::Iterator(); // error (could not resolve dimension region)
371
372 // no need to continue if sample is silent
373 if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator();
374
375 dmsg(2,("sample -> \"%s\"\n", pDimRgn->pSample->pInfo->Name.c_str()));
376
377 // allocate a new voice for the key
378 Pool<Voice>::Iterator itNewVoice = GetVoicePool()->allocAppend();
379
380 int res = InitNewVoice (
381 pChannel, pDimRgn, itNoteOnEvent, VoiceType, iLayer,
382 iKeyGroup, ReleaseTriggerVoice, VoiceStealing, itNewVoice
383 );
384 if (!res) return itNewVoice;
385
386 return Pool<Voice>::Iterator(); // no free voice or error
387 }
388
389 bool Engine::DiskStreamSupported() {
390 return true;
391 }
392
393 String Engine::Description() {
394 return "GigaSampler Format Engine";
395 }
396
397 String Engine::Version() {
398 String s = "$Revision$";
399 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
400 }
401
402 }} // namespace LinuxSampler::gig

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC