Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/fluidsynth/src/sfloader/fluid_sfont.c
4396 views
1
/* FluidSynth - A Software Synthesizer
2
*
3
* Copyright (C) 2003 Peter Hanappe and others.
4
*
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public License
7
* as published by the Free Software Foundation; either version 2.1 of
8
* the License, or (at your option) any later version.
9
*
10
* This library is distributed in the hope that it will be useful, but
11
* WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
14
*
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free
17
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
* 02110-1301, USA
19
*/
20
21
#include "fluid_sfont.h"
22
#include "fluid_sys.h"
23
24
25
void *default_fopen(const char *path)
26
{
27
const char* msg;
28
FILE* handle = fluid_file_open(path, &msg);
29
30
if(handle == NULL)
31
{
32
FLUID_LOG(FLUID_ERR, "fluid_sfloader_load(): Failed to open '%s': %s", path, msg);
33
}
34
35
return handle;
36
}
37
38
int default_fclose(void *handle)
39
{
40
return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED;
41
}
42
43
fluid_long_long_t default_ftell(void *handle)
44
{
45
return FLUID_FTELL((FILE *)handle);
46
}
47
48
#ifdef _WIN32
49
#define FLUID_PRIi64 "I64d"
50
#else
51
#define FLUID_PRIi64 "lld"
52
#endif
53
54
int safe_fread(void *buf, fluid_long_long_t count, void *fd)
55
{
56
if(FLUID_FREAD(buf, (size_t)count, 1, (FILE *)fd) != 1)
57
{
58
if(feof((FILE *)fd))
59
{
60
FLUID_LOG(FLUID_ERR, "EOF while attempting to read %" FLUID_PRIi64 " bytes", count);
61
}
62
else
63
{
64
FLUID_LOG(FLUID_ERR, "File read failed");
65
}
66
67
return FLUID_FAILED;
68
}
69
70
return FLUID_OK;
71
}
72
73
int safe_fseek(void *fd, fluid_long_long_t ofs, int whence)
74
{
75
if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0)
76
{
77
FLUID_LOG(FLUID_ERR, "File seek failed with offset = %" FLUID_PRIi64 " and whence = %d", ofs, whence);
78
return FLUID_FAILED;
79
}
80
81
return FLUID_OK;
82
}
83
84
#undef FLUID_PRIi64
85
86
/**
87
* Creates a new SoundFont loader.
88
*
89
* @param load Pointer to a function that provides a #fluid_sfont_t (see #fluid_sfloader_load_t).
90
* @param free Pointer to a function that destroys this instance (see #fluid_sfloader_free_t).
91
* Unless any private data needs to be freed it is sufficient to set this to delete_fluid_sfloader().
92
*
93
* @return the SoundFont loader instance on success, NULL otherwise.
94
*/
95
fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free)
96
{
97
fluid_sfloader_t *loader;
98
99
fluid_return_val_if_fail(load != NULL, NULL);
100
fluid_return_val_if_fail(free != NULL, NULL);
101
102
loader = FLUID_NEW(fluid_sfloader_t);
103
104
if(loader == NULL)
105
{
106
FLUID_LOG(FLUID_ERR, "Out of memory");
107
return NULL;
108
}
109
110
FLUID_MEMSET(loader, 0, sizeof(*loader));
111
112
loader->load = load;
113
loader->free = free;
114
fluid_sfloader_set_callbacks(loader,
115
default_fopen,
116
safe_fread,
117
safe_fseek,
118
default_ftell,
119
default_fclose);
120
121
return loader;
122
}
123
124
/**
125
* Frees a SoundFont loader created with new_fluid_sfloader().
126
*
127
* @param loader The SoundFont loader instance to free.
128
*/
129
void delete_fluid_sfloader(fluid_sfloader_t *loader)
130
{
131
fluid_return_if_fail(loader != NULL);
132
133
FLUID_FREE(loader);
134
}
135
136
/**
137
* Specify private data to be used by #fluid_sfloader_load_t.
138
*
139
* @param loader The SoundFont loader instance.
140
* @param data The private data to store.
141
* @return #FLUID_OK on success, #FLUID_FAILED otherwise.
142
*/
143
int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data)
144
{
145
fluid_return_val_if_fail(loader != NULL, FLUID_FAILED);
146
147
loader->data = data;
148
return FLUID_OK;
149
}
150
151
/**
152
* Obtain private data previously set with fluid_sfloader_set_data().
153
*
154
* @param loader The SoundFont loader instance.
155
* @return The private data or NULL if none explicitly set before.
156
*/
157
void *fluid_sfloader_get_data(fluid_sfloader_t *loader)
158
{
159
fluid_return_val_if_fail(loader != NULL, NULL);
160
161
return loader->data;
162
}
163
164
/**
165
* Set custom callbacks to be used upon soundfont loading.
166
*
167
* @param loader The SoundFont loader instance.
168
* @param open A function implementing #fluid_sfloader_callback_open_t.
169
* @param read A function implementing #fluid_sfloader_callback_read_t.
170
* @param seek A function implementing #fluid_sfloader_callback_seek_t.
171
* @param tell A function implementing #fluid_sfloader_callback_tell_t.
172
* @param close A function implementing #fluid_sfloader_callback_close_t.
173
* @return #FLUID_OK if the callbacks have been successfully set, #FLUID_FAILED otherwise.
174
*
175
* Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example.
176
*
177
*/
178
int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader,
179
fluid_sfloader_callback_open_t open,
180
fluid_sfloader_callback_read_t read,
181
fluid_sfloader_callback_seek_t seek,
182
fluid_sfloader_callback_tell_t tell,
183
fluid_sfloader_callback_close_t close)
184
{
185
fluid_file_callbacks_t *cb;
186
187
fluid_return_val_if_fail(loader != NULL, FLUID_FAILED);
188
fluid_return_val_if_fail(open != NULL, FLUID_FAILED);
189
fluid_return_val_if_fail(read != NULL, FLUID_FAILED);
190
fluid_return_val_if_fail(seek != NULL, FLUID_FAILED);
191
fluid_return_val_if_fail(tell != NULL, FLUID_FAILED);
192
fluid_return_val_if_fail(close != NULL, FLUID_FAILED);
193
194
cb = &loader->file_callbacks;
195
196
cb->fopen = open;
197
cb->fread = read;
198
cb->fseek = seek;
199
cb->ftell = tell;
200
cb->fclose = close;
201
202
// NOTE: if we ever make the instpatch loader public, this may return FLUID_FAILED
203
return FLUID_OK;
204
}
205
206
/**
207
* Creates a new virtual SoundFont instance structure.
208
*
209
* @param get_name A function implementing #fluid_sfont_get_name_t.
210
* @param get_preset A function implementing #fluid_sfont_get_preset_t.
211
* @param iter_start A function implementing #fluid_sfont_iteration_start_t, or NULL if preset iteration not needed.
212
* @param iter_next A function implementing #fluid_sfont_iteration_next_t, or NULL if preset iteration not needed.
213
* @param free A function implementing #fluid_sfont_free_t.
214
* @return The soundfont instance on success or NULL otherwise.
215
*/
216
fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name,
217
fluid_sfont_get_preset_t get_preset,
218
fluid_sfont_iteration_start_t iter_start,
219
fluid_sfont_iteration_next_t iter_next,
220
fluid_sfont_free_t free)
221
{
222
fluid_sfont_t *sfont;
223
224
fluid_return_val_if_fail(get_name != NULL, NULL);
225
fluid_return_val_if_fail(get_preset != NULL, NULL);
226
fluid_return_val_if_fail(free != NULL, NULL);
227
228
sfont = FLUID_NEW(fluid_sfont_t);
229
230
if(sfont == NULL)
231
{
232
FLUID_LOG(FLUID_ERR, "Out of memory");
233
return NULL;
234
}
235
236
FLUID_MEMSET(sfont, 0, sizeof(*sfont));
237
238
sfont->get_name = get_name;
239
sfont->get_preset = get_preset;
240
sfont->iteration_start = iter_start;
241
sfont->iteration_next = iter_next;
242
sfont->free = free;
243
244
return sfont;
245
}
246
247
/**
248
* Set private data to use with a SoundFont instance.
249
*
250
* @param sfont The SoundFont instance.
251
* @param data The private data to store.
252
* @return #FLUID_OK on success, #FLUID_FAILED otherwise.
253
*/
254
int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data)
255
{
256
fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED);
257
258
sfont->data = data;
259
return FLUID_OK;
260
}
261
262
/**
263
* Retrieve the private data of a SoundFont instance.
264
*
265
* @param sfont The SoundFont instance.
266
* @return The private data or NULL if none explicitly set before.
267
*/
268
void *fluid_sfont_get_data(fluid_sfont_t *sfont)
269
{
270
fluid_return_val_if_fail(sfont != NULL, NULL);
271
272
return sfont->data;
273
}
274
275
/**
276
* Retrieve the unique ID of a SoundFont instance.
277
*
278
* @param sfont The SoundFont instance.
279
* @return The SoundFont ID.
280
*/
281
int fluid_sfont_get_id(fluid_sfont_t *sfont)
282
{
283
return sfont->id;
284
}
285
286
/**
287
* Retrieve the name of a SoundFont instance.
288
*
289
* @param sfont The SoundFont instance.
290
* @return The name of the SoundFont.
291
*/
292
const char *fluid_sfont_get_name(fluid_sfont_t *sfont)
293
{
294
return sfont->get_name(sfont);
295
}
296
297
/**
298
* Retrieve the preset assigned the a SoundFont instance for the given bank and preset number.
299
*
300
* @param sfont The SoundFont instance.
301
* @param bank bank number of the preset
302
* @param prenum program number of the preset
303
* @return The preset instance or NULL if none found.
304
*/
305
fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum)
306
{
307
return sfont->get_preset(sfont, bank, prenum);
308
}
309
310
311
/**
312
* Starts / re-starts virtual preset iteration in a SoundFont.
313
*
314
* @param sfont Virtual SoundFont instance
315
*/
316
void fluid_sfont_iteration_start(fluid_sfont_t *sfont)
317
{
318
fluid_return_if_fail(sfont != NULL);
319
fluid_return_if_fail(sfont->iteration_start != NULL);
320
321
sfont->iteration_start(sfont);
322
}
323
324
/**
325
* Virtual SoundFont preset iteration function.
326
*
327
* Returns preset information to the caller and advances the
328
* internal iteration state to the next preset for subsequent calls.
329
* @param sfont The SoundFont instance.
330
* @return NULL when no more presets are available, otherwise the a pointer to the current preset
331
*/
332
fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont)
333
{
334
fluid_return_val_if_fail(sfont != NULL, NULL);
335
fluid_return_val_if_fail(sfont->iteration_next != NULL, NULL);
336
337
return sfont->iteration_next(sfont);
338
}
339
340
/**
341
* Destroys a SoundFont instance created with new_fluid_sfont().
342
*
343
* @param sfont The SoundFont instance to destroy.
344
* @return Always returns 0.
345
*
346
* Implements #fluid_sfont_free_t.
347
*
348
*/
349
int delete_fluid_sfont(fluid_sfont_t *sfont)
350
{
351
fluid_return_val_if_fail(sfont != NULL, 0);
352
353
FLUID_FREE(sfont);
354
return 0;
355
}
356
357
/**
358
* Create a virtual SoundFont preset instance.
359
*
360
* @param parent_sfont The SoundFont instance this preset shall belong to
361
* @param get_name A function implementing #fluid_preset_get_name_t
362
* @param get_bank A function implementing #fluid_preset_get_banknum_t
363
* @param get_num A function implementing #fluid_preset_get_num_t
364
* @param noteon A function implementing #fluid_preset_noteon_t
365
* @param free A function implementing #fluid_preset_free_t
366
* @return The preset instance on success, NULL otherwise.
367
*/
368
fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont,
369
fluid_preset_get_name_t get_name,
370
fluid_preset_get_banknum_t get_bank,
371
fluid_preset_get_num_t get_num,
372
fluid_preset_noteon_t noteon,
373
fluid_preset_free_t free)
374
{
375
fluid_preset_t *preset;
376
377
fluid_return_val_if_fail(parent_sfont != NULL, NULL);
378
fluid_return_val_if_fail(get_name != NULL, NULL);
379
fluid_return_val_if_fail(get_bank != NULL, NULL);
380
fluid_return_val_if_fail(get_num != NULL, NULL);
381
fluid_return_val_if_fail(noteon != NULL, NULL);
382
fluid_return_val_if_fail(free != NULL, NULL);
383
384
preset = FLUID_NEW(fluid_preset_t);
385
386
if(preset == NULL)
387
{
388
FLUID_LOG(FLUID_ERR, "Out of memory");
389
return NULL;
390
}
391
392
FLUID_MEMSET(preset, 0, sizeof(*preset));
393
394
preset->sfont = parent_sfont;
395
preset->get_name = get_name;
396
preset->get_banknum = get_bank;
397
preset->get_num = get_num;
398
preset->noteon = noteon;
399
preset->free = free;
400
401
return preset;
402
}
403
404
/**
405
* Set private data to use with a SoundFont preset instance.
406
*
407
* @param preset The SoundFont preset instance.
408
* @param data The private data to store.
409
* @return #FLUID_OK on success, #FLUID_FAILED otherwise.
410
*/
411
int fluid_preset_set_data(fluid_preset_t *preset, void *data)
412
{
413
fluid_return_val_if_fail(preset != NULL, FLUID_FAILED);
414
415
preset->data = data;
416
return FLUID_OK;
417
}
418
419
/**
420
* Retrieve the private data of a SoundFont preset instance.
421
*
422
* @param preset The SoundFont preset instance.
423
* @return The private data or NULL if none explicitly set before.
424
*/
425
void *fluid_preset_get_data(fluid_preset_t *preset)
426
{
427
fluid_return_val_if_fail(preset != NULL, NULL);
428
429
return preset->data;
430
}
431
432
/**
433
* Retrieves the presets name by executing the \p get_name function
434
* provided on its creation.
435
*
436
* @param preset The SoundFont preset instance.
437
* @return Pointer to a NULL-terminated string containing the presets name.
438
*/
439
const char *fluid_preset_get_name(fluid_preset_t *preset)
440
{
441
return preset->get_name(preset);
442
}
443
444
/**
445
* Retrieves the presets bank number by executing the \p get_bank function
446
* provided on its creation.
447
*
448
* @param preset The SoundFont preset instance.
449
* @return The bank number of \p preset.
450
*/
451
int fluid_preset_get_banknum(fluid_preset_t *preset)
452
{
453
return preset->get_banknum(preset);
454
}
455
456
/**
457
* Retrieves the presets (instrument) number by executing the \p get_num function
458
* provided on its creation.
459
*
460
* @param preset The SoundFont preset instance.
461
* @return The number of \p preset.
462
*/
463
int fluid_preset_get_num(fluid_preset_t *preset)
464
{
465
return preset->get_num(preset);
466
}
467
468
/**
469
* Retrieves the presets parent SoundFont instance.
470
*
471
* @param preset The SoundFont preset instance.
472
* @return The parent SoundFont of \p preset.
473
*/
474
fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset)
475
{
476
return preset->sfont;
477
}
478
479
/**
480
* Destroys a SoundFont preset instance created with new_fluid_preset().
481
*
482
* @param preset The SoundFont preset instance to destroy.
483
*
484
* Implements #fluid_preset_free_t.
485
*
486
*/
487
void delete_fluid_preset(fluid_preset_t *preset)
488
{
489
fluid_return_if_fail(preset != NULL);
490
491
FLUID_FREE(preset);
492
}
493
494
/**
495
* Create a new sample instance.
496
*
497
* @return The sample on success, NULL otherwise.
498
*/
499
fluid_sample_t *
500
new_fluid_sample(void)
501
{
502
fluid_sample_t *sample = NULL;
503
504
sample = FLUID_NEW(fluid_sample_t);
505
506
if(sample == NULL)
507
{
508
FLUID_LOG(FLUID_ERR, "Out of memory");
509
return NULL;
510
}
511
512
FLUID_MEMSET(sample, 0, sizeof(*sample));
513
514
return sample;
515
}
516
517
/**
518
* Destroy a sample instance previously created with new_fluid_sample().
519
*
520
* @param sample The sample to destroy.
521
*/
522
void
523
delete_fluid_sample(fluid_sample_t *sample)
524
{
525
fluid_return_if_fail(sample != NULL);
526
527
if(sample->auto_free)
528
{
529
FLUID_FREE(sample->data);
530
FLUID_FREE(sample->data24);
531
}
532
533
FLUID_FREE(sample);
534
}
535
536
/**
537
* Returns the size of the fluid_sample_t structure.
538
*
539
* @return Size of fluid_sample_t in bytes
540
*
541
* Useful in low latency scenarios e.g. to allocate a pool of samples.
542
*
543
* @note It is recommend to zero initialize the memory before using the object.
544
*
545
* @warning Do NOT allocate samples on the stack and assign them to a voice!
546
*/
547
size_t fluid_sample_sizeof(void)
548
{
549
return sizeof(fluid_sample_t);
550
}
551
552
/**
553
* Set the name of a SoundFont sample.
554
*
555
* @param sample SoundFont sample
556
* @param name Name to assign to sample (20 chars in length + zero terminator)
557
* @return #FLUID_OK on success, #FLUID_FAILED otherwise
558
*/
559
int fluid_sample_set_name(fluid_sample_t *sample, const char *name)
560
{
561
fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
562
fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
563
564
FLUID_STRNCPY(sample->name, name, sizeof(sample->name));
565
return FLUID_OK;
566
}
567
568
/**
569
* Assign sample data to a SoundFont sample.
570
*
571
* @param sample SoundFont sample
572
* @param data Buffer containing 16 bit (mono-)audio sample data
573
* @param data24 If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples
574
* @param nbframes Number of samples in \a data
575
* @param sample_rate Sampling rate of the sample data
576
* @param copy_data TRUE to copy the sample data (and automatically free it upon delete_fluid_sample()), FALSE to use it directly (and not free it)
577
* @return #FLUID_OK on success, #FLUID_FAILED otherwise
578
*
579
* @note If \a copy_data is FALSE, data should have 8 unused frames at start
580
* and 8 unused frames at the end and \a nbframes should be >=48
581
*/
582
int
583
fluid_sample_set_sound_data(fluid_sample_t *sample,
584
short *data,
585
char *data24,
586
unsigned int nbframes,
587
unsigned int sample_rate,
588
short copy_data
589
)
590
{
591
/* the number of samples before the start and after the end */
592
#define SAMPLE_LOOP_MARGIN 8U
593
594
fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
595
fluid_return_val_if_fail(data != NULL, FLUID_FAILED);
596
fluid_return_val_if_fail(nbframes != 0, FLUID_FAILED);
597
598
/* in case we already have some data */
599
if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free)
600
{
601
FLUID_FREE(sample->data);
602
FLUID_FREE(sample->data24);
603
}
604
605
sample->data = NULL;
606
sample->data24 = NULL;
607
608
if(copy_data)
609
{
610
unsigned int storedNbFrames;
611
612
/* nbframes should be >= 48 (SoundFont specs) */
613
storedNbFrames = nbframes;
614
615
if(storedNbFrames < 48)
616
{
617
storedNbFrames = 48;
618
}
619
620
storedNbFrames += 2 * SAMPLE_LOOP_MARGIN;
621
622
sample->data = FLUID_ARRAY(short, storedNbFrames);
623
624
if(sample->data == NULL)
625
{
626
goto error_rec;
627
}
628
629
FLUID_MEMSET(sample->data, 0, storedNbFrames * sizeof(short));
630
FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short));
631
632
if(data24 != NULL)
633
{
634
sample->data24 = FLUID_ARRAY(char, storedNbFrames);
635
636
if(sample->data24 == NULL)
637
{
638
goto error_rec;
639
}
640
641
FLUID_MEMSET(sample->data24, 0, storedNbFrames);
642
FLUID_MEMCPY(sample->data24 + SAMPLE_LOOP_MARGIN, data24, nbframes * sizeof(char));
643
}
644
645
/* pointers */
646
/* all from the start of data */
647
sample->start = SAMPLE_LOOP_MARGIN;
648
sample->end = SAMPLE_LOOP_MARGIN + nbframes - 1;
649
}
650
else
651
{
652
/* we cannot assure the SAMPLE_LOOP_MARGIN */
653
sample->data = data;
654
sample->data24 = data24;
655
sample->start = 0;
656
sample->end = nbframes - 1;
657
}
658
659
sample->samplerate = sample_rate;
660
sample->sampletype = FLUID_SAMPLETYPE_MONO;
661
sample->auto_free = copy_data;
662
663
return FLUID_OK;
664
665
error_rec:
666
FLUID_LOG(FLUID_ERR, "Out of memory");
667
FLUID_FREE(sample->data);
668
FLUID_FREE(sample->data24);
669
sample->data = NULL;
670
sample->data24 = NULL;
671
return FLUID_FAILED;
672
673
#undef SAMPLE_LOOP_MARGIN
674
}
675
676
/**
677
* Set the loop of a sample.
678
*
679
* @param sample SoundFont sample
680
* @param loop_start Start sample index of the loop.
681
* @param loop_end End index of the loop (must be a valid sample as it marks the last sample to be played).
682
* @return #FLUID_OK on success, #FLUID_FAILED otherwise.
683
*/
684
int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end)
685
{
686
fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
687
688
sample->loopstart = loop_start;
689
sample->loopend = loop_end;
690
691
return FLUID_OK;
692
}
693
694
/**
695
* Set the pitch of a sample.
696
*
697
* @param sample SoundFont sample
698
* @param root_key Root MIDI note of sample (0-127)
699
* @param fine_tune Fine tune in cents
700
* @return #FLUID_OK on success, #FLUID_FAILED otherwise.
701
*/
702
int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune)
703
{
704
fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
705
fluid_return_val_if_fail(0 <= root_key && root_key <= 127, FLUID_FAILED);
706
707
sample->origpitch = root_key;
708
sample->pitchadj = fine_tune;
709
710
return FLUID_OK;
711
}
712
713
714
/**
715
* Validate parameters of a sample
716
*
717
*/
718
int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size)
719
{
720
#define EXCLUSIVE_FLAGS (FLUID_SAMPLETYPE_MONO | FLUID_SAMPLETYPE_RIGHT | FLUID_SAMPLETYPE_LEFT)
721
static const unsigned int supported_flags = EXCLUSIVE_FLAGS | FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_OGG_VORBIS | FLUID_SAMPLETYPE_ROM;
722
723
/* ROM samples are unusable for us by definition */
724
if(sample->sampletype & FLUID_SAMPLETYPE_ROM)
725
{
726
FLUID_LOG(FLUID_WARN, "Sample '%s': ROM sample ignored", sample->name);
727
return FLUID_FAILED;
728
}
729
730
if(sample->sampletype & ~supported_flags)
731
{
732
FLUID_LOG(FLUID_WARN, "Sample '%s' has unknown flags, possibly using an unsupported compression; sample ignored", sample->name);
733
return FLUID_FAILED;
734
}
735
736
if((sample->sampletype & EXCLUSIVE_FLAGS) & ((sample->sampletype & EXCLUSIVE_FLAGS) - 1))
737
{
738
FLUID_LOG(FLUID_INFO, "Sample '%s' should be either mono or left or right; using it anyway", sample->name);
739
}
740
741
if((sample->sampletype & FLUID_SAMPLETYPE_LINKED) && (sample->sampletype & EXCLUSIVE_FLAGS))
742
{
743
FLUID_LOG(FLUID_INFO, "Linked sample '%s' should not be mono, left or right at the same time; using it anyway", sample->name);
744
}
745
746
if((sample->sampletype & EXCLUSIVE_FLAGS) == 0)
747
{
748
FLUID_LOG(FLUID_INFO, "Sample '%s' has no flags set, assuming mono", sample->name);
749
sample->sampletype = FLUID_SAMPLETYPE_MONO;
750
}
751
752
/* Ogg vorbis compressed samples in the SF3 format use byte indices for
753
* sample start and end pointers before decompression. Standard SF2 samples
754
* use sample word indices for all pointers, so use half the buffer_size
755
* for validation. */
756
if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))
757
{
758
if(buffer_size % 2)
759
{
760
FLUID_LOG(FLUID_WARN, "Sample '%s': invalid buffer size", sample->name);
761
return FLUID_FAILED;
762
}
763
764
buffer_size /= 2;
765
}
766
767
if((sample->end > buffer_size) || (sample->start >= sample->end))
768
{
769
FLUID_LOG(FLUID_WARN, "Sample '%s': invalid start/end file positions", sample->name);
770
return FLUID_FAILED;
771
}
772
773
return FLUID_OK;
774
#undef EXCLUSIVE_FLAGS
775
}
776
777
/* Check the sample loop pointers and optionally convert them to something
778
* usable in case they are broken. Return a boolean indicating if the pointers
779
* have been modified, so the user can be notified of possible audio glitches.
780
*/
781
int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size)
782
{
783
int modified = FALSE;
784
unsigned int max_end = buffer_size / 2;
785
/* In fluid_sample_t the sample end pointer points to the last sample, not
786
* to the data word after the last sample. FIXME: why? */
787
unsigned int sample_end = sample->end + 1;
788
789
if(sample->loopstart == sample->loopend)
790
{
791
/* Some SoundFonts disable loops by setting loopstart = loopend. While
792
* technically invalid, we decided to accept those samples anyway.
793
* Before fluidsynth 2.2.5 we've set those indices to zero, as this
794
* change was believed to be inaudible. This turned out to be an
795
* incorrect assumption, as the loop points may still be modified by
796
* loop offset modulators afterwards.
797
*/
798
if(sample->loopstart != sample->start)
799
{
800
// Many soundfonts set loopstart == loopend == sample->start to disabled to loop.
801
// Only report cases where it's not equal to the sample->start, to avoid spam.
802
FLUID_LOG(FLUID_DBG, "Sample '%s': zero length loop detected: loopstart == loopend == '%d', sample start '%d', using it anyway",
803
sample->name, sample->loopstart, sample->start);
804
}
805
}
806
else if(sample->loopstart > sample->loopend)
807
{
808
unsigned int tmp;
809
810
/* If loop start and end are reversed, try to swap them around and
811
* continue validation */
812
FLUID_LOG(FLUID_DBG, "Sample '%s': reversed loop pointers '%d' - '%d', trying to fix",
813
sample->name, sample->loopstart, sample->loopend);
814
tmp = sample->loopstart;
815
sample->loopstart = sample->loopend;
816
sample->loopend = tmp;
817
modified = TRUE;
818
}
819
820
/* The SoundFont 2.4 spec defines the loopstart index as the first sample
821
* point of the loop while loopend is the first point AFTER the last sample
822
* of the loop. However we cannot be sure whether any of loopend or end is
823
* correct. Hours of thinking through this have concluded that it would be
824
* best practice to mangle with loops as little as necessary by only making
825
* sure the pointers are within sample->start to max_end. Incorrect
826
* soundfont shall preferably fail loudly. */
827
if((sample->loopstart < sample->start) || (sample->loopstart > max_end))
828
{
829
FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop start '%d', setting to sample start '%d'",
830
sample->name, sample->loopstart, sample->start);
831
sample->loopstart = sample->start;
832
modified = TRUE;
833
}
834
835
if((sample->loopend < sample->start) || (sample->loopend > max_end))
836
{
837
FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop end '%d', setting to sample end '%d'",
838
sample->name, sample->loopend, sample_end);
839
sample->loopend = sample_end;
840
modified = TRUE;
841
}
842
843
if((sample->loopstart > sample_end) || (sample->loopend > sample_end))
844
{
845
FLUID_LOG(FLUID_DBG, "Sample '%s': loop range '%d - %d' after sample end '%d', using it anyway",
846
sample->name, sample->loopstart, sample->loopend, sample_end);
847
}
848
849
return modified;
850
}
851
852