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