Fourcc support (#482)

Have a direct comparison to the fourcc code rather than searching through chunk ids all the time with chunkid()
This commit is contained in:
carlo-bramini 2018-12-19 14:09:29 +01:00 committed by Tom M
parent b7af84fbd5
commit c9b8d40fc2
4 changed files with 199 additions and 174 deletions

View file

@ -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

View file

@ -34,77 +34,80 @@
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
#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 */
#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')
/* Set when the FCC code is unknown */
#define UNKN_ID FLUID_N_ELEMENTS(idlist)
/*
* This declares a uint32_t array containing the SF2 chunk identifiers.
*/
static const uint32_t idlist[] =
{
RIFF_ID,
LIST_ID,
SFBK_ID,
INFO_ID,
SDTA_ID,
PDTA_ID, /* info/sample/preset */
RIFF_FCC,
LIST_FCC,
SFBK_FCC,
INFO_FCC,
SDTA_FCC,
PDTA_FCC,
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 */
IFIL_FCC,
ISNG_FCC,
INAM_FCC,
IROM_FCC,
IVER_FCC,
ICRD_FCC,
IENG_FCC,
IPRD_FCC,
ICOP_FCC,
ICMT_FCC,
ISFT_FCC,
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
SNAM_FCC,
SMPL_FCC,
PHDR_FCC,
PBAG_FCC,
PMOD_FCC,
PGEN_FCC,
IHDR_FCC,
IBAG_FCC,
IMOD_FCC,
IGEN_FCC,
SHDR_FCC,
SM24_FCC
};
/*
* 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
*/
static const union fluid_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"
};
/* 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;
}

View file

@ -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.

View file

@ -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) \