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

Contents of /linuxsampler/trunk/src/engines/EngineChannel.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3697 - (show annotations) (download)
Sat Jan 4 12:09:45 2020 UTC (4 years, 3 months ago) by schoenebeck
File size: 15977 byte(s)
Implemented support for compressed RPN/NRPN message sequences:

* Allow processing MIDI data entry messages without having to be
  preceded strictly always by RPN/NRPN parameter selection messages
  prior to each value change (i.e. data entry CC).

* Bumped version (2.1.1.svn33).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2020 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #include "EngineChannel.h"
25
26 #include <algorithm>
27
28 #include "../Sampler.h"
29 #include "../common/global_private.h"
30 #include "../drivers/midi/MidiInstrumentMapper.h"
31 #include "../common/atomic.h"
32
33 #define NO_MIDI_INSTRUMENT_MAP -1
34 #define DEFAULT_MIDI_INSTRUMENT_MAP -2
35
36 namespace LinuxSampler {
37
38 struct EngineChannel::private_data_t {
39 int iMute;
40 bool bSolo;
41 uint8_t uiMidiProgram;
42 uint8_t uiMidiBankMsb;
43 uint8_t uiMidiBankLsb;
44 uint8_t uiMidiRpnMsb; ///< MIDI Registered Parameter Number (upper 7 bits / coarse)
45 uint8_t uiMidiRpnLsb; ///< MIDI Registered Parameter Number (lower 7 bits / fine)
46 uint8_t uiMidiRpnDataMsb; ///< New MIDI RPN Parameter Value (upper 7 bits / coarse)
47 uint8_t uiMidiRpnDataLsb; ///< New MIDI RPN Parameter Value (lower 7 bits / fine)
48 uint8_t uiMidiNrpnMsb; ///< MIDI Non-Registered Parameter Number (upper 7 bits / coarse)
49 uint8_t uiMidiNrpnLsb; ///< MIDI Non-Registered Parameter Number (lower 7 bits / fine)
50 uint8_t uiMidiNrpnDataMsb; ///< New MIDI NRPN Parameter Value (upper 7 bits / coarse)
51 uint8_t uiMidiNrpnDataLsb; ///< New MIDI NRPN Parameter Value (lower 7 bits / fine)
52 bool bMidiBankMsbReceived;
53 bool bMidiBankLsbReceived;
54 bool bProgramChangeReceived;
55 bool bMidiRpnReceived;
56 bool bMidiNrpnReceived;
57 int iMidiInstrumentMap;
58 atomic_t voiceCount;
59 atomic_t diskStreamCount;
60 SamplerChannel* pSamplerChannel;
61 ListenerList<FxSendCountListener*> llFxSendCountListeners;
62 };
63
64 EngineChannel::EngineChannel() : p(new private_data_t) {
65 p->iMute = 0;
66 p->bSolo = false;
67 p->uiMidiBankMsb = 0;
68 p->uiMidiBankLsb = 0;
69 p->uiMidiProgram = 0;
70 p->bProgramChangeReceived =
71 p->bMidiBankMsbReceived =
72 p->bMidiBankLsbReceived = false;
73 p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP;
74 SetVoiceCount(0);
75 SetDiskStreamCount(0);
76 p->pSamplerChannel = NULL;
77 ResetMidiRpnParameter();
78 ResetMidiNrpnParameter();
79 }
80
81 EngineChannel::~EngineChannel() {
82 delete p;
83 }
84
85 /**
86 * Sometimes an instrument is splitted over several files. For example
87 * the GigaStudio format may split an instrument over a certain amount
88 * of files like: "Foo.gig", "Foo.gx01", "Foo.gx02", ...
89 * This method allows to retrieve the whole list of files that is used
90 * for the currently loaded instrument on this engine channel.
91 * Calling this method with index 0 is equivalent as calling the equal
92 * named method without any argument.
93 *
94 * @param index - index of sought file name (0, 1, 2, ...)
95 * @returns file name or empty string if index out of bounds
96 */
97 String EngineChannel::InstrumentFileName(int index) {
98 return (index == 0) ? InstrumentFileName() : "";
99 }
100
101 void EngineChannel::SetMute(int state) throw (Exception) {
102 if (p->iMute == state) return;
103 if (state < -1 || state > 1)
104 throw Exception("Invalid Mute state: " + ToString(state));
105
106 p->iMute = state;
107
108 StatusChanged(true);
109 }
110
111 int EngineChannel::GetMute() {
112 return p->iMute;
113 }
114
115 void EngineChannel::SetSolo(bool solo) {
116 if (p->bSolo == solo) return;
117 p->bSolo = solo;
118 StatusChanged(true);
119 }
120
121 bool EngineChannel::GetSolo() {
122 return p->bSolo;
123 }
124
125 /*
126 We use a workaround for MIDI devices (i.e. old keyboards) which either
127 only send bank select MSB or only bank select LSB messages. Bank
128 selects will be modified according to the following table:
129
130 MIDI Sequence received: -> GetMidiBankMsb()= | GetMidiBankLsb()=
131 ---------------------------------------------------------------------------
132 program change -> 0 | 0
133 bank LSB, program change -> 0 | LSB value
134 bank MSB, program change -> 0 | MSB value
135 bank LSB, bank MSB, program change -> MSB value | LSB value
136 bank MSB, bank LSB, program change -> MSB value | LSB value
137 ---------------------------------------------------------------------------
138
139 That way we ensure those limited devices always to switch between the
140 following set of MIDI instrument banks: { 0, 1, 2, ..., 127 }
141 */
142
143 uint8_t EngineChannel::GetMidiProgram() {
144 return p->uiMidiProgram; // AFAIK atomic on all systems
145 }
146
147 void EngineChannel::SetMidiProgram(uint8_t Program) {
148 p->bProgramChangeReceived = true;
149 p->uiMidiProgram = Program; // AFAIK atomic on all systems
150 }
151
152 uint8_t EngineChannel::GetMidiBankMsb() {
153 return (p->bMidiBankMsbReceived && p->bMidiBankLsbReceived)
154 ? p->uiMidiBankMsb : 0;
155 }
156
157 void EngineChannel::SetMidiBankMsb(uint8_t BankMSB) {
158 if (p->bProgramChangeReceived) {
159 p->bProgramChangeReceived =
160 p->bMidiBankLsbReceived = false;
161 }
162 p->bMidiBankMsbReceived = true;
163 p->uiMidiBankMsb = BankMSB; // AFAIK atomic on all systems
164 }
165
166 uint8_t EngineChannel::GetMidiBankLsb() {
167 return (!p->bMidiBankMsbReceived && !p->bMidiBankLsbReceived)
168 ? 0
169 : (p->bMidiBankMsbReceived && !p->bMidiBankLsbReceived)
170 ? p->uiMidiBankMsb
171 : p->uiMidiBankLsb;
172 }
173
174 void EngineChannel::SetMidiBankLsb(uint8_t BankLSB) {
175 if (p->bProgramChangeReceived) {
176 p->bProgramChangeReceived =
177 p->bMidiBankMsbReceived = false;
178 }
179 p->bMidiBankLsbReceived = true;
180 p->uiMidiBankLsb = BankLSB; // AFAIK atomic on all systems
181 }
182
183 bool EngineChannel::UsesNoMidiInstrumentMap() {
184 return (p->iMidiInstrumentMap == NO_MIDI_INSTRUMENT_MAP);
185 }
186
187 bool EngineChannel::UsesDefaultMidiInstrumentMap() {
188 return (p->iMidiInstrumentMap == DEFAULT_MIDI_INSTRUMENT_MAP);
189 }
190
191 int EngineChannel::GetMidiInstrumentMap() throw (Exception) {
192 if (UsesNoMidiInstrumentMap())
193 throw Exception("EngineChannel is using no MIDI instrument map");
194 if (UsesDefaultMidiInstrumentMap())
195 throw Exception("EngineChannel is using default MIDI instrument map");
196 // check if the stored map still exists in the MIDI instrument mapper
197 std::vector<int> maps = MidiInstrumentMapper::Maps();
198 if (find(maps.begin(), maps.end(), p->iMidiInstrumentMap) == maps.end()) {
199 // it doesn't exist anymore, so fall back to NONE and throw an exception
200 p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP;
201 throw Exception("Assigned MIDI instrument map doesn't exist anymore, falling back to NONE");
202 }
203 return p->iMidiInstrumentMap;
204 }
205
206 void EngineChannel::SetMidiInstrumentMapToNone() {
207 if (p->iMidiInstrumentMap == NO_MIDI_INSTRUMENT_MAP) return;
208 p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP;
209 StatusChanged(true);
210 }
211
212 void EngineChannel::SetMidiInstrumentMapToDefault() {
213 if (p->iMidiInstrumentMap == DEFAULT_MIDI_INSTRUMENT_MAP) return;
214 p->iMidiInstrumentMap = DEFAULT_MIDI_INSTRUMENT_MAP;
215 StatusChanged(true);
216 }
217
218 void EngineChannel::SetMidiInstrumentMap(int MidiMap) throw (Exception) {
219 if (p->iMidiInstrumentMap == MidiMap) return;
220
221 // check if given map actually exists in the MIDI instrument mapper
222 std::vector<int> maps = MidiInstrumentMapper::Maps();
223 if (find(maps.begin(), maps.end(), MidiMap) == maps.end())
224 throw Exception("MIDI instrument map doesn't exist");
225 p->iMidiInstrumentMap = MidiMap; // assign the new map ID
226 StatusChanged(true);
227 }
228
229 // RPNs ...
230
231 void EngineChannel::SetMidiRpnParameterMsb(uint8_t ParamMSB) {
232 p->uiMidiRpnMsb = ParamMSB & 127;
233 p->bMidiRpnReceived = true;
234 }
235
236 void EngineChannel::SetMidiRpnControllerMsb(uint8_t CtrlMSB) { // deprecated API
237 SetMidiRpnParameterMsb(CtrlMSB);
238 }
239
240 void EngineChannel::SetMidiRpnParameterLsb(uint8_t ParamLSB) {
241 p->uiMidiRpnLsb = ParamLSB & 127;
242 p->bMidiRpnReceived = true;
243 }
244
245 void EngineChannel::SetMidiRpnControllerLsb(uint8_t CtrlLSB) { // deprecated API
246 SetMidiRpnParameterLsb(CtrlLSB);
247 }
248
249 void EngineChannel::SetMidiRpnDataMsb(uint8_t DataMSB) {
250 p->uiMidiRpnDataMsb = DataMSB & 127;
251 }
252
253 void EngineChannel::SetMidiRpnDataLsb(uint8_t DataLSB) {
254 p->uiMidiRpnDataLsb = DataLSB & 127;
255 }
256
257 void EngineChannel::ResetMidiRpnParameter() {
258 p->uiMidiRpnMsb = p->uiMidiRpnLsb = 0;
259 p->uiMidiRpnDataMsb = p->uiMidiRpnDataLsb = 0;
260 p->bMidiRpnReceived = false;
261 }
262
263 void EngineChannel::ResetMidiRpnController() { // deprecated API
264 ResetMidiRpnParameter();
265 }
266
267 int EngineChannel::GetMidiRpnParameter() {
268 return (p->bMidiRpnReceived) ?
269 (p->uiMidiRpnMsb << 7) | p->uiMidiRpnLsb : -1;
270 }
271
272 int EngineChannel::GetMidiRpnController() { // deprecated API
273 return (p->bMidiRpnReceived) ?
274 (p->uiMidiRpnMsb << 8) | p->uiMidiRpnLsb : -1;
275 }
276
277 int EngineChannel::GetMidiRpnData() {
278 return (p->bMidiRpnReceived) ?
279 (p->uiMidiRpnDataMsb << 7) | p->uiMidiRpnDataLsb : 0;
280 }
281
282 // NRPNs ...
283
284 void EngineChannel::SetMidiNrpnParameterMsb(uint8_t ParamMSB) {
285 p->uiMidiNrpnMsb = ParamMSB & 127;
286 p->bMidiNrpnReceived = true;
287 }
288
289 void EngineChannel::SetMidiNrpnControllerMsb(uint8_t CtrlMSB) { // deprecated API
290 SetMidiNrpnParameterMsb(CtrlMSB);
291 }
292
293 void EngineChannel::SetMidiNrpnParameterLsb(uint8_t ParamLSB) {
294 p->uiMidiNrpnLsb = ParamLSB & 127;
295 p->bMidiNrpnReceived = true;
296 }
297
298 void EngineChannel::SetMidiNrpnControllerLsb(uint8_t CtrlLSB) { // deprecated API
299 SetMidiNrpnParameterLsb(CtrlLSB);
300 }
301
302 void EngineChannel::SetMidiNrpnDataMsb(uint8_t DataMSB) {
303 p->uiMidiNrpnDataMsb = DataMSB & 127;
304 }
305
306 void EngineChannel::SetMidiNrpnDataLsb(uint8_t DataLSB) {
307 p->uiMidiNrpnDataLsb = DataLSB & 127;
308 }
309
310 void EngineChannel::ResetMidiNrpnParameter() {
311 p->uiMidiNrpnMsb = p->uiMidiNrpnLsb = 0;
312 p->uiMidiNrpnDataMsb = p->uiMidiNrpnDataLsb = 0;
313 p->bMidiNrpnReceived = false;
314 }
315
316 void EngineChannel::ResetMidiNrpnController() { // deprecated API
317 ResetMidiNrpnParameter();
318 }
319
320 int EngineChannel::GetMidiNrpnParameter() {
321 return (p->bMidiNrpnReceived) ?
322 (p->uiMidiNrpnMsb << 7) | p->uiMidiNrpnLsb : -1;
323 }
324
325 int EngineChannel::GetMidiNrpnController() { // deprecated API
326 return (p->bMidiNrpnReceived) ?
327 (p->uiMidiNrpnMsb << 8) | p->uiMidiNrpnLsb : -1;
328 }
329
330 int EngineChannel::GetMidiNrpnData() {
331 return (p->bMidiNrpnReceived) ?
332 (p->uiMidiNrpnDataMsb << 7) | p->uiMidiNrpnDataLsb : 0;
333 }
334
335 uint EngineChannel::GetVoiceCount() {
336 return atomic_read(&p->voiceCount);
337 }
338
339 void EngineChannel::SetVoiceCount(uint Voices) {
340 atomic_set(&p->voiceCount, Voices);
341 }
342
343 uint EngineChannel::GetDiskStreamCount() {
344 return atomic_read(&p->diskStreamCount);
345 }
346
347 void EngineChannel::SetDiskStreamCount(uint Streams) {
348 atomic_set(&p->diskStreamCount, Streams);
349 }
350
351 SamplerChannel* EngineChannel::GetSamplerChannel() {
352 if (p->pSamplerChannel == NULL) {
353 std::cerr << "EngineChannel::GetSamplerChannel(): pSamplerChannel is NULL, this is a bug!\n" << std::flush;
354 }
355 return p->pSamplerChannel;
356 }
357
358 void EngineChannel::SetSamplerChannel(SamplerChannel* pChannel) {
359 p->pSamplerChannel = pChannel;
360 }
361
362 Sampler* EngineChannel::GetSampler() {
363 if (GetSamplerChannel() == NULL) return NULL;
364 return GetSamplerChannel()->GetSampler();
365 }
366
367 void EngineChannel::AddFxSendCountListener(FxSendCountListener* l) {
368 p->llFxSendCountListeners.AddListener(l);
369 }
370
371 void EngineChannel::RemoveFxSendCountListener(FxSendCountListener* l) {
372 p->llFxSendCountListeners.RemoveListener(l);
373 }
374
375 void EngineChannel::RemoveAllFxSendCountListeners() {
376 p->llFxSendCountListeners.RemoveAllListeners();
377 }
378
379 void EngineChannel::fireFxSendCountChanged(int ChannelId, int NewCount) {
380 for (int i = 0; i < p->llFxSendCountListeners.GetListenerCount(); i++) {
381 p->llFxSendCountListeners.GetListener(i)->FxSendCountChanged(ChannelId, NewCount);
382 }
383 }
384
385 void EngineChannel::ExecuteProgramChange(uint32_t Program) {
386 uint8_t hb = (Program >> 16) & 0xff;
387 uint8_t lb = (Program >> 8) & 0xff;
388 uint8_t pc = Program & 0x7f;
389
390 dmsg(1,("Received MIDI program change (msb=%d) (lsb=%d) (prog=%d)\n", hb ,lb, pc));
391 std::vector<int> maps = MidiInstrumentMapper::Maps();
392 if (maps.empty()) return;
393
394 if (UsesNoMidiInstrumentMap()) return;
395 if (MidiInstrumentMapper::GetMapCount() == 0) return;
396 // retrieve the MIDI instrument map this engine channel is assigned to
397 int iMapID = (UsesDefaultMidiInstrumentMap())
398 ? MidiInstrumentMapper::GetDefaultMap() /*default*/ : GetMidiInstrumentMap();
399 // is there an entry for this MIDI bank&prog pair in that map?
400 midi_prog_index_t midiIndex;
401 midiIndex.midi_bank_msb = hb;
402 midiIndex.midi_bank_lsb = lb;
403 midiIndex.midi_prog = pc;
404 optional<MidiInstrumentMapper::entry_t> mapping =
405 MidiInstrumentMapper::GetEntry(iMapID, midiIndex);
406 if (mapping) { // if mapping exists ...
407 InstrumentManager::instrument_id_t id;
408 id.FileName = mapping->InstrumentFile;
409 id.Index = mapping->InstrumentIndex;
410 //TODO: we should switch the engine type here
411 InstrumentManager::LoadInstrumentInBackground(id, this);
412 Volume(mapping->Volume);
413 }
414 }
415
416 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC