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

Contents of /linuxsampler/trunk/src/engines/sfz/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: 14198 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-2012 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 sfz {
30 Engine::Format Engine::GetEngineFormat() { return SFZ; }
31
32 void Engine::CreateInstrumentScriptVM() {
33 dmsg(2,("sfz::Engine created SFZ format scriptvm\n"));
34 if (pScriptVM) return;
35 pScriptVM = new InstrumentScriptVM; // sfz format specific extended script runner
36 }
37
38 Engine::Engine() {
39 pCCPool = new Pool<CCSignalUnit::CC>(GLOBAL_MAX_VOICES * MaxCCPerVoice);
40 pSmootherPool = new Pool<Smoother>(GLOBAL_MAX_VOICES * MaxCCPerVoice);
41 for (VoiceIterator iterVoice = GetVoicePool()->allocAppend(); iterVoice == GetVoicePool()->last(); iterVoice = GetVoicePool()->allocAppend()) {
42 (static_cast<SfzSignalUnitRack*>(iterVoice->pSignalUnitRack))->InitRTLists();
43 }
44 GetVoicePool()->clear();
45 }
46
47 Engine::~Engine() {
48 if (pCCPool) {
49 pCCPool->clear();
50 delete pCCPool;
51 }
52
53 if (pSmootherPool) {
54 pSmootherPool->clear();
55 delete pSmootherPool;
56 }
57 }
58
59 void Engine::PostSetMaxVoices(int iVoices) {
60 pCCPool->resizePool(iVoices * MaxCCPerVoice);
61 pSmootherPool->resizePool(iVoices * MaxCCPerVoice);
62
63 for (VoiceIterator iterVoice = GetVoicePool()->allocAppend(); iterVoice == GetVoicePool()->last(); iterVoice = GetVoicePool()->allocAppend()) {
64 (static_cast<SfzSignalUnitRack*>(iterVoice->pSignalUnitRack))->InitRTLists();
65 }
66 GetVoicePool()->clear();
67 }
68
69 /**
70 * Reacts on supported control change commands (e.g. pitch bend wheel,
71 * modulation wheel, aftertouch).
72 *
73 * @param pEngineChannel - engine channel on which this event occured on
74 * @param itControlChangeEvent - controller, value and time stamp of the event
75 */
76 void Engine::ProcessControlChange (
77 LinuxSampler::EngineChannel* pEngineChannel,
78 Pool<Event>::Iterator& itControlChangeEvent
79 ) {
80 uint8_t cc = itControlChangeEvent->Param.CC.Controller;
81 dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", cc, itControlChangeEvent->Param.CC.Value));
82
83 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
84
85 // update controller value in the engine channel's controller table
86 pChannel->ControllerTable[cc] = itControlChangeEvent->Param.CC.Value;
87
88 ProcessHardcodedControllers(pEngineChannel, itControlChangeEvent);
89
90 // handle FX send controllers
91 ProcessFxSendControllers(pChannel, itControlChangeEvent);
92
93 // handle control triggered regions: a control change event
94 // can trigger a new voice
95 if (pChannel->pInstrument && cc < 128) {
96
97 ::sfz::Query q;
98 q.chan = itControlChangeEvent->Param.CC.Channel + 1;
99 q.key = 60;
100 q.vel = 127;
101 q.bend = pChannel->Pitch;
102 q.bpm = 0;
103 q.chanaft = pChannel->ControllerTable[128];
104 q.polyaft = 0;
105 q.prog = 0;
106 q.rand = Random();
107 q.cc = pChannel->ControllerTable;
108 q.timer = 0;
109 q.sw = pChannel->PressedKeys;
110 q.last_sw_key = pChannel->LastKeySwitch;
111 q.prev_sw_key = pChannel->LastKey;
112 q.trig = TRIGGER_ATTACK | TRIGGER_FIRST;
113
114 q.search(pChannel->pInstrument, cc);
115
116 NoteIterator itNewNote;
117
118 int i = 0;
119 while (::sfz::Region* region = q.next()) {
120 if (!RegionSuspended(region)) {
121 itControlChangeEvent->Param.Note.Key = 60;
122 itControlChangeEvent->Param.Note.Velocity = 127;
123 itControlChangeEvent->Param.Note.pRegion = region;
124 if (!itNewNote) {
125 const note_id_t noteID = LaunchNewNote(pEngineChannel, itControlChangeEvent);
126 itNewNote = GetNotePool()->fromID(noteID);
127 if (!itNewNote) {
128 dmsg(1,("sfz::Engine: Note pool empty!\n"));
129 return;
130 }
131 }
132 VoiceIterator itNewVoice =
133 LaunchVoice(pChannel, itControlChangeEvent, i, false, false, true);
134 if (itNewVoice)
135 itNewVoice.moveToEndOf(itNewNote->pActiveVoices);
136 }
137 i++;
138 }
139 }
140 }
141
142 void Engine::ProcessChannelPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itChannelPressureEvent) {
143 // forward this to the CC routine, so it updates the current aftertouch value
144 ProcessControlChange(pEngineChannel, itChannelPressureEvent);
145
146 // if required: engine global aftertouch handling (apart from the per voice handling)
147 }
148
149 void Engine::ProcessPolyphonicKeyPressure(LinuxSampler::EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNotePressureEvent) {
150 // if required: engine global aftertouch handling (apart from the per voice handling)
151 }
152
153 DiskThread* Engine::CreateDiskThread() {
154 return new DiskThread (
155 iMaxDiskStreams,
156 ((pAudioOutputDevice->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
157 &instruments
158 );
159 }
160
161 void Engine::TriggerNewVoices (
162 LinuxSampler::EngineChannel* pEngineChannel,
163 RTList<Event>::Iterator& itNoteOnEvent,
164 bool HandleKeyGroupConflicts
165 ) {
166 NoteIterator itNote = GetNotePool()->fromID(itNoteOnEvent->Param.Note.ID);
167 if (!itNote) {
168 dmsg(1,("sfz::Engine: No Note object for triggering new voices!\n"));
169 return;
170 }
171 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
172 //MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNoteOnEvent->Param.Note.Key];
173 ::sfz::Query q;
174 q.chan = itNoteOnEvent->Param.Note.Channel + 1;
175 q.key = itNote->cause.Param.Note.Key; //itNoteOnEvent->Param.Note.Key; <- using note object instead, since note nr might been modified by script
176 q.vel = itNote->cause.Param.Note.Velocity; //itNoteOnEvent->Param.Note.Velocity; <- using note object instead, since velocity might been modified by script
177 q.bend = pChannel->Pitch;
178 q.bpm = 0;
179 q.chanaft = pChannel->ControllerTable[128];
180 q.polyaft = 0;
181 q.prog = 0;
182 q.rand = Random();
183 q.cc = pChannel->ControllerTable;
184 q.timer = 0;
185 q.sw = pChannel->PressedKeys;
186 q.last_sw_key = pChannel->LastKeySwitch;
187 q.prev_sw_key = pChannel->LastKey;
188 q.trig = TRIGGER_ATTACK |
189 ((pChannel->LastKey != -1 &&
190 pChannel->PressedKeys[pChannel->LastKey] &&
191 pChannel->LastKey != q.key) ?
192 TRIGGER_LEGATO : TRIGGER_FIRST);
193
194 q.search(pChannel->pInstrument);
195
196 int i = 0;
197 while (::sfz::Region* region = q.next()) {
198 if (!RegionSuspended(region)) {
199 itNoteOnEvent->Param.Note.pRegion = region;
200 VoiceIterator itNewVoice =
201 LaunchVoice(pChannel, itNoteOnEvent, i, false, true, HandleKeyGroupConflicts);
202 if (itNewVoice)
203 itNewVoice.moveToEndOf(itNote->pActiveVoices);
204 }
205 i++;
206 }
207 }
208
209 void Engine::TriggerReleaseVoices (
210 LinuxSampler::EngineChannel* pEngineChannel,
211 RTList<Event>::Iterator& itNoteOffEvent
212 ) {
213 NoteIterator itNote = GetNotePool()->fromID(itNoteOffEvent->Param.Note.ID);
214 if (!itNote) {
215 dmsg(1,("sfz::Engine: No Note object for triggering new release voices!\n"));
216 return;
217 }
218 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
219 MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNote->cause.Param.Note.Key];
220
221 ::sfz::Query q;
222 q.chan = itNoteOffEvent->Param.Note.Channel + 1;
223 q.key = itNote->cause.Param.Note.Key; //itNoteOffEvent->Param.Note.Key; <- using note object instead, since note nr might been modified by script
224
225 // MIDI note-on velocity is used instead of note-off velocity
226 // if note-off velocity is exactly zero (which means missing sensors
227 // in keyboard), otherwise if note-off velocity is non-zero then use
228 // note object instead of MIDI note-off event, since note-off velocity
229 // might have been modified by RT instrument script
230 q.vel = (itNote->cause.Param.Note.Velocity) ?: pKey->Velocity;
231 itNote->cause.Param.Note.Velocity = q.vel;
232
233 q.bend = pChannel->Pitch;
234 q.bpm = 0;
235 q.chanaft = pChannel->ControllerTable[128];
236 q.polyaft = 0;
237 q.prog = 0;
238 q.rand = Random();
239 q.cc = pChannel->ControllerTable;
240 q.timer = 0;
241 q.sw = pChannel->PressedKeys;
242 q.last_sw_key = pChannel->LastKeySwitch;
243 q.prev_sw_key = pChannel->LastKey;
244 q.trig = TRIGGER_RELEASE;
245
246 q.search(pChannel->pInstrument);
247
248 // now launch the required amount of voices
249 int i = 0;
250 while (::sfz::Region* region = q.next()) {
251 itNoteOffEvent->Param.Note.pRegion = region;
252 VoiceIterator itNewVoice =
253 LaunchVoice(pChannel, itNoteOffEvent, i, true, false, true); //FIXME: for the moment we don't perform voice stealing for release triggered samples
254 if (itNewVoice)
255 itNewVoice.moveToEndOf(itNote->pActiveVoices);
256 i++;
257 }
258 }
259
260 Pool<Voice>::Iterator Engine::LaunchVoice (
261 LinuxSampler::EngineChannel* pEngineChannel,
262 Pool<Event>::Iterator& itNoteOnEvent,
263 int iLayer,
264 bool ReleaseTriggerVoice,
265 bool VoiceStealing,
266 bool HandleKeyGroupConflicts
267 ) {
268 EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
269 //int key = itNoteOnEvent->Param.Note.Key;
270 //EngineChannel::MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];
271 ::sfz::Region* pRgn = static_cast< ::sfz::Region*>(itNoteOnEvent->Param.Note.pRegion);
272
273 Voice::type_t VoiceType =
274 itNoteOnEvent->Type == Event::type_control_change ? Voice::type_controller_triggered :
275 ReleaseTriggerVoice ? Voice::type_release_trigger :
276 iLayer == 0 ? Voice::type_release_trigger_required :
277 Voice::type_normal;
278 if (pRgn->loop_mode == ::sfz::ONE_SHOT) VoiceType |= Voice::type_one_shot;
279
280 Pool<Voice>::Iterator itNewVoice;
281
282 if (HandleKeyGroupConflicts) pChannel->HandleKeyGroupConflicts(pRgn->group, itNoteOnEvent);
283
284 // no need to process if sample is silent
285 if (!pRgn->GetSample(false) || !pRgn->GetSample()->GetTotalFrameCount()) return Pool<Voice>::Iterator();
286
287 // allocate a new voice for the key
288 itNewVoice = GetVoicePool()->allocAppend();
289 int res = InitNewVoice (
290 pChannel, pRgn, itNoteOnEvent, VoiceType, iLayer,
291 pRgn->off_by, ReleaseTriggerVoice, VoiceStealing, itNewVoice
292 );
293 if (!res) return itNewVoice;
294
295 // return if this is a release triggered voice and there is no
296 // releasetrigger dimension (could happen if an instrument
297 // change has occured between note on and off)
298 //if (ReleaseTriggerVoice && VoiceType != Voice::type_release_trigger) return Pool<Voice>::Iterator();
299
300 return Pool<Voice>::Iterator(); // no free voice or error
301 }
302
303 bool Engine::DiskStreamSupported() {
304 return true;
305 }
306
307 String Engine::Description() {
308 return "SFZ Format Engine";
309 }
310
311 String Engine::Version() {
312 String s = "$Revision$";
313 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
314 }
315
316 }} // namespace LinuxSampler::sfz

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC