/[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 2559 - (show annotations) (download)
Sun May 18 17:38:25 2014 UTC (9 years, 11 months ago) by schoenebeck
File size: 16889 byte(s)
* Aftertouch: extended API to explicitly handle channel pressure and
  polyphonic key pressure events (so far polyphonic pressure was not
  supported at all, and channel pressure was rerouted as CC128 but not
  used so far).
* Gig Engine: Fixed support for 'aftertouch' attenuation controller.
* Bumped version (1.0.0.svn39).

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
28 namespace LinuxSampler { namespace gig {
29 Engine::Format Engine::GetEngineFormat() { return GIG; }
30
31 /**
32 * Reacts on supported control change commands (e.g. pitch bend wheel,
33 * modulation wheel, aftertouch).
34 *
35 * @param pEngineChannel - engine channel on which this event occured on
36 * @param itControlChangeEvent - controller, value and time stamp of the event
37 */
38 void Engine::ProcessControlChange (
39 LinuxSampler::EngineChannel* pEngineChannel,
40 Pool<Event>::Iterator& itControlChangeEvent
41 ) {
42 dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));
43
44 EngineChannel* pChannel = dynamic_cast<EngineChannel*>(pEngineChannel);
45 // handle the "control triggered" MIDI rule: a control change
46 // event can trigger a new note on or note off event
47 if (pChannel->pInstrument) {
48
49 ::gig::MidiRule* rule;
50 for (int i = 0 ; (rule = pChannel->pInstrument->GetMidiRule(i)) ; i++) {
51
52 if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
53 dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
54 if (itControlChangeEvent->Param.CC.Controller ==
55 ctrlTrigger->ControllerNumber) {
56
57 uint8_t oldCCValue = pChannel->ControllerTable[
58 itControlChangeEvent->Param.CC.Controller];
59 uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
60
61 for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
62 ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
63 &ctrlTrigger->pTriggers[i];
64
65 // check if the controller has passed the
66 // trigger point in the right direction
67 if ((pTrigger->Descending &&
68 oldCCValue > pTrigger->TriggerPoint &&
69 newCCValue <= pTrigger->TriggerPoint) ||
70 (!pTrigger->Descending &&
71 oldCCValue < pTrigger->TriggerPoint &&
72 newCCValue >= pTrigger->TriggerPoint)) {
73
74 RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
75 if (itNewEvent) {
76 *itNewEvent = *itControlChangeEvent;
77 itNewEvent->Param.Note.Key = pTrigger->Key;
78
79 if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
80 itNewEvent->Type = Event::type_note_off;
81 itNewEvent->Param.Note.Velocity = 100;
82
83 ProcessNoteOff(pEngineChannel, itNewEvent);
84 } else {
85 itNewEvent->Type = Event::type_note_on;
86 //TODO: if Velocity is 255, the triggered velocity should
87 // depend on how fast the controller is moving
88 itNewEvent->Param.Note.Velocity =
89 pTrigger->Velocity == 255 ? 100 :
90 pTrigger->Velocity;
91
92 ProcessNoteOn(pEngineChannel, itNewEvent);
93 }
94 }
95 else dmsg(1,("Event pool emtpy!\n"));
96 }
97 }
98 }
99 }
100 }
101 }
102
103 // update controller value in the engine channel's controller table
104 pChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
105
106 ProcessHardcodedControllers(pEngineChannel, itControlChangeEvent);
107
108 // handle FX send controllers
109 ProcessFxSendControllers(pChannel, itControlChangeEvent);
110 }
111
112 void Engine::ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) {
113 // if required: engine global aftertouch handling (apart from the per voice handling)
114 }
115
116 void Engine::ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) {
117 // if required: engine global aftertouch handling (apart from the per voice handling)
118 }
119
120 DiskThread* Engine::CreateDiskThread() {
121 return new DiskThread (
122 iMaxDiskStreams,
123 ((pAudioOutputDevice->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
124 &instruments
125 );
126 }
127
128 void Engine::TriggerNewVoices (
129 LinuxSampler::EngineChannel* pEngineChannel,
130 RTList<Event>::Iterator& itNoteOnEvent,
131 bool HandleKeyGroupConflicts
132 ) {
133 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
134 // first, get total amount of required voices (dependant on amount of layers)
135 ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOnEvent->Param.Note.Key);
136 if (pRegion && !RegionSuspended(pRegion)) {
137 int voicesRequired = pRegion->Layers;
138 // now launch the required amount of voices
139 for (int i = 0; i < voicesRequired; i++)
140 LaunchVoice(pChannel, itNoteOnEvent, i, false, true, HandleKeyGroupConflicts);
141 }
142 }
143
144 void Engine::TriggerReleaseVoices (
145 LinuxSampler::EngineChannel* pEngineChannel,
146 RTList<Event>::Iterator& itNoteOffEvent
147 ) {
148 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
149 MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNoteOffEvent->Param.Note.Key];
150 // first, get total amount of required voices (dependant on amount of layers)
151 ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOffEvent->Param.Note.Key);
152 if (pRegion) {
153 int voicesRequired = pRegion->Layers;
154
155 // MIDI note-on velocity is used instead of note-off velocity
156 itNoteOffEvent->Param.Note.Velocity = pKey->Velocity;
157
158 // now launch the required amount of voices
159 for (int i = 0; i < voicesRequired; i++)
160 LaunchVoice(pChannel, itNoteOffEvent, i, true, false, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples
161 }
162 }
163
164 Pool<Voice>::Iterator Engine::LaunchVoice (
165 LinuxSampler::EngineChannel* pEngineChannel,
166 Pool<Event>::Iterator& itNoteOnEvent,
167 int iLayer,
168 bool ReleaseTriggerVoice,
169 bool VoiceStealing,
170 bool HandleKeyGroupConflicts
171 ) {
172 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
173 int MIDIKey = itNoteOnEvent->Param.Note.Key;
174 EngineChannel::MidiKey* pKey = &pChannel->pMIDIKeyInfo[MIDIKey];
175 ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(MIDIKey);
176
177 // if nothing defined for this key
178 if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do
179
180 int iKeyGroup = pRegion->KeyGroup;
181 // only need to send a group event from the first voice in a layered region,
182 // as all layers in a region always belongs to the same key group
183 if (HandleKeyGroupConflicts && iLayer == 0) pChannel->HandleKeyGroupConflicts(iKeyGroup, itNoteOnEvent);
184
185 Voice::type_t VoiceType = Voice::type_normal;
186
187 // get current dimension values to select the right dimension region
188 //TODO: for stolen voices this dimension region selection block is processed twice, this should be changed
189 //FIXME: controller values for selecting the dimension region here are currently not sample accurate
190 uint DimValues[8] = { 0 };
191 for (int i = pRegion->Dimensions - 1; i >= 0; i--) {
192 switch (pRegion->pDimensionDefinitions[i].dimension) {
193 case ::gig::dimension_samplechannel:
194 DimValues[i] = 0; //TODO: we currently ignore this dimension
195 break;
196 case ::gig::dimension_layer:
197 DimValues[i] = iLayer;
198 break;
199 case ::gig::dimension_velocity:
200 DimValues[i] = itNoteOnEvent->Param.Note.Velocity;
201 break;
202 case ::gig::dimension_channelaftertouch:
203 DimValues[i] = pChannel->ControllerTable[128];
204 break;
205 case ::gig::dimension_releasetrigger:
206 VoiceType = (ReleaseTriggerVoice) ? Voice::type_release_trigger : (!iLayer) ? Voice::type_release_trigger_required : Voice::type_normal;
207 DimValues[i] = (uint) ReleaseTriggerVoice;
208 break;
209 case ::gig::dimension_keyboard:
210 DimValues[i] = (uint) (pChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);
211 break;
212 case ::gig::dimension_roundrobin:
213 DimValues[i] = uint(*pChannel->pMIDIKeyInfo[MIDIKey].pRoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on in this Region
214 break;
215 case ::gig::dimension_roundrobinkeyboard:
216 DimValues[i] = uint(pChannel->RoundRobinIndex % pRegion->pDimensionDefinitions[i].zones); // RoundRobinIndex is incremented for each note on
217 break;
218 case ::gig::dimension_random:
219 DimValues[i] = uint(Random() * pRegion->pDimensionDefinitions[i].zones);
220 break;
221 case ::gig::dimension_modwheel:
222 DimValues[i] = pChannel->ControllerTable[1];
223 break;
224 case ::gig::dimension_breath:
225 DimValues[i] = pChannel->ControllerTable[2];
226 break;
227 case ::gig::dimension_foot:
228 DimValues[i] = pChannel->ControllerTable[4];
229 break;
230 case ::gig::dimension_portamentotime:
231 DimValues[i] = pChannel->ControllerTable[5];
232 break;
233 case ::gig::dimension_effect1:
234 DimValues[i] = pChannel->ControllerTable[12];
235 break;
236 case ::gig::dimension_effect2:
237 DimValues[i] = pChannel->ControllerTable[13];
238 break;
239 case ::gig::dimension_genpurpose1:
240 DimValues[i] = pChannel->ControllerTable[16];
241 break;
242 case ::gig::dimension_genpurpose2:
243 DimValues[i] = pChannel->ControllerTable[17];
244 break;
245 case ::gig::dimension_genpurpose3:
246 DimValues[i] = pChannel->ControllerTable[18];
247 break;
248 case ::gig::dimension_genpurpose4:
249 DimValues[i] = pChannel->ControllerTable[19];
250 break;
251 case ::gig::dimension_sustainpedal:
252 DimValues[i] = pChannel->ControllerTable[64];
253 break;
254 case ::gig::dimension_portamento:
255 DimValues[i] = pChannel->ControllerTable[65];
256 break;
257 case ::gig::dimension_sostenutopedal:
258 DimValues[i] = pChannel->ControllerTable[66];
259 break;
260 case ::gig::dimension_softpedal:
261 DimValues[i] = pChannel->ControllerTable[67];
262 break;
263 case ::gig::dimension_genpurpose5:
264 DimValues[i] = pChannel->ControllerTable[80];
265 break;
266 case ::gig::dimension_genpurpose6:
267 DimValues[i] = pChannel->ControllerTable[81];
268 break;
269 case ::gig::dimension_genpurpose7:
270 DimValues[i] = pChannel->ControllerTable[82];
271 break;
272 case ::gig::dimension_genpurpose8:
273 DimValues[i] = pChannel->ControllerTable[83];
274 break;
275 case ::gig::dimension_effect1depth:
276 DimValues[i] = pChannel->ControllerTable[91];
277 break;
278 case ::gig::dimension_effect2depth:
279 DimValues[i] = pChannel->ControllerTable[92];
280 break;
281 case ::gig::dimension_effect3depth:
282 DimValues[i] = pChannel->ControllerTable[93];
283 break;
284 case ::gig::dimension_effect4depth:
285 DimValues[i] = pChannel->ControllerTable[94];
286 break;
287 case ::gig::dimension_effect5depth:
288 DimValues[i] = pChannel->ControllerTable[95];
289 break;
290 case ::gig::dimension_none:
291 std::cerr << "gig::Engine::LaunchVoice() Error: dimension=none\n" << std::flush;
292 break;
293 default:
294 std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;
295 }
296 }
297
298 // return if this is a release triggered voice and there is no
299 // releasetrigger dimension (could happen if an instrument
300 // change has occured between note on and off)
301 if (ReleaseTriggerVoice && !(VoiceType & Voice::type_release_trigger)) return Pool<Voice>::Iterator();
302
303 ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);
304
305 // no need to continue if sample is silent
306 if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator();
307
308 // allocate a new voice for the key
309 Pool<Voice>::Iterator itNewVoice = pKey->pActiveVoices->allocAppend();
310
311 int res = InitNewVoice (
312 pChannel, pDimRgn, itNoteOnEvent, VoiceType, iLayer,
313 iKeyGroup, ReleaseTriggerVoice, VoiceStealing, itNewVoice
314 );
315 if (!res) return itNewVoice;
316
317 return Pool<Voice>::Iterator(); // no free voice or error
318 }
319
320 bool Engine::DiskStreamSupported() {
321 return true;
322 }
323
324 String Engine::Description() {
325 return "GigaSampler Format Engine";
326 }
327
328 String Engine::Version() {
329 String s = "$Revision$";
330 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
331 }
332
333 }} // namespace LinuxSampler::gig

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC