Path: blob/master/libs/fluidsynth/src/sfloader/fluid_sfont.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_sfont.h"21#include "fluid_sys.h"222324void *default_fopen(const char *path)25{26const char* msg;27FILE* handle = fluid_file_open(path, &msg);2829if(handle == NULL)30{31FLUID_LOG(FLUID_ERR, "fluid_sfloader_load(): Failed to open '%s': %s", path, msg);32}3334return handle;35}3637int default_fclose(void *handle)38{39return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED;40}4142fluid_long_long_t default_ftell(void *handle)43{44return FLUID_FTELL((FILE *)handle);45}4647#ifdef _WIN3248#define FLUID_PRIi64 "I64d"49#else50#define FLUID_PRIi64 "lld"51#endif5253int safe_fread(void *buf, fluid_long_long_t count, void *fd)54{55if(FLUID_FREAD(buf, (size_t)count, 1, (FILE *)fd) != 1)56{57if(feof((FILE *)fd))58{59FLUID_LOG(FLUID_ERR, "EOF while attempting to read %" FLUID_PRIi64 " bytes", count);60}61else62{63FLUID_LOG(FLUID_ERR, "File read failed");64}6566return FLUID_FAILED;67}6869return FLUID_OK;70}7172int safe_fseek(void *fd, fluid_long_long_t ofs, int whence)73{74if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0)75{76FLUID_LOG(FLUID_ERR, "File seek failed with offset = %" FLUID_PRIi64 " and whence = %d", ofs, whence);77return FLUID_FAILED;78}7980return FLUID_OK;81}8283#undef FLUID_PRIi648485/**86* Creates a new SoundFont loader.87*88* @param load Pointer to a function that provides a #fluid_sfont_t (see #fluid_sfloader_load_t).89* @param free Pointer to a function that destroys this instance (see #fluid_sfloader_free_t).90* Unless any private data needs to be freed it is sufficient to set this to delete_fluid_sfloader().91*92* @return the SoundFont loader instance on success, NULL otherwise.93*/94fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free)95{96fluid_sfloader_t *loader;9798fluid_return_val_if_fail(load != NULL, NULL);99fluid_return_val_if_fail(free != NULL, NULL);100101loader = FLUID_NEW(fluid_sfloader_t);102103if(loader == NULL)104{105FLUID_LOG(FLUID_ERR, "Out of memory");106return NULL;107}108109FLUID_MEMSET(loader, 0, sizeof(*loader));110111loader->load = load;112loader->free = free;113fluid_sfloader_set_callbacks(loader,114default_fopen,115safe_fread,116safe_fseek,117default_ftell,118default_fclose);119120return loader;121}122123/**124* Frees a SoundFont loader created with new_fluid_sfloader().125*126* @param loader The SoundFont loader instance to free.127*/128void delete_fluid_sfloader(fluid_sfloader_t *loader)129{130fluid_return_if_fail(loader != NULL);131132FLUID_FREE(loader);133}134135/**136* Specify private data to be used by #fluid_sfloader_load_t.137*138* @param loader The SoundFont loader instance.139* @param data The private data to store.140* @return #FLUID_OK on success, #FLUID_FAILED otherwise.141*/142int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data)143{144fluid_return_val_if_fail(loader != NULL, FLUID_FAILED);145146loader->data = data;147return FLUID_OK;148}149150/**151* Obtain private data previously set with fluid_sfloader_set_data().152*153* @param loader The SoundFont loader instance.154* @return The private data or NULL if none explicitly set before.155*/156void *fluid_sfloader_get_data(fluid_sfloader_t *loader)157{158fluid_return_val_if_fail(loader != NULL, NULL);159160return loader->data;161}162163/**164* Set custom callbacks to be used upon soundfont loading.165*166* @param loader The SoundFont loader instance.167* @param open A function implementing #fluid_sfloader_callback_open_t.168* @param read A function implementing #fluid_sfloader_callback_read_t.169* @param seek A function implementing #fluid_sfloader_callback_seek_t.170* @param tell A function implementing #fluid_sfloader_callback_tell_t.171* @param close A function implementing #fluid_sfloader_callback_close_t.172* @return #FLUID_OK if the callbacks have been successfully set, #FLUID_FAILED otherwise.173*174* Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example.175*176*/177int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader,178fluid_sfloader_callback_open_t open,179fluid_sfloader_callback_read_t read,180fluid_sfloader_callback_seek_t seek,181fluid_sfloader_callback_tell_t tell,182fluid_sfloader_callback_close_t close)183{184fluid_file_callbacks_t *cb;185186fluid_return_val_if_fail(loader != NULL, FLUID_FAILED);187fluid_return_val_if_fail(open != NULL, FLUID_FAILED);188fluid_return_val_if_fail(read != NULL, FLUID_FAILED);189fluid_return_val_if_fail(seek != NULL, FLUID_FAILED);190fluid_return_val_if_fail(tell != NULL, FLUID_FAILED);191fluid_return_val_if_fail(close != NULL, FLUID_FAILED);192193cb = &loader->file_callbacks;194195cb->fopen = open;196cb->fread = read;197cb->fseek = seek;198cb->ftell = tell;199cb->fclose = close;200201// NOTE: if we ever make the instpatch loader public, this may return FLUID_FAILED202return FLUID_OK;203}204205/**206* Creates a new virtual SoundFont instance structure.207*208* @param get_name A function implementing #fluid_sfont_get_name_t.209* @param get_preset A function implementing #fluid_sfont_get_preset_t.210* @param iter_start A function implementing #fluid_sfont_iteration_start_t, or NULL if preset iteration not needed.211* @param iter_next A function implementing #fluid_sfont_iteration_next_t, or NULL if preset iteration not needed.212* @param free A function implementing #fluid_sfont_free_t.213* @return The soundfont instance on success or NULL otherwise.214*/215fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name,216fluid_sfont_get_preset_t get_preset,217fluid_sfont_iteration_start_t iter_start,218fluid_sfont_iteration_next_t iter_next,219fluid_sfont_free_t free)220{221fluid_sfont_t *sfont;222223fluid_return_val_if_fail(get_name != NULL, NULL);224fluid_return_val_if_fail(get_preset != NULL, NULL);225fluid_return_val_if_fail(free != NULL, NULL);226227sfont = FLUID_NEW(fluid_sfont_t);228229if(sfont == NULL)230{231FLUID_LOG(FLUID_ERR, "Out of memory");232return NULL;233}234235FLUID_MEMSET(sfont, 0, sizeof(*sfont));236237sfont->get_name = get_name;238sfont->get_preset = get_preset;239sfont->iteration_start = iter_start;240sfont->iteration_next = iter_next;241sfont->free = free;242243return sfont;244}245246/**247* Set private data to use with a SoundFont instance.248*249* @param sfont The SoundFont instance.250* @param data The private data to store.251* @return #FLUID_OK on success, #FLUID_FAILED otherwise.252*/253int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data)254{255fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED);256257sfont->data = data;258return FLUID_OK;259}260261/**262* Retrieve the private data of a SoundFont instance.263*264* @param sfont The SoundFont instance.265* @return The private data or NULL if none explicitly set before.266*/267void *fluid_sfont_get_data(fluid_sfont_t *sfont)268{269fluid_return_val_if_fail(sfont != NULL, NULL);270271return sfont->data;272}273274/**275* Retrieve the unique ID of a SoundFont instance.276*277* @param sfont The SoundFont instance.278* @return The SoundFont ID.279*/280int fluid_sfont_get_id(fluid_sfont_t *sfont)281{282return sfont->id;283}284285/**286* Retrieve the name of a SoundFont instance.287*288* @param sfont The SoundFont instance.289* @return The name of the SoundFont.290*/291const char *fluid_sfont_get_name(fluid_sfont_t *sfont)292{293return sfont->get_name(sfont);294}295296/**297* Retrieve the preset assigned the a SoundFont instance for the given bank and preset number.298*299* @param sfont The SoundFont instance.300* @param bank bank number of the preset301* @param prenum program number of the preset302* @return The preset instance or NULL if none found.303*/304fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum)305{306return sfont->get_preset(sfont, bank, prenum);307}308309310/**311* Starts / re-starts virtual preset iteration in a SoundFont.312*313* @param sfont Virtual SoundFont instance314*/315void fluid_sfont_iteration_start(fluid_sfont_t *sfont)316{317fluid_return_if_fail(sfont != NULL);318fluid_return_if_fail(sfont->iteration_start != NULL);319320sfont->iteration_start(sfont);321}322323/**324* Virtual SoundFont preset iteration function.325*326* Returns preset information to the caller and advances the327* internal iteration state to the next preset for subsequent calls.328* @param sfont The SoundFont instance.329* @return NULL when no more presets are available, otherwise the a pointer to the current preset330*/331fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont)332{333fluid_return_val_if_fail(sfont != NULL, NULL);334fluid_return_val_if_fail(sfont->iteration_next != NULL, NULL);335336return sfont->iteration_next(sfont);337}338339/**340* Destroys a SoundFont instance created with new_fluid_sfont().341*342* @param sfont The SoundFont instance to destroy.343* @return Always returns 0.344*345* Implements #fluid_sfont_free_t.346*347*/348int delete_fluid_sfont(fluid_sfont_t *sfont)349{350fluid_return_val_if_fail(sfont != NULL, 0);351352FLUID_FREE(sfont);353return 0;354}355356/**357* Create a virtual SoundFont preset instance.358*359* @param parent_sfont The SoundFont instance this preset shall belong to360* @param get_name A function implementing #fluid_preset_get_name_t361* @param get_bank A function implementing #fluid_preset_get_banknum_t362* @param get_num A function implementing #fluid_preset_get_num_t363* @param noteon A function implementing #fluid_preset_noteon_t364* @param free A function implementing #fluid_preset_free_t365* @return The preset instance on success, NULL otherwise.366*/367fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont,368fluid_preset_get_name_t get_name,369fluid_preset_get_banknum_t get_bank,370fluid_preset_get_num_t get_num,371fluid_preset_noteon_t noteon,372fluid_preset_free_t free)373{374fluid_preset_t *preset;375376fluid_return_val_if_fail(parent_sfont != NULL, NULL);377fluid_return_val_if_fail(get_name != NULL, NULL);378fluid_return_val_if_fail(get_bank != NULL, NULL);379fluid_return_val_if_fail(get_num != NULL, NULL);380fluid_return_val_if_fail(noteon != NULL, NULL);381fluid_return_val_if_fail(free != NULL, NULL);382383preset = FLUID_NEW(fluid_preset_t);384385if(preset == NULL)386{387FLUID_LOG(FLUID_ERR, "Out of memory");388return NULL;389}390391FLUID_MEMSET(preset, 0, sizeof(*preset));392393preset->sfont = parent_sfont;394preset->get_name = get_name;395preset->get_banknum = get_bank;396preset->get_num = get_num;397preset->noteon = noteon;398preset->free = free;399400return preset;401}402403/**404* Set private data to use with a SoundFont preset instance.405*406* @param preset The SoundFont preset instance.407* @param data The private data to store.408* @return #FLUID_OK on success, #FLUID_FAILED otherwise.409*/410int fluid_preset_set_data(fluid_preset_t *preset, void *data)411{412fluid_return_val_if_fail(preset != NULL, FLUID_FAILED);413414preset->data = data;415return FLUID_OK;416}417418/**419* Retrieve the private data of a SoundFont preset instance.420*421* @param preset The SoundFont preset instance.422* @return The private data or NULL if none explicitly set before.423*/424void *fluid_preset_get_data(fluid_preset_t *preset)425{426fluid_return_val_if_fail(preset != NULL, NULL);427428return preset->data;429}430431/**432* Retrieves the presets name by executing the \p get_name function433* provided on its creation.434*435* @param preset The SoundFont preset instance.436* @return Pointer to a NULL-terminated string containing the presets name.437*/438const char *fluid_preset_get_name(fluid_preset_t *preset)439{440return preset->get_name(preset);441}442443/**444* Retrieves the presets bank number by executing the \p get_bank function445* provided on its creation.446*447* @param preset The SoundFont preset instance.448* @return The bank number of \p preset.449*/450int fluid_preset_get_banknum(fluid_preset_t *preset)451{452return preset->get_banknum(preset);453}454455/**456* Retrieves the presets (instrument) number by executing the \p get_num function457* provided on its creation.458*459* @param preset The SoundFont preset instance.460* @return The number of \p preset.461*/462int fluid_preset_get_num(fluid_preset_t *preset)463{464return preset->get_num(preset);465}466467/**468* Retrieves the presets parent SoundFont instance.469*470* @param preset The SoundFont preset instance.471* @return The parent SoundFont of \p preset.472*/473fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset)474{475return preset->sfont;476}477478/**479* Destroys a SoundFont preset instance created with new_fluid_preset().480*481* @param preset The SoundFont preset instance to destroy.482*483* Implements #fluid_preset_free_t.484*485*/486void delete_fluid_preset(fluid_preset_t *preset)487{488fluid_return_if_fail(preset != NULL);489490FLUID_FREE(preset);491}492493/**494* Create a new sample instance.495*496* @return The sample on success, NULL otherwise.497*/498fluid_sample_t *499new_fluid_sample(void)500{501fluid_sample_t *sample = NULL;502503sample = FLUID_NEW(fluid_sample_t);504505if(sample == NULL)506{507FLUID_LOG(FLUID_ERR, "Out of memory");508return NULL;509}510511FLUID_MEMSET(sample, 0, sizeof(*sample));512513return sample;514}515516/**517* Destroy a sample instance previously created with new_fluid_sample().518*519* @param sample The sample to destroy.520*/521void522delete_fluid_sample(fluid_sample_t *sample)523{524fluid_return_if_fail(sample != NULL);525526if(sample->auto_free)527{528FLUID_FREE(sample->data);529FLUID_FREE(sample->data24);530}531532FLUID_FREE(sample);533}534535/**536* Returns the size of the fluid_sample_t structure.537*538* @return Size of fluid_sample_t in bytes539*540* Useful in low latency scenarios e.g. to allocate a pool of samples.541*542* @note It is recommend to zero initialize the memory before using the object.543*544* @warning Do NOT allocate samples on the stack and assign them to a voice!545*/546size_t fluid_sample_sizeof(void)547{548return sizeof(fluid_sample_t);549}550551/**552* Set the name of a SoundFont sample.553*554* @param sample SoundFont sample555* @param name Name to assign to sample (20 chars in length + zero terminator)556* @return #FLUID_OK on success, #FLUID_FAILED otherwise557*/558int fluid_sample_set_name(fluid_sample_t *sample, const char *name)559{560fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);561fluid_return_val_if_fail(name != NULL, FLUID_FAILED);562563FLUID_STRNCPY(sample->name, name, sizeof(sample->name));564return FLUID_OK;565}566567/**568* Assign sample data to a SoundFont sample.569*570* @param sample SoundFont sample571* @param data Buffer containing 16 bit (mono-)audio sample data572* @param data24 If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples573* @param nbframes Number of samples in \a data574* @param sample_rate Sampling rate of the sample data575* @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)576* @return #FLUID_OK on success, #FLUID_FAILED otherwise577*578* @note If \a copy_data is FALSE, data should have 8 unused frames at start579* and 8 unused frames at the end and \a nbframes should be >=48580*/581int582fluid_sample_set_sound_data(fluid_sample_t *sample,583short *data,584char *data24,585unsigned int nbframes,586unsigned int sample_rate,587short copy_data588)589{590/* the number of samples before the start and after the end */591#define SAMPLE_LOOP_MARGIN 8U592593fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);594fluid_return_val_if_fail(data != NULL, FLUID_FAILED);595fluid_return_val_if_fail(nbframes != 0, FLUID_FAILED);596597/* in case we already have some data */598if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free)599{600FLUID_FREE(sample->data);601FLUID_FREE(sample->data24);602}603604sample->data = NULL;605sample->data24 = NULL;606607if(copy_data)608{609unsigned int storedNbFrames;610611/* nbframes should be >= 48 (SoundFont specs) */612storedNbFrames = nbframes;613614if(storedNbFrames < 48)615{616storedNbFrames = 48;617}618619storedNbFrames += 2 * SAMPLE_LOOP_MARGIN;620621sample->data = FLUID_ARRAY(short, storedNbFrames);622623if(sample->data == NULL)624{625goto error_rec;626}627628FLUID_MEMSET(sample->data, 0, storedNbFrames * sizeof(short));629FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short));630631if(data24 != NULL)632{633sample->data24 = FLUID_ARRAY(char, storedNbFrames);634635if(sample->data24 == NULL)636{637goto error_rec;638}639640FLUID_MEMSET(sample->data24, 0, storedNbFrames);641FLUID_MEMCPY(sample->data24 + SAMPLE_LOOP_MARGIN, data24, nbframes * sizeof(char));642}643644/* pointers */645/* all from the start of data */646sample->start = SAMPLE_LOOP_MARGIN;647sample->end = SAMPLE_LOOP_MARGIN + nbframes - 1;648}649else650{651/* we cannot assure the SAMPLE_LOOP_MARGIN */652sample->data = data;653sample->data24 = data24;654sample->start = 0;655sample->end = nbframes - 1;656}657658sample->samplerate = sample_rate;659sample->sampletype = FLUID_SAMPLETYPE_MONO;660sample->auto_free = copy_data;661662return FLUID_OK;663664error_rec:665FLUID_LOG(FLUID_ERR, "Out of memory");666FLUID_FREE(sample->data);667FLUID_FREE(sample->data24);668sample->data = NULL;669sample->data24 = NULL;670return FLUID_FAILED;671672#undef SAMPLE_LOOP_MARGIN673}674675/**676* Set the loop of a sample.677*678* @param sample SoundFont sample679* @param loop_start Start sample index of the loop.680* @param loop_end End index of the loop (must be a valid sample as it marks the last sample to be played).681* @return #FLUID_OK on success, #FLUID_FAILED otherwise.682*/683int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end)684{685fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);686687sample->loopstart = loop_start;688sample->loopend = loop_end;689690return FLUID_OK;691}692693/**694* Set the pitch of a sample.695*696* @param sample SoundFont sample697* @param root_key Root MIDI note of sample (0-127)698* @param fine_tune Fine tune in cents699* @return #FLUID_OK on success, #FLUID_FAILED otherwise.700*/701int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune)702{703fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);704fluid_return_val_if_fail(0 <= root_key && root_key <= 127, FLUID_FAILED);705706sample->origpitch = root_key;707sample->pitchadj = fine_tune;708709return FLUID_OK;710}711712713/**714* Validate parameters of a sample715*716*/717int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size)718{719#define EXCLUSIVE_FLAGS (FLUID_SAMPLETYPE_MONO | FLUID_SAMPLETYPE_RIGHT | FLUID_SAMPLETYPE_LEFT)720static const unsigned int supported_flags = EXCLUSIVE_FLAGS | FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_OGG_VORBIS | FLUID_SAMPLETYPE_ROM;721722/* ROM samples are unusable for us by definition */723if(sample->sampletype & FLUID_SAMPLETYPE_ROM)724{725FLUID_LOG(FLUID_WARN, "Sample '%s': ROM sample ignored", sample->name);726return FLUID_FAILED;727}728729if(sample->sampletype & ~supported_flags)730{731FLUID_LOG(FLUID_WARN, "Sample '%s' has unknown flags, possibly using an unsupported compression; sample ignored", sample->name);732return FLUID_FAILED;733}734735if((sample->sampletype & EXCLUSIVE_FLAGS) & ((sample->sampletype & EXCLUSIVE_FLAGS) - 1))736{737FLUID_LOG(FLUID_INFO, "Sample '%s' should be either mono or left or right; using it anyway", sample->name);738}739740if((sample->sampletype & FLUID_SAMPLETYPE_LINKED) && (sample->sampletype & EXCLUSIVE_FLAGS))741{742FLUID_LOG(FLUID_INFO, "Linked sample '%s' should not be mono, left or right at the same time; using it anyway", sample->name);743}744745if((sample->sampletype & EXCLUSIVE_FLAGS) == 0)746{747FLUID_LOG(FLUID_INFO, "Sample '%s' has no flags set, assuming mono", sample->name);748sample->sampletype = FLUID_SAMPLETYPE_MONO;749}750751/* Ogg vorbis compressed samples in the SF3 format use byte indices for752* sample start and end pointers before decompression. Standard SF2 samples753* use sample word indices for all pointers, so use half the buffer_size754* for validation. */755if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))756{757if(buffer_size % 2)758{759FLUID_LOG(FLUID_WARN, "Sample '%s': invalid buffer size", sample->name);760return FLUID_FAILED;761}762763buffer_size /= 2;764}765766if((sample->end > buffer_size) || (sample->start >= sample->end))767{768FLUID_LOG(FLUID_WARN, "Sample '%s': invalid start/end file positions", sample->name);769return FLUID_FAILED;770}771772return FLUID_OK;773#undef EXCLUSIVE_FLAGS774}775776/* Check the sample loop pointers and optionally convert them to something777* usable in case they are broken. Return a boolean indicating if the pointers778* have been modified, so the user can be notified of possible audio glitches.779*/780int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size)781{782int modified = FALSE;783unsigned int max_end = buffer_size / 2;784/* In fluid_sample_t the sample end pointer points to the last sample, not785* to the data word after the last sample. FIXME: why? */786unsigned int sample_end = sample->end + 1;787788if(sample->loopstart == sample->loopend)789{790/* Some SoundFonts disable loops by setting loopstart = loopend. While791* technically invalid, we decided to accept those samples anyway.792* Before fluidsynth 2.2.5 we've set those indices to zero, as this793* change was believed to be inaudible. This turned out to be an794* incorrect assumption, as the loop points may still be modified by795* loop offset modulators afterwards.796*/797if(sample->loopstart != sample->start)798{799// Many soundfonts set loopstart == loopend == sample->start to disabled to loop.800// Only report cases where it's not equal to the sample->start, to avoid spam.801FLUID_LOG(FLUID_DBG, "Sample '%s': zero length loop detected: loopstart == loopend == '%d', sample start '%d', using it anyway",802sample->name, sample->loopstart, sample->start);803}804}805else if(sample->loopstart > sample->loopend)806{807unsigned int tmp;808809/* If loop start and end are reversed, try to swap them around and810* continue validation */811FLUID_LOG(FLUID_DBG, "Sample '%s': reversed loop pointers '%d' - '%d', trying to fix",812sample->name, sample->loopstart, sample->loopend);813tmp = sample->loopstart;814sample->loopstart = sample->loopend;815sample->loopend = tmp;816modified = TRUE;817}818819/* The SoundFont 2.4 spec defines the loopstart index as the first sample820* point of the loop while loopend is the first point AFTER the last sample821* of the loop. However we cannot be sure whether any of loopend or end is822* correct. Hours of thinking through this have concluded that it would be823* best practice to mangle with loops as little as necessary by only making824* sure the pointers are within sample->start to max_end. Incorrect825* soundfont shall preferably fail loudly. */826if((sample->loopstart < sample->start) || (sample->loopstart > max_end))827{828FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop start '%d', setting to sample start '%d'",829sample->name, sample->loopstart, sample->start);830sample->loopstart = sample->start;831modified = TRUE;832}833834if((sample->loopend < sample->start) || (sample->loopend > max_end))835{836FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop end '%d', setting to sample end '%d'",837sample->name, sample->loopend, sample_end);838sample->loopend = sample_end;839modified = TRUE;840}841842if((sample->loopstart > sample_end) || (sample->loopend > sample_end))843{844FLUID_LOG(FLUID_DBG, "Sample '%s': loop range '%d - %d' after sample end '%d', using it anyway",845sample->name, sample->loopstart, sample->loopend, sample_end);846}847848return modified;849}850851852