-
Notifications
You must be signed in to change notification settings - Fork 0
/
sf2.hpp
2949 lines (2732 loc) · 103 KB
/
sf2.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#pragma once
//if long comments quoting standard bother you,
//use /\*(.|\n)*?\*/ regex command to remove them
#include <string>
#include <vector>
#include <cstdint>
#include <algorithm>
#include <cmath>
#include <fstream>
#ifndef M_TAU
#define M_TAU 6.28318530717958647692
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include "RIFF.hpp"
#include "DynamicPool.hpp"
#ifdef SF2_DEBUG
#define SF2_DEBUG_OUTPUT(msg) {printf(msg);}
#else
#define SF2_DEBUG_OUTPUT(msg) {}
#endif
namespace SF2
{
typedef uint8_t byte;
typedef uint16_t word;
typedef uint32_t doubleword;
typedef int8_t CHAR;
typedef uint8_t BYTE;
typedef int16_t SHORT;
typedef uint16_t WORD;
typedef uint32_t DWORD;
double fastPow(double a, double b) {
union {
double d;
int x[2];
} u = { a };
u.x[1] = (int)(b * (u.x[1] - 1072632447) + 1072632447);
u.x[0] = 0;
return u.d;
}
inline float cents_to_hertz(float cents)
{
return std::pow(2.0f, cents / 1200.0f);
}
inline float hertz_to_cents(float hz)
{
return 1200.0f * std::log2(hz);
}
inline float decibels_to_gain(float db)
{
//return (db > -100.0f)?std::pow(10.0f, db * 0.05f):0.0f;
return (db > -100.0f)?fastPow(10.0f, db * 0.05f):0.0f;
}
inline float gain_to_decibels(float gain)
{
return (gain <= 0.00001f)?-100.0f:(20.0f * std::log10(gain));
}
inline float calc_interval_cents(float hz1, float hz2)
{
return hertz_to_cents(hz2/hz1);
}
inline float apply_interval_cents(float hz, float cents)
{
return hz*cents_to_hertz(cents);
}
float timecents_to_seconds(float timecents)
{
//return (timecents == -32768)?0.000001f:std::pow(2.0f, timecents / 1200.0f);
return (timecents <= -12000)?0.001f:std::pow(2.0f, timecents / 1200.0f);
}
float seconds_to_timecents(float seconds)
{
return 1200.0f*std::log2(seconds);
}
inline void constant_power_pan(float& factor_L, float& factor_R, float pan)
{
//constant is sqrt(2.0)/2.0
constexpr const float sqrt2_2 = 0.70710678f;
pan = pan*M_TAU*0.125f;
factor_L = sqrt2_2*(cos(pan)-sin(pan));
factor_R = sqrt2_2*(cos(pan)+sin(pan));
}
inline float clamp_panning(float pan)
{
pan = std::max(std::min(pan, 2.0f), -2.0f);
pan = (pan > 1.0f)?(2.0f-pan):((pan < -1.0f)?-(-2.0f+pan):pan);
return pan;
}
inline float lerp(float a, float b, float f)
{
return (a * (1.0f - f)) + (b * f);
}
//algebraically simplified algorithm, less precise
inline float fast_lerp(float a, float b, float f)
{
return a + f * (b - a);
}
//input must be in range [0, 127]
//might as well precompute a table for it later
//TODO: write a program to precompute 127 values for convex curve
inline float convex_curve(float input)
{
if(input == 0.0f) return 0.0f;//mathematically undefined
return 1.0f+20.0f*std::log10((input*input)/(127.0f*127.0f))/96.0f;
}
inline float concave_curve(float input)
{
return 1.0f-convex_curve(127.0f-input);
}
//SoundFont2-structured RIFF
//I was planning to use this for editing, but...
struct RIFF_SoundFont2
{
//Supplemental Information
struct INFO_chunk
{
RIFF::RIFF::chunk* ifil;
//Refers to the target Sound Engine
RIFF::RIFF::chunk* isng;
//Refers to the Sound Font Bank Name
RIFF::RIFF::chunk* INAM;
//Refers to the Sound ROM Name
RIFF::RIFF::chunk* irom;
//Refers to the Sound ROM Version
RIFF::RIFF::chunk* iver;
//Refers to the Date of Creation of the Bank
RIFF::RIFF::chunk* ICRD;
//Sound Designers and Engineers for the Bank
RIFF::RIFF::chunk* IENG;
//Product for which the Bank was intended
RIFF::RIFF::chunk* IPRD;
//Contains any Copyright message
RIFF::RIFF::chunk* ICOP;
//Contains any Comments on the Bank
RIFF::RIFF::chunk* ICMT;
//The SoundFont tools used to create and alter the bank
RIFF::RIFF::chunk* ISFT;
};
INFO_chunk INFO;
//The Sample Binary Data
struct sdta_chunk
{
//The Digital Audio Samples for the upper 16 bits
RIFF::RIFF::chunk* smpl;
//The Digital Audio Samples for the lower 8 bits
RIFF::RIFF::chunk* sm24;
};
sdta_chunk sdta;
//The Preset, Instrument, and Sample Header data
struct pdta_chunk
{
//The Preset Headers
RIFF::RIFF::chunk* phdr;
//The Preset Index list
RIFF::RIFF::chunk* pbag;
//The Preset Modulator list
RIFF::RIFF::chunk* pmod;
//The Preset Generator list
RIFF::RIFF::chunk* pgen;
//The Instrument Names and Indices
RIFF::RIFF::chunk* inst;
//The Instrument Index list
RIFF::RIFF::chunk* ibag;
//The Instrument Modulator list
RIFF::RIFF::chunk* imod;
//The Instrument Generator list
RIFF::RIFF::chunk* igen;
//The Sample Headers
RIFF::RIFF::chunk* shdr;
};
pdta_chunk pdta;
bool structurally_unsound = false;
RIFF_SoundFont2(RIFF::RIFF* riff)
{
#define cond_check(cond)\
{\
if(cond)\
{\
structurally_unsound = true;\
return;\
}\
}
#define get_subchunk(chunk, riff, id, start_index)\
{\
chunk.id = riff->get_chunk_by_id(\
RIFF::string_to_FOURCC(#id),\
start_index\
);\
}
auto& chunks = riff->chunks;
cond_check(chunks.empty());
//Check RIFF chunk, must be first
cond_check(!RIFF::FOURCC_equals(chunks[0]->id, "RIFF") || !RIFF::FOURCC_equals(chunks[0]->type, "sfbk"));
//Get INFO-list chunk
auto INFO_list_index = riff->get_chunk_index_by_id_type(
RIFF::string_to_FOURCC("LIST"),
RIFF::string_to_FOURCC("INFO")
);
//Check INFO-list chunk, must exist
cond_check(INFO_list_index == -1);
//Get INFO-list subchunks
get_subchunk(INFO, riff, ifil, INFO_list_index);
get_subchunk(INFO, riff, isng, INFO_list_index);
get_subchunk(INFO, riff, INAM, INFO_list_index);
get_subchunk(INFO, riff, irom, INFO_list_index);
get_subchunk(INFO, riff, iver, INFO_list_index);
get_subchunk(INFO, riff, ICRD, INFO_list_index);
get_subchunk(INFO, riff, IENG, INFO_list_index);
get_subchunk(INFO, riff, IPRD, INFO_list_index);
get_subchunk(INFO, riff, ICOP, INFO_list_index);
get_subchunk(INFO, riff, ICMT, INFO_list_index);
get_subchunk(INFO, riff, ISFT, INFO_list_index);
//Check mandatory subchunks
cond_check(!INFO.ifil);
//Get sdta chunk
auto sdta_index = riff->get_chunk_index_by_id_type(
RIFF::string_to_FOURCC("LIST"),
RIFF::string_to_FOURCC("sdta")
);
//Check sdta chunk, must exist
cond_check(sdta_index == -1);
//Get sdta subchunks
get_subchunk(sdta, riff, smpl, sdta_index);
get_subchunk(sdta, riff, sm24, sdta_index);
//Get pdta chunk
auto pdta_index = riff->get_chunk_index_by_id_type(
RIFF::string_to_FOURCC("LIST"),
RIFF::string_to_FOURCC("sdta")
);
//Check pdta chunk, must exist
cond_check(pdta_index == -1);
//Get pdta subchunks
get_subchunk(pdta, riff, phdr, pdta_index);
get_subchunk(pdta, riff, pbag, pdta_index);
get_subchunk(pdta, riff, pmod, pdta_index);
get_subchunk(pdta, riff, pgen, pdta_index);
get_subchunk(pdta, riff, inst, pdta_index);
get_subchunk(pdta, riff, ibag, pdta_index);
get_subchunk(pdta, riff, imod, pdta_index);
get_subchunk(pdta, riff, igen, pdta_index);
get_subchunk(pdta, riff, shdr, pdta_index);
//Check chunk sizes
cond_check(pdta.phdr->size % 38);
cond_check(pdta.pbag->size % 4);
cond_check(pdta.pmod->size % 10);
cond_check(pdta.pgen->size % 4);
cond_check(pdta.inst->size % 22);
cond_check(pdta.ibag->size % 4);
cond_check(pdta.imod->size % 10);
cond_check(pdta.igen->size % 4);
cond_check(pdta.shdr->size % 46);
#undef check
#undef get_subchunk
}
};
struct SoundFont2
{
struct rangesType
{
BYTE byLo;
BYTE byHi;
//expand
inline rangesType& operator+=(const rangesType& rhs)
{
byLo = std::min(byLo, rhs.byLo);
byHi = std::max(byHi, rhs.byHi);
return *this;
}
//expand
friend inline rangesType operator+(rangesType lhs, const rangesType& rhs)
{
lhs += rhs;
return lhs;
}
//intersect
inline rangesType& operator*=(const rangesType& rhs)
{
byLo = std::max(byLo, rhs.byLo);
byHi = std::min(byHi, rhs.byHi);
return *this;
}
//intersect
friend inline rangesType operator*(rangesType lhs, const rangesType& rhs)
{
lhs *= rhs;
return lhs;
}
};
typedef union
{
rangesType ranges;
SHORT shAmount;
WORD wAmount;
} genAmountType;
typedef enum
{
monoSample = 1,
rightSample = 2,
leftSample = 4,
linkedSample = 8,
RomMonoSample = 0x8001,
RomRightSample = 0x8002,
RomLeftSample = 0x8004,
RomLinkedSample = 0x8008
} SFSampleLink;
inline bool IsSampleROM(SFSampleLink type) {return type & 0xFFF0;};
inline bool CheckSampleLinkType(SFSampleLink type)
{
return (type >= 1 && type <= 8) || (type >= 0x8001 && type <= 0x8008);
};
struct SFModulator
{
WORD enumeration;
};
struct SFGenerator
{
enum class GenType : WORD
{
/*The offset, in sample data points, beyond the Start sample header parameter to the
first sample data point to be played for this instrument. For example, if Start were 7
and startAddrOffset were 2, the first sample data point played would be sample data
point 9. */
startAddrsOffset = 0,
/*The offset, in sample sample data points, beyond the End sample header parameter to
the last sample data point to be played for this instrument. For example, if End were
17 and endAddrOffset were -2, the last sample data point played would be sample
data point 15.*/
endAddrsOffset = 1,
/*The offset, in sample data points, beyond the Startloop sample header parameter to
the first sample data point to be repeated in the loop for this instrument. For
example, if Startloop were 10 and startloopAddrsOffset were -1, the first repeated
loop sample data point would be sample data point 9.*/
startloopAddrsOffset = 2,
/*The offset, in sample data points, beyond the Endloop sample header parameter to
the sample data point considered equivalent to the Startloop sample data point for the
loop for this instrument. For example, if Endloop were 15 and endloopAddrsOffset
were 2, sample data point 17 would be considered equivalent to the Startloop sample
data point, and hence sample data point 16 would effectively precede Startloop
during looping. */
endloopAddrsOffset = 3,
/*The offset, in 32768 sample data point increments beyond the Start sample header
parameter and the first sample data point to be played in this instrument. This
parameter is added to the startAddrsOffset parameter. For example, if Start were 5,
startAddrsOffset were 3 and startAddrsCoarseOffset were 2, the first sample data
point played would be sample data point 65544. */
startAddrsCoarseOffset = 4,
/*This is the degree, in cents, to which a full scale excursion of the Modulation LFO
will influence pitch. A positive value indicates a positive LFO excursion increases
pitch; a negative value indicates a positive excursion decreases pitch. Pitch is always
modified logarithmically, that is the deviation is in cents, semitones, and octaves
rather than in Hz. For example, a value of 100 indicates that the pitch will first rise 1
semitone, then fall one semitone. */
modLfoToPitch = 5,
/*This is the degree, in cents, to which a full scale excursion of the Vibrato LFO will
influence pitch. A positive value indicates a positive LFO excursion increases pitch;
a negative value indicates a positive excursion decreases pitch. Pitch is always
modified logarithmically, that is the deviation is in cents, semitones, and octaves
rather than in Hz. For example, a value of 100 indicates that the pitch will first rise 1
semitone, then fall one semitone. */
vibLfoToPitch = 6,
/*This is the degree, in cents, to which a full scale excursion of the Modulation
Envelope will influence pitch. A positive value indicates an increase in pitch; a
negative value indicates a decrease in pitch. Pitch is always modified
logarithmically, that is the deviation is in cents, semitones, and octaves rather than in
Hz. For example, a value of 100 indicates that the pitch will rise 1 semitone at the
envelope peak. */
modEnvToPitch = 7,
/*This is the cutoff and resonant frequency of the lowpass filter in absolute cent units.
The lowpass filter is defined as a second order resonant pole pair whose pole
frequency in Hz is defined by the Initial Filter Cutoff parameter. When the cutoff
frequency exceeds 20kHz and the Q (resonance) of the filter is zero, the filter does
not affect the signal. */
initialFilterFc = 8,
/*This is the height above DC gain in centibels which the filter resonance exhibits at
the cutoff frequency. A value of zero or less indicates the filter is not resonant; the
gain at the cutoff frequency (pole angle) may be less than zero when zero is
specified. The filter gain at DC is also affected by this parameter such that the gain
at DC is reduced by half the specified gain. For example, for a value of 100, the
filter gain at DC would be 5 dB below unity gain, and the height of the resonant peak
would be 10 dB above the DC gain, or 5 dB above unity gain. Note also that if
initialFilterQ is set to zero or less and the cutoff frequency exceeds 20 kHz, then the
filter response is flat and unity gain. */
initialFilterQ = 9,
/*This is the degree, in cents, to which a full scale excursion of the Modulation LFO
will influence filter cutoff frequency. A positive number indicates a positive LFO
excursion increases cutoff frequency; a negative number indicates a positive
excursion decreases cutoff frequency. Filter cutoff frequency is always modified
logarithmically, that is the deviation is in cents, semitones, and octaves rather than in
Hz. For example, a value of 1200 indicates that the cutoff frequency will first rise 1
octave, then fall one octave. */
modLfoToFilterFc = 10,
/*This is the degree, in cents, to which a full scale excursion of the Modulation
Envelope will influence filter cutoff frequency. A positive number indicates an
increase in cutoff frequency; a negative number indicates a decrease in filter cutoff
frequency. Filter cutoff frequency is always modified logarithmically, that is the
deviation is in cents, semitones, and octaves rather than in Hz. For example, a value
of 1000 indicates that the cutoff frequency will rise one octave at the envelope attack
peak. */
modEnvToFilterFc = 11,
/*The offset, in 32768 sample data point increments beyond the End sample header
parameter and the last sample data point to be played in this instrument. This
parameter is added to the endAddrsOffset parameter. For example, if End were
65536, startAddrsOffset were -3 and startAddrsCoarseOffset were -1, the last sample
data point played would be sample data point 32765. */
endAddrsCoarseOffset = 12,
/*This is the degree, in centibels, to which a full scale excursion of the Modulation
LFO will influence volume. A positive number indicates a positive LFO excursion
increases volume; a negative number indicates a positive excursion decreases
volume. Volume is always modified logarithmically, that is the deviation is in
decibels rather than in linear amplitude. For example, a value of 100 indicates that
the volume will first rise ten dB, then fall ten dB. */
modLfoToVolume = 13,
/*Unused, reserved. Should be ignored if encountered. */
unused1 = 14,
/*This is the degree, in 0.1% units, to which the audio output of the note is sent to the
chorus effects processor. A value of 0% or less indicates no signal is sent from this
note; a value of 100% or more indicates the note is sent at full level. Note that this
parameter has no effect on the amount of this signal sent to the dry or unprocessed
portion of the output. For example, a value of 250 indicates that the signal is sent at
25% of full level (attenuation of 12 dB from full level) to the chorus effects
processor. */
chorusEffectsSend = 15,
/*This is the degree, in 0.1% units, to which the audio output of the note is sent to the
reverb effects processor. A value of 0% or less indicates no signal is sent from this
note; a value of 100% or more indicates the note is sent at full level. Note that this
parameter has no effect on the amount of this signal sent to the dry or unprocessed
portion of the output. For example, a value of 250 indicates that the signal is sent at
25% of full level (attenuation of 12 dB from full level) to the reverb effects
processor. */
reverbEffectsSend = 16,
/*This is the degree, in 0.1% units, to which the dry audio output of the note is
positioned to the left or right output. A value of -50% or less indicates the signal is
sent entirely to the left output and not sent to the right output; a value of +50% or
more indicates the note is sent entirely to the right and not sent to the left. A value of
zero places the signal centered between left and right. For example, a value of -250
indicates that the signal is sent at 75% of full level to the left output and 25% of full
level to the right output. */
pan = 17,
/*Unused, reserved. Should be ignored if encountered. */
unused2 = 18,
/*Unused, reserved. Should be ignored if encountered. */
unused3 = 19,
/*Unused, reserved. Should be ignored if encountered. */
unused4 = 20,
/*This is the delay time, in absolute timecents, from key on until the Modulation LFO
begins its upward ramp from zero value. A value of 0 indicates a 1 second delay. A
negative value indicates a delay less than one second and a positive value a delay
longer than one second. The most negative number (-32768) conventionally
indicates no delay. For example, a delay of 10 msec would be 1200log2(.01) = -
7973. */
delayModLFO = 21,
/*This is the frequency, in absolute cents, of the Modulation LFOs triangular period.
A value of zero indicates a frequency of 8.176 Hz. A negative value indicates a
frequency less than 8.176 Hz; a positive value a frequency greater than 8.176 Hz.
For example, a frequency of 10 mHz would be 1200log2(.01/8.176) = -11610. */
freqModLFO = 22,
/*This is the delay time, in absolute timecents, from key on until the Vibrato LFO
begins its upward ramp from zero value. A value of 0 indicates a 1 second delay. A
negative value indicates a delay less than one second; a positive value a delay longer
than one second. The most negative number (-32768) conventionally indicates no
delay. For example, a delay of 10 msec would be 1200log2(.01) = -7973. */
delayVibLFO = 23,
/*This is the frequency, in absolute cents, of the Vibrato LFOs triangular period. A
value of zero indicates a frequency of 8.176 Hz. A negative value indicates a
frequency less than 8.176 Hz; a positive value a frequency greater than 8.176 Hz.
For example, a frequency of 10 mHz would be 1200log2(.01/8.176) = -11610. */
freqVibLFO = 24,
/*This is the delay time, in absolute timecents, between key on and the start of the
attack phase of the Modulation envelope. A value of 0 indicates a 1 second delay. A
negative value indicates a delay less than one second; a positive value a delay longer
than one second. The most negative number (-32768) conventionally indicates no
delay. For example, a delay of 10 msec would be 1200log2(.01) = -7973. */
delayModEnv = 25,
/*This is the time, in absolute timecents, from the end of the Modulation Envelope
Delay Time until the point at which the Modulation Envelope value reaches its peak.
Note that the attack is convex; the curve is nominally such that when applied to a
decibel or semitone parameter, the result is linear in amplitude or Hz respectively. A
value of 0 indicates a 1 second attack time. A negative value indicates a time less
than one second; a positive value a time longer than one second. The most negative
number (-32768) conventionally indicates instantaneous attack. For example, an
attack time of 10 msec would be 1200log2(.01) = -7973. */
attackModEnv = 26,
/*This is the time, in absolute timecents, from the end of the attack phase to the entry
into decay phase, during which the envelope value is held at its peak. A value of 0
indicates a 1 second hold time. A negative value indicates a time less than one
second; a positive value a time longer than one second. The most negative number (-
32768) conventionally indicates no hold phase. For example, a hold time of 10 msec
would be 1200log2(.01) = -7973. */
holdModEnv = 27,
/*This is the time, in absolute timecents, for a 100% change in the Modulation
Envelope value during decay phase. For the Modulation Envelope, the decay phase
linearly ramps toward the sustain level. If the sustain level were zero, the
Modulation Envelope Decay Time would be the time spent in decay phase. A value
of 0 indicates a 1 second decay time for a zero-sustain level. A negative value
indicates a time less than one second; a positive value a time longer than one second.
For example, a decay time of 10 msec would be 1200log2(.01) = -7973. */
decayModEnv = 28,
/*This is the decrease in level, expressed in 0.1% units, to which the Modulation
Envelope value ramps during the decay phase. For the Modulation Envelope, the
sustain level is properly expressed in percent of full scale. Because the volume
envelope sustain level is expressed as an attenuation from full scale, the sustain level
is analogously expressed as a decrease from full scale. A value of 0 indicates the
sustain level is full level; this implies a zero duration of decay phase regardless of
decay time. A positive value indicates a decay to the corresponding level. Values
less than zero are to be interpreted as zero; values above 1000 are to be interpreted as
1000. For example, a sustain level which corresponds to an absolute value 40% of
peak would be 600. */
sustainModEnv = 29,
/*This is the time, in absolute timecents, for a 100% change in the Modulation
Envelope value during release phase. For the Modulation Envelope, the release
phase linearly ramps toward zero from the current level. If the current level were
full scale, the Modulation Envelope Release Time would be the time spent in release
phase until zero value were reached. A value of 0 indicates a 1 second decay time
for a release from full level. A negative value indicates a time less than one second;
a positive value a time longer than one second. For example, a release time of 10
msec would be 1200log2(.01) = -7973. */
releaseModEnv = 30,
/*This is the degree, in timecents per KeyNumber units, to which the hold time of the
Modulation Envelope is decreased by increasing MIDI key number. The hold time
at key number 60 is always unchanged. The unit scaling is such that a value of 100
provides a hold time which tracks the keyboard; that is, an upward octave causes the
hold time to halve. For example, if the Modulation Envelope Hold Time were -7973
= 10 msec and the Key Number to Mod Env Hold were 50 when key number 36 was
played, the hold time would be 20 msec. */
keynumToModEnvHold = 31,
/*This is the degree, in timecents per KeyNumber units, to which the hold time of the
Modulation Envelope is decreased by increasing MIDI key number. The hold time
at key number 60 is always unchanged. The unit scaling is such that a value of 100
provides a hold time that tracks the keyboard; that is, an upward octave causes the
hold time to halve. For example, if the Modulation Envelope Hold Time were -7973
= 10 msec and the Key Number to Mod Env Hold were 50 when key number 36 was
played, the hold time would be 20 msec. */
keynumToModEnvDecay = 32,
/*This is the delay time, in absolute timecents, between key on and the start of the
attack phase of the Volume envelope. A value of 0 indicates a 1 second delay. A
negative value indicates a delay less than one second; a positive value a delay longer
than one second. The most negative number (-32768) conventionally indicates no
delay. For example, a delay of 10 msec would be 1200log2(.01) = -7973. */
delayVolEnv = 33,
/*This is the time, in absolute timecents, from the end of the Volume Envelope Delay
Time until the point at which the Volume Envelope value reaches its peak. Note that
the attack is convex; the curve is nominally such that when applied to the decibel
volume parameter, the result is linear in amplitude. A value of 0 indicates a 1 second
attack time. A negative value indicates a time less than one second; a positive value
a time longer than one second. The most negative number (-32768) conventionally
indicates instantaneous attack. For example, an attack time of 10 msec would be
1200log2(.01) = -7973. */
attackVolEnv = 34,
/*This is the time, in absolute timecents, from the end of the attack phase to the entry
into decay phase, during which the Volume envelope value is held at its peak. A
value of 0 indicates a 1 second hold time. A negative value indicates a time less than
one second; a positive value a time longer than one second. The most negative
number (-32768) conventionally indicates no hold phase. For example, a hold time
of 10 msec would be 1200log2(.01) = -7973. */
holdVolEnv = 35,
/*This is the time, in absolute timecents, for a 100% change in the Volume Envelope
value during decay phase. For the Volume Envelope, the decay phase linearly ramps
toward the sustain level, causing a constant dB change for each time unit. If the
sustain level were -100dB, the Volume Envelope Decay Time would be the time
spent in decay phase. A value of 0 indicates a 1-second decay time for a zero-sustain
level. A negative value indicates a time less than one second; a positive value a time
longer than one second. For example, a decay time of 10 msec would be
1200log2(.01) = -7973. */
decayVolEnv = 36,
/*This is the decrease in level, expressed in centibels, to which the Volume Envelope
value ramps during the decay phase. For the Volume Envelope, the sustain level is
best expressed in centibels of attenuation from full scale. A value of 0 indicates the
sustain level is full level; this implies a zero duration of decay phase regardless of
decay time. A positive value indicates a decay to the corresponding level. Values
less than zero are to be interpreted as zero; conventionally 1000 indicates full
attenuation. For example, a sustain level which corresponds to an absolute value
12dB below of peak would be 120. */
sustainVolEnv = 37,
/*This is the time, in absolute timecents, for a 100% change in the Volume Envelope
value during release phase. For the Volume Envelope, the release phase linearly
ramps toward zero from the current level, causing a constant dB change for each
time unit. If the current level were full scale, the Volume Envelope Release Time
would be the time spent in release phase until 100dB attenuation were reached. A
value of 0 indicates a 1-second decay time for a release from full level. A negative
value indicates a time less than one second; a positive value a time longer than one
second. For example, a release time of 10 msec would be 1200log2(.01) = -7973. */
releaseVolEnv = 38,
/*This is the degree, in timecents per KeyNumber units, to which the hold time of the
Volume Envelope is decreased by increasing MIDI key number. The hold time at
key number 60 is always unchanged. The unit scaling is such that a value of 100
provides a hold time which tracks the keyboard; that is, an upward octave causes the
hold time to halve. For example, if the Volume Envelope Hold Time were -7973 =
10 msec and the Key Number to Vol Env Hold were 50 when key number 36 was
played, the hold time would be 20 msec. */
keynumToVolEnvHold = 39,
/*This is the degree, in timecents per KeyNumber units, to which the hold time of the
Volume Envelope is decreased by increasing MIDI key number. The hold time at
key number 60 is always unchanged. The unit scaling is such that a value of 100
provides a hold time that tracks the keyboard; that is, an upward octave causes the
hold time to halve. For example, if the Volume Envelope Hold Time were -7973 =
10 msec and the Key Number to Vol Env Hold were 50 when key number 36 was
played, the hold time would be 20 msec. */
keynumToVolEnvDecay = 40,
/*This is the index into the INST sub-chunk providing the instrument to be used for the
current preset zone. A value of zero indicates the first instrument in the list. The
value should never exceed two less than the size of the instrument list. The
instrument enumerator is the terminal generator for PGEN zones. As such, it should
only appear in the PGEN sub-chunk, and it must appear as the last generator
enumerator in all but the global preset zone. */
instrument = 41,
/*Unused, reserved. Should be ignored if encountered. */
reserved1 = 42,
/*This is the minimum and maximum MIDI key number values for which this preset
zone or instrument zone is active. The LS byte indicates the highest and the MS byte
the lowest valid key. The keyRange enumerator is optional, but when it does appear,
it must be the first generator in the zone generator list. */
keyRange = 43,
/*This is the minimum and maximum MIDI velocity values for which this preset zone
or instrument zone is active. The LS byte indicates the highest and the MS byte the
lowest valid velocity. The velRange enumerator is optional, but when it does appear,
it must be preceded only by keyRange in the zone generator list. */
velRange = 44,
/*The offset, in 32768 sample data point increments beyond the Startloop sample
header parameter and the first sample data point to be repeated in this instruments
loop. This parameter is added to the startloopAddrsOffset parameter. For example,
if Startloop were 5, startloopAddrsOffset were 3 and startAddrsCoarseOffset were 2,
the first sample data point in the loop would be sample data point 65544. */
startloopAddrsCoarseOffset = 45,
/*This enumerator forces the MIDI key number to effectively be interpreted as the
value given. This generator can only appear at the instrument level. Valid values are
from 0 to 127. */
keynum = 46,
/*This enumerator forces the MIDI velocity to effectively be interpreted as the value
given. This generator can only appear at the instrument level. Valid values are from
0 to 127. */
velocity = 47,
/*This is the attenuation, in centibels, by which a note is attenuated below full scale. A
value of zero indicates no attenuation; the note will be played at full scale. For
example, a value of 60 indicates the note will be played at 6 dB below full scale for
the note. */
initialAttenuation = 48,
/*Unused, reserved. Should be ignored if encountered. */
reserved2 = 49,
/*The offset, in 32768 sample data point increments beyond the Endloop sample
header parameter to the sample data point considered equivalent to the Startloop
sample data point for the loop for this instrument. This parameter is added to the
endloopAddrsOffset parameter. For example, if Endloop were 5,
endloopAddrsOffset were 3 and endAddrsCoarseOffset were 2, sample data point
65544 would be considered equivalent to the Startloop sample data point, and hence
sample data point 65543 would effectively precede Startloop during looping. */
endloopAddrsCoarseOffset = 50,
/*This is a pitch offset, in semitones, which should be applied to the note. A positive
value indicates the sound is reproduced at a higher pitch; a negative value indicates a
lower pitch. For example, a Coarse Tune value of -4 would cause the sound to be
reproduced four semitones flat. */
coarseTune = 51,
/*This is a pitch offset, in cents, which should be applied to the note. It is additive
with coarseTune. A positive value indicates the sound is reproduced at a higher
pitch; a negative value indicates a lower pitch. For example, a Fine Tuning value of -
5 would cause the sound to be reproduced five cents flat. */
fineTune = 52,
/*This is the index into the SHDR sub-chunk providing the sample to be used for the
current instrument zone. A value of zero indicates the first sample in the list. The
value should never exceed two less than the size of the sample list. The sampleID
enumerator is the terminal generator for IGEN zones. As such, it should only appear
in the IGEN sub-chunk, and it must appear as the last generator enumerator in all but
the global zone. */
sampleID = 53,
/*This enumerator indicates a value which gives a variety of Boolean flags describing
the sample for the current instrument zone. The sampleModes should only appear in
the IGEN sub-chunk, and should not appear in the global zone. The two LS bits of
the value indicate the type of loop in the sample: 0 indicates a sound reproduced with
no loop, 1 indicates a sound which loops continuously, 2 is unused but should be
interpreted as indicating no loop, and 3 indicates a sound which loops for the
duration of key depression then proceeds to play the remainder of the sample. */
sampleModes = 54,
/*Unused, reserved. Should be ignored if encountered. */
reserved3 = 55,
/*This parameter represents the degree to which MIDI key number influences pitch. A
value of zero indicates that MIDI key number has no effect on pitch; a value of 100
represents the usual tempered semitone scale. */
scaleTuning = 56,
/*This parameter provides the capability for a key depression in a given instrument to
terminate the playback of other instruments. This is particularly useful for
percussive instruments such as a hi-hat cymbal. An exclusive class value of zero
indicates no exclusive class; no special action is taken. Any other value indicates
that when this note is initiated, any other sounding note with the same exclusive class
value should be rapidly terminated. The exclusive class generator can only appear at
the instrument level. The scope of the exclusive class is the entire preset. In other
words, any other instrument zone within the same preset holding a corresponding
exclusive class will be terminated. */
exclusiveClass = 57,
/*This parameter represents the MIDI key number at which the sample is to be played
back at its original sample rate. If not present, or if present with a value of -1, then
the sample header parameter Original Key is used in its place. If it is present in the
range 0-127, then the indicated key number will cause the sample to be played back
at its sample header Sample Rate. For example, if the sample were a recording of a
piano middle C (Original Key = 60) at a sample rate of 22.050 kHz, and Root Key
were set to 69, then playing MIDI key number 69 (A above middle C) would cause a
piano note of pitch middle C to be heard.*/
overridingRootKey = 58,
/*Unused, reserved. Should be ignored if encountered. */
unused5 = 59,
/*Unused, reserved. Should be ignored if encountered. Unique name provides value
to end of defined list. */
endOper = 60
};
GenType enumeration;
friend inline bool operator==(const SFGenerator& lhs, const SFGenerator& rhs)
{
return lhs.enumeration == rhs.enumeration;
}
friend inline bool operator!=(const SFGenerator& lhs, const SFGenerator& rhs)
{
return !(lhs == rhs);
}
friend inline bool operator==(const SFGenerator& lhs, const GenType& rhs)
{
return lhs.enumeration == rhs;
}
friend inline bool operator!=(const SFGenerator& lhs, const GenType& rhs)
{
return !(lhs == rhs);
}
};
struct SFTransform
{
WORD enumeration;
};
struct sfVersionTag
{
WORD wMajor;
WORD wMinor;
};
RIFF::stream* stream;
sfVersionTag ifil;
sfVersionTag iver;
std::string szSoundEngine;
std::string szROM;
std::string szName;
std::string szDate;
std::string szProduct;
std::string szCreator;
std::string szCopyright;
std::string szComment;
std::string szTools;
struct HYDRA
{
struct sfPresetHeader
{
CHAR achPresetName[20];
WORD wPreset;//MIDI Preset Number
WORD wBank;//MIDI Bank Number
WORD wPresetBagNdx;//index to the presets zone list
DWORD dwLibrary;//reserved
DWORD dwGenre;//reserved
DWORD dwMorphology;//reserved
};
//Preset header list
std::vector<std::unique_ptr<sfPresetHeader>> phdr;
struct sfPresetBag
{
WORD wGenNdx;//index to the presets zone list of generators in PGEN
WORD wModNdx;//index to the list of modulators in PMOD
};
//Pointers to first entries of preset zone generator and modulator lists
std::vector<std::unique_ptr<sfPresetBag>> pbag;
struct sfModList
{
//indicates the source of data for the modulator
SFModulator sfModSrcOper;
//destination of the modulator
SFGenerator sfModDestOper;
//degree to which the source modulates the destination
//a zero value indicates there is no fixed amount.
SHORT modAmount;
//indicates the degree to which the source modulates the destination
//is to be controlled by the specified modulation source
SFModulator sfModAmtSrcOper;
//indicates that a transform of the specified type will be applied
//to the modulation source before application to the modulator
SFTransform sfModTransOper;
};
//Preset zone modulators
std::vector<std::unique_ptr<sfModList>> pmod;
struct sfGenList
{
SFGenerator sfGenOper;
//value to be assigned to the specified generator
//note that this can be of three formats.
genAmountType genAmount;
};
//Preset zone generators
std::vector<std::unique_ptr<sfGenList>> pgen;
struct sfInst
{
CHAR achInstName[20];
//index to the instruments zone list in the IBAG sub-chunk
WORD wInstBagNdx;
};
//Instrument list
std::vector<std::unique_ptr<sfInst>> inst;
struct sfInstBag
{
//index to the instrument zones list of generators in the IGEN sub-chunk
WORD wInstGenNdx;
//index to the list of modulators in the IMOD sub-chunk
WORD wInstModNdx;
};
//Pointers to first entries of instrument zone generator and modulator lists
std::vector<std::unique_ptr<sfInstBag>> ibag;
struct sfInstModList
{
//indicates the source of data for the modulator
SFModulator sfModSrcOper;
//indicates the destination of the modulator
SFGenerator sfModDestOper;
//indicates the degree to which the source modulates the destination
//a zero value indicates there is no fixed amount.
SHORT modAmount;
//indicates the degree to which the source modulates the destination
//is to be controlled by the specified modulation source
SFModulator sfModAmtSrcOper;
//indicates that a transform of the specified type will be applied
//to the modulation source before application to the modulator
SFTransform sfModTransOper;
};
//Instrument zone modulators
std::vector<std::unique_ptr<sfInstModList>> imod;
struct sfInstGenList
{
SFGenerator sfGenOper;
//value to be assigned to the specified generator
//note that this can be of three formats.
genAmountType genAmount;
};
//Instrument zone generators
std::vector<std::unique_ptr<sfInstGenList>> igen;
struct sfSample
{
CHAR achSampleName[20];
//index, in sample data points, from the beginning of the sample data
//field to the first data point of this sample.
DWORD dwStart;
//index, in sample data points, from the beginning of the sample data
//field to the first of the set of 46 zero valued data points following
//this sample.
DWORD dwEnd;
//index, in sample data points, from the beginning of the sample data
//field to the first data point in the loop of this sample.
DWORD dwStartloop;
//index, in sample data points, from the beginning of the sample data
//field to the first data point following the loop of this sample.
//Note that this is the data point equivalent to the first loop data point,
//and that to produce portable artifact free loops, the eight proximal data
//points surrounding both the Startloop and Endloop points should be identical.
DWORD dwEndloop;
//the sample rate, in hertz, at which this sample was acquired or to which
//it was most recently converted.
DWORD dwSampleRate;
//contains the MIDI key number of the recorded pitch of the sample
BYTE byOriginalKey;
//contains a pitch correction in cents that should be applied to
//the sample on playback.
//The purpose of this field is to compensate for any pitch errors during
//the sample recording process. The correction value is that of the correction
//to be applied. For example, if the sound is 4 cents sharp, a correction
//bringing it 4 cents flat is required; thus the value should be -4.
CHAR chCorrection;
//sample header link, accordingly indicated by sfSampleType
WORD wSampleLink;
//sample type
SFSampleLink sfSampleType;
};
//Samples
std::vector<std::unique_ptr<sfSample>> shdr;
};
HYDRA hydra;
size_t sample_data_offset;
size_t sample_data_24_offset;
struct Sample
{
std::string name;
uint32_t loop_start;
uint32_t loop_end;
uint32_t sample_rate;
uint8_t original_key;
int8_t correction;
uint32_t data_stream_offset;
std::unique_ptr<float[]> data;
uint32_t size;
SFSampleLink sample_type;
Sample* linked_sample;
void load_data(SoundFont2& sf2)
{
SF2_DEBUG_OUTPUT((std::string("Loading sample data \"") + name + "\"...\n").c_str());
auto data16 = std::make_unique<int16_t[]>(size);
//set up position of the stream
sf2.stream->setpos(sf2.sample_data_offset+data_stream_offset*sizeof(int16_t));
//read 16 bit samples
sf2.stream->read(data16.get(), size*sizeof(int16_t));
if(sf2.sample_data_24_offset)
{
auto data24 = std::make_unique<uint8_t[]>(size);