#include #include #include #include #include #include "midi.h" /* static void swap32(int32_t *i); */ static void swapu32(u_int32_t *i); /* static int MidiFGetVarlenNumber(FILE *fp); static int MidiGetVarlenNumber(const char *buf, int max); */ int MidiIsFileMidi(const char *filename); int MidiRead(const char *filename, FILE *fp, MidiDataStruct *md); void MidiDestroy(MidiDataStruct *md); #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define CLIP(a,l,h) (MIN(MAX((a),(l)),(h))) #define ABSOLUTE(x) (((x) < 0) ? ((x) * -1) : (x)) #if 0 void swap32(int32_t *i) { int32_t tmp; int8_t *src, *tar; if(i == NULL) return; tmp = *i; src = (int8_t *)&tmp; tar = (int8_t *)i; tar[3] = src[0]; tar[2] = src[1]; tar[1] = src[2]; tar[0] = src[3]; } #endif void swapu32(u_int32_t *i) { u_int32_t tmp = *i; u_int8_t *src = (u_int8_t *)&tmp, *tar = (u_int8_t *)i; tar[3] = src[0]; tar[2] = src[1]; tar[1] = src[2]; tar[0] = src[3]; } #if 0 /* * Reads variable-length number from FILE pointer fp, * fp will be incremented to the point just after the bytes * making up the variable-length number. */ int MidiFGetVarlenNumber(FILE *fp) { int c, p = 0, val = 0; if(fp == NULL) return(0); do { c = fgetc(fp); if(c == EOF) break; val += (((u_int8_t)c & 0x7f) << (p * 8)); p++; } while(((u_int8_t)c & 0x80) && (p <= 4)); return(val); } #endif #if 0 /* * Reads variable-length number from buffer pointer buf. * Will not read more than max bytes. */ int MidiGetVarlenNumber(const char *buf, int max) { int p = 0, val = 0; if(buf == NULL) return(0); do { val += (((u_int8_t)*buf & 0x7f) << (p * 8)); buf++; p++; } while(((u_int8_t)*buf & 0x80) && (p <= max)); return(val); } #endif /* * Returns MidiSuccess if filename is a MIDI file or * MidiBadValue if filename is not a MIDI file. */ int MidiIsFileMidi(const char *filename) { int i, c; struct stat stat_buf; FILE *fp; char chunk_type_name[MidiChunkTypeNameLen + 1]; if(filename == NULL) return(MidiBadValue); /* Check if file exists. */ if(stat(filename, &stat_buf)) return(MidiBadValue); /* Open file. */ fp = fopen(filename, "rb"); if(fp == NULL) return(MidiBadValue); /* Read first few bytes. */ *chunk_type_name = '\0'; for(i = 0; i < MidiChunkTypeNameLen; i++) { c = fgetc(fp); if(c == EOF) break; chunk_type_name[i] = (char)c; } chunk_type_name[MidiChunkTypeNameLen] = '\0'; /* Close file. */ fclose(fp); if(!strcmp(chunk_type_name, MidiChunkTypeNameHeader) || !strcmp(chunk_type_name, MidiChunkTypeNameTrack) ) return(MidiSuccess); else return(MidiBadValue); } /* * Reads the header from the stream fp and initializes the given * md structure if fp is valid and a midi file. * * The given filename is used as referance purposes, it can be NULL. * * If MidiSuccess is returned then the given fp will be transfered * to the md structure and should not be referanced again. For all * other return values, the calling function is responsible for * closing the given fp. */ int MidiRead(const char *filename, FILE *fp, MidiDataStruct *md) { int need_break; int i, c; struct stat stat_buf; off_t filepos, filesize; char chunk_type_name[MidiChunkTypeNameLen + 1]; u_int32_t chunk_len; MidiChunkStruct *chunk; u_int8_t *buf_ptr; int buf_len; if(md == NULL) return(MidiNoBuffers); if(fp == NULL) return(MidiNoAccess); /* Rewind fp to beginning of midi file. */ rewind(fp); /* Reset values. */ memset(md, 0x00, sizeof(MidiDataStruct)); /* Get size of file. */ if(fstat(fileno(fp), &stat_buf)) return(MidiNoAccess); filesize = stat_buf.st_size; if(filesize == 0) return(MidiErrorNotMidi); /* Check if this is a midi file by reading the first few bytes. */ *chunk_type_name = '\0'; for(i = 0; i < MidiChunkTypeNameLen; i++) { c = fgetc(fp); if(c == EOF) break; chunk_type_name[i] = (char)c; } chunk_type_name[MidiChunkTypeNameLen] = '\0'; if(strcmp(chunk_type_name, MidiChunkTypeNameHeader) && strcmp(chunk_type_name, MidiChunkTypeNameTrack) ) return(MidiErrorNotMidi); /* Begin reading midi file header. */ /* Record file name. */ if(filename != NULL) md->filename = strdup(filename); /* Read each chunk in the file. */ rewind(fp); filepos = 0; while(filepos < filesize) { need_break = 0; /* Chunk type (4 bytes). */ for(i = 0; i < MidiChunkTypeNameLen; i++) { c = fgetc(fp); if(c == EOF) { need_break = 1; break; } chunk_type_name[i] = (char)c; } chunk_type_name[MidiChunkTypeNameLen] = '\0'; /* Safe. */ if(need_break) break; /* Read chunk length (4 bytes). */ fread(&chunk_len, sizeof(u_int32_t), 1, fp); swapu32(&chunk_len); /* Chunk length specified in the file data does not include * chunk type and length bytes so add 8 to chunk_len. */ chunk_len += (MidiChunkTypeNameLen + sizeof(u_int32_t)); /* Allocate raw data buffer and read raw chunk data into it. */ buf_len = (int)chunk_len - 8; if(buf_len > 0) { buf_ptr = (u_int8_t *)malloc(buf_len * sizeof(u_int8_t)); if(buf_ptr != NULL) fread(buf_ptr, sizeof(u_int8_t), buf_len, fp); else break; } else { buf_ptr = NULL; buf_len = 0; } /* Allocate a new chunk structure. */ i = md->total_chunks; md->total_chunks++; md->chunk = (MidiChunkStruct **)realloc( md->chunk, md->total_chunks * sizeof(MidiChunkStruct *) ); if(md->chunk == NULL) { md->total_chunks = 0; break; } md->chunk[i] = chunk = (MidiChunkStruct *)calloc( 1, sizeof(MidiChunkStruct) ); if(chunk == NULL) { md->total_chunks -= 1; break; } /* Put fetched data into chunk structure. */ /* Chunk type. */ if(!strcmp(chunk_type_name, MidiChunkTypeNameHeader)) chunk->type = MidiChunkTypeHeader; else if(!strcmp(chunk_type_name, MidiChunkTypeNameTrack)) chunk->type = MidiChunkTypeTrack; else chunk->type = MidiChunkTypeUnknown; /* Chunk length. */ chunk->len = chunk_len; /* Raw data buffer and length of it (might be NULL). */ chunk->buf_len = buf_len; chunk->buf = buf_ptr; /* Parse by chunk type. */ /* Header chunk. */ if(chunk->type == MidiChunkTypeHeader) { if(buf_len >= 6) { /* Update the header information. */ md->format = (int16_t)( ((u_int16_t)buf_ptr[0] << 8) + (u_int16_t)buf_ptr[1] ); md->tracks = (int16_t)( ((u_int16_t)buf_ptr[2] << 8) + (u_int16_t)buf_ptr[3] ); md->divisions = (int16_t)( ((u_int16_t)buf_ptr[4] << 8) + (u_int16_t)buf_ptr[5] ); } } /* Track chunk. */ else if(chunk->type == MidiChunkTypeTrack) { /* TODO */ } /* Increment file position. */ filepos += chunk_len; if(fseek(fp, filepos, SEEK_SET) == -1) break; } /* Close file since we are returning MidiSuccess and the calling * function will not referance the given fp again. */ /* Note, later on if we are reading the midi file, we should record * the given fp on the md structure. */ fclose(fp); return(MidiSuccess); } /* * Deallocates all allocated substructures in md. */ void MidiDestroy(MidiDataStruct *md) { int i, n; MidiChunkStruct *chunk; if(md == NULL) return; free(md->filename); md->filename = NULL; /* Free each chunk. */ for(i = 0; i < md->total_chunks; i++) { chunk = md->chunk[i]; if(chunk == NULL) continue; /* Free each event in the chunk. */ for(n = 0; n < chunk->total_events; n++) free(chunk->event[n]); free(chunk->event); /* Free raw data buffer. */ free(chunk->buf); /* Free structure itself. */ free(chunk); } free(md->chunk); md->chunk = NULL; md->total_chunks = 0; }