Révision | d291e9cd0ee6b9d18a81c9044aa517ecfd8c01d0 (tree) |
---|---|
l'heure | 2018-07-09 03:20:29 |
Auteur | Starg <starg@user...> |
Commiter | Starg |
Add DLS support
@@ -78,6 +78,7 @@ add_definitions( | ||
78 | 78 | -DUSE_TWSYN_BRIDGE |
79 | 79 | -DENABLE_SFZ |
80 | 80 | -DTIMW32G_USE_NEW_CONSOLE |
81 | + -DENABLE_DLS | |
81 | 82 | |
82 | 83 | # TiMidity++ audio |
83 | 84 | -DAU_W32 |
@@ -8,6 +8,7 @@ add_executable( | ||
8 | 8 | ../timidity/common.c |
9 | 9 | ../timidity/controls.c |
10 | 10 | ../timidity/decode.c |
11 | + ../timidity/dls.cpp | |
11 | 12 | ../timidity/envelope.c |
12 | 13 | ../timidity/filter.c |
13 | 14 | ../timidity/freq.c |
@@ -31,6 +32,7 @@ add_executable( | ||
31 | 32 | ../timidity/common.h |
32 | 33 | ../timidity/controls.h |
33 | 34 | ../timidity/decode.h |
35 | + ../timidity/dls.h | |
34 | 36 | ../timidity/envelope.h |
35 | 37 | ../timidity/filter.h |
36 | 38 | ../timidity/freq.h |
@@ -697,7 +697,19 @@ static void init_trace_window_chan(int ch) | ||
697 | 697 | wprintw(dftwin, "(%s)", prog->comment); |
698 | 698 | } |
699 | 699 | #endif |
700 | - } | |
700 | +#ifdef ENABLE_DLS | |
701 | + else if (type == INST_DLS) | |
702 | + { | |
703 | + if (prog->name) | |
704 | + { | |
705 | + waddch(dftwin, ' '); | |
706 | + waddstr(dftwin, prog->name); | |
707 | + } | |
708 | + if (prog->comment != NULL) | |
709 | + wprintw(dftwin, "(%s)", prog->comment); | |
710 | + } | |
711 | +#endif | |
712 | + } | |
701 | 713 | } |
702 | 714 | } |
703 | 715 | } |
@@ -65,6 +65,7 @@ | ||
65 | 65 | #include "resample.h" |
66 | 66 | #include "mix.h" |
67 | 67 | #include "thread.h" |
68 | +#include "dls.h" | |
68 | 69 | #include "sfz.h" |
69 | 70 | |
70 | 71 | #include <tchar.h> |
@@ -712,6 +713,9 @@ void PrefSettingApplyReally(void) | ||
712 | 713 | #ifdef INT_SYNTH |
713 | 714 | init_int_synth(); |
714 | 715 | #endif // INT_SYNTH |
716 | +#ifdef ENABLE_DLS | |
717 | + init_dls(); | |
718 | +#endif | |
715 | 719 | #ifdef ENABLE_SFZ |
716 | 720 | init_sfz(); |
717 | 721 | #endif |
@@ -794,6 +798,9 @@ static void PrefSettingApply(void) | ||
794 | 798 | #ifdef INT_SYNTH |
795 | 799 | init_int_synth(); |
796 | 800 | #endif // INT_SYNTH |
801 | +#ifdef ENABLE_DLS | |
802 | + init_dls(); | |
803 | +#endif | |
797 | 804 | #ifdef ENABLE_SFZ |
798 | 805 | init_sfz(); |
799 | 806 | #endif |
@@ -843,6 +850,9 @@ void reload_cfg(void) | ||
843 | 850 | #ifdef ENABLE_SFZ |
844 | 851 | free_sfz(); |
845 | 852 | #endif |
853 | +#ifdef ENABLE_DLS | |
854 | + free_dls(); | |
855 | +#endif | |
846 | 856 | #ifdef INT_SYNTH |
847 | 857 | free_int_synth(); |
848 | 858 | #endif // INT_SYNTH |
@@ -14,6 +14,7 @@ add_executable( | ||
14 | 14 | ../timidity/common.c |
15 | 15 | ../timidity/controls.c |
16 | 16 | ../timidity/decode.c |
17 | + ../timidity/dls.cpp | |
17 | 18 | ../timidity/envelope.c |
18 | 19 | ../timidity/filter.c |
19 | 20 | ../timidity/freq.c |
@@ -40,6 +41,7 @@ add_executable( | ||
40 | 41 | ../timidity/common.h |
41 | 42 | ../timidity/controls.h |
42 | 43 | ../timidity/decode.h |
44 | + ../timidity/dls.h | |
43 | 45 | ../timidity/envelope.h |
44 | 46 | ../timidity/filter.h |
45 | 47 | ../timidity/instrum.h |
@@ -67,6 +67,7 @@ add_library( | ||
67 | 67 | audio_cnv.c |
68 | 68 | common.c |
69 | 69 | decode.c |
70 | + dls.cpp | |
70 | 71 | effect.c |
71 | 72 | envelope.c |
72 | 73 | filter.c |
@@ -126,6 +127,7 @@ add_library( | ||
126 | 127 | audriv.h |
127 | 128 | common.h |
128 | 129 | decode.h |
130 | + dls.h | |
129 | 131 | dlutils.h |
130 | 132 | filter.h |
131 | 133 | freq.h |
@@ -0,0 +1,1606 @@ | ||
1 | +// DLS Support Routines for TiMidity++ | |
2 | +// Copyright (c) 2018 Starg <https://osdn.net/projects/timidity41> | |
3 | + | |
4 | +extern "C" | |
5 | +{ | |
6 | +#ifdef HAVE_CONFIG_H | |
7 | +#include "config.h" | |
8 | +#endif | |
9 | +#include "timidity.h" | |
10 | +#include "common.h" | |
11 | +#include "controls.h" | |
12 | +#include "output.h" | |
13 | +#include "instrum.h" | |
14 | +#include "playmidi.h" | |
15 | +#include "tables.h" | |
16 | + | |
17 | +#include "tables.h" | |
18 | + | |
19 | +#include "dls.h" | |
20 | +} | |
21 | + | |
22 | +#include <cassert> | |
23 | +#include <cmath> | |
24 | +#include <cstddef> | |
25 | +#include <cstdint> | |
26 | +#include <cstdio> | |
27 | +#include <cstring> | |
28 | + | |
29 | +#include <algorithm> | |
30 | +#include <exception> | |
31 | +#include <memory> | |
32 | +#include <optional> | |
33 | +#include <string> | |
34 | +#include <string_view> | |
35 | +#include <unordered_map> | |
36 | +#include <utility> | |
37 | +#include <vector> | |
38 | + | |
39 | +#ifdef max | |
40 | +#undef max | |
41 | +#endif | |
42 | + | |
43 | +#ifdef min | |
44 | +#undef min | |
45 | +#endif | |
46 | + | |
47 | +namespace TimDLS | |
48 | +{ | |
49 | + | |
50 | +struct TFFileCloser | |
51 | +{ | |
52 | + void operator()(timidity_file* pFile) const | |
53 | + { | |
54 | + if (pFile) | |
55 | + { | |
56 | + ::close_file(pFile); | |
57 | + } | |
58 | + } | |
59 | +}; | |
60 | + | |
61 | +struct InstrumentDeleter | |
62 | +{ | |
63 | + void operator()(Instrument* pInstrument) const | |
64 | + { | |
65 | + if (pInstrument) | |
66 | + { | |
67 | + ::free_instrument(pInstrument); | |
68 | + } | |
69 | + } | |
70 | +}; | |
71 | + | |
72 | +struct TimDeleter | |
73 | +{ | |
74 | + void operator()(void* p) const | |
75 | + { | |
76 | + safe_free(p); | |
77 | + } | |
78 | +}; | |
79 | + | |
80 | +class DLSParserException : public std::exception | |
81 | +{ | |
82 | +public: | |
83 | + DLSParserException(std::string fileName, std::string_view msg) : m_Message(fileName.append(": error: ").append(msg)) | |
84 | + { | |
85 | + } | |
86 | + | |
87 | + virtual const char* what() const noexcept override | |
88 | + { | |
89 | + return m_Message.c_str(); | |
90 | + } | |
91 | + | |
92 | +private: | |
93 | + std::string m_Message; | |
94 | +}; | |
95 | + | |
96 | +struct DLSWaveSampleLoop | |
97 | +{ | |
98 | + enum class LoopType : std::uint32_t | |
99 | + { | |
100 | + Forward | |
101 | + }; | |
102 | + | |
103 | + LoopType Type; | |
104 | + std::uint32_t LoopStart; | |
105 | + std::uint32_t LoopLength; | |
106 | +}; | |
107 | + | |
108 | +struct DLSWaveSampleInfo | |
109 | +{ | |
110 | + std::uint8_t UnityNote; | |
111 | + std::int16_t FineTune; | |
112 | + std::int32_t Attenuation; | |
113 | + bool NoTruncation; | |
114 | + bool NoCompression; | |
115 | + std::vector<DLSWaveSampleLoop> SampleLoops; | |
116 | +}; | |
117 | + | |
118 | +struct DLSConnectionBlock | |
119 | +{ | |
120 | + enum class SourceKind : std::uint16_t | |
121 | + { | |
122 | + None, | |
123 | + LFO, | |
124 | + KeyOnVelocity, | |
125 | + KeyNumber, | |
126 | + EG1, | |
127 | + EG2, | |
128 | + PitchWheel, | |
129 | + | |
130 | + CC1 = 0x81, | |
131 | + CC7 = 0x87, | |
132 | + CC10 = 0x8A, | |
133 | + CC11 = 0x8B | |
134 | + }; | |
135 | + | |
136 | + SourceKind Source; | |
137 | + SourceKind Control; | |
138 | + | |
139 | + enum class DestinationKind : std::uint16_t | |
140 | + { | |
141 | + None, | |
142 | + Attenuation, | |
143 | + Pitch = 3, | |
144 | + Pan, | |
145 | + | |
146 | + LFOFrequency = 0x104, | |
147 | + LFOStartDelay, | |
148 | + | |
149 | + EG1AttackTime = 0x206, | |
150 | + EG1DecayTime, | |
151 | + EG1ReleaseTime = 0x209, | |
152 | + EG1SustainLevel, | |
153 | + | |
154 | + EG2AttackTime = 0x30A, | |
155 | + EG2DecayTime, | |
156 | + EG2ReleaseTime = 0x30D, | |
157 | + EG2SustainLevel | |
158 | + }; | |
159 | + | |
160 | + DestinationKind Destination; | |
161 | + | |
162 | + enum class TransformKind : std::uint16_t | |
163 | + { | |
164 | + None, | |
165 | + Concave | |
166 | + }; | |
167 | + | |
168 | + TransformKind Transform; | |
169 | + | |
170 | + std::int32_t Scale; | |
171 | +}; | |
172 | + | |
173 | +struct DLSArticulator | |
174 | +{ | |
175 | + std::vector<DLSConnectionBlock> ConnectionBlocks; | |
176 | +}; | |
177 | + | |
178 | +struct DLSWaveLink | |
179 | +{ | |
180 | + bool PhaseMaster; | |
181 | + std::uint16_t PhaseGroup; | |
182 | + bool Left; | |
183 | + bool Right; | |
184 | + std::uint32_t TableIndex; | |
185 | +}; | |
186 | + | |
187 | +struct DLSRegion | |
188 | +{ | |
189 | + std::uint16_t LoKey; | |
190 | + std::uint16_t HiKey; | |
191 | + std::uint16_t LoVelocity; | |
192 | + std::uint16_t HiVelocity; | |
193 | + bool SelfNonExclusive; | |
194 | + std::uint16_t KeyGroup; | |
195 | + | |
196 | + std::optional<DLSWaveSampleInfo> SampleInfo; | |
197 | + DLSWaveLink WaveLink; | |
198 | + std::vector<DLSArticulator> Articulators; | |
199 | +}; | |
200 | + | |
201 | +struct DLSInstrument | |
202 | +{ | |
203 | + std::string Name; | |
204 | + std::uint8_t ProgramNumber; | |
205 | + std::uint16_t Bank; | |
206 | + std::vector<DLSRegion> Regions; | |
207 | + std::vector<DLSArticulator> Articulators; | |
208 | +}; | |
209 | + | |
210 | +struct DLSWaveInfo | |
211 | +{ | |
212 | + std::uint16_t FormatTag; | |
213 | + std::uint16_t Channels; | |
214 | + std::uint32_t SamplesPerSec; | |
215 | + std::uint32_t AvgBytesPerSec; | |
216 | + std::uint16_t BlockAlign; | |
217 | + std::uint16_t BitsPerSample; | |
218 | + | |
219 | + std::optional<DLSWaveSampleInfo> SampleInfo; | |
220 | + | |
221 | + std::unique_ptr<char, TimDeleter> pData; | |
222 | + std::uint32_t DataLength; | |
223 | +}; | |
224 | + | |
225 | +struct DLSCollection | |
226 | +{ | |
227 | + std::vector<DLSInstrument> Instruments; | |
228 | + std::vector<std::uint32_t> PoolTable; | |
229 | + std::uint32_t WavePoolOffset; // offset of wave pool from the beginning of the DLS file | |
230 | +}; | |
231 | + | |
232 | +std::int32_t CalcRate(std::int32_t diff, double sec) | |
233 | +{ | |
234 | + const std::int32_t envMax = 0x3FFFFFFF; | |
235 | + const std::int32_t envMin = 1; | |
236 | + | |
237 | + if (std::abs(sec) < 1.0e-6) | |
238 | + { | |
239 | + return envMax + 1; | |
240 | + } | |
241 | + | |
242 | + diff = std::max(diff, 1) << 14; | |
243 | + | |
244 | + double rate = static_cast<double>(diff) / ::play_mode->rate * ::control_ratio / sec; | |
245 | + | |
246 | + if (::fast_decay) | |
247 | + { | |
248 | + rate *= 2.0; | |
249 | + } | |
250 | + | |
251 | + return std::clamp(static_cast<std::int32_t>(std::lround(rate)), envMin, envMax); | |
252 | +} | |
253 | + | |
254 | +std::int32_t ToOffset(std::int32_t n) | |
255 | +{ | |
256 | + return n << 14; | |
257 | +} | |
258 | + | |
259 | +double TimeCentToSecond(std::int32_t tc) | |
260 | +{ | |
261 | + return tc == static_cast<std::int32_t>(0x80000000) ? 0.0 : std::pow(2.0, tc / (1200.0 * 65536)); | |
262 | +} | |
263 | + | |
264 | +class DLSParser | |
265 | +{ | |
266 | +public: | |
267 | + explicit DLSParser(std::string_view fileName) : m_FileName(fileName) | |
268 | + { | |
269 | + } | |
270 | + | |
271 | + void Parse() | |
272 | + { | |
273 | + std::unique_ptr<timidity_file, TFFileCloser> pFile(::open_file(m_FileName.data(), 1, OF_NORMAL)); | |
274 | + | |
275 | + if (!pFile) | |
276 | + { | |
277 | + throw DLSParserException(m_FileName, "unable to open file"); | |
278 | + } | |
279 | + | |
280 | + ParseRIFF(pFile.get()); | |
281 | + | |
282 | +#if 0 | |
283 | + for (const DLSInstrument& i : m_DLS.Instruments) | |
284 | + { | |
285 | + if (i.Bank == 128) | |
286 | + { | |
287 | + for (const DLSRegion& r : i.Regions) | |
288 | + { | |
289 | + ctl->cmsg( | |
290 | + CMSG_INFO, | |
291 | + VERB_NORMAL, | |
292 | + "%d %%dls \"gm.dls\" 128 %d %d # %s", | |
293 | + r.SampleInfo->UnityNote, | |
294 | + i.ProgramNumber, | |
295 | + r.SampleInfo->UnityNote, | |
296 | + i.Name.c_str() | |
297 | + ); | |
298 | + } | |
299 | + } | |
300 | + else | |
301 | + { | |
302 | + ctl->cmsg( | |
303 | + CMSG_INFO, | |
304 | + VERB_NORMAL, | |
305 | + "%d %%dls \"gm.dls\" %d %d # %s", | |
306 | + i.ProgramNumber, | |
307 | + i.Bank, | |
308 | + i.ProgramNumber, | |
309 | + i.Name.c_str() | |
310 | + ); | |
311 | + } | |
312 | + } | |
313 | +#endif | |
314 | + } | |
315 | + | |
316 | + std::unique_ptr<Instrument, InstrumentDeleter> BuildInstrument(std::uint8_t bank, std::int8_t programNumber, std::int8_t note) | |
317 | + { | |
318 | + // find DLSInstrument | |
319 | + auto itInst = std::find_if( | |
320 | + m_DLS.Instruments.begin(), | |
321 | + m_DLS.Instruments.end(), | |
322 | + [bank, programNumber] (const DLSInstrument& inst) | |
323 | + { | |
324 | + return inst.Bank == bank && inst.ProgramNumber == programNumber; | |
325 | + } | |
326 | + ); | |
327 | + | |
328 | + if (itInst == m_DLS.Instruments.end()) | |
329 | + { | |
330 | + ctl->cmsg( | |
331 | + CMSG_ERROR, | |
332 | + VERB_NORMAL, | |
333 | + "%s: no instrument found with [bank = %d, pc = %d]", | |
334 | + m_FileName.c_str(), | |
335 | + bank, | |
336 | + programNumber | |
337 | + ); | |
338 | + | |
339 | + return nullptr; | |
340 | + } | |
341 | + | |
342 | + std::unique_ptr<Instrument, InstrumentDeleter> pInstrument(reinterpret_cast<Instrument*>(safe_calloc(sizeof(Instrument), 1))); | |
343 | + pInstrument->type = INST_DLS; | |
344 | + pInstrument->instname = safe_strdup(itInst->Name.c_str()); | |
345 | + | |
346 | + ctl->cmsg( | |
347 | + CMSG_INFO, | |
348 | + VERB_NOISY, | |
349 | + "%s: loading instrument '%s' [bank = %d, pc = %d]", | |
350 | + m_FileName.c_str(), | |
351 | + pInstrument->instname, | |
352 | + bank, | |
353 | + programNumber | |
354 | + ); | |
355 | + | |
356 | + auto noteMatcher = [note] (const DLSRegion& r) | |
357 | + { | |
358 | + return note < 0 || (r.LoKey <= static_cast<std::uint16_t>(note) && static_cast<std::uint16_t>(note) <= r.HiKey); | |
359 | + }; | |
360 | + | |
361 | + std::size_t regionCount = std::count_if(itInst->Regions.begin(), itInst->Regions.end(), noteMatcher); | |
362 | + pInstrument->samples = static_cast<int>(regionCount); | |
363 | + pInstrument->sample = reinterpret_cast<Sample*>(safe_calloc(sizeof(Sample), regionCount)); | |
364 | + | |
365 | + std::size_t filledSamples = 0; | |
366 | + for (const DLSRegion& r : itInst->Regions) | |
367 | + { | |
368 | + if (noteMatcher(r)) | |
369 | + { | |
370 | + Sample* pSample = &pInstrument->sample[filledSamples]; | |
371 | + auto waveInfo = ParseWAVEList(m_DLS.WavePoolOffset + m_DLS.PoolTable.at(r.WaveLink.TableIndex)); | |
372 | + | |
373 | + pSample->data = reinterpret_cast<sample_t*>(waveInfo.pData.release()); | |
374 | + pSample->data_alloced = 1; | |
375 | + pSample->data_length = static_cast<splen_t>(waveInfo.DataLength) << FRACTION_BITS; | |
376 | + pSample->data_type = SAMPLE_TYPE_INT16; | |
377 | + pSample->sample_rate = static_cast<std::int32_t>(waveInfo.SamplesPerSec); | |
378 | + | |
379 | + { | |
380 | + const DLSWaveSampleInfo* pSampleInfo = nullptr; | |
381 | + | |
382 | + if (r.SampleInfo.has_value()) | |
383 | + { | |
384 | + pSampleInfo = &*r.SampleInfo; | |
385 | + } | |
386 | + else if (waveInfo.SampleInfo.has_value()) | |
387 | + { | |
388 | + pSampleInfo = &*waveInfo.SampleInfo; | |
389 | + } | |
390 | + | |
391 | + DLSWaveSampleLoop loop{DLSWaveSampleLoop::LoopType::Forward, 0, waveInfo.DataLength}; | |
392 | + | |
393 | + if (pSampleInfo) | |
394 | + { | |
395 | + if (!pSampleInfo->SampleLoops.empty()) | |
396 | + { | |
397 | + pSample->modes |= MODES_LOOPING | MODES_SUSTAIN; | |
398 | + loop = pSampleInfo->SampleLoops[0]; | |
399 | + } | |
400 | + | |
401 | + pSample->root_key = std::clamp<std::int8_t>(pSampleInfo->UnityNote, 0, 127); | |
402 | + pSample->tune = std::pow(2.0, pSampleInfo->FineTune / 1200.0); | |
403 | + pSample->volume = (pSampleInfo->Attenuation == 0x80000000 ? 0.0 : std::pow(10.0, pSampleInfo->Attenuation / 200.0 / 65536.0)); | |
404 | + } | |
405 | + else | |
406 | + { | |
407 | + pSample->root_key = 60; | |
408 | + pSample->tune = 1.0; | |
409 | + pSample->volume = 1.0; | |
410 | + } | |
411 | + | |
412 | + pSample->loop_start = std::clamp<splen_t>(loop.LoopStart, 0, waveInfo.DataLength) << FRACTION_BITS; | |
413 | + pSample->loop_end = std::clamp<splen_t>(loop.LoopStart + loop.LoopLength, loop.LoopStart, waveInfo.DataLength) << FRACTION_BITS; | |
414 | + | |
415 | + pSample->root_freq = ::freq_table[pSample->root_key]; | |
416 | + } | |
417 | + | |
418 | + pSample->low_key = static_cast<std::int8_t>(r.LoKey); | |
419 | + pSample->high_key = static_cast<std::int8_t>(r.HiKey); | |
420 | + pSample->low_vel = static_cast<std::uint8_t>(r.LoVelocity); | |
421 | + pSample->high_vel = static_cast<std::uint8_t>(r.HiVelocity); | |
422 | + | |
423 | + pSample->def_pan = 64; | |
424 | + pSample->cfg_amp = 1.0; | |
425 | + pSample->modes |= (waveInfo.BitsPerSample == 16 ? MODES_16BIT : 0); | |
426 | + | |
427 | + pSample->cutoff_freq = 20000; | |
428 | + pSample->cutoff_low_limit = -1; | |
429 | + pSample->envelope_velf_bpo = 64; | |
430 | + pSample->modenv_velf_bpo = 64; | |
431 | + pSample->key_to_fc_bpo = 60; | |
432 | + pSample->scale_freq = 60; | |
433 | + pSample->scale_factor = 1024; | |
434 | + | |
435 | + pSample->sample_type = (r.WaveLink.Left ? SF_SAMPLETYPE_LEFT : (r.WaveLink.Right ? SF_SAMPLETYPE_RIGHT : SF_SAMPLETYPE_MONO)); | |
436 | + pSample->sf_sample_link = -1; | |
437 | + | |
438 | + pSample->lpf_type = -1; | |
439 | + pSample->hpf[0] = -1; | |
440 | + pSample->hpf[1] = 10; | |
441 | + | |
442 | + double attackTime = 0.0; | |
443 | + double holdTime = 0.0; | |
444 | + double decayTime = 0.0; | |
445 | + std::int32_t sustainLevel = 65533; | |
446 | + double releaseTime = 0.0; | |
447 | + | |
448 | + // DLS Level 1 specifies that melodic instruments use global articulators and that drum instruments use region articulators. | |
449 | + if (bank != 128 && !r.Articulators.empty()) | |
450 | + { | |
451 | + ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "%s: warning: region articulators ignored for melodic instrument", m_FileName.c_str()); | |
452 | + } | |
453 | + | |
454 | + if (bank == 128 && !itInst->Articulators.empty()) | |
455 | + { | |
456 | + ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "%s: warning: global articulators ignored for drum instrument", m_FileName.c_str()); | |
457 | + } | |
458 | + | |
459 | + for (const DLSArticulator& a : (bank == 128 ? r.Articulators : itInst->Articulators)) | |
460 | + { | |
461 | + for (const DLSConnectionBlock& b : a.ConnectionBlocks) | |
462 | + { | |
463 | + switch (b.Destination) | |
464 | + { | |
465 | + case DLSConnectionBlock::DestinationKind::EG1AttackTime: | |
466 | + if (b.Source == DLSConnectionBlock::SourceKind::None && b.Control == DLSConnectionBlock::SourceKind::None && b.Transform == DLSConnectionBlock::TransformKind::None) | |
467 | + { | |
468 | + attackTime = TimeCentToSecond(b.Scale); | |
469 | + continue; | |
470 | + } | |
471 | + break; | |
472 | + | |
473 | + case DLSConnectionBlock::DestinationKind::EG1DecayTime: | |
474 | + if (b.Source == DLSConnectionBlock::SourceKind::None && b.Control == DLSConnectionBlock::SourceKind::None && b.Transform == DLSConnectionBlock::TransformKind::None) | |
475 | + { | |
476 | + decayTime = TimeCentToSecond(b.Scale); | |
477 | + continue; | |
478 | + } | |
479 | + break; | |
480 | + | |
481 | + case DLSConnectionBlock::DestinationKind::EG1SustainLevel: | |
482 | + if (b.Source == DLSConnectionBlock::SourceKind::None && b.Control == DLSConnectionBlock::SourceKind::None && b.Transform == DLSConnectionBlock::TransformKind::None) | |
483 | + { | |
484 | + sustainLevel = std::lround(65533.0 * std::clamp(b.Scale, 0, 1000) / 1000.0); | |
485 | + continue; | |
486 | + } | |
487 | + break; | |
488 | + | |
489 | + case DLSConnectionBlock::DestinationKind::EG1ReleaseTime: | |
490 | + if (b.Source == DLSConnectionBlock::SourceKind::None && b.Control == DLSConnectionBlock::SourceKind::None && b.Transform == DLSConnectionBlock::TransformKind::None) | |
491 | + { | |
492 | + releaseTime = TimeCentToSecond(b.Scale); | |
493 | + continue; | |
494 | + } | |
495 | + break; | |
496 | + | |
497 | + default: | |
498 | + break; | |
499 | + } | |
500 | + | |
501 | + ctl->cmsg( | |
502 | + CMSG_WARNING, | |
503 | + VERB_NOISY, | |
504 | + "%s: unsupported connection block [source = %d, control = %d, scale = %d, destination = %d, transform = %d]", | |
505 | + m_FileName.c_str(), | |
506 | + b.Source, | |
507 | + b.Control, | |
508 | + b.Scale, | |
509 | + b.Destination, | |
510 | + b.Transform | |
511 | + ); | |
512 | + } | |
513 | + } | |
514 | + | |
515 | + pSample->envelope_offset[0] = ToOffset(65535); | |
516 | + pSample->envelope_rate[0] = CalcRate(65535, attackTime); | |
517 | + pSample->envelope_offset[1] = ToOffset(65534); | |
518 | + pSample->envelope_rate[1] = CalcRate(1, holdTime); | |
519 | + | |
520 | + pSample->envelope_offset[2] = ToOffset(sustainLevel); | |
521 | + pSample->envelope_rate[2] = CalcRate(65534 - sustainLevel, std::clamp(decayTime, 0.0, 100.0)); | |
522 | + | |
523 | + pSample->envelope_offset[3] = 0; | |
524 | + pSample->envelope_rate[3] = CalcRate(sustainLevel, releaseTime); | |
525 | + pSample->envelope_offset[4] = pSample->envelope_offset[3]; | |
526 | + pSample->envelope_rate[4] = pSample->envelope_rate[3]; | |
527 | + pSample->envelope_offset[5] = pSample->envelope_offset[3]; | |
528 | + pSample->envelope_rate[5] = pSample->envelope_rate[3]; | |
529 | + | |
530 | + filledSamples++; | |
531 | + | |
532 | + if (filledSamples >= regionCount) | |
533 | + { | |
534 | + break; | |
535 | + } | |
536 | + } | |
537 | + } | |
538 | + | |
539 | + return pInstrument; | |
540 | + } | |
541 | + | |
542 | +private: | |
543 | + enum class ChunkLocation | |
544 | + { | |
545 | + DLS, | |
546 | + INS, | |
547 | + RGN, | |
548 | + WAVE | |
549 | + }; | |
550 | + | |
551 | + void PrintUnknownChunk(std::uint32_t cc) | |
552 | + { | |
553 | + ctl->cmsg( | |
554 | + CMSG_WARNING, | |
555 | + VERB_DEBUG, | |
556 | + "%s: warning: skipping unknown chunk '%c%c%c%c'", | |
557 | + m_FileName.c_str(), | |
558 | + cc & 0xFF, | |
559 | + (cc >> 8) & 0xFF, | |
560 | + (cc >> 16) & 0xFF, | |
561 | + cc >> 24 | |
562 | + ); | |
563 | + } | |
564 | + | |
565 | + // array size is 5 because string literals include a terminating NULL character | |
566 | + static constexpr std::uint32_t MakeFourCC(const char (&cc)[5]) | |
567 | + { | |
568 | + return (cc[3] << 24) | (cc[2] << 16) | (cc[1] << 8) | cc[0]; | |
569 | + } | |
570 | + | |
571 | + std::uint32_t ReadFourCC(timidity_file* pFile) | |
572 | + { | |
573 | + char cc[5] = {}; | |
574 | + | |
575 | + if (::tf_read(cc, 1, 4, pFile) != 4) | |
576 | + { | |
577 | + throw DLSParserException(m_FileName, "unexpected end of file"); | |
578 | + } | |
579 | + | |
580 | + return MakeFourCC(cc); | |
581 | + } | |
582 | + | |
583 | + std::uint16_t ReadUInt16(timidity_file* pFile) | |
584 | + { | |
585 | + std::uint16_t n; | |
586 | + | |
587 | + if (::tf_read(&n, sizeof(n), 1, pFile) != 1) | |
588 | + { | |
589 | + throw DLSParserException(m_FileName, "unexpected end of file"); | |
590 | + } | |
591 | + | |
592 | +#ifdef LITTLE_ENDIAN | |
593 | + return n; | |
594 | +#else | |
595 | + return (n << 8) | (n >> 8); | |
596 | +#endif | |
597 | + } | |
598 | + | |
599 | + std::uint32_t ReadUInt32(timidity_file* pFile) | |
600 | + { | |
601 | + std::uint32_t n; | |
602 | + | |
603 | + if (::tf_read(&n, sizeof(n), 1, pFile) != 1) | |
604 | + { | |
605 | + throw DLSParserException(m_FileName, "unexpected end of file"); | |
606 | + } | |
607 | + | |
608 | +#ifdef LITTLE_ENDIAN | |
609 | + return n; | |
610 | +#else | |
611 | + return (n << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | (n >> 24); | |
612 | +#endif | |
613 | + } | |
614 | + | |
615 | + std::string ReadString(timidity_file* pFile, std::uint32_t length) | |
616 | + { | |
617 | + std::string str(length, '\0'); | |
618 | + | |
619 | + if (::tf_read(str.data(), 1, length, pFile) != length) | |
620 | + { | |
621 | + throw DLSParserException(m_FileName, "unexpected end of file"); | |
622 | + } | |
623 | + | |
624 | + return str; | |
625 | + } | |
626 | + | |
627 | + std::uint32_t Align2(std::uint32_t n) | |
628 | + { | |
629 | + return n & 1 ? n + 1 : n; | |
630 | + } | |
631 | + | |
632 | + void DoSkip(timidity_file* pFile, std::uint32_t n) | |
633 | + { | |
634 | + if (n) | |
635 | + { | |
636 | + if (::tf_seek(pFile, n, SEEK_CUR) == -1) | |
637 | + { | |
638 | + throw DLSParserException(m_FileName, "unexpected end of file"); | |
639 | + } | |
640 | + } | |
641 | + } | |
642 | + | |
643 | + std::uint32_t SkipChunk(timidity_file* pFile) | |
644 | + { | |
645 | + std::uint32_t totalChunkSize = Align2(ReadUInt32(pFile)); | |
646 | + DoSkip(pFile, totalChunkSize); | |
647 | + return totalChunkSize + 4; | |
648 | + } | |
649 | + | |
650 | + std::uint32_t ParseINFOItem(timidity_file* pFile, ChunkLocation loc) | |
651 | + { | |
652 | + std::uint32_t cc = ReadFourCC(pFile); | |
653 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
654 | + | |
655 | + switch (cc) | |
656 | + { | |
657 | + case MakeFourCC("INAM"): | |
658 | + if (loc == ChunkLocation::INS) | |
659 | + { | |
660 | + assert(!m_DLS.Instruments.empty()); | |
661 | + m_DLS.Instruments.back().Name = ReadString(pFile, chunkSize); | |
662 | + ctl->cmsg(CMSG_INFO, VERB_DEBUG, "%s: INAM: %s", m_FileName.c_str(), m_DLS.Instruments.back().Name.c_str()); | |
663 | + break; | |
664 | + } | |
665 | + [[fallthrough]]; | |
666 | + | |
667 | + default: | |
668 | + ctl->cmsg( | |
669 | + CMSG_INFO, | |
670 | + VERB_DEBUG, | |
671 | + "%s: %c%c%c%c: %s", | |
672 | + m_FileName.c_str(), | |
673 | + cc & 0xFF, | |
674 | + (cc >> 8) & 0xFF, | |
675 | + (cc >> 16) & 0xFF, | |
676 | + cc >> 24, | |
677 | + ReadString(pFile, chunkSize).c_str() | |
678 | + ); | |
679 | + break; | |
680 | + } | |
681 | + | |
682 | + if (chunkSize & 1) | |
683 | + { | |
684 | + DoSkip(pFile, 1); | |
685 | + } | |
686 | + | |
687 | + return Align2(chunkSize) + 8; | |
688 | + } | |
689 | + | |
690 | + std::uint32_t ParseVERS(timidity_file* pFile) | |
691 | + { | |
692 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
693 | + | |
694 | + if (chunkSize == 8) | |
695 | + { | |
696 | + std::uint32_t versionMS = ReadUInt32(pFile); | |
697 | + std::uint32_t versionLS = ReadUInt32(pFile); | |
698 | + | |
699 | + ctl->cmsg( | |
700 | + CMSG_INFO, | |
701 | + VERB_DEBUG, | |
702 | + "%s: DLS version: %d.%d.%d.%d", | |
703 | + m_FileName.c_str(), | |
704 | + versionMS >> 16, | |
705 | + versionMS & 0xFFFF, | |
706 | + versionLS >> 16, | |
707 | + versionLS & 0xFFFF | |
708 | + ); | |
709 | + } | |
710 | + else | |
711 | + { | |
712 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'vers' chunk has invalid size", m_FileName.c_str()); | |
713 | + DoSkip(pFile, Align2(chunkSize)); | |
714 | + } | |
715 | + | |
716 | + return Align2(chunkSize) + 4; | |
717 | + } | |
718 | + | |
719 | + std::uint32_t ParseDLID(timidity_file* pFile) | |
720 | + { | |
721 | + return SkipChunk(pFile); | |
722 | + } | |
723 | + | |
724 | + std::uint32_t ParseCOLH(timidity_file* pFile) | |
725 | + { | |
726 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
727 | + | |
728 | + if (chunkSize == 4) | |
729 | + { | |
730 | + std::uint32_t count = ReadUInt32(pFile); | |
731 | + | |
732 | + ctl->cmsg( | |
733 | + CMSG_INFO, | |
734 | + VERB_DEBUG, | |
735 | + "%s: instrument count: %d", | |
736 | + m_FileName.c_str(), | |
737 | + count | |
738 | + ); | |
739 | + } | |
740 | + else | |
741 | + { | |
742 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'colh' chunk has invalid size", m_FileName.c_str()); | |
743 | + DoSkip(pFile, Align2(chunkSize)); | |
744 | + } | |
745 | + | |
746 | + return Align2(chunkSize) + 4; | |
747 | + } | |
748 | + | |
749 | + std::uint32_t ParseINSH(timidity_file* pFile) | |
750 | + { | |
751 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
752 | + | |
753 | + if (chunkSize == 12) | |
754 | + { | |
755 | + /* std::uint32_t regionCount = */ ReadUInt32(pFile); | |
756 | + | |
757 | + assert(!m_DLS.Instruments.empty()); | |
758 | + auto& instrument = m_DLS.Instruments.back(); | |
759 | + std::uint32_t bank = ReadUInt32(pFile); | |
760 | + instrument.Bank = static_cast<std::uint16_t>(bank & 0x80000000 ? 128 : bank >> 8); // FIXME!! | |
761 | + | |
762 | + std::uint32_t programNumber = ReadUInt32(pFile); | |
763 | + instrument.ProgramNumber = static_cast<std::uint8_t>(programNumber); | |
764 | + } | |
765 | + else | |
766 | + { | |
767 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'insh' chunk has invalid size", m_FileName.c_str()); | |
768 | + DoSkip(pFile, Align2(chunkSize)); | |
769 | + } | |
770 | + | |
771 | + return Align2(chunkSize) + 4; | |
772 | + } | |
773 | + | |
774 | + std::uint32_t ParseLARTItem(timidity_file* pFile, ChunkLocation loc) | |
775 | + { | |
776 | + std::uint32_t cc = ReadFourCC(pFile); | |
777 | + std::uint32_t readSize = 4; | |
778 | + | |
779 | + switch (cc) | |
780 | + { | |
781 | + case MakeFourCC("art1"): | |
782 | + { | |
783 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
784 | + | |
785 | + if (chunkSize >= 8) | |
786 | + { | |
787 | + std::uint32_t structSize = ReadUInt32(pFile); | |
788 | + | |
789 | + if (structSize == 8) | |
790 | + { | |
791 | + std::uint32_t connectionBlockCount = ReadUInt32(pFile); | |
792 | + | |
793 | + if (chunkSize == structSize + connectionBlockCount * 12) | |
794 | + { | |
795 | + DLSArticulator* pArt; | |
796 | + | |
797 | + switch (loc) | |
798 | + { | |
799 | + case ChunkLocation::INS: | |
800 | + assert(!m_DLS.Instruments.empty()); | |
801 | + pArt = &m_DLS.Instruments.back().Articulators.emplace_back(); | |
802 | + break; | |
803 | + | |
804 | + case ChunkLocation::RGN: | |
805 | + assert(!m_DLS.Instruments.empty()); | |
806 | + assert(!m_DLS.Instruments.back().Regions.empty()); | |
807 | + | |
808 | + pArt = &m_DLS.Instruments.back().Regions.back().Articulators.emplace_back(); | |
809 | + break; | |
810 | + | |
811 | + default: | |
812 | + assert(false); | |
813 | + throw DLSParserException(m_FileName, "invalid argument passed while parsing 'art1'"); | |
814 | + break; | |
815 | + } | |
816 | + | |
817 | + pArt->ConnectionBlocks.resize(connectionBlockCount); | |
818 | + | |
819 | + for (auto& c : pArt->ConnectionBlocks) | |
820 | + { | |
821 | + c.Source = DLSConnectionBlock::SourceKind{ReadUInt16(pFile)}; | |
822 | + c.Control = DLSConnectionBlock::SourceKind{ReadUInt16(pFile)}; | |
823 | + c.Destination = DLSConnectionBlock::DestinationKind{ReadUInt16(pFile)}; | |
824 | + c.Transform = DLSConnectionBlock::TransformKind{ReadUInt16(pFile)}; | |
825 | + c.Scale = static_cast<std::int32_t>(ReadUInt32(pFile)); | |
826 | + } | |
827 | + } | |
828 | + else | |
829 | + { | |
830 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'art1' chunk has invalid size", m_FileName.c_str()); | |
831 | + DoSkip(pFile, Align2(chunkSize) - 8); | |
832 | + } | |
833 | + } | |
834 | + else | |
835 | + { | |
836 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'art1' chunk has invalid size", m_FileName.c_str()); | |
837 | + DoSkip(pFile, Align2(chunkSize) - 4); | |
838 | + } | |
839 | + } | |
840 | + else | |
841 | + { | |
842 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'art1' chunk has invalid size", m_FileName.c_str()); | |
843 | + DoSkip(pFile, Align2(chunkSize)); | |
844 | + } | |
845 | + | |
846 | + readSize += Align2(chunkSize) + 4; | |
847 | + } | |
848 | + break; | |
849 | + | |
850 | + default: | |
851 | + PrintUnknownChunk(cc); | |
852 | + readSize += SkipChunk(pFile); | |
853 | + break; | |
854 | + } | |
855 | + | |
856 | + return readSize; | |
857 | + } | |
858 | + | |
859 | + std::uint32_t ParseRGNH(timidity_file* pFile) | |
860 | + { | |
861 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
862 | + | |
863 | + if (chunkSize == 12) | |
864 | + { | |
865 | + assert(!m_DLS.Instruments.empty()); | |
866 | + assert(!m_DLS.Instruments.back().Regions.empty()); | |
867 | + auto& region = m_DLS.Instruments.back().Regions.back(); | |
868 | + region.LoKey = ReadUInt16(pFile); | |
869 | + region.HiKey = ReadUInt16(pFile); | |
870 | + region.LoVelocity = ReadUInt16(pFile); | |
871 | + region.HiVelocity = ReadUInt16(pFile); | |
872 | + region.SelfNonExclusive = !!(ReadUInt16(pFile) & 1); | |
873 | + region.KeyGroup = ReadUInt16(pFile); | |
874 | + } | |
875 | + else | |
876 | + { | |
877 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'rgnh' chunk has invalid size", m_FileName.c_str()); | |
878 | + DoSkip(pFile, Align2(chunkSize)); | |
879 | + } | |
880 | + | |
881 | + return Align2(chunkSize) + 4; | |
882 | + } | |
883 | + | |
884 | + std::uint32_t ParseWSMP(timidity_file* pFile, ChunkLocation loc, DLSWaveSampleInfo* pSampleInfo = nullptr) | |
885 | + { | |
886 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
887 | + | |
888 | + if (chunkSize >= 20) | |
889 | + { | |
890 | + std::uint32_t structSize = ReadUInt32(pFile); | |
891 | + | |
892 | + if (structSize == 20) | |
893 | + { | |
894 | + switch (loc) | |
895 | + { | |
896 | + case ChunkLocation::RGN: | |
897 | + assert(!m_DLS.Instruments.empty()); | |
898 | + assert(!m_DLS.Instruments.back().Regions.empty()); | |
899 | + | |
900 | + pSampleInfo = &m_DLS.Instruments.back().Regions.back().SampleInfo.emplace(); | |
901 | + break; | |
902 | + | |
903 | + case ChunkLocation::WAVE: | |
904 | + assert(pSampleInfo); | |
905 | + break; | |
906 | + | |
907 | + default: | |
908 | + assert(false); | |
909 | + throw DLSParserException(m_FileName, "invalid argument passed while parsing 'wsmp'"); | |
910 | + } | |
911 | + | |
912 | + pSampleInfo->UnityNote = static_cast<std::uint8_t>(ReadUInt16(pFile)); | |
913 | + pSampleInfo->FineTune = static_cast<std::int16_t>(ReadUInt16(pFile)); | |
914 | + pSampleInfo->Attenuation = static_cast<std::int32_t>(ReadUInt32(pFile)); | |
915 | + | |
916 | + std::uint32_t options = ReadUInt32(pFile); | |
917 | + pSampleInfo->NoTruncation = !!(options & 1); | |
918 | + pSampleInfo->NoCompression = !!(options & 2); | |
919 | + | |
920 | + std::uint32_t loopCount = ReadUInt32(pFile); | |
921 | + | |
922 | + if (loopCount == 1) | |
923 | + { | |
924 | + std::uint32_t loopInfoSize = ReadUInt32(pFile); | |
925 | + | |
926 | + if (loopInfoSize == 16) | |
927 | + { | |
928 | + auto& loop = pSampleInfo->SampleLoops.emplace_back(); | |
929 | + | |
930 | + ReadUInt32(pFile); | |
931 | + loop.Type = DLSWaveSampleLoop::LoopType::Forward; | |
932 | + | |
933 | + loop.LoopStart = ReadUInt32(pFile); | |
934 | + loop.LoopLength = ReadUInt32(pFile); | |
935 | + } | |
936 | + else | |
937 | + { | |
938 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: invalid loop info size", m_FileName.c_str()); | |
939 | + DoSkip(pFile, Align2(chunkSize) - 24); | |
940 | + } | |
941 | + } | |
942 | + else if (loopCount == 0) | |
943 | + { | |
944 | + // skip | |
945 | + } | |
946 | + else | |
947 | + { | |
948 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: invalid loop count", m_FileName.c_str()); | |
949 | + DoSkip(pFile, Align2(chunkSize) - 20); | |
950 | + } | |
951 | + } | |
952 | + else | |
953 | + { | |
954 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'wsmp' chunk has invalid size", m_FileName.c_str()); | |
955 | + DoSkip(pFile, Align2(chunkSize) - 4); | |
956 | + } | |
957 | + } | |
958 | + else | |
959 | + { | |
960 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'wsmp' chunk has invalid size", m_FileName.c_str()); | |
961 | + DoSkip(pFile, Align2(chunkSize)); | |
962 | + } | |
963 | + | |
964 | + return Align2(chunkSize) + 4; | |
965 | + } | |
966 | + | |
967 | + std::uint32_t ParseWLNK(timidity_file* pFile) | |
968 | + { | |
969 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
970 | + | |
971 | + if (chunkSize == 12) | |
972 | + { | |
973 | + assert(!m_DLS.Instruments.empty()); | |
974 | + assert(!m_DLS.Instruments.back().Regions.empty()); | |
975 | + | |
976 | + auto& waveLink = m_DLS.Instruments.back().Regions.back().WaveLink; | |
977 | + waveLink.PhaseMaster = !!(ReadUInt16(pFile) & 1); | |
978 | + waveLink.PhaseGroup = ReadUInt16(pFile); | |
979 | + | |
980 | + std::uint32_t channel = ReadUInt32(pFile); | |
981 | + waveLink.Left = !!(channel & 1); | |
982 | + waveLink.Right = !!(channel & 2); | |
983 | + | |
984 | + waveLink.TableIndex = ReadUInt32(pFile); | |
985 | + } | |
986 | + else | |
987 | + { | |
988 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'wlnk' chunk has invalid size", m_FileName.c_str()); | |
989 | + DoSkip(pFile, Align2(chunkSize)); | |
990 | + } | |
991 | + | |
992 | + return Align2(chunkSize) + 4; | |
993 | + } | |
994 | + | |
995 | + std::uint32_t ParseLRGNItem(timidity_file* pFile) | |
996 | + { | |
997 | + std::uint32_t cc = ReadFourCC(pFile); | |
998 | + std::uint32_t readSize = 4; | |
999 | + | |
1000 | + switch (cc) | |
1001 | + { | |
1002 | + case MakeFourCC("LIST"): | |
1003 | + { | |
1004 | + std::uint32_t listSize = ReadUInt32(pFile); | |
1005 | + readSize += 4; | |
1006 | + std::uint32_t listType = ReadFourCC(pFile); | |
1007 | + std::uint32_t currentListSize = listSize - 4; | |
1008 | + | |
1009 | + switch (listType) | |
1010 | + { | |
1011 | + case MakeFourCC("rgn "): | |
1012 | + assert(!m_DLS.Instruments.empty()); | |
1013 | + m_DLS.Instruments.back().Regions.emplace_back(); | |
1014 | + | |
1015 | + while (currentListSize >= 2) | |
1016 | + { | |
1017 | + std::uint32_t cc = ReadFourCC(pFile); | |
1018 | + currentListSize -= 4; | |
1019 | + | |
1020 | + switch (cc) | |
1021 | + { | |
1022 | + case MakeFourCC("LIST"): | |
1023 | + { | |
1024 | + std::uint32_t listSize2 = ReadUInt32(pFile); | |
1025 | + currentListSize -= 4; | |
1026 | + std::uint32_t listType2 = ReadFourCC(pFile); | |
1027 | + std::uint32_t currentListSize2 = listSize2 - 4; | |
1028 | + | |
1029 | + switch (listType2) | |
1030 | + { | |
1031 | + case MakeFourCC("lart"): | |
1032 | + while (currentListSize2 >= 2) | |
1033 | + { | |
1034 | + currentListSize2 -= ParseLARTItem(pFile, ChunkLocation::RGN); | |
1035 | + } | |
1036 | + break; | |
1037 | + | |
1038 | + default: | |
1039 | + PrintUnknownChunk(listType2); | |
1040 | + break; | |
1041 | + } | |
1042 | + | |
1043 | + DoSkip(pFile, Align2(currentListSize2)); | |
1044 | + currentListSize -= Align2(listSize2); | |
1045 | + } | |
1046 | + break; | |
1047 | + | |
1048 | + case MakeFourCC("rgnh"): | |
1049 | + currentListSize -= ParseRGNH(pFile); | |
1050 | + break; | |
1051 | + | |
1052 | + case MakeFourCC("wsmp"): | |
1053 | + currentListSize -= ParseWSMP(pFile, ChunkLocation::RGN); | |
1054 | + break; | |
1055 | + | |
1056 | + case MakeFourCC("wlnk"): | |
1057 | + currentListSize -= ParseWLNK(pFile); | |
1058 | + break; | |
1059 | + | |
1060 | + default: | |
1061 | + PrintUnknownChunk(cc); | |
1062 | + currentListSize -= SkipChunk(pFile); | |
1063 | + break; | |
1064 | + } | |
1065 | + } | |
1066 | + break; | |
1067 | + | |
1068 | + default: | |
1069 | + PrintUnknownChunk(listType); | |
1070 | + break; | |
1071 | + } | |
1072 | + | |
1073 | + DoSkip(pFile, Align2(currentListSize)); | |
1074 | + readSize += Align2(listSize); | |
1075 | + } | |
1076 | + break; | |
1077 | + | |
1078 | + default: | |
1079 | + PrintUnknownChunk(cc); | |
1080 | + readSize += SkipChunk(pFile); | |
1081 | + break; | |
1082 | + } | |
1083 | + | |
1084 | + return readSize; | |
1085 | + } | |
1086 | + | |
1087 | + std::uint32_t ParseLINSItem(timidity_file* pFile) | |
1088 | + { | |
1089 | + std::uint32_t cc = ReadFourCC(pFile); | |
1090 | + std::uint32_t readSize = 4; | |
1091 | + | |
1092 | + switch (cc) | |
1093 | + { | |
1094 | + case MakeFourCC("LIST"): | |
1095 | + { | |
1096 | + std::uint32_t listSize = ReadUInt32(pFile); | |
1097 | + readSize += 4; | |
1098 | + std::uint32_t listType = ReadFourCC(pFile); | |
1099 | + std::uint32_t currentListSize = listSize - 4; | |
1100 | + | |
1101 | + switch (listType) | |
1102 | + { | |
1103 | + case MakeFourCC("ins "): | |
1104 | + m_DLS.Instruments.emplace_back(); | |
1105 | + | |
1106 | + while (currentListSize >= 2) | |
1107 | + { | |
1108 | + std::uint32_t cc = ReadFourCC(pFile); | |
1109 | + currentListSize -= 4; | |
1110 | + | |
1111 | + switch (cc) | |
1112 | + { | |
1113 | + case MakeFourCC("LIST"): | |
1114 | + { | |
1115 | + std::uint32_t listSize2 = ReadUInt32(pFile); | |
1116 | + currentListSize -= 4; | |
1117 | + std::uint32_t listType2 = ReadFourCC(pFile); | |
1118 | + std::uint32_t currentListSize2 = listSize2 - 4; | |
1119 | + | |
1120 | + switch (listType2) | |
1121 | + { | |
1122 | + case MakeFourCC("INFO"): | |
1123 | + while (currentListSize2 >= 2) | |
1124 | + { | |
1125 | + currentListSize2 -= ParseINFOItem(pFile, ChunkLocation::INS); | |
1126 | + } | |
1127 | + break; | |
1128 | + | |
1129 | + case MakeFourCC("lrgn"): | |
1130 | + while (currentListSize2 >= 2) | |
1131 | + { | |
1132 | + currentListSize2 -= ParseLRGNItem(pFile); | |
1133 | + } | |
1134 | + break; | |
1135 | + | |
1136 | + case MakeFourCC("lart"): | |
1137 | + while (currentListSize2 >= 2) | |
1138 | + { | |
1139 | + currentListSize2 -= ParseLARTItem(pFile, ChunkLocation::INS); | |
1140 | + } | |
1141 | + break; | |
1142 | + | |
1143 | + default: | |
1144 | + PrintUnknownChunk(listType2); | |
1145 | + break; | |
1146 | + } | |
1147 | + | |
1148 | + DoSkip(pFile, Align2(currentListSize2)); | |
1149 | + currentListSize -= Align2(listSize2); | |
1150 | + } | |
1151 | + break; | |
1152 | + | |
1153 | + case MakeFourCC("dlid"): | |
1154 | + currentListSize -= ParseDLID(pFile); | |
1155 | + break; | |
1156 | + | |
1157 | + case MakeFourCC("insh"): | |
1158 | + currentListSize -= ParseINSH(pFile); | |
1159 | + break; | |
1160 | + | |
1161 | + default: | |
1162 | + PrintUnknownChunk(cc); | |
1163 | + currentListSize -= SkipChunk(pFile); | |
1164 | + break; | |
1165 | + } | |
1166 | + } | |
1167 | + break; | |
1168 | + | |
1169 | + default: | |
1170 | + PrintUnknownChunk(listType); | |
1171 | + break; | |
1172 | + } | |
1173 | + | |
1174 | + DoSkip(pFile, Align2(currentListSize)); | |
1175 | + readSize += Align2(listSize); | |
1176 | + } | |
1177 | + break; | |
1178 | + | |
1179 | + default: | |
1180 | + PrintUnknownChunk(cc); | |
1181 | + readSize += SkipChunk(pFile); | |
1182 | + break; | |
1183 | + } | |
1184 | + | |
1185 | + return readSize; | |
1186 | + } | |
1187 | + | |
1188 | + std::uint32_t ParsePTBL(timidity_file* pFile) | |
1189 | + { | |
1190 | + std::uint32_t chunkSize = ReadUInt32(pFile); | |
1191 | + | |
1192 | + if (chunkSize >= 8) | |
1193 | + { | |
1194 | + std::uint32_t structSize = ReadUInt32(pFile); | |
1195 | + | |
1196 | + if (structSize == 8) | |
1197 | + { | |
1198 | + std::uint32_t cueCount = ReadUInt32(pFile); | |
1199 | + | |
1200 | + if (chunkSize == 8 + cueCount * 4) | |
1201 | + { | |
1202 | + m_DLS.PoolTable.resize(cueCount); | |
1203 | + | |
1204 | +#ifdef LITTLE_ENDIAN | |
1205 | + if (::tf_read(m_DLS.PoolTable.data(), 4, cueCount, pFile) != cueCount) | |
1206 | + { | |
1207 | + throw DLSParserException(m_FileName, "unexpected end of file"); | |
1208 | + } | |
1209 | +#else | |
1210 | + for (auto& c : m_DLS.PoolTable) | |
1211 | + { | |
1212 | + c = ReadUInt32(pFile); | |
1213 | + } | |
1214 | +#endif | |
1215 | + } | |
1216 | + else | |
1217 | + { | |
1218 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'wlnk' chunk has invalid size", m_FileName.c_str()); | |
1219 | + DoSkip(pFile, Align2(chunkSize) - 8); | |
1220 | + } | |
1221 | + } | |
1222 | + else | |
1223 | + { | |
1224 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'wlnk' chunk has invalid size", m_FileName.c_str()); | |
1225 | + DoSkip(pFile, Align2(chunkSize) - 4); | |
1226 | + } | |
1227 | + } | |
1228 | + else | |
1229 | + { | |
1230 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'ptbl' chunk has invalid size", m_FileName.c_str()); | |
1231 | + DoSkip(pFile, Align2(chunkSize)); | |
1232 | + } | |
1233 | + | |
1234 | + return Align2(chunkSize) + 4; | |
1235 | + } | |
1236 | + | |
1237 | + void ParseRIFF(timidity_file* pFile) | |
1238 | + { | |
1239 | + if (ReadFourCC(pFile) != MakeFourCC("RIFF")) | |
1240 | + { | |
1241 | + throw DLSParserException(m_FileName, "not an RIFF file"); | |
1242 | + } | |
1243 | + | |
1244 | + std::size_t riffSize = ReadUInt32(pFile); | |
1245 | + | |
1246 | + if (riffSize >= 4 && ReadFourCC(pFile) != MakeFourCC("DLS ")) | |
1247 | + { | |
1248 | + throw DLSParserException(m_FileName, "not a DLS file"); | |
1249 | + } | |
1250 | + | |
1251 | + riffSize -= 4; | |
1252 | + | |
1253 | + while (riffSize >= 2) | |
1254 | + { | |
1255 | + std::uint32_t cc = ReadFourCC(pFile); | |
1256 | + riffSize -= 4; | |
1257 | + | |
1258 | + switch (cc) | |
1259 | + { | |
1260 | + case MakeFourCC("LIST"): | |
1261 | + { | |
1262 | + std::uint32_t listSize = ReadUInt32(pFile); | |
1263 | + riffSize -= 4; | |
1264 | + | |
1265 | + std::uint32_t listType = ReadFourCC(pFile); | |
1266 | + std::uint32_t currentListSize = listSize - 4; | |
1267 | + | |
1268 | + switch (listType) | |
1269 | + { | |
1270 | + case MakeFourCC("INFO"): | |
1271 | + while (currentListSize >= 2) | |
1272 | + { | |
1273 | + currentListSize -= ParseINFOItem(pFile, ChunkLocation::DLS); | |
1274 | + } | |
1275 | + break; | |
1276 | + | |
1277 | + case MakeFourCC("lins"): | |
1278 | + while (currentListSize >= 2) | |
1279 | + { | |
1280 | + currentListSize -= ParseLINSItem(pFile); | |
1281 | + } | |
1282 | + break; | |
1283 | + | |
1284 | + case MakeFourCC("wvpl"): | |
1285 | + m_DLS.WavePoolOffset = static_cast<std::uint32_t>(::tf_tell(pFile)); | |
1286 | + break; | |
1287 | + | |
1288 | + default: | |
1289 | + PrintUnknownChunk(listType); | |
1290 | + break; | |
1291 | + } | |
1292 | + | |
1293 | + DoSkip(pFile, Align2(currentListSize)); | |
1294 | + riffSize -= Align2(listSize); | |
1295 | + } | |
1296 | + break; | |
1297 | + | |
1298 | + case MakeFourCC("vers"): | |
1299 | + riffSize -= ParseVERS(pFile); | |
1300 | + break; | |
1301 | + | |
1302 | + case MakeFourCC("dlid"): | |
1303 | + riffSize -= ParseDLID(pFile); | |
1304 | + break; | |
1305 | + | |
1306 | + case MakeFourCC("colh"): | |
1307 | + riffSize -= ParseCOLH(pFile); | |
1308 | + break; | |
1309 | + | |
1310 | + case MakeFourCC("ptbl"): | |
1311 | + riffSize -= ParsePTBL(pFile); | |
1312 | + break; | |
1313 | + | |
1314 | + default: | |
1315 | + PrintUnknownChunk(cc); | |
1316 | + riffSize -= SkipChunk(pFile); | |
1317 | + break; | |
1318 | + } | |
1319 | + } | |
1320 | + | |
1321 | + if (riffSize) | |
1322 | + { | |
1323 | + // skip padding byte | |
1324 | + tf_getc(pFile); | |
1325 | + riffSize--; | |
1326 | + } | |
1327 | + | |
1328 | + if (tf_getc(pFile) != EOF) | |
1329 | + { | |
1330 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: data exist after the RIFF chunk", m_FileName.c_str()); | |
1331 | + } | |
1332 | + } | |
1333 | + | |
1334 | + DLSWaveInfo ParseWAVEList(std::uint32_t offset) | |
1335 | + { | |
1336 | + std::unique_ptr<timidity_file, TFFileCloser> pFile(::open_file(m_FileName.data(), 1, OF_NORMAL)); | |
1337 | + | |
1338 | + if (::tf_seek(pFile.get(), offset, SEEK_SET) == -1) | |
1339 | + { | |
1340 | + throw DLSParserException(m_FileName, "cannot seek file"); | |
1341 | + } | |
1342 | + | |
1343 | + if (ReadFourCC(pFile.get()) != MakeFourCC("LIST")) | |
1344 | + { | |
1345 | + throw DLSParserException(m_FileName, "expected 'LIST'"); | |
1346 | + } | |
1347 | + | |
1348 | + std::uint32_t listSize = ReadUInt32(pFile.get()); | |
1349 | + | |
1350 | + if (ReadFourCC(pFile.get()) != MakeFourCC("wave")) | |
1351 | + { | |
1352 | + throw DLSParserException(m_FileName, "expected 'wave'"); | |
1353 | + } | |
1354 | + | |
1355 | + listSize -= 4; | |
1356 | + DLSWaveInfo waveInfo = {}; | |
1357 | + | |
1358 | + while (listSize >= 2) | |
1359 | + { | |
1360 | + std::uint32_t cc = ReadFourCC(pFile.get()); | |
1361 | + listSize -= 4; | |
1362 | + | |
1363 | + switch (cc) | |
1364 | + { | |
1365 | + case MakeFourCC("LIST"): | |
1366 | + { | |
1367 | + std::uint32_t listSize2 = ReadUInt32(pFile.get()); | |
1368 | + listSize -= 4; | |
1369 | + std::uint32_t listType2 = ReadFourCC(pFile.get()); | |
1370 | + std::uint32_t currentListSize2 = listSize2 - 4; | |
1371 | + | |
1372 | + switch (listType2) | |
1373 | + { | |
1374 | + case MakeFourCC("INFO"): | |
1375 | + while (currentListSize2 >= 2) | |
1376 | + { | |
1377 | + currentListSize2 -= ParseINFOItem(pFile.get(), ChunkLocation::WAVE); | |
1378 | + } | |
1379 | + break; | |
1380 | + | |
1381 | + default: | |
1382 | + PrintUnknownChunk(listType2); | |
1383 | + break; | |
1384 | + } | |
1385 | + | |
1386 | + DoSkip(pFile.get(), Align2(currentListSize2)); | |
1387 | + listSize -= Align2(listSize2); | |
1388 | + } | |
1389 | + break; | |
1390 | + | |
1391 | + case MakeFourCC("dlib"): | |
1392 | + listSize -= ParseDLID(pFile.get()); | |
1393 | + break; | |
1394 | + | |
1395 | + case MakeFourCC("fmt "): | |
1396 | + { | |
1397 | + std::uint32_t chunkSize = ReadUInt32(pFile.get()); | |
1398 | + listSize -= 4; | |
1399 | + | |
1400 | + if (chunkSize >= 16) | |
1401 | + { | |
1402 | + waveInfo.FormatTag = ReadUInt16(pFile.get()); | |
1403 | + waveInfo.Channels = ReadUInt16(pFile.get()); | |
1404 | + | |
1405 | + if (waveInfo.Channels != 1) | |
1406 | + { | |
1407 | + throw DLSParserException(m_FileName, "multichannel samples are not supported"); | |
1408 | + } | |
1409 | + | |
1410 | + waveInfo.SamplesPerSec = ReadUInt32(pFile.get()); | |
1411 | + waveInfo.AvgBytesPerSec = ReadUInt32(pFile.get()); | |
1412 | + waveInfo.BlockAlign = ReadUInt16(pFile.get()); | |
1413 | + waveInfo.BitsPerSample = ReadUInt16(pFile.get()); | |
1414 | + | |
1415 | + DoSkip(pFile.get(), Align2(chunkSize) - 16); | |
1416 | + } | |
1417 | + else | |
1418 | + { | |
1419 | + ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: warning: 'fmt ' chunk has invalid size", m_FileName.c_str()); | |
1420 | + DoSkip(pFile.get(), Align2(chunkSize)); | |
1421 | + } | |
1422 | + | |
1423 | + listSize -= Align2(chunkSize); | |
1424 | + } | |
1425 | + break; | |
1426 | + | |
1427 | + case MakeFourCC("data"): | |
1428 | + { | |
1429 | + std::uint32_t chunkSize = ReadUInt32(pFile.get()); | |
1430 | + listSize -= 4; | |
1431 | + waveInfo.pData.reset(reinterpret_cast<char*>(safe_malloc(chunkSize))); | |
1432 | + | |
1433 | +#ifdef LITTLE_ENDIAN | |
1434 | + if (::tf_read(waveInfo.pData.get(), 1, chunkSize, pFile.get()) != chunkSize) | |
1435 | + { | |
1436 | + throw DLSParserException(m_FileName, "unexpected end of file"); | |
1437 | + } | |
1438 | +#else | |
1439 | + for (std::size_t i = 0; i < chunkSize / 2; i++) | |
1440 | + { | |
1441 | + reinterpret_cast<std::uint16_t*>(waveInfo.pData.get())[i] = ReadUInt16(pFile.get()); | |
1442 | + } | |
1443 | +#endif | |
1444 | + waveInfo.DataLength = chunkSize; | |
1445 | + | |
1446 | + if (chunkSize & 1) | |
1447 | + { | |
1448 | + DoSkip(pFile.get(), 1); | |
1449 | + } | |
1450 | + | |
1451 | + listSize -= Align2(chunkSize); | |
1452 | + } | |
1453 | + break; | |
1454 | + | |
1455 | + case MakeFourCC("wsmp"): | |
1456 | + listSize -= ParseWSMP(pFile.get(), ChunkLocation::WAVE, &waveInfo.SampleInfo.emplace()); | |
1457 | + break; | |
1458 | + | |
1459 | + default: | |
1460 | + PrintUnknownChunk(cc); | |
1461 | + listSize -= SkipChunk(pFile.get()); | |
1462 | + break; | |
1463 | + } | |
1464 | + } | |
1465 | + | |
1466 | + waveInfo.DataLength /= (waveInfo.BitsPerSample == 0 ? 1 : waveInfo.BitsPerSample / 8); | |
1467 | + return waveInfo; | |
1468 | + } | |
1469 | + | |
1470 | + std::string m_FileName; | |
1471 | + DLSCollection m_DLS = {}; | |
1472 | +}; | |
1473 | + | |
1474 | +struct InstrumentCacheEntry | |
1475 | +{ | |
1476 | + InstrumentCacheEntry( | |
1477 | + std::string_view filePath, | |
1478 | + std::uint8_t bank, | |
1479 | + std::uint8_t programNumber, | |
1480 | + std::uint8_t note, | |
1481 | + std::unique_ptr<Instrument, InstrumentDeleter> pInstrument | |
1482 | + ) | |
1483 | + : FilePath(filePath), Bank(bank), ProgramNumber(programNumber), Note(note), pInstrument(std::move(pInstrument)) | |
1484 | + { | |
1485 | + } | |
1486 | + | |
1487 | + std::string FilePath; | |
1488 | + std::uint8_t Bank; | |
1489 | + std::int8_t ProgramNumber; | |
1490 | + std::int8_t Note; | |
1491 | + std::unique_ptr<Instrument, InstrumentDeleter> pInstrument; | |
1492 | + std::vector<Instrument*> RefInstruments; | |
1493 | +}; | |
1494 | + | |
1495 | +class InstrumentCache | |
1496 | +{ | |
1497 | +public: | |
1498 | + Instrument* LoadDLS(std::string filePath, std::uint8_t bank, std::int8_t programNumber, std::int8_t note) | |
1499 | + { | |
1500 | + try | |
1501 | + { | |
1502 | + auto itDLS = m_DLSParsers.find(filePath); | |
1503 | + | |
1504 | + if (itDLS == m_DLSParsers.end()) | |
1505 | + { | |
1506 | + DLSParser parser(filePath); | |
1507 | + parser.Parse(); | |
1508 | + itDLS = m_DLSParsers.emplace(filePath, std::move(parser)).first; | |
1509 | + } | |
1510 | + | |
1511 | + auto itInst = std::find_if( | |
1512 | + m_Instruments.begin(), | |
1513 | + m_Instruments.end(), | |
1514 | + [&filePath, bank, programNumber, note] (auto&& x) | |
1515 | + { | |
1516 | + return x.FilePath == filePath && x.Bank == bank && x.ProgramNumber == programNumber | |
1517 | + && (note < 0 || x.Note == note); | |
1518 | + } | |
1519 | + ); | |
1520 | + | |
1521 | + if (itInst == m_Instruments.end()) | |
1522 | + { | |
1523 | + auto pInstrument = itDLS->second.BuildInstrument(bank, programNumber, note); | |
1524 | + | |
1525 | + m_Instruments.emplace_back(filePath, bank, programNumber, note, std::move(pInstrument)); | |
1526 | + itInst = std::prev(m_Instruments.end()); | |
1527 | + } | |
1528 | + | |
1529 | + std::unique_ptr<Instrument, InstrumentDeleter> pInstRef(reinterpret_cast<Instrument*>(safe_calloc(sizeof(Instrument), 1))); | |
1530 | + itInst->RefInstruments.push_back(pInstRef.get()); | |
1531 | + pInstRef->type = itInst->pInstrument->type; | |
1532 | + pInstRef->instname = safe_strdup(itInst->pInstrument->instname); | |
1533 | + pInstRef->samples = itInst->pInstrument->samples; | |
1534 | + pInstRef->sample = reinterpret_cast<Sample*>(safe_calloc(sizeof(Sample), itInst->pInstrument->samples)); | |
1535 | + std::copy_n(itInst->pInstrument->sample, itInst->pInstrument->samples, pInstRef->sample); | |
1536 | + std::for_each(pInstRef->sample, pInstRef->sample + pInstRef->samples, [] (auto&& x) { x.data_alloced = false; }); | |
1537 | + | |
1538 | + return pInstRef.release(); | |
1539 | + } | |
1540 | + catch (const std::exception& e) | |
1541 | + { | |
1542 | + char str[] = "%s"; | |
1543 | + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, str, e.what()); | |
1544 | + return nullptr; | |
1545 | + } | |
1546 | + } | |
1547 | + | |
1548 | + void FreeInstrument(Instrument* pInstrument) | |
1549 | + { | |
1550 | + safe_free(pInstrument->instname); | |
1551 | + pInstrument->instname = nullptr; | |
1552 | + | |
1553 | + auto it = std::find_if( | |
1554 | + m_Instruments.begin(), | |
1555 | + m_Instruments.end(), | |
1556 | + [pInstrument] (auto&& x) | |
1557 | + { | |
1558 | + auto it = std::find(x.RefInstruments.begin(), x.RefInstruments.end(), pInstrument); | |
1559 | + return it != x.RefInstruments.end(); | |
1560 | + } | |
1561 | + ); | |
1562 | + | |
1563 | + if (it != m_Instruments.end()) | |
1564 | + { | |
1565 | + it->RefInstruments.erase(std::find(it->RefInstruments.begin(), it->RefInstruments.end(), pInstrument)); | |
1566 | + | |
1567 | + if (it->RefInstruments.empty()) | |
1568 | + { | |
1569 | + m_Instruments.erase(it); | |
1570 | + } | |
1571 | + } | |
1572 | + } | |
1573 | + | |
1574 | + void FreeAll() | |
1575 | + { | |
1576 | + m_Instruments.clear(); | |
1577 | + m_DLSParsers.clear(); | |
1578 | + } | |
1579 | + | |
1580 | +private: | |
1581 | + std::unordered_map<std::string, DLSParser> m_DLSParsers; | |
1582 | + std::vector<InstrumentCacheEntry> m_Instruments; | |
1583 | +}; | |
1584 | + | |
1585 | +InstrumentCache GlobalInstrumentCache; | |
1586 | + | |
1587 | +} // namespace TimDLS | |
1588 | + | |
1589 | +extern "C" void init_dls(void) | |
1590 | +{ | |
1591 | +} | |
1592 | + | |
1593 | +extern "C" void free_dls(void) | |
1594 | +{ | |
1595 | + TimDLS::GlobalInstrumentCache.FreeAll(); | |
1596 | +} | |
1597 | + | |
1598 | +extern "C" Instrument *extract_dls_file(char *sample_file, uint8 font_bank, int8 font_preset, int8 font_keynote) | |
1599 | +{ | |
1600 | + return TimDLS::GlobalInstrumentCache.LoadDLS(sample_file, font_bank, font_preset, font_keynote); | |
1601 | +} | |
1602 | + | |
1603 | +extern "C" void free_dls_file(Instrument *ip) | |
1604 | +{ | |
1605 | + TimDLS::GlobalInstrumentCache.FreeInstrument(ip); | |
1606 | +} |
@@ -0,0 +1,11 @@ | ||
1 | + | |
2 | +#pragma once | |
3 | + | |
4 | +#ifdef ENABLE_DLS | |
5 | + | |
6 | +void init_dls(void); | |
7 | +void free_dls(void); | |
8 | +Instrument *extract_dls_file(char *sample_file, uint8 font_bank, int8 font_preset, int8 font_keynote); | |
9 | +void free_dls_file(Instrument *ip); | |
10 | + | |
11 | +#endif |
@@ -50,6 +50,7 @@ | ||
50 | 50 | #include "quantity.h" |
51 | 51 | #include "freq.h" |
52 | 52 | #include "support.h" |
53 | +#include "dls.h" | |
53 | 54 | #include "sfz.h" |
54 | 55 | |
55 | 56 | #define INSTRUMENT_HASH_SIZE 128 |
@@ -143,6 +144,9 @@ void free_instrument(Instrument *ip) | ||
143 | 144 | #ifdef INT_SYNTH |
144 | 145 | extern void free_int_synth_file(Instrument *ip); |
145 | 146 | #endif |
147 | +#ifdef ENABLE_DLS | |
148 | + extern void free_dls_file(Instrument *ip); | |
149 | +#endif | |
146 | 150 | #ifdef ENABLE_SFZ |
147 | 151 | extern void free_sfz_file(Instrument *ip); |
148 | 152 | #endif |
@@ -175,6 +179,11 @@ void free_instrument(Instrument *ip) | ||
175 | 179 | free_sfz_file(ip); |
176 | 180 | break; |
177 | 181 | #endif |
182 | +#ifdef ENABLE_DLS | |
183 | + case INST_DLS: | |
184 | + free_dls_file(ip); | |
185 | + break; | |
186 | +#endif | |
178 | 187 | } |
179 | 188 | safe_free(ip); |
180 | 189 | } |
@@ -1670,6 +1679,14 @@ Instrument *load_instrument(int dr, int b, int prog, int elm) | ||
1670 | 1679 | ip = extract_sfz_file(bank->tone[prog][elm]->name); |
1671 | 1680 | break; |
1672 | 1681 | #endif |
1682 | +#ifdef ENABLE_DLS | |
1683 | + case 6: /* dls extension */ | |
1684 | + font_bank = bank->tone[prog][elm]->font_bank; | |
1685 | + font_preset = bank->tone[prog][elm]->font_preset; | |
1686 | + font_keynote = bank->tone[prog][elm]->font_keynote; | |
1687 | + ip = extract_dls_file(bank->tone[prog][elm]->name, font_bank, font_preset, font_keynote); | |
1688 | + break; | |
1689 | +#endif | |
1673 | 1690 | default: |
1674 | 1691 | goto TONEBANK_INSTRUMENT_NULL; |
1675 | 1692 | break; |
@@ -110,6 +110,7 @@ enum { | ||
110 | 110 | #define INST_MMS 4 /* %mms */ |
111 | 111 | #define INST_SCC 5 /* %scc */ |
112 | 112 | #define INST_SFZ 6 /* %sfz */ |
113 | +#define INST_DLS 7 /* %dls */ | |
113 | 114 | |
114 | 115 | /* sfSampleType */ |
115 | 116 | #define SF_SAMPLETYPE_MONO 1 |
@@ -144,7 +145,8 @@ typedef struct { | ||
144 | 145 | 3: %mms |
145 | 146 | 4: %scc |
146 | 147 | 5: %sfz |
147 | - 6-255: reserved | |
148 | + 6: %dls | |
149 | + 7-255: reserved | |
148 | 150 | */ |
149 | 151 | int16 amp; |
150 | 152 | int8 amp_normalize; |
@@ -562,6 +562,9 @@ void mix_voice(DATA_T *buf, int v, int32 c) | ||
562 | 562 | #ifdef ENABLE_SFZ |
563 | 563 | case INST_SFZ: |
564 | 564 | #endif |
565 | +#ifdef ENABLE_DLS | |
566 | + case INST_DLS: | |
567 | +#endif | |
565 | 568 | if(opt_resample_over_sampling){ |
566 | 569 | int32 c2 = c * opt_resample_over_sampling; |
567 | 570 | resample_voice(v, sp, c2); |
@@ -405,6 +405,9 @@ void mix_voice_thread(DATA_T *buf, int v, int32 c, int thread) | ||
405 | 405 | #ifdef ENABLE_SFZ |
406 | 406 | case INST_SFZ: |
407 | 407 | #endif |
408 | +#ifdef ENABLE_DLS | |
409 | + case INST_DLS: | |
410 | +#endif | |
408 | 411 | if(opt_resample_over_sampling){ |
409 | 412 | int32 c2 = c * opt_resample_over_sampling; |
410 | 413 | resample_voice(v, sp, c2); |
@@ -103,6 +103,7 @@ | ||
103 | 103 | #include "thread.h" |
104 | 104 | #include "miditrace.h" |
105 | 105 | #include "flac_a.h" |
106 | +#include "dls.h" | |
106 | 107 | #include "sfz.h" |
107 | 108 | ///r |
108 | 109 | #ifdef __BORLANDC__ |
@@ -1824,7 +1825,48 @@ static int set_gus_patchconf(const char *name, int line, | ||
1824 | 1825 | opts++; |
1825 | 1826 | } |
1826 | 1827 | #endif |
1827 | - else if(strcmp(pat, "%pat") == 0) /* pat extention */ | |
1828 | +#ifdef ENABLE_DLS | |
1829 | + else if (strcmp(pat, "%dls") == 0) /* dls extension */ | |
1830 | + { | |
1831 | + /* %dls filename bank prog [note-to-use] | |
1832 | + * %dls filename 128 bank key | |
1833 | + */ | |
1834 | + | |
1835 | + if (opts[0] == NULL || opts[1] == NULL || opts[2] == NULL || | |
1836 | + (atoi(opts[1]) == 128 && opts[3] == NULL)) | |
1837 | + { | |
1838 | + ctl->cmsg(CMSG_ERROR, VERB_NORMAL, | |
1839 | + "%s: line %d: Syntax error", name, line); | |
1840 | + return 1; | |
1841 | + } | |
1842 | + tone->name = safe_strdup(opts[0]); | |
1843 | + tone->instype = 6; // dls | |
1844 | + if (atoi(opts[1]) == 128) /* drum */ | |
1845 | + { | |
1846 | + tone->font_bank = 128; | |
1847 | + tone->font_preset = atoi(opts[2]); | |
1848 | + tone->font_keynote = atoi(opts[3]); | |
1849 | + opts += 4; | |
1850 | + } | |
1851 | + else | |
1852 | + { | |
1853 | + tone->font_bank = atoi(opts[1]); | |
1854 | + tone->font_preset = atoi(opts[2]); | |
1855 | + | |
1856 | + if (opts[3] && isdigit(opts[3][0])) | |
1857 | + { | |
1858 | + tone->font_keynote = atoi(opts[3]); | |
1859 | + opts += 4; | |
1860 | + } | |
1861 | + else | |
1862 | + { | |
1863 | + tone->font_keynote = -1; | |
1864 | + opts += 3; | |
1865 | + } | |
1866 | + } | |
1867 | + } | |
1868 | +#endif | |
1869 | + else if(strcmp(pat, "%pat") == 0) /* pat extention */ | |
1828 | 1870 | { |
1829 | 1871 | tone->instype = 0; // pat |
1830 | 1872 | tone->name = safe_strdup(pat); |
@@ -8407,6 +8449,9 @@ MAIN_INTERFACE void timidity_init_player(void) | ||
8407 | 8449 | #ifdef INT_SYNTH |
8408 | 8450 | init_int_synth(); |
8409 | 8451 | #endif // INT_SYNTH |
8452 | +#ifdef ENABLE_DLS | |
8453 | + init_dls(); | |
8454 | +#endif | |
8410 | 8455 | #ifdef ENABLE_SFZ |
8411 | 8456 | init_sfz(); |
8412 | 8457 | #endif |
@@ -9062,6 +9107,9 @@ int main(int argc, char **argv) | ||
9062 | 9107 | #ifdef ENABLE_SFZ |
9063 | 9108 | free_sfz(); |
9064 | 9109 | #endif |
9110 | +#ifdef ENABLE_DLS | |
9111 | + free_dls(); | |
9112 | +#endif | |
9065 | 9113 | #ifdef INT_SYNTH |
9066 | 9114 | free_int_synth(); |
9067 | 9115 | #endif // INT_SYNTH |
@@ -9144,6 +9192,9 @@ static void w32_exit(void) | ||
9144 | 9192 | #ifdef ENABLE_SFZ |
9145 | 9193 | free_sfz(); |
9146 | 9194 | #endif |
9195 | +#ifdef ENABLE_DLS | |
9196 | + free_dls(); | |
9197 | +#endif | |
9147 | 9198 | #ifdef INT_SYNTH |
9148 | 9199 | free_int_synth(); |
9149 | 9200 | #endif // INT_SYNTH |