/[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 3687 - (show annotations) (download)
Thu Jan 2 22:37:07 2020 UTC (4 years, 2 months ago) by schoenebeck
File size: 14723 byte(s)
EngineChannel: Overhaul of RPN / NRPN related methods:

* Added new *Rpn*Parameter*() methods as replacement for previous
  *Rpn*Controller*() methods and marked the old ones as deprecated.

* Added new *Nrpn*Parameter*() methods as replacement for previous
  *Nrpn*Controller*() methods and marked the old ones as deprecated.

* The new GetMidiRpnParameter() and GetMidiNrpnParameter() methods
  return their result as common (in MIDI world) 14 bit representation
  of sender's MSB and LSB values, instead of the uncommon 16 bit
  representation of the old GetMidiRpnController() and
  GetMidiNrpnController() methods.

* Bumped version (2.1.1.svn28).

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

  ViewVC Help
Powered by ViewVC