/[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 2277 - (show annotations) (download)
Sat Oct 1 08:23:02 2011 UTC (12 years, 6 months ago) by persson
File size: 12845 byte(s)
* fixed handling of rapid bank select and program change messages sent
  to the same sampler channel (patch from the Open Octave project,
  slightly adjusted)

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2011 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 8 bits / coarse)
45 uint8_t uiMidiRpnLsb; ///< MIDI Registered Parameter Number (lower 8 bits / fine)
46 uint8_t uiMidiNrpnMsb; ///< MIDI Non-Registered Parameter Number (upper 8 bits / coarse)
47 uint8_t uiMidiNrpnLsb; ///< MIDI Non-Registered Parameter Number (lower 8 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 ResetMidiRpnController();
74 ResetMidiNrpnController();
75 }
76
77 EngineChannel::~EngineChannel() {
78 delete p;
79 }
80
81 void EngineChannel::SetMute(int state) throw (Exception) {
82 if (p->iMute == state) return;
83 if (state < -1 || state > 1)
84 throw Exception("Invalid Mute state: " + ToString(state));
85
86 p->iMute = state;
87
88 StatusChanged(true);
89 }
90
91 int EngineChannel::GetMute() {
92 return p->iMute;
93 }
94
95 void EngineChannel::SetSolo(bool solo) {
96 if (p->bSolo == solo) return;
97 p->bSolo = solo;
98 StatusChanged(true);
99 }
100
101 bool EngineChannel::GetSolo() {
102 return p->bSolo;
103 }
104
105 /*
106 We use a workaround for MIDI devices (i.e. old keyboards) which either
107 only send bank select MSB or only bank select LSB messages. Bank
108 selects will be modified according to the following table:
109
110 MIDI Sequence received: -> GetMidiBankMsb()= | GetMidiBankLsb()=
111 ---------------------------------------------------------------------------
112 program change -> 0 | 0
113 bank LSB, program change -> 0 | LSB value
114 bank MSB, program change -> 0 | MSB value
115 bank LSB, bank MSB, program change -> MSB value | LSB value
116 bank MSB, bank LSB, program change -> MSB value | LSB value
117 ---------------------------------------------------------------------------
118
119 That way we ensure those limited devices always to switch between the
120 following set of MIDI instrument banks: { 0, 1, 2, ..., 127 }
121 */
122
123 uint8_t EngineChannel::GetMidiProgram() {
124 return p->uiMidiProgram; // AFAIK atomic on all systems
125 }
126
127 void EngineChannel::SetMidiProgram(uint8_t Program) {
128 p->bProgramChangeReceived = true;
129 p->uiMidiProgram = Program; // AFAIK atomic on all systems
130 }
131
132 uint8_t EngineChannel::GetMidiBankMsb() {
133 return (p->bMidiBankMsbReceived && p->bMidiBankLsbReceived)
134 ? p->uiMidiBankMsb : 0;
135 }
136
137 void EngineChannel::SetMidiBankMsb(uint8_t BankMSB) {
138 if (p->bProgramChangeReceived) {
139 p->bProgramChangeReceived =
140 p->bMidiBankLsbReceived = false;
141 }
142 p->bMidiBankMsbReceived = true;
143 p->uiMidiBankMsb = BankMSB; // AFAIK atomic on all systems
144 }
145
146 uint8_t EngineChannel::GetMidiBankLsb() {
147 return (!p->bMidiBankMsbReceived && !p->bMidiBankLsbReceived)
148 ? 0
149 : (p->bMidiBankMsbReceived && !p->bMidiBankLsbReceived)
150 ? p->uiMidiBankMsb
151 : p->uiMidiBankLsb;
152 }
153
154 void EngineChannel::SetMidiBankLsb(uint8_t BankLSB) {
155 if (p->bProgramChangeReceived) {
156 p->bProgramChangeReceived =
157 p->bMidiBankMsbReceived = false;
158 }
159 p->bMidiBankLsbReceived = true;
160 p->uiMidiBankLsb = BankLSB; // AFAIK atomic on all systems
161 }
162
163 bool EngineChannel::UsesNoMidiInstrumentMap() {
164 return (p->iMidiInstrumentMap == NO_MIDI_INSTRUMENT_MAP);
165 }
166
167 bool EngineChannel::UsesDefaultMidiInstrumentMap() {
168 return (p->iMidiInstrumentMap == DEFAULT_MIDI_INSTRUMENT_MAP);
169 }
170
171 int EngineChannel::GetMidiInstrumentMap() throw (Exception) {
172 if (UsesNoMidiInstrumentMap())
173 throw Exception("EngineChannel is using no MIDI instrument map");
174 if (UsesDefaultMidiInstrumentMap())
175 throw Exception("EngineChannel is using default MIDI instrument map");
176 // check if the stored map still exists in the MIDI instrument mapper
177 std::vector<int> maps = MidiInstrumentMapper::Maps();
178 if (find(maps.begin(), maps.end(), p->iMidiInstrumentMap) == maps.end()) {
179 // it doesn't exist anymore, so fall back to NONE and throw an exception
180 p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP;
181 throw Exception("Assigned MIDI instrument map doesn't exist anymore, falling back to NONE");
182 }
183 return p->iMidiInstrumentMap;
184 }
185
186 void EngineChannel::SetMidiInstrumentMapToNone() {
187 if (p->iMidiInstrumentMap == NO_MIDI_INSTRUMENT_MAP) return;
188 p->iMidiInstrumentMap = NO_MIDI_INSTRUMENT_MAP;
189 StatusChanged(true);
190 }
191
192 void EngineChannel::SetMidiInstrumentMapToDefault() {
193 if (p->iMidiInstrumentMap == DEFAULT_MIDI_INSTRUMENT_MAP) return;
194 p->iMidiInstrumentMap = DEFAULT_MIDI_INSTRUMENT_MAP;
195 StatusChanged(true);
196 }
197
198 void EngineChannel::SetMidiInstrumentMap(int MidiMap) throw (Exception) {
199 if (p->iMidiInstrumentMap == MidiMap) return;
200
201 // check if given map actually exists in the MIDI instrument mapper
202 std::vector<int> maps = MidiInstrumentMapper::Maps();
203 if (find(maps.begin(), maps.end(), MidiMap) == maps.end())
204 throw Exception("MIDI instrument map doesn't exist");
205 p->iMidiInstrumentMap = MidiMap; // assign the new map ID
206 StatusChanged(true);
207 }
208
209 // RPNs ...
210
211 void EngineChannel::SetMidiRpnControllerMsb(uint8_t CtrlMSB) {
212 p->uiMidiRpnMsb = CtrlMSB;
213 p->bMidiRpnReceived = true;
214 }
215
216 void EngineChannel::SetMidiRpnControllerLsb(uint8_t CtrlLSB) {
217 p->uiMidiRpnLsb = CtrlLSB;
218 p->bMidiRpnReceived = true;
219 }
220
221 void EngineChannel::ResetMidiRpnController() {
222 p->uiMidiRpnMsb = p->uiMidiRpnLsb = 0;
223 p->bMidiRpnReceived = false;
224 }
225
226 int EngineChannel::GetMidiRpnController() {
227 return (p->bMidiRpnReceived) ?
228 (p->uiMidiRpnMsb << 8) | p->uiMidiRpnLsb : -1;
229 }
230
231 // NRPNs ...
232
233 void EngineChannel::SetMidiNrpnControllerMsb(uint8_t CtrlMSB) {
234 p->uiMidiNrpnMsb = CtrlMSB;
235 p->bMidiNrpnReceived = true;
236 }
237
238 void EngineChannel::SetMidiNrpnControllerLsb(uint8_t CtrlLSB) {
239 p->uiMidiNrpnLsb = CtrlLSB;
240 p->bMidiNrpnReceived = true;
241 }
242
243 void EngineChannel::ResetMidiNrpnController() {
244 p->uiMidiNrpnMsb = p->uiMidiNrpnLsb = 0;
245 p->bMidiNrpnReceived = false;
246 }
247
248 int EngineChannel::GetMidiNrpnController() {
249 return (p->bMidiNrpnReceived) ?
250 (p->uiMidiNrpnMsb << 8) | p->uiMidiNrpnLsb : -1;
251 }
252
253 uint EngineChannel::GetVoiceCount() {
254 return atomic_read(&p->voiceCount);
255 }
256
257 void EngineChannel::SetVoiceCount(uint Voices) {
258 atomic_set(&p->voiceCount, Voices);
259 }
260
261 uint EngineChannel::GetDiskStreamCount() {
262 return atomic_read(&p->diskStreamCount);
263 }
264
265 void EngineChannel::SetDiskStreamCount(uint Streams) {
266 atomic_set(&p->diskStreamCount, Streams);
267 }
268
269 SamplerChannel* EngineChannel::GetSamplerChannel() {
270 if (p->pSamplerChannel == NULL) {
271 std::cerr << "EngineChannel::GetSamplerChannel(): pSamplerChannel is NULL, this is a bug!\n" << std::flush;
272 }
273 return p->pSamplerChannel;
274 }
275
276 void EngineChannel::SetSamplerChannel(SamplerChannel* pChannel) {
277 p->pSamplerChannel = pChannel;
278 }
279
280 Sampler* EngineChannel::GetSampler() {
281 if (GetSamplerChannel() == NULL) return NULL;
282 return GetSamplerChannel()->GetSampler();
283 }
284
285 void EngineChannel::AddFxSendCountListener(FxSendCountListener* l) {
286 p->llFxSendCountListeners.AddListener(l);
287 }
288
289 void EngineChannel::RemoveFxSendCountListener(FxSendCountListener* l) {
290 p->llFxSendCountListeners.RemoveListener(l);
291 }
292
293 void EngineChannel::RemoveAllFxSendCountListeners() {
294 p->llFxSendCountListeners.RemoveAllListeners();
295 }
296
297 void EngineChannel::fireFxSendCountChanged(int ChannelId, int NewCount) {
298 for (int i = 0; i < p->llFxSendCountListeners.GetListenerCount(); i++) {
299 p->llFxSendCountListeners.GetListener(i)->FxSendCountChanged(ChannelId, NewCount);
300 }
301 }
302
303 void EngineChannel::ExecuteProgramChange(uint32_t Program) {
304 uint8_t hb = (Program >> 16) & 0xff;
305 uint8_t lb = (Program >> 8) & 0xff;
306 uint8_t pc = Program & 0x7f;
307
308 dmsg(1,("Received MIDI program change (msb=%d) (lsb=%d) (prog=%d)\n", hb ,lb, pc));
309 std::vector<int> maps = MidiInstrumentMapper::Maps();
310 if (maps.empty()) return;
311
312 if (UsesNoMidiInstrumentMap()) return;
313 if (MidiInstrumentMapper::GetMapCount() == 0) return;
314 // retrieve the MIDI instrument map this engine channel is assigned to
315 int iMapID = (UsesDefaultMidiInstrumentMap())
316 ? MidiInstrumentMapper::GetDefaultMap() /*default*/ : GetMidiInstrumentMap();
317 // is there an entry for this MIDI bank&prog pair in that map?
318 midi_prog_index_t midiIndex;
319 midiIndex.midi_bank_msb = hb;
320 midiIndex.midi_bank_lsb = lb;
321 midiIndex.midi_prog = pc;
322 optional<MidiInstrumentMapper::entry_t> mapping =
323 MidiInstrumentMapper::GetEntry(iMapID, midiIndex);
324 if (mapping) { // if mapping exists ...
325 InstrumentManager::instrument_id_t id;
326 id.FileName = mapping->InstrumentFile;
327 id.Index = mapping->InstrumentIndex;
328 //TODO: we should switch the engine type here
329 InstrumentManager::LoadInstrumentInBackground(id, this);
330 Volume(mapping->Volume);
331 }
332 }
333
334 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC