From c9b8d40fc25bcaacae5ac8a18fccec5f9269e79a Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Wed, 19 Dec 2018 14:09:29 +0100 Subject: [PATCH] Fourcc support (#482) Have a direct comparison to the fourcc code rather than searching through chunk ids all the time with chunkid() --- src/midi/fluid_midi.c | 37 ++++- src/sfloader/fluid_sffile.c | 263 +++++++++++++++++++++--------------- src/utils/fluid_sys.c | 65 --------- src/utils/fluid_sys.h | 8 ++ 4 files changed, 199 insertions(+), 174 deletions(-) diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c index 4795fceb..f672124c 100644 --- a/src/midi/fluid_midi.c +++ b/src/midi/fluid_midi.c @@ -81,6 +81,41 @@ static int fluid_midi_file_get_division(fluid_midi_file *midifile); * MIDIFILE */ +/** + * Check if a file is a MIDI file. + * @param filename Path to the file to check + * @return TRUE if it could be a MIDI file, FALSE otherwise + * + * The current implementation only checks for the "MThd" header in the file. + * It is useful only to distinguish between SoundFont and MIDI files. + */ +int fluid_is_midifile(const char *filename) +{ + FILE *fp = FLUID_FOPEN(filename, "rb"); + uint32_t id; + int retcode = 0; + + do + { + if(fp == NULL) + { + break; + } + + if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1) + { + break; + } + + retcode = (id == FLUID_FOURCC('M', 'T', 'h', 'd')); + } + while(0); + + FLUID_FCLOSE(fp); + + return retcode; +} + /** * Return a new MIDI file handle for parsing an already-loaded MIDI file. * @internal @@ -1597,7 +1632,7 @@ new_fluid_player(fluid_synth_t *synth) fluid_settings_getint(synth->settings, "player.reset-synth", &i); fluid_player_handle_reset_synth(player, NULL, i); - + fluid_settings_callback_int(synth->settings, "player.reset-synth", fluid_player_handle_reset_synth, player); diff --git a/src/sfloader/fluid_sffile.c b/src/sfloader/fluid_sffile.c index f9382b10..0725e1f3 100644 --- a/src/sfloader/fluid_sffile.c +++ b/src/sfloader/fluid_sffile.c @@ -34,76 +34,79 @@ Borrowed from Smurf SoundFont Editor by Josh Green =================================================================*/ -/* - functions for loading data from sfont files, with appropriate byte swapping - on big endian machines. Sfont IDs are not swapped because the ID read is - equivalent to the matching ID list in memory regardless of LE/BE machine -*/ +/* FOURCC definitions */ +#define RIFF_FCC FLUID_FOURCC('R','I','F','F') +#define LIST_FCC FLUID_FOURCC('L','I','S','T') +#define SFBK_FCC FLUID_FOURCC('s','f','b','k') +#define INFO_FCC FLUID_FOURCC('I','N','F','O') +#define SDTA_FCC FLUID_FOURCC('s','d','t','a') +#define PDTA_FCC FLUID_FOURCC('p','d','t','a') /* info/sample/preset */ -/* sf file chunk IDs */ -enum -{ - RIFF_ID, - LIST_ID, - SFBK_ID, - INFO_ID, - SDTA_ID, - PDTA_ID, /* info/sample/preset */ +#define IFIL_FCC FLUID_FOURCC('i','f','i','l') +#define ISNG_FCC FLUID_FOURCC('i','s','n','g') +#define INAM_FCC FLUID_FOURCC('I','N','A','M') +#define IROM_FCC FLUID_FOURCC('i','r','o','m') /* info ids (1st byte of info strings) */ +#define IVER_FCC FLUID_FOURCC('i','v','e','r') +#define ICRD_FCC FLUID_FOURCC('I','C','R','D') +#define IENG_FCC FLUID_FOURCC('I','E','N','G') +#define IPRD_FCC FLUID_FOURCC('I','P','R','D') /* more info ids */ +#define ICOP_FCC FLUID_FOURCC('I','C','O','P') +#define ICMT_FCC FLUID_FOURCC('I','C','M','T') +#define ISFT_FCC FLUID_FOURCC('I','S','F','T') /* and yet more info ids */ - IFIL_ID, - ISNG_ID, - INAM_ID, - IROM_ID, /* info ids (1st byte of info strings) */ - IVER_ID, - ICRD_ID, - IENG_ID, - IPRD_ID, /* more info ids */ - ICOP_ID, - ICMT_ID, - ISFT_ID, /* and yet more info ids */ +#define SNAM_FCC FLUID_FOURCC('s','n','a','m') +#define SMPL_FCC FLUID_FOURCC('s','m','p','l') /* sample ids */ +#define PHDR_FCC FLUID_FOURCC('p','h','d','r') +#define PBAG_FCC FLUID_FOURCC('p','b','a','g') +#define PMOD_FCC FLUID_FOURCC('p','m','o','d') +#define PGEN_FCC FLUID_FOURCC('p','g','e','n') /* preset ids */ +#define IHDR_FCC FLUID_FOURCC('i','n','s','t') +#define IBAG_FCC FLUID_FOURCC('i','b','a','g') +#define IMOD_FCC FLUID_FOURCC('i','m','o','d') +#define IGEN_FCC FLUID_FOURCC('i','g','e','n') /* instrument ids */ +#define SHDR_FCC FLUID_FOURCC('s','h','d','r') /* sample info */ +#define SM24_FCC FLUID_FOURCC('s','m','2','4') - SNAM_ID, - SMPL_ID, /* sample ids */ - PHDR_ID, - PBAG_ID, - PMOD_ID, - PGEN_ID, /* preset ids */ - IHDR_ID, - IBAG_ID, - IMOD_ID, - IGEN_ID, /* instrument ids */ - SHDR_ID, /* sample info */ - SM24_ID, - - UNKN_ID -}; +/* Set when the FCC code is unknown */ +#define UNKN_ID FLUID_N_ELEMENTS(idlist) /* - * This declares a char array containing the SF2 chunk identifiers. This - * array is being accessed like an uint32 below to simplify id comparison. - * To make sure it is suitably aligned for uint32 access, we must wrap it - * inside a union along with a uint32 telling the compiler to align it - * for integer access and avoiding undefined behaviour. - * This basically is the C89 equivalent to what is written in C11 as: - * alignas(uint32_t) static const char idlist[] = {}; - * - * See: EXP36-C. Do not cast pointers into more strictly aligned pointer - * types - SEI CERT C Coding Standard + * This declares a uint32_t array containing the SF2 chunk identifiers. */ -static const union fluid_idlist +static const uint32_t idlist[] = { - /* - * Cannot be char c[ ], because in C89, arrays wraped in unions - * must have a fixed size. Otherwise the size of the union would depend - * on the initialization of its first member, which results in - * different sizes for different instances of the same union type. - */ - char c[116]; - uint32_t i; -} idlist = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD" - "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24" - }; + RIFF_FCC, + LIST_FCC, + SFBK_FCC, + INFO_FCC, + SDTA_FCC, + PDTA_FCC, + IFIL_FCC, + ISNG_FCC, + INAM_FCC, + IROM_FCC, + IVER_FCC, + ICRD_FCC, + IENG_FCC, + IPRD_FCC, + ICOP_FCC, + ICMT_FCC, + ISFT_FCC, + + SNAM_FCC, + SMPL_FCC, + PHDR_FCC, + PBAG_FCC, + PMOD_FCC, + PGEN_FCC, + IHDR_FCC, + IBAG_FCC, + IMOD_FCC, + IGEN_FCC, + SHDR_FCC, + SM24_FCC +}; /* generator types */ typedef enum @@ -206,8 +209,6 @@ static const unsigned short invalid_preset_gen[] = }; -#define CHNKIDSTR(id) &idlist.c[(id - 1) * 4] - /* sfont file chunk sizes */ #define SF_PHDR_SIZE (38) #define SF_BAG_SIZE (4) @@ -323,6 +324,56 @@ static void delete_zone(SFZone *zone); static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data); static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24); +/** + * Check if a file is a SoundFont file. + * @param filename Path to the file to check + * @return TRUE if it could be a SoundFont, FALSE otherwise + * + * @note The current implementation only checks for the "RIFF" and "sfbk" headers in + * the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files. + */ +int fluid_is_soundfont(const char *filename) +{ + FILE *fp = FLUID_FOPEN(filename, "rb"); + uint32_t fcc; + int retcode = 0; + + do + { + if(fp == NULL) + { + break; + } + + if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) + { + break; + } + + if(fcc != RIFF_FCC) + { + break; + } + + if(FLUID_FSEEK(fp, 4, SEEK_CUR)) + { + break; + } + + if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) + { + break; + } + + retcode = (fcc == SFBK_FCC); + } + while(0); + + FLUID_FCLOSE(fp); + + return retcode; +} + /* * Open a SoundFont file and parse it's contents into a SFData structure. * @@ -512,11 +563,10 @@ void fluid_sffile_close(SFData *sf) static int chunkid(uint32_t id) { unsigned int i; - const uint32_t *p = &idlist.i; - for(i = 0; i < sizeof(idlist) / sizeof(idlist.i); i++, p += 1) + for(i = 0; i < FLUID_N_ELEMENTS(idlist); i++) { - if(*p == id) + if(idlist[i] == id) { break; } @@ -532,7 +582,7 @@ static int load_header(SFData *sf) READCHUNK(sf, &chunk); /* load RIFF chunk */ - if(chunkid(chunk.id) != RIFF_ID) + if(chunk.id != RIFF_FCC) { /* error if not RIFF */ FLUID_LOG(FLUID_ERR, "Not a RIFF file"); @@ -541,7 +591,7 @@ static int load_header(SFData *sf) READID(sf, &chunk.id); /* load file ID */ - if(chunkid(chunk.id) != SFBK_ID) + if(chunk.id != SFBK_FCC) { /* error if not SFBK_ID */ FLUID_LOG(FLUID_ERR, "Not a SoundFont file"); @@ -560,7 +610,7 @@ static int load_header(SFData *sf) return FALSE; } - if(chunkid(chunk.id) != INFO_ID) + if(chunk.id != INFO_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk"); return FALSE; @@ -577,7 +627,7 @@ static int load_header(SFData *sf) return FALSE; } - if(chunkid(chunk.id) != SDTA_ID) + if(chunk.id != SDTA_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk"); return FALSE; @@ -594,7 +644,7 @@ static int load_header(SFData *sf) return FALSE; } - if(chunkid(chunk.id) != PDTA_ID) + if(chunk.id != PDTA_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk"); return FALSE; @@ -639,7 +689,7 @@ static int read_listchunk(SFData *sf, SFChunk *chunk) { READCHUNK(sf, chunk); /* read list chunk */ - if(chunkid(chunk->id) != LIST_ID) /* error if ! list chunk */ + if(chunk->id != LIST_FCC) /* error if ! list chunk */ { FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse"); return FALSE; @@ -653,8 +703,11 @@ static int read_listchunk(SFData *sf, SFChunk *chunk) static int process_info(SFData *sf, int size) { SFChunk chunk; - unsigned char id; - char *item; + union + { + char *chr; + uint32_t *fcc; + } item; unsigned short ver; while(size > 0) @@ -662,9 +715,7 @@ static int process_info(SFData *sf, int size) READCHUNK(sf, &chunk); size -= 8; - id = chunkid(chunk.id); - - if(id == IFIL_ID) + if(chunk.id == IFIL_FCC) { /* sound font version chunk? */ if(chunk.size != 4) @@ -705,7 +756,7 @@ static int process_info(SFData *sf, int size) return FALSE; } } - else if(id == IVER_ID) + else if(chunk.id == IVER_FCC) { /* ROM version chunk? */ if(chunk.size != 4) @@ -719,34 +770,35 @@ static int process_info(SFData *sf, int size) READW(sf, ver); sf->romver.minor = ver; } - else if(id != UNKN_ID) + else if(chunkid(chunk.id) != UNKN_ID) { - if((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2)) + if((chunk.id != ICMT_FCC && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2)) { FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes", &chunk.id, chunk.size); return FALSE; } - /* alloc for chunk id and da chunk */ - if(!(item = FLUID_MALLOC(chunk.size + 1))) + /* alloc for chunk fcc and da chunk */ + if(!(item.fcc = FLUID_MALLOC(chunk.size + sizeof(uint32_t) + 1))) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } /* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */ - sf->info = fluid_list_append(sf->info, item); + sf->info = fluid_list_append(sf->info, item.fcc); - *(unsigned char *)item = id; + /* save chunk fcc and update pointer to data value */ + *item.fcc++ = chunk.id; - if(sf->fcbs->fread(&item[1], chunk.size, sf->sffd) == FLUID_FAILED) + if(sf->fcbs->fread(item.chr, chunk.size, sf->sffd) == FLUID_FAILED) { return FALSE; } - /* force terminate info item (don't forget uint8 info ID) */ - *(item + chunk.size) = '\0'; + /* force terminate info item */ + item.chr[chunk.size] = '\0'; } else { @@ -779,7 +831,7 @@ static int process_sdta(SFData *sf, unsigned int size) READCHUNK(sf, &chunk); size -= 8; - if(chunkid(chunk.id) != SMPL_ID) + if(chunk.id != SMPL_FCC) { FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead"); return FALSE; @@ -812,7 +864,7 @@ static int process_sdta(SFData *sf, unsigned int size) READCHUNK(sf, &chunk); size -= 8; - if(chunkid(chunk.id) == SM24_ID) + if(chunk.id == SM24_FCC) { int sm24size, sdtahalfsize; @@ -852,29 +904,24 @@ ret: static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size) { - unsigned int id; - const char *expstr; - - expstr = CHNKIDSTR(expid); /* in case we need it */ - READCHUNK(sf, chunk); *size -= 8; - if((id = chunkid(chunk->id)) != expid) + if(chunk->id != expid) { - FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", expstr); + FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", &expid); return FALSE; } if(chunk->size % reclen) /* valid chunk size? */ { - FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", expstr, reclen); + FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", &expid, reclen); return FALSE; } if((*size -= chunk->size) < 0) { - FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", expstr); + FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", &expid); return FALSE; } @@ -885,7 +932,7 @@ static int process_pdta(SFData *sf, int size) { SFChunk chunk; - if(!pdtahelper(sf, PHDR_ID, SF_PHDR_SIZE, &chunk, &size)) + if(!pdtahelper(sf, PHDR_FCC, SF_PHDR_SIZE, &chunk, &size)) { return FALSE; } @@ -895,7 +942,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, PBAG_ID, SF_BAG_SIZE, &chunk, &size)) + if(!pdtahelper(sf, PBAG_FCC, SF_BAG_SIZE, &chunk, &size)) { return FALSE; } @@ -905,7 +952,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, PMOD_ID, SF_MOD_SIZE, &chunk, &size)) + if(!pdtahelper(sf, PMOD_FCC, SF_MOD_SIZE, &chunk, &size)) { return FALSE; } @@ -915,7 +962,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, PGEN_ID, SF_GEN_SIZE, &chunk, &size)) + if(!pdtahelper(sf, PGEN_FCC, SF_GEN_SIZE, &chunk, &size)) { return FALSE; } @@ -925,7 +972,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, IHDR_ID, SF_IHDR_SIZE, &chunk, &size)) + if(!pdtahelper(sf, IHDR_FCC, SF_IHDR_SIZE, &chunk, &size)) { return FALSE; } @@ -935,7 +982,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, IBAG_ID, SF_BAG_SIZE, &chunk, &size)) + if(!pdtahelper(sf, IBAG_FCC, SF_BAG_SIZE, &chunk, &size)) { return FALSE; } @@ -945,7 +992,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, IMOD_ID, SF_MOD_SIZE, &chunk, &size)) + if(!pdtahelper(sf, IMOD_FCC, SF_MOD_SIZE, &chunk, &size)) { return FALSE; } @@ -955,7 +1002,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, IGEN_ID, SF_GEN_SIZE, &chunk, &size)) + if(!pdtahelper(sf, IGEN_FCC, SF_GEN_SIZE, &chunk, &size)) { return FALSE; } @@ -965,7 +1012,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, SHDR_ID, SF_SHDR_SIZE, &chunk, &size)) + if(!pdtahelper(sf, SHDR_FCC, SF_SHDR_SIZE, &chunk, &size)) { return FALSE; } diff --git a/src/utils/fluid_sys.c b/src/utils/fluid_sys.c index 3df88fc2..4ac1344b 100644 --- a/src/utils/fluid_sys.c +++ b/src/utils/fluid_sys.c @@ -274,71 +274,6 @@ fluid_error() return fluid_errbuf; } -/** - * Check if a file is a MIDI file. - * @param filename Path to the file to check - * @return TRUE if it could be a MIDI file, FALSE otherwise - * - * The current implementation only checks for the "MThd" header in the file. - * It is useful only to distinguish between SoundFont and MIDI files. - */ -int -fluid_is_midifile(const char *filename) -{ - FILE *fp = fopen(filename, "rb"); - char id[4]; - - if(fp == NULL) - { - return 0; - } - - if(fread((void *) id, 1, 4, fp) != 4) - { - fclose(fp); - return 0; - } - - fclose(fp); - - return FLUID_STRNCMP(id, "MThd", 4) == 0; -} - -/** - * Check if a file is a SoundFont file. - * @param filename Path to the file to check - * @return TRUE if it could be a SoundFont, FALSE otherwise - * - * @note The current implementation only checks for the "RIFF" and "sfbk" headers in - * the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files. - */ -int -fluid_is_soundfont(const char *filename) -{ - FILE *fp = fopen(filename, "rb"); - char riff_id[4], sfbk_id[4]; - - if(fp == NULL) - { - return 0; - } - - if((fread((void *) riff_id, 1, sizeof(riff_id), fp) != sizeof(riff_id)) || - (fseek(fp, 4, SEEK_CUR) != 0) || - (fread((void *) sfbk_id, 1, sizeof(sfbk_id), fp) != sizeof(sfbk_id))) - { - goto error_rec; - } - - fclose(fp); - return (FLUID_STRNCMP(riff_id, "RIFF", sizeof(riff_id)) == 0) && - (FLUID_STRNCMP(sfbk_id, "sfbk", sizeof(sfbk_id)) == 0); - -error_rec: - fclose(fp); - return 0; -} - /** * Suspend the execution of the current thread for the specified amount of time. * @param milliseconds to wait. diff --git a/src/utils/fluid_sys.h b/src/utils/fluid_sys.h index 47cc95c1..72b32302 100644 --- a/src/utils/fluid_sys.h +++ b/src/utils/fluid_sys.h @@ -77,6 +77,14 @@ #define FLUID_LE32TOH(x) GINT32_FROM_LE(x) #define FLUID_LE16TOH(x) GINT16_FROM_LE(x) +#if FLUID_IS_BIG_ENDIAN +#define FLUID_FOURCC(_a, _b, _c, _d) \ + (uint32_t)(((uint32_t)(_a) << 24) | ((uint32_t)(_b) << 16) | ((uint32_t)(_c) << 8) | (uint32_t)(_d)) +#else +#define FLUID_FOURCC(_a, _b, _c, _d) \ + (uint32_t)(((uint32_t)(_d) << 24) | ((uint32_t)(_c) << 16) | ((uint32_t)(_b) << 8) | (uint32_t)(_a)) +#endif + #define fluid_return_if_fail(cond) \ if(cond) \