1 |
/*************************************************************************** |
2 |
* * |
3 |
* LinuxSampler - modular, streaming capable sampler * |
4 |
* * |
5 |
* Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005 - 2009 Christian Schoenebeck * |
7 |
* Copyright (C) 2009 Grigor Iliev * |
8 |
* * |
9 |
* This program is free software; you can redistribute it and/or modify * |
10 |
* it under the terms of the GNU General Public License as published by * |
11 |
* the Free Software Foundation; either version 2 of the License, or * |
12 |
* (at your option) any later version. * |
13 |
* * |
14 |
* This program is distributed in the hope that it will be useful, * |
15 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of * |
16 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
17 |
* GNU General Public License for more details. * |
18 |
* * |
19 |
* You should have received a copy of the GNU General Public License * |
20 |
* along with this program; if not, write to the Free Software * |
21 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * |
22 |
* MA 02111-1307 USA * |
23 |
***************************************************************************/ |
24 |
|
25 |
#include <sstream> |
26 |
|
27 |
#include "InstrumentResourceManager.h" |
28 |
#include "EngineChannel.h" |
29 |
#include "Engine.h" |
30 |
|
31 |
#include "../../common/global_private.h" |
32 |
#include "../../common/Path.h" |
33 |
#include "../../plugins/InstrumentEditorFactory.h" |
34 |
|
35 |
|
36 |
namespace LinuxSampler { namespace sfz { |
37 |
|
38 |
// data stored as long as an instrument resource exists |
39 |
struct instr_entry_t { |
40 |
InstrumentManager::instrument_id_t ID; |
41 |
::sfz::File* pSfz; |
42 |
uint MaxSamplesPerCycle; ///< if some engine requests an already allocated instrument with a higher value, we have to reallocate the instrument |
43 |
}; |
44 |
|
45 |
// some data needed for the libgig callback function |
46 |
struct progress_callback_arg_t { |
47 |
InstrumentResourceManager* pManager; |
48 |
InstrumentManager::instrument_id_t* pInstrumentKey; |
49 |
}; |
50 |
|
51 |
std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::Instruments() { |
52 |
return Entries(); |
53 |
} |
54 |
|
55 |
String InstrumentResourceManager::GetInstrumentName(instrument_id_t ID) { |
56 |
Lock(); |
57 |
::sfz::Instrument* pInstrument = Resource(ID, false); |
58 |
String res = (pInstrument) ? pInstrument->GetName() : ""; |
59 |
Unlock(); |
60 |
return res; |
61 |
} |
62 |
|
63 |
std::vector<InstrumentResourceManager::instrument_id_t> InstrumentResourceManager::GetInstrumentFileContent(String File) throw (InstrumentManagerException) { |
64 |
std::vector<instrument_id_t> result; |
65 |
instrument_id_t id; |
66 |
id.FileName = File; |
67 |
id.Index = 0; |
68 |
result.push_back(id); |
69 |
return result; |
70 |
} |
71 |
|
72 |
InstrumentResourceManager::instrument_info_t InstrumentResourceManager::GetInstrumentInfo(instrument_id_t ID) throw (InstrumentManagerException) { |
73 |
if (ID.Index) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName); |
74 |
Lock(); |
75 |
::sfz::Instrument* pInstrument = Resource(ID, false); |
76 |
bool loaded = (pInstrument != NULL); |
77 |
if (!loaded) Unlock(); |
78 |
|
79 |
::sfz::File* sfz = NULL; |
80 |
try { |
81 |
if(!loaded) { |
82 |
sfz = new ::sfz::File(ID.FileName); |
83 |
pInstrument = sfz->GetInstrument(); |
84 |
} |
85 |
|
86 |
if (!pInstrument) throw InstrumentManagerException("There is no instrument " + ToString(ID.Index) + " in " + ID.FileName); |
87 |
|
88 |
instrument_info_t info; |
89 |
info.InstrumentName = Path::getBaseName(ID.FileName); |
90 |
|
91 |
for (int i = 0; i < 128; i++) { |
92 |
info.KeyBindings[i] = pInstrument->HasKeyBinding(i); |
93 |
info.KeySwitchBindings[i] = pInstrument->HasKeySwitchBinding(i); |
94 |
} |
95 |
|
96 |
if (loaded) Unlock(); |
97 |
|
98 |
if (sfz) delete sfz; |
99 |
return info; |
100 |
} catch (::sfz::Exception e) { |
101 |
if (loaded) Unlock(); |
102 |
if (sfz) delete sfz; |
103 |
throw InstrumentManagerException(e.Message()); |
104 |
} catch (...) { |
105 |
if (loaded) Unlock(); |
106 |
if (sfz) delete sfz; |
107 |
throw InstrumentManagerException("Unknown exception while trying to parse '" + ID.FileName + "'"); |
108 |
} |
109 |
} |
110 |
|
111 |
::sfz::Instrument* InstrumentResourceManager::Create(instrument_id_t Key, InstrumentConsumer* pConsumer, void*& pArg) { |
112 |
// get sfz file from internal sfz file manager |
113 |
::sfz::File* pSfz = Sfzs.Borrow(Key.FileName, (SfzConsumer*) Key.Index); // conversion kinda hackish :/ |
114 |
|
115 |
dmsg(1,("Loading sfz instrument ('%s',%d)...",Key.FileName.c_str(),Key.Index)); |
116 |
if (Key.Index) { |
117 |
std::stringstream msg; |
118 |
msg << "There's no instrument with index " << Key.Index << "."; |
119 |
throw InstrumentManagerException(msg.str()); |
120 |
} |
121 |
::sfz::Instrument* pInstrument = pSfz->GetInstrument(); |
122 |
if (!pInstrument) { |
123 |
std::stringstream msg; |
124 |
msg << "There's no instrument with index " << Key.Index << "."; |
125 |
throw InstrumentManagerException(msg.str()); |
126 |
} |
127 |
dmsg(1,("OK\n")); |
128 |
|
129 |
// cache initial samples points (for actually needed samples) |
130 |
dmsg(1,("Caching initial samples...")); |
131 |
int regionCount = pInstrument->regions.size(); |
132 |
for(int i = 0 ; i < regionCount; i++) { |
133 |
const float localProgress = (float) i / (float) regionCount; |
134 |
DispatchResourceProgressEvent(Key, localProgress); |
135 |
CacheInitialSamples(pInstrument->regions[i]->GetSample(), dynamic_cast<AbstractEngineChannel*>(pConsumer)); |
136 |
//pInstrument->regions[i]->GetSample()->Close(); |
137 |
} |
138 |
dmsg(1,("OK\n")); |
139 |
DispatchResourceProgressEvent(Key, 1.0f); // done; notify all consumers about progress 100% |
140 |
|
141 |
// we need the following for destruction later |
142 |
instr_entry_t* pEntry = new instr_entry_t; |
143 |
pEntry->ID.FileName = Key.FileName; |
144 |
pEntry->ID.Index = Key.Index; |
145 |
pEntry->pSfz = pSfz; |
146 |
|
147 |
EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(pConsumer); |
148 |
// and we save this to check if we need to reallocate for a engine with higher value of 'MaxSamplesPerSecond' |
149 |
pEntry->MaxSamplesPerCycle = |
150 |
(!pEngineChannel) ? 0 /* don't care for instrument editors */ : |
151 |
(pEngineChannel->GetEngine()) ? |
152 |
dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle() |
153 |
: GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE; |
154 |
pArg = pEntry; |
155 |
|
156 |
return pInstrument; |
157 |
} |
158 |
|
159 |
void InstrumentResourceManager::Destroy( ::sfz::Instrument* pResource, void* pArg) { |
160 |
instr_entry_t* pEntry = (instr_entry_t*) pArg; |
161 |
// we don't need the .sfz file here anymore |
162 |
Sfzs.HandBack(pEntry->pSfz, (SfzConsumer*) pEntry->ID.Index); // conversion kinda hackish :/ |
163 |
delete pEntry; |
164 |
} |
165 |
|
166 |
void InstrumentResourceManager::OnBorrow(::sfz::Instrument* pResource, InstrumentConsumer* pConsumer, void*& pArg) { |
167 |
instr_entry_t* pEntry = (instr_entry_t*) pArg; |
168 |
EngineChannel* pEngineChannel = dynamic_cast<EngineChannel*>(pConsumer); |
169 |
uint maxSamplesPerCycle = |
170 |
(pEngineChannel && pEngineChannel->GetEngine()) ? dynamic_cast<Engine*>(pEngineChannel->GetEngine())->pAudioOutputDevice->MaxSamplesPerCycle() |
171 |
: GIG_RESOURCE_MANAGER_DEFAULT_MAX_SAMPLES_PER_CYCLE; |
172 |
if (pEntry->MaxSamplesPerCycle < maxSamplesPerCycle) { |
173 |
Update(pResource, pConsumer); |
174 |
} |
175 |
} |
176 |
|
177 |
void InstrumentResourceManager::DeleteRegionIfNotUsed(::sfz::Region* pRegion, region_info_t* pRegInfo) { |
178 |
::sfz::File* file = pRegInfo->file; |
179 |
if (file == NULL) return; |
180 |
|
181 |
file->GetInstrument()->DestroyRegion(pRegion); |
182 |
if(file->GetInstrument()->regions.empty()) { |
183 |
dmsg(2,("No more regions in use - freeing sfz\n")); |
184 |
delete file; |
185 |
} |
186 |
} |
187 |
|
188 |
void InstrumentResourceManager::DeleteSampleIfNotUsed(Sample* pSample, region_info_t* pRegInfo) { |
189 |
|
190 |
} |
191 |
|
192 |
|
193 |
|
194 |
// internal sfz file manager |
195 |
|
196 |
::sfz::File* InstrumentResourceManager::SfzResourceManager::Create(String Key, SfzConsumer* pConsumer, void*& pArg) { |
197 |
dmsg(1,("Loading sfz file \'%s\'...", Key.c_str())); |
198 |
::sfz::File* pSfz = new ::sfz::File(Key, &sampleManager); |
199 |
dmsg(1,("OK\n")); |
200 |
return pSfz; |
201 |
} |
202 |
|
203 |
void InstrumentResourceManager::SfzResourceManager::Destroy(::sfz::File* pResource, void* pArg) { |
204 |
dmsg(1,("Freeing sfz file from memory...")); |
205 |
|
206 |
// Delete as much as possible of the sfz file. Some of the |
207 |
// regions and samples may still be in use - these |
208 |
// will be deleted later by the HandBackRegion function. |
209 |
bool deleteInstrument = true; |
210 |
::sfz::Instrument* pInstr = pResource->GetInstrument(); |
211 |
|
212 |
for (int i = pInstr->regions.size() - 1; i >= 0 ; i--) { |
213 |
::sfz::Region* pRegion = pInstr->regions[i]; |
214 |
std::map< ::sfz::Region*, region_info_t>::iterator iter = parent->RegionInfo.find(pRegion); |
215 |
if (iter != parent->RegionInfo.end()) { |
216 |
region_info_t& regInfo = (*iter).second; |
217 |
regInfo.file = pResource; |
218 |
deleteInstrument = false; |
219 |
} else { |
220 |
pInstr->DestroyRegion(pRegion); |
221 |
} |
222 |
} |
223 |
|
224 |
if(deleteInstrument) delete pResource; |
225 |
else dmsg(2,("keeping some samples that are in use...")); |
226 |
|
227 |
dmsg(1,("OK\n")); |
228 |
} |
229 |
|
230 |
}} // namespace LinuxSampler::sfz |