/[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 2427 - (hide annotations) (download)
Sat Mar 2 07:03:04 2013 UTC (11 years, 1 month ago) by persson
File size: 18792 byte(s)
* code refactoring: added a lock guard class for exception safe mutex
  handling and used it everywhere appropriate

1 schoenebeck 947 /***************************************************************************
2     * *
3 persson 2427 * Copyright (C) 2006 - 2013 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 persson 2427 {
132     LockGuard lock(midiMapsMutex);
133     if (midiMaps.empty()) {
134     throw Exception("There is no MIDI instrument map, you have to add one first.");
135     }
136 schoenebeck 973 }
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 persson 2427 bool MapFound = false;
170     {
171     LockGuard lock(midiMapsMutex);
172     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
173     if (iterMap != midiMaps.end()) { // map found
174     MapFound = true;
175     Replaced = (iterMap->second.find(Index) != iterMap->second.end());
176     iterMap->second[Index] = privateEntry;
177     InstrCount = iterMap->second.size();
178     }
179     }
180     EngineFactory::Destroy(pEngine);
181     if (!MapFound) {
182 schoenebeck 973 throw Exception("There is no MIDI instrument map " + ToString(Map));
183     }
184 persson 2427
185 iliev 981 if (Replaced) {
186 iliev 1754 int Bank = (int(Index.midi_bank_msb) << 7) | int(Index.midi_bank_lsb);
187 iliev 1130 fireMidiInstrumentInfoChanged(Map, Bank, Index.midi_prog);
188 iliev 981 } else {
189 iliev 1130 fireMidiInstrumentCountChanged(Map, InstrCount);
190 iliev 981 }
191 schoenebeck 947 }
192    
193 iliev 1763 void MidiInstrumentMapper::SetLoadMode(entry_t* pEntry) {
194     Engine* pEngine = EngineFactory::Create(pEntry->EngineName);
195     if (!pEngine) { // invalid mapping
196     throw Exception("Invalid mapping");
197     }
198    
199     InstrumentManager* pManager = pEngine->GetInstrumentManager();
200     if (pManager) { // engine provides an InstrumentManager
201     InstrumentManager::instrument_id_t id;
202     id.FileName = pEntry->InstrumentFile;
203     id.Index = pEntry->InstrumentIndex;
204     pEntry->LoadMode = static_cast<mode_t>(pManager->GetMode(id));
205     } else { // engine does not provide an InstrumentManager
206     // use default value
207     pEntry->LoadMode = ON_DEMAND;
208     }
209    
210     EngineFactory::Destroy(pEngine);
211     }
212    
213     MidiInstrumentMapper::entry_t MidiInstrumentMapper::GetEntry(int Map, uint MidiBank, uint MidiProg) {
214 persson 2427 LockGuard lock(midiMapsMutex);
215    
216 iliev 1763 std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
217     if (iterMap == midiMaps.end()) { // no such map
218     throw Exception("There is no MIDI instrument map " + ToString(Map));
219     }
220    
221     midi_prog_index_t idx;
222     idx.midi_bank_msb = (MidiBank >> 7) & 0x7f;
223     idx.midi_bank_lsb = MidiBank & 0x7f;
224     idx.midi_prog = MidiProg;
225    
226     std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.find(idx);
227     if (iterEntry == iterMap->second.end()) {
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     throw e;
242     }
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 persson 2427 {
250     LockGuard lock(midiMapsMutex);
251 iliev 981
252 persson 2427 std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
253     if (iterMap != midiMaps.end()) { // map found
254     iterMap->second.erase(Index); // remove entry
255     InstrCount = iterMap->second.size();
256     }
257 schoenebeck 973 }
258 iliev 981
259     if (InstrCount != -1) {
260 iliev 1130 fireMidiInstrumentCountChanged(Map, InstrCount);
261 iliev 981 }
262 schoenebeck 947 }
263    
264 schoenebeck 973 void MidiInstrumentMapper::RemoveAllEntries(int Map) {
265 iliev 981 int InstrCount = -1;
266 persson 2427 {
267     LockGuard lock(midiMapsMutex);
268 iliev 981
269 persson 2427 std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
270     if (iterMap != midiMaps.end()) { // map found
271     iterMap->second.clear(); // clear that map
272     InstrCount = 0;
273     }
274 schoenebeck 973 }
275 iliev 981
276     if (InstrCount != -1) {
277 iliev 1130 fireMidiInstrumentCountChanged(Map, InstrCount);
278 iliev 981 }
279 schoenebeck 947 }
280    
281 schoenebeck 973 std::map<midi_prog_index_t,MidiInstrumentMapper::entry_t> MidiInstrumentMapper::Entries(int Map) throw (Exception) {
282 schoenebeck 947 std::map<midi_prog_index_t,entry_t> result;
283 schoenebeck 973
284 schoenebeck 947 // copy the internal map first
285     {
286 persson 2427 LockGuard lock(midiMapsMutex);
287    
288     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
289     if (iterMap == midiMaps.end()) { // no such map
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     {
295     entry_t entry;
296     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     }
303 schoenebeck 947 }
304 schoenebeck 973
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 persson 2427 LockGuard lock(midiMapsMutex);
322 schoenebeck 973 for (std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.begin();
323     iterMap != midiMaps.end(); iterMap++)
324     {
325     result.push_back(iterMap->first);
326     }
327     return result;
328     }
329    
330 iliev 1135 int MidiInstrumentMapper::GetMapCount() {
331 persson 2427 LockGuard lock(midiMapsMutex);
332     return midiMaps.size();
333 iliev 1135 }
334    
335 iliev 1763 int MidiInstrumentMapper::GetInstrumentCount(int Map) {
336 persson 2427 LockGuard lock(midiMapsMutex);
337 iliev 1763 std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
338     if (iterMap == midiMaps.end()) { // no such map
339     throw Exception("There is no MIDI instrument map " + ToString(Map));
340     }
341    
342 persson 2427 return iterMap->second.size();
343 iliev 1763 }
344    
345     int MidiInstrumentMapper::GetInstrumentCount() {
346     int count = 0;
347    
348 persson 2427 LockGuard lock(midiMapsMutex);
349 iliev 1763 std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.begin();
350     for (;iterMap != midiMaps.end(); iterMap++) {
351     count += iterMap->second.size();
352     }
353    
354     return count;
355     }
356    
357 schoenebeck 973 int MidiInstrumentMapper::AddMap(String MapName) throw (Exception) {
358     int ID;
359 persson 2427 LockGuard lock(midiMapsMutex);
360    
361 schoenebeck 973 if (midiMaps.empty()) ID = 0;
362     else {
363     // get the highest existing map ID
364     uint lastIndex = (--(midiMaps.end()))->first;
365     // check if we reached the index limit
366     if (lastIndex + 1 < lastIndex) {
367     // search for an unoccupied map ID starting from 0
368     for (uint i = 0; i < lastIndex; i++) {
369     if (midiMaps.find(i) != midiMaps.end()) continue;
370     // we found an unused ID, so insert the new map there
371     ID = i;
372     goto __create_map;
373     }
374     throw Exception("Internal error: could not find unoccupied MIDI instrument map ID.");
375     }
376 iliev 976 ID = lastIndex + 1;
377 schoenebeck 973 }
378     __create_map:
379     midiMaps[ID].name = MapName;
380 iliev 1135
381     fireMidiInstrumentMapCountChanged(Maps().size());
382     // If there were no maps until now we must set a default map.
383     if (midiMaps.size() == 1) SetDefaultMap(ID);
384    
385 schoenebeck 973 return ID;
386     }
387    
388     String MidiInstrumentMapper::MapName(int Map) throw (Exception) {
389 persson 2427 LockGuard lock(midiMapsMutex);
390 schoenebeck 973 std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
391     if (iterMap == midiMaps.end()) {
392     throw Exception("There is no MIDI instrument map " + ToString(Map));
393     }
394 persson 2427 return iterMap->second.name;
395 schoenebeck 973 }
396    
397     void MidiInstrumentMapper::RenameMap(int Map, String NewName) throw (Exception) {
398 persson 2427 {
399     LockGuard lock(midiMapsMutex);
400     std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
401     if (iterMap == midiMaps.end()) {
402     throw Exception("There is no MIDI instrument map " + ToString(Map));
403     }
404     iterMap->second.name = NewName;
405 schoenebeck 973 }
406 iliev 1130 fireMidiInstrumentMapInfoChanged(Map);
407 schoenebeck 973 }
408    
409     void MidiInstrumentMapper::RemoveMap(int Map) {
410 persson 2427 LockGuard lock(midiMapsMutex);
411    
412 schoenebeck 973 midiMaps.erase(Map);
413 persson 2427 if (Map == GetDefaultMap()) {
414 iliev 1135 SetDefaultMap(midiMaps.empty() ? -1 : (*(midiMaps.begin())).first);
415     }
416     fireMidiInstrumentMapCountChanged(Maps().size());
417 schoenebeck 973 }
418    
419     void MidiInstrumentMapper::RemoveAllMaps() {
420 persson 2427 LockGuard lock(midiMapsMutex);
421    
422 schoenebeck 973 midiMaps.clear();
423 iliev 1135 SetDefaultMap(-1);
424     fireMidiInstrumentMapCountChanged(Maps().size());
425 schoenebeck 973 }
426    
427 iliev 1135 int MidiInstrumentMapper::GetDefaultMap() {
428 persson 2427 LockGuard lock(midiMapsMutex);
429    
430     return DefaultMap;
431 iliev 1135 }
432    
433     void MidiInstrumentMapper::SetDefaultMap(int MapId) {
434 persson 2427 {
435     LockGuard lock(midiMapsMutex);
436    
437     DefaultMap = MapId;
438     }
439 iliev 1135
440     if (MapId != -1) fireMidiInstrumentMapInfoChanged(MapId);
441     }
442    
443 schoenebeck 973 optional<MidiInstrumentMapper::entry_t> MidiInstrumentMapper::GetEntry(int Map, midi_prog_index_t Index) {
444 schoenebeck 947 optional<entry_t> result;
445 persson 2427 LockGuard lock(midiMapsMutex);
446    
447 schoenebeck 973 std::map<int,MidiInstrumentMap>::iterator iterMap = midiMaps.find(Map);
448     if (iterMap != midiMaps.end()) { // map found
449     std::map<midi_prog_index_t,private_entry_t>::iterator iterEntry = iterMap->second.find(Index);
450     if (iterEntry != iterMap->second.end()) {
451     entry_t entry;
452     entry.EngineName = iterEntry->second.EngineName;
453     entry.InstrumentFile = iterEntry->second.InstrumentFile;
454     entry.InstrumentIndex = iterEntry->second.InstrumentIndex;
455     entry.Volume = iterEntry->second.Volume;
456 persson 1924 //TODO: for now we skip the LoadMode and Name entry here, since we don't need it in the EngineChannel
457 schoenebeck 973 result = entry;
458     }
459 schoenebeck 947 }
460     return result;
461     }
462    
463     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC