/[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 2879 - (show annotations) (download)
Tue Apr 19 14:07:53 2016 UTC (8 years ago) by schoenebeck
File size: 13070 byte(s)
* All engines: Active voices are now internally grouped to "Note" objects,
  instead of being directly assigned to a keyboard key. This allows more
  fine graded processing of voices, which is i.e. required for certain
  instrument script features.
* Built-in script function "play_note()": Added support for passing
  special value -1 for "duration-us" argument, which will cause the
  triggered note to be released once the original note was released.
* Bumped version (2.0.0.svn3).

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

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC