/[svn]/linuxsampler/trunk/src/drivers/midi/MidiInstrumentMapper.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/drivers/midi/MidiInstrumentMapper.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1924 - (hide annotations) (download)
Sun Jun 28 16:43:38 2009 UTC (14 years, 9 months ago) by persson
File size: 19286 byte(s)
* made program change handling in MIDI thread real-time safe by moving
  the logic to a non-RT thread

1 schoenebeck 947 /***************************************************************************
2     * *
3 persson 1924 * Copyright (C) 2006 - 2009 Christian Schoenebeck *
4 schoenebeck 947 * *
5     * This library is free software; you can redistribute it and/or modify *
6     * it under the terms of the GNU General Public License as published by *
7     * the Free Software Foundation; either version 2 of the License, or *
8     * (at your option) any later version. *
9     * *
10     * This library is distributed in the hope that it will be useful, *
11     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13     * GNU General Public License for more details. *
14     * *
15     * You should have received a copy of the GNU General Public License *
16     * along with this library; if not, write to the Free Software *
17     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
18     * MA 02111-1307 USA *
19     ***************************************************************************/
20    
21     #include "MidiInstrumentMapper.h"
22    
23 schoenebeck 1424 #include "../../common/global_private.h"
24 schoenebeck 947 #include "../../common/Mutex.h"
25     #include "../../engines/EngineFactory.h"
26     #include "../../engines/Engine.h"
27    
28 iliev 1827 #include <RIFF.h>
29    
30 schoenebeck 947 namespace LinuxSampler {
31    
32     // same as entry_t but without 'LoadMode'
33     struct private_entry_t {
34     String EngineName;
35     String InstrumentFile;
36     uint InstrumentIndex;
37 schoenebeck 958 float Volume;
38 schoenebeck 947 String Name;
39     };
40    
41 schoenebeck 973 // internal map type (MIDI bank&prog) -> (Engine,File,Index)
42     class MidiInstrumentMap : public std::map<midi_prog_index_t,private_entry_t> {
43     public:
44     String name;
45     };
46 schoenebeck 947
47 schoenebeck 973 // here we store all maps
48     std::map<int,MidiInstrumentMap> midiMaps;
49 schoenebeck 947
50 schoenebeck 973 // for synchronization of midiMaps
51     Mutex midiMapsMutex;
52 schoenebeck 947
53 iliev 1130 ListenerList<MidiInstrumentCountListener*> MidiInstrumentMapper::llMidiInstrumentCountListeners;
54     ListenerList<MidiInstrumentInfoListener*> MidiInstrumentMapper::llMidiInstrumentInfoListeners;
55     ListenerList<MidiInstrumentMapCountListener*> MidiInstrumentMapper::llMidiInstrumentMapCountListeners;
56     ListenerList<MidiInstrumentMapInfoListener*> MidiInstrumentMapper::llMidiInstrumentMapInfoListeners;
57 iliev 1135 int MidiInstrumentMapper::DefaultMap;
58 iliev 1130
59     void MidiInstrumentMapper::AddMidiInstrumentCountListener(MidiInstrumentCountListener* l) {
60     llMidiInstrumentCountListeners.AddListener(l);
61     }
62    
63     void MidiInstrumentMapper::RemoveMidiInstrumentCountListener(MidiInstrumentCountListener* l) {
64     llMidiInstrumentCountListeners.RemoveListener(l);
65     }
66    
67     void MidiInstrumentMapper::fireMidiInstrumentCountChanged(int MapId, int NewCount) {
68     for (int i = 0; i < llMidiInstrumentCountListeners.GetListenerCount(); i++) {
69     llMidiInstrumentCountListeners.GetListener(i)->MidiInstrumentCountChanged(MapId, NewCount);
70     }
71     }
72    
73     void MidiInstrumentMapper::AddMidiInstrumentInfoListener(MidiInstrumentInfoListener* l) {
74     llMidiInstrumentInfoListeners.AddListener(l);
75     }
76    
77     void MidiInstrumentMapper::RemoveMidiInstrumentInfoListener(MidiInstrumentInfoListener* l) {
78     llMidiInstrumentInfoListeners.RemoveListener(l);
79     }
80    
81     void MidiInstrumentMapper::fireMidiInstrumentInfoChanged(int MapId, int Bank, int Program) {
82     for (int i = 0; i < llMidiInstrumentInfoListeners.GetListenerCount(); i++) {
83     llMidiInstrumentInfoListeners.GetListener(i)->MidiInstrumentInfoChanged(MapId, Bank, Program);
84     }
85     }
86    
87     void MidiInstrumentMapper::AddMidiInstrumentMapCountListener(MidiInstrumentMapCountListener* l) {
88     llMidiInstrumentMapCountListeners.AddListener(l);
89     }
90    
91     void MidiInstrumentMapper::RemoveMidiInstrumentMapCountListener(MidiInstrumentMapCountListener* l) {
92     llMidiInstrumentMapCountListeners.RemoveListener(l);
93     }
94    
95     void MidiInstrumentMapper::fireMidiInstrumentMapCountChanged(int NewCount) {
96     for (int i = 0; i < llMidiInstrumentMapCountListeners.GetListenerCount(); i++) {
97     llMidiInstrumentMapCountListeners.GetListener(i)->MidiInstrumentMapCountChanged(NewCount);
98     }
99     }
100    
101     void MidiInstrumentMapper::AddMidiInstrumentMapInfoListener(MidiInstrumentMapInfoListener* l) {
102     llMidiInstrumentMapInfoListeners.AddListener(l);
103     }
104    
105     void MidiInstrumentMapper::RemoveMidiInstrumentMapInfoListener(MidiInstrumentMapInfoListener* l) {
106     llMidiInstrumentMapInfoListeners.RemoveListener(l);
107     }
108    
109     void MidiInstrumentMapper::fireMidiInstrumentMapInfoChanged(int MapId) {
110     for (int i = 0; i < llMidiInstrumentMapInfoListeners.GetListenerCount(); i++) {
111     llMidiInstrumentMapInfoListeners.GetListener(i)->MidiInstrumentMapInfoChanged(MapId);
112     }
113     }
114    
115 schoenebeck 973 void MidiInstrumentMapper::AddOrReplaceEntry(int Map, midi_prog_index_t Index, entry_t Entry, bool bInBackground) throw (Exception) {
116 schoenebeck 947 if (bInBackground) {
117 schoenebeck 973 dmsg(3,("MidiInstrumentMapper: updating mapping %d (%d,%d,%d) -> ('%s','%s',%d) with vol=%f, mode=%d in background\n",
118     Map,
119 schoenebeck 947 Index.midi_bank_msb,Index.midi_bank_lsb,Index.midi_prog,
120     Entry.EngineName.c_str(),Entry.InstrumentFile.c_str(),Entry.InstrumentIndex,
121     Entry.Volume,Entry.LoadMode)
122     );
123     } else {
124 schoenebeck 973 dmsg(3,("MidiInstrumentMapper: updating mapping %d (%d,%d,%d) -> ('%s','%s',%d) with vol=%f, mode=%d\n",
125     Map,
126 schoenebeck 947 Index.midi_bank_msb,Index.midi_bank_lsb,Index.midi_prog,
127     Entry.EngineName.c_str(),Entry.InstrumentFile.c_str(),Entry.InstrumentIndex,
128     Entry.Volume,Entry.LoadMode)
129     );
130     }
131 schoenebeck 973 midiMapsMutex.Lock();
132     if (midiMaps.empty()) {
133     midiMapsMutex.Unlock();
134     throw Exception("There is no MIDI instrument map, you have to add one first.");
135     }
136     midiMapsMutex.Unlock();
137 schoenebeck 970 if (!Entry.InstrumentFile.size())
138     throw Exception("No instrument file name given");
139 schoenebeck 1040 // TODO: an easy one - we should check here if given file exists and throw an exception if it doesn't
140 schoenebeck 947 if (Entry.Volume < 0.0)
141     throw Exception("Volume may not be a negative value");
142     Engine* pEngine = EngineFactory::Create(Entry.EngineName);
143     if (!pEngine)
144     throw Exception("No such engine type '"+Entry.EngineName+"'");
145     Entry.EngineName = pEngine->EngineName(); // make sure to use the official engine name
146     if (pEngine->GetInstrumentManager()) {
147     InstrumentManager::instrument_id_t id;
148     id.FileName = Entry.InstrumentFile;
149     id.Index = Entry.InstrumentIndex;
150 senoner 1481 if (Entry.LoadMode != DONTCARE) {
151 schoenebeck 947 if (bInBackground)
152     pEngine->GetInstrumentManager()->SetModeInBackground(id, static_cast<InstrumentManager::mode_t>(Entry.LoadMode));
153     else
154 iliev 1827 try { pEngine->GetInstrumentManager()->SetMode(id, static_cast<InstrumentManager::mode_t>(Entry.LoadMode)); }
155     catch (RIFF::Exception e) { throw Exception(e.Message); }
156 schoenebeck 947 }
157     } else {
158     dmsg(1,("WARNING: no InstrumentManager for engine '%s'\n",Entry.EngineName.c_str()));
159     }
160     private_entry_t privateEntry;
161     privateEntry.EngineName = Entry.EngineName;
162     privateEntry.InstrumentFile = Entry.InstrumentFile;
163     privateEntry.InstrumentIndex = Entry.InstrumentIndex;
164 schoenebeck 958 privateEntry.Volume = Entry.Volume;
165 schoenebeck 947 privateEntry.Name = Entry.Name;
166 iliev 981
167     bool Replaced = false;
168     int InstrCount = 0;
169    
170 schoenebeck 973 midiMapsMutex.Lock();
171     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
172     if (iterMap != midiMaps.end()) { // map found
173 iliev 981 Replaced = (iterMap->second.find(Index) != iterMap->second.end());
174 schoenebeck 973 iterMap->second[Index] = privateEntry;
175 iliev 981 InstrCount = iterMap->second.size();
176 schoenebeck 973 } else { // no such map
177     midiMapsMutex.Unlock();
178     EngineFactory::Destroy(pEngine);
179     throw Exception("There is no MIDI instrument map " + ToString(Map));
180     }
181     midiMapsMutex.Unlock();
182 schoenebeck 947 EngineFactory::Destroy(pEngine);
183 iliev 981
184     if (Replaced) {
185 iliev 1754 int Bank = (int(Index.midi_bank_msb) << 7) | int(Index.midi_bank_lsb);
186 iliev 1130 fireMidiInstrumentInfoChanged(Map, Bank, Index.midi_prog);
187 iliev 981 } else {
188 iliev 1130 fireMidiInstrumentCountChanged(Map, InstrCount);
189 iliev 981 }
190 schoenebeck 947 }
191    
192 iliev 1763 void MidiInstrumentMapper::SetLoadMode(entry_t* pEntry) {
193     Engine* pEngine = EngineFactory::Create(pEntry->EngineName);
194     if (!pEngine) { // invalid mapping
195     throw Exception("Invalid mapping");
196     }
197    
198     InstrumentManager* pManager = pEngine->GetInstrumentManager();
199     if (pManager) { // engine provides an InstrumentManager
200     InstrumentManager::instrument_id_t id;
201     id.FileName = pEntry->InstrumentFile;
202     id.Index = pEntry->InstrumentIndex;
203     pEntry->LoadMode = static_cast<mode_t>(pManager->GetMode(id));
204     } else { // engine does not provide an InstrumentManager
205     // use default value
206     pEntry->LoadMode = ON_DEMAND;
207     }
208    
209     EngineFactory::Destroy(pEngine);
210     }
211    
212     MidiInstrumentMapper::entry_t MidiInstrumentMapper::GetEntry(int Map, uint MidiBank, uint MidiProg) {
213     midiMapsMutex.Lock();
214     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
215     if (iterMap == midiMaps.end()) { // no such map
216     midiMapsMutex.Unlock();
217     throw Exception("There is no MIDI instrument map " + ToString(Map));
218     }
219    
220     midi_prog_index_t idx;
221     idx.midi_bank_msb = (MidiBank >> 7) & 0x7f;
222     idx.midi_bank_lsb = MidiBank & 0x7f;
223     idx.midi_prog = MidiProg;
224    
225     std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.find(idx);
226     if (iterEntry == iterMap->second.end()) {
227     midiMapsMutex.Unlock();
228     throw Exception("There is no map entry with that index");
229     }
230    
231     entry_t entry;
232     entry.EngineName = iterEntry->second.EngineName;
233     entry.InstrumentFile = iterEntry->second.InstrumentFile;
234     entry.InstrumentIndex = iterEntry->second.InstrumentIndex;
235     entry.Volume = iterEntry->second.Volume;
236     entry.Name = iterEntry->second.Name;
237    
238     try {
239     SetLoadMode(&entry);
240     } catch(Exception e) {
241     midiMapsMutex.Unlock();
242     throw e;
243     }
244    
245     midiMapsMutex.Unlock();
246    
247     return entry;
248     }
249    
250 schoenebeck 973 void MidiInstrumentMapper::RemoveEntry(int Map, midi_prog_index_t Index) {
251 iliev 981 int InstrCount = -1;
252    
253 schoenebeck 973 midiMapsMutex.Lock();
254     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
255     if (iterMap != midiMaps.end()) { // map found
256     iterMap->second.erase(Index); // remove entry
257 iliev 981 InstrCount = iterMap->second.size();
258 schoenebeck 973 }
259     midiMapsMutex.Unlock();
260 iliev 981
261     if (InstrCount != -1) {
262 iliev 1130 fireMidiInstrumentCountChanged(Map, InstrCount);
263 iliev 981 }
264 schoenebeck 947 }
265    
266 schoenebeck 973 void MidiInstrumentMapper::RemoveAllEntries(int Map) {
267 iliev 981 int InstrCount = -1;
268    
269 schoenebeck 973 midiMapsMutex.Lock();
270     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
271     if (iterMap != midiMaps.end()) { // map found
272     iterMap->second.clear(); // clear that map
273 iliev 981 InstrCount = 0;
274 schoenebeck 973 }
275     midiMapsMutex.Unlock();
276 iliev 981
277     if (InstrCount != -1) {
278 iliev 1130 fireMidiInstrumentCountChanged(Map, InstrCount);
279 iliev 981 }
280 schoenebeck 947 }
281    
282 schoenebeck 973 std::map<midi_prog_index_t,MidiInstrumentMapper::entry_t> MidiInstrumentMapper::Entries(int Map) throw (Exception) {
283 schoenebeck 947 std::map<midi_prog_index_t,entry_t> result;
284 schoenebeck 973
285 schoenebeck 947 // copy the internal map first
286 schoenebeck 973 midiMapsMutex.Lock();
287     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
288     if (iterMap == midiMaps.end()) { // no such map
289     midiMapsMutex.Unlock();
290     throw Exception("There is no MIDI instrument map " + ToString(Map));
291     }
292     for (std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.begin();
293     iterEntry != iterMap->second.end(); iterEntry++)
294 schoenebeck 947 {
295     entry_t entry;
296 schoenebeck 973 entry.EngineName = iterEntry->second.EngineName;
297     entry.InstrumentFile = iterEntry->second.InstrumentFile;
298     entry.InstrumentIndex = iterEntry->second.InstrumentIndex;
299     entry.Volume = iterEntry->second.Volume;
300     entry.Name = iterEntry->second.Name;
301     result[iterEntry->first] = entry;
302 schoenebeck 947 }
303 schoenebeck 973 midiMapsMutex.Unlock();
304    
305 schoenebeck 958 // complete it with current LoadMode of each entry
306 schoenebeck 947 for (std::map<midi_prog_index_t,entry_t>::iterator iter = result.begin();
307     iter != result.end(); iter++)
308     {
309 iliev 1763 try {
310     SetLoadMode(&(iter->second));
311     } catch(Exception e) {
312 schoenebeck 973 RemoveEntry(Map, iter->first);
313 schoenebeck 947 result.erase(iter);
314     }
315     }
316     return result;
317     }
318    
319 schoenebeck 973 std::vector<int> MidiInstrumentMapper::Maps() {
320     std::vector<int> result;
321     midiMapsMutex.Lock();
322     for (std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.begin();
323     iterMap != midiMaps.end(); iterMap++)
324     {
325     result.push_back(iterMap->first);
326     }
327     midiMapsMutex.Unlock();
328     return result;
329     }
330    
331 iliev 1135 int MidiInstrumentMapper::GetMapCount() {
332     midiMapsMutex.Lock();
333     int i = midiMaps.size();
334     midiMapsMutex.Unlock();
335     return i;
336     }
337    
338 iliev 1763 int MidiInstrumentMapper::GetInstrumentCount(int Map) {
339     midiMapsMutex.Lock();
340     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
341     if (iterMap == midiMaps.end()) { // no such map
342     midiMapsMutex.Unlock();
343     throw Exception("There is no MIDI instrument map " + ToString(Map));
344     }
345    
346     int i = iterMap->second.size();
347     midiMapsMutex.Unlock();
348     return i;
349     }
350    
351     int MidiInstrumentMapper::GetInstrumentCount() {
352     int count = 0;
353    
354     midiMapsMutex.Lock();
355     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.begin();
356     for (;iterMap != midiMaps.end(); iterMap++) {
357     count += iterMap->second.size();
358     }
359     midiMapsMutex.Unlock();
360    
361     return count;
362     }
363    
364 schoenebeck 973 int MidiInstrumentMapper::AddMap(String MapName) throw (Exception) {
365     int ID;
366     midiMapsMutex.Lock();
367     if (midiMaps.empty()) ID = 0;
368     else {
369     // get the highest existing map ID
370     uint lastIndex = (--(midiMaps.end()))->first;
371     // check if we reached the index limit
372     if (lastIndex + 1 < lastIndex) {
373     // search for an unoccupied map ID starting from 0
374     for (uint i = 0; i < lastIndex; i++) {
375     if (midiMaps.find(i) != midiMaps.end()) continue;
376     // we found an unused ID, so insert the new map there
377     ID = i;
378     goto __create_map;
379     }
380     throw Exception("Internal error: could not find unoccupied MIDI instrument map ID.");
381     }
382 iliev 976 ID = lastIndex + 1;
383 schoenebeck 973 }
384     __create_map:
385     midiMaps[ID].name = MapName;
386 iliev 1135
387     fireMidiInstrumentMapCountChanged(Maps().size());
388     // If there were no maps until now we must set a default map.
389     if (midiMaps.size() == 1) SetDefaultMap(ID);
390 schoenebeck 973 midiMapsMutex.Unlock();
391 iliev 1135
392 schoenebeck 973 return ID;
393     }
394    
395     String MidiInstrumentMapper::MapName(int Map) throw (Exception) {
396     String result;
397     midiMapsMutex.Lock();
398     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
399     if (iterMap == midiMaps.end()) {
400     midiMapsMutex.Unlock();
401     throw Exception("There is no MIDI instrument map " + ToString(Map));
402     }
403     result = iterMap->second.name;
404     midiMapsMutex.Unlock();
405     return result;
406     }
407    
408     void MidiInstrumentMapper::RenameMap(int Map, String NewName) throw (Exception) {
409     midiMapsMutex.Lock();
410     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
411     if (iterMap == midiMaps.end()) {
412     midiMapsMutex.Unlock();
413     throw Exception("There is no MIDI instrument map " + ToString(Map));
414     }
415     iterMap->second.name = NewName;
416     midiMapsMutex.Unlock();
417 iliev 1130 fireMidiInstrumentMapInfoChanged(Map);
418 schoenebeck 973 }
419    
420     void MidiInstrumentMapper::RemoveMap(int Map) {
421     midiMapsMutex.Lock();
422     midiMaps.erase(Map);
423 iliev 1135 if(Map == GetDefaultMap()) {
424     SetDefaultMap(midiMaps.empty() ? -1 : (*(midiMaps.begin())).first);
425     }
426     fireMidiInstrumentMapCountChanged(Maps().size());
427 schoenebeck 973 midiMapsMutex.Unlock();
428     }
429    
430     void MidiInstrumentMapper::RemoveAllMaps() {
431     midiMapsMutex.Lock();
432     midiMaps.clear();
433 iliev 1135 SetDefaultMap(-1);
434     fireMidiInstrumentMapCountChanged(Maps().size());
435 schoenebeck 973 midiMapsMutex.Unlock();
436     }
437    
438 iliev 1135 int MidiInstrumentMapper::GetDefaultMap() {
439     midiMapsMutex.Lock();
440     int i = DefaultMap;
441     midiMapsMutex.Unlock();
442     return i;
443     }
444    
445     void MidiInstrumentMapper::SetDefaultMap(int MapId) {
446     midiMapsMutex.Lock();
447     DefaultMap = MapId;
448     midiMapsMutex.Unlock();
449    
450     if (MapId != -1) fireMidiInstrumentMapInfoChanged(MapId);
451     }
452    
453 schoenebeck 973 optional<MidiInstrumentMapper::entry_t> MidiInstrumentMapper::GetEntry(int Map, midi_prog_index_t Index) {
454 schoenebeck 947 optional<entry_t> result;
455 schoenebeck 973 midiMapsMutex.Lock();
456     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
457     if (iterMap != midiMaps.end()) { // map found
458     std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.find(Index);
459     if (iterEntry != iterMap->second.end()) {
460     entry_t entry;
461     entry.EngineName = iterEntry->second.EngineName;
462     entry.InstrumentFile = iterEntry->second.InstrumentFile;
463     entry.InstrumentIndex = iterEntry->second.InstrumentIndex;
464     entry.Volume = iterEntry->second.Volume;
465 persson 1924 //TODO: for now we skip the LoadMode and Name entry here, since we don't need it in the EngineChannel
466 schoenebeck 973 result = entry;
467     }
468 schoenebeck 947 }
469 schoenebeck 973 midiMapsMutex.Unlock();
470 schoenebeck 947 return result;
471     }
472    
473     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC