/[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 2611 - (show annotations) (download)
Mon Jun 9 19:20:37 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 17989 byte(s)
* Fixed crash when loading an instrument script.
* Fixed "init" script handler only to be executed once:
  when the script was loaded.
* Fixed aftertouch script event which always had value zero
  and controller number was set to aftertouch value instead.
* gig Engine: Fixed handling of "smartmidi" dimension, which
  was recognized as "unknown" dimension.
* Fixed script function gig_set_dim_zone(): was accessing
  wrong event.
* ls_instr_script command line tool: is now not limited to
  core language scripts, but can now also parse sampler format
  dependent instrument scripts, with the respective specific
  built-in script variables and functions.
* ScriptVM: Fixed runtime behavior of "and" and "or" binary
  script expressions, which also evaluated the right hand side
  of the expression even if the left hand side already failed
  the overall expression semantic to become true.
* Bumped version (1.0.0.svn46).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005-2008 Christian Schoenebeck *
7 * Copyright (C) 2009-2010 Christian Schoenebeck and 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 // if required: engine global aftertouch handling (apart from the per voice handling)
121 }
122
123 void Engine::ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) {
124 // if required: engine global aftertouch handling (apart from the per voice handling)
125 }
126
127 DiskThread* Engine::CreateDiskThread() {
128 return new DiskThread (
129 iMaxDiskStreams,
130 ((pAudioOutputDevice->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
131 &instruments
132 );
133 }
134
135 void Engine::TriggerNewVoices (
136 LinuxSampler::EngineChannel* pEngineChannel,
137 RTList<Event>::Iterator& itNoteOnEvent,
138 bool HandleKeyGroupConflicts
139 ) {
140 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
141 // first, get total amount of required voices (dependant on amount of layers)
142 ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOnEvent->Param.Note.Key);
143 if (pRegion && !RegionSuspended(pRegion)) {
144 int voicesRequired = pRegion->Layers;
145 // now launch the required amount of voices
146 for (int i = 0; i < voicesRequired; i++)
147 LaunchVoice(pChannel, itNoteOnEvent, i, false, true, HandleKeyGroupConflicts);
148 }
149 }
150
151 void Engine::TriggerReleaseVoices (
152 LinuxSampler::EngineChannel* pEngineChannel,
153 RTList<Event>::Iterator& itNoteOffEvent
154 ) {
155 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
156 MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNoteOffEvent->Param.Note.Key];
157 // first, get total amount of required voices (dependant on amount of layers)
158 ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOffEvent->Param.Note.Key);
159 if (pRegion) {
160 int voicesRequired = pRegion->Layers;
161
162 // MIDI note-on velocity is used instead of note-off velocity
163 itNoteOffEvent->Param.Note.Velocity = pKey->Velocity;
164
165 // now launch the required amount of voices
166 for (int i = 0; i < voicesRequired; i++)
167 LaunchVoice(pChannel, itNoteOffEvent, i, true, false, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples
168 }
169 }
170
171 Pool<Voice>::Iterator Engine::LaunchVoice (
172 LinuxSampler::EngineChannel* pEngineChannel,
173 Pool<Event>::Iterator& itNoteOnEvent,
174 int iLayer,
175 bool ReleaseTriggerVoice,
176 bool VoiceStealing,
177 bool HandleKeyGroupConflicts
178 ) {
179 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
180 int MIDIKey = itNoteOnEvent->Param.Note.Key;
181 EngineChannel::MidiKey* pKey = &pChannel->pMIDIKeyInfo[MIDIKey];
182 ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(MIDIKey);
183
184 // if nothing defined for this key
185 if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do
186
187 int iKeyGroup = pRegion->KeyGroup;
188 // only need to send a group event from the first voice in a layered region,
189 // as all layers in a region always belongs to the same key group
190 if (HandleKeyGroupConflicts && iLayer == 0) pChannel->HandleKeyGroupConflicts(iKeyGroup, itNoteOnEvent);
191
192 Voice::type_t VoiceType = Voice::type_normal;
193
194 // get current dimension values to select the right dimension region
195 //TODO: for stolen voices this dimension region selection block is processed twice, this should be changed
196 //FIXME: controller values for selecting the dimension region here are currently not sample accurate
197 uint DimValues[8] = { 0 };
198 for (int i = pRegion->Dimensions - 1; i >= 0; i--) {
199 switch (pRegion->pDimensionDefinitions[i].dimension) {
200 case ::gig::dimension_samplechannel:
201 DimValues[i] = 0; //TODO: we currently ignore this dimension
202 break;
203 case ::gig::dimension_layer:
204 DimValues[i] = iLayer;
205 break;
206 case ::gig::dimension_velocity:
207 DimValues[i] = itNoteOnEvent->Param.Note.Velocity;
208 break;
209 case ::gig::dimension_channelaftertouch:
210 DimValues[i] = pChannel->ControllerTable[128];
211 break;
212 case ::gig::dimension_releasetrigger:
213 VoiceType = (ReleaseTriggerVoice) ? Voice::type_release_trigger : (!iLayer) ? Voice::type_release_trigger_required : Voice::type_normal;
214 DimValues[i] = (uint) ReleaseTriggerVoice;
215 break;
216 case ::gig::dimension_keyboard:
217 DimValues[i] = (uint) (pChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);
218 break;
219 case ::gig::dimension_roundrobin:
220 DimValues[i] = uint(*pChannel->pMIDIKeyInfo[MIDIKey].pRoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on in this Region
221 break;
222 case ::gig::dimension_roundrobinkeyboard:
223 DimValues[i] = uint(pChannel->RoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on
224 break;
225 case ::gig::dimension_random:
226 DimValues[i] = uint(Random() * pRegion->pDimensionDefinitions[i].zones);
227 break;
228 case ::gig::dimension_smartmidi:
229 DimValues[i] = 0;
230 break;
231 case ::gig::dimension_modwheel:
232 DimValues[i] = pChannel->ControllerTable[1];
233 break;
234 case ::gig::dimension_breath:
235 DimValues[i] = pChannel->ControllerTable[2];
236 break;
237 case ::gig::dimension_foot:
238 DimValues[i] = pChannel->ControllerTable[4];
239 break;
240 case ::gig::dimension_portamentotime:
241 DimValues[i] = pChannel->ControllerTable[5];
242 break;
243 case ::gig::dimension_effect1:
244 DimValues[i] = pChannel->ControllerTable[12];
245 break;
246 case ::gig::dimension_effect2:
247 DimValues[i] = pChannel->ControllerTable[13];
248 break;
249 case ::gig::dimension_genpurpose1:
250 DimValues[i] = pChannel->ControllerTable[16];
251 break;
252 case ::gig::dimension_genpurpose2:
253 DimValues[i] = pChannel->ControllerTable[17];
254 break;
255 case ::gig::dimension_genpurpose3:
256 DimValues[i] = pChannel->ControllerTable[18];
257 break;
258 case ::gig::dimension_genpurpose4:
259 DimValues[i] = pChannel->ControllerTable[19];
260 break;
261 case ::gig::dimension_sustainpedal:
262 DimValues[i] = pChannel->ControllerTable[64];
263 break;
264 case ::gig::dimension_portamento:
265 DimValues[i] = pChannel->ControllerTable[65];
266 break;
267 case ::gig::dimension_sostenutopedal:
268 DimValues[i] = pChannel->ControllerTable[66];
269 break;
270 case ::gig::dimension_softpedal:
271 DimValues[i] = pChannel->ControllerTable[67];
272 break;
273 case ::gig::dimension_genpurpose5:
274 DimValues[i] = pChannel->ControllerTable[80];
275 break;
276 case ::gig::dimension_genpurpose6:
277 DimValues[i] = pChannel->ControllerTable[81];
278 break;
279 case ::gig::dimension_genpurpose7:
280 DimValues[i] = pChannel->ControllerTable[82];
281 break;
282 case ::gig::dimension_genpurpose8:
283 DimValues[i] = pChannel->ControllerTable[83];
284 break;
285 case ::gig::dimension_effect1depth:
286 DimValues[i] = pChannel->ControllerTable[91];
287 break;
288 case ::gig::dimension_effect2depth:
289 DimValues[i] = pChannel->ControllerTable[92];
290 break;
291 case ::gig::dimension_effect3depth:
292 DimValues[i] = pChannel->ControllerTable[93];
293 break;
294 case ::gig::dimension_effect4depth:
295 DimValues[i] = pChannel->ControllerTable[94];
296 break;
297 case ::gig::dimension_effect5depth:
298 DimValues[i] = pChannel->ControllerTable[95];
299 break;
300 case ::gig::dimension_none:
301 std::cerr << "gig::Engine::LaunchVoice() Error: dimension=none\n" << std::flush;
302 break;
303 default:
304 std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;
305 }
306 }
307
308 // return if this is a release triggered voice and there is no
309 // releasetrigger dimension (could happen if an instrument
310 // change has occured between note on and off)
311 if (ReleaseTriggerVoice && !(VoiceType & Voice::type_release_trigger)) return Pool<Voice>::Iterator();
312
313
314 ::gig::DimensionRegion* pDimRgn;
315 if (!itNoteOnEvent->Format.Gig.DimMask) { // normal case ...
316 pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);
317 } else { // some dimension zones were overridden (i.e. by instrument script) ...
318 dmsg(3,("trigger with dim mask=%d val=%d\n", itNoteOnEvent->Format.Gig.DimMask, itNoteOnEvent->Format.Gig.DimBits));
319 int index = pRegion->GetDimensionRegionIndexByValue(DimValues);
320 index &= ~itNoteOnEvent->Format.Gig.DimMask;
321 index |= itNoteOnEvent->Format.Gig.DimBits & itNoteOnEvent->Format.Gig.DimMask;
322 pDimRgn = pRegion->pDimensionRegions[index & 255];
323 }
324 if (!pDimRgn) return Pool<Voice>::Iterator(); // error (could not resolve dimension region)
325
326 // no need to continue if sample is silent
327 if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator();
328
329 // allocate a new voice for the key
330 Pool<Voice>::Iterator itNewVoice = pKey->pActiveVoices->allocAppend();
331
332 int res = InitNewVoice (
333 pChannel, pDimRgn, itNoteOnEvent, VoiceType, iLayer,
334 iKeyGroup, ReleaseTriggerVoice, VoiceStealing, itNewVoice
335 );
336 if (!res) return itNewVoice;
337
338 return Pool<Voice>::Iterator(); // no free voice or error
339 }
340
341 bool Engine::DiskStreamSupported() {
342 return true;
343 }
344
345 String Engine::Description() {
346 return "GigaSampler Format Engine";
347 }
348
349 String Engine::Version() {
350 String s = "$Revision$";
351 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
352 }
353
354 }} // namespace LinuxSampler::gig

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC