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