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