/[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 1763 - (hide annotations) (download)
Wed Sep 3 17:18:51 2008 UTC (15 years, 6 months ago) by iliev
File size: 19181 byte(s)
* Optimized the retrieval of the MIDI instrument mappings

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

  ViewVC Help
Powered by ViewVC