home *** CD-ROM | disk | FTP | other *** search
- /* ScummC
- * Copyright (C) 2006 Alban Bedel
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
- /** @file scc_smf.c
- * @brief A simple Standard MIDI File parser/writer.
- */
-
- #include "config.h"
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <inttypes.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
-
- #include "scc_util.h"
- #include "scc_fd.h"
- #include "scc_smf.h"
-
-
- unsigned scc_fd_read_smf_int(scc_fd_t* fd, unsigned max_size,
- uint32_t* ret) {
- unsigned v = 0x80, val = 0, n = 0;
- while((v & 0x80) && n < max_size) {
- v = scc_fd_r8(fd);
- val <<= 7;
- val |= v&0x7F;
- n++;
- }
- if(v & 0x80) return 0;
- *ret = val;
- return n;
- }
-
- int scc_fd_write_smf_int(scc_fd_t* fd, unsigned v) {
- int i;
- if(!v) return scc_fd_w8(fd,v);
- // Find the start
- for(i = 4 ; i >= 0 ; i--)
- if(v & (0x7F << (i*7))) break;
- for( ; i > 0 ; i--)
- scc_fd_w8(fd,0x80 | (v >> (i*7)));
- return scc_fd_w8(fd,v & 0x7F);
- }
-
- static int scc_smf_parse_track(scc_fd_t* fd, scc_smf_track_t* track,
- unsigned size) {
- scc_smf_event_t* ev = NULL;
- unsigned pos = 0;
- uint64_t time = 0;
-
- while(pos < size) {
- uint32_t delta, arg_size;
- uint8_t cmd, meta = 0, runaway = 0;
- int n = scc_fd_read_smf_int(fd,size-pos,&delta);
- if(!n || pos + n + 2 > size) return 0;
- cmd = scc_fd_r8(fd);
- pos += n+1;
- // Meta and sysex events
- switch(cmd) {
- case SMF_META_EVENT:
- if(pos + 2 > size) {
- scc_log(LOG_ERR,"Invalid Meta Event size.\n");
- return 0;
- }
- meta = scc_fd_r8(fd), pos++;
- case SMF_SYSEX_EVENT:
- case SMF_SYSEX_CONTINUATION:
- // Read the event length
- if(!(n = scc_fd_read_smf_int(fd,size-pos,&arg_size))) {
- scc_log(LOG_ERR,"Failed to read SysEx length.\n");
- return 0;
- }
- pos += n;
- break;
- default:
- if(cmd & 0x80) { // Normal event
- if((cmd & 0xF0) == 0xC0 ||
- (cmd & 0xF0) == 0xD0)
- arg_size = 1;
- else
- arg_size = 2;
- } else { // Runaway event
- if(!track->last_event) {
- scc_log(LOG_ERR,"Runaway event without a preceding event.\n");
- return 0;
- }
- arg_size = track->last_event->args_size-1;
- runaway = 1;
- //vent = track->last_event->event;
- }
- }
-
- if(pos + arg_size > size) {
- scc_log(LOG_ERR,"Invalid argument size.\n");
- return 0;
- }
-
- ev = malloc(sizeof(scc_smf_event_t) + runaway + arg_size);
- ev->next = NULL;
- ev->time = time + delta;
- if(runaway) {
- ev->cmd = track->last_event->cmd;
- ev->args[0] = cmd;
- } else
- ev->cmd = cmd;
- ev->meta = meta;
- ev->args_size = runaway + arg_size;
- if(arg_size > 0 && scc_fd_read(fd,&ev->args[runaway],arg_size) != arg_size) {
- scc_log(LOG_ERR,"Failed to read arguments.\n");
- free(ev);
- return 0;
- }
- pos += arg_size;
- SCC_LIST_ADD(track->events,track->last_event,ev);
- time += delta;
- }
-
- // Compute the size ourself in case the input data
- // is not encoded in the shortest possible way.
- track->size = scc_smf_track_get_size(track);
-
- return 1;
- }
-
- scc_smf_t* scc_smf_parse(scc_fd_t* fd) {
- scc_smf_t* smf;
- uint32_t chunk, size;
-
- while(1) {
- chunk = scc_fd_r32(fd);
- // Skip scumm chunk headers
- if(chunk == MKID('A','D','L',' ') ||
- chunk == MKID('R','O','L',' ') ||
- chunk == MKID('G','M','D',' ')) {
- scc_fd_r32(fd);
- continue;
- }
- if(chunk == MKID('M','D','h','d') ||
- chunk == MKID('M','D','p','g')) {
- size = scc_fd_r32be(fd);
- scc_fd_seek(fd,size,SEEK_CUR);
- continue;
- }
-
- if(chunk != MKID('M','T','h','d') ||
- scc_fd_r32be(fd) != 6) {
- scc_log(LOG_ERR,"%s doesn't seem to be a valid MIDI file.\n",fd->filename);
- return 0;
- }
- break;
- }
-
- smf = calloc(1,sizeof(scc_smf_t));
- smf->size = 8 + 6;
- smf->type = scc_fd_r16be(fd);
- scc_fd_r16be(fd); //smf->num_track = scc_fd_r16be(fd);
- smf->division = scc_fd_r16be(fd);
-
- // Read the tracks
- while(1) {
- chunk = scc_fd_r32(fd);
- size = scc_fd_r32be(fd);
-
- // EOF
- if(!chunk || !size) break;
-
- // Not a MIDI track: skip it
- if(chunk != MKID('M','T','r','k')) {
- scc_log(LOG_WARN,"Skipping unknown midi chunk: %c%c%c%c\n",UNMKID(chunk));
- scc_fd_seek(fd,SEEK_CUR,size);
- continue;
- }
- smf->track = realloc(smf->track,(smf->num_track+1)*sizeof(scc_smf_track_t));
- memset(&smf->track[smf->num_track],0,sizeof(scc_smf_track_t));
- if(!scc_smf_parse_track(fd,&smf->track[smf->num_track],size)) {
- scc_log(LOG_ERR,"Failed to parse MIDI track %d\n",smf->num_track);
- scc_smf_free(smf);
- return NULL;
- }
- smf->size += smf->track[smf->num_track].size;
- smf->num_track++;
- }
-
- return smf;
- }
-
- scc_smf_t* scc_smf_parse_file(char* file) {
- scc_fd_t* fd = new_scc_fd(file,O_RDONLY,0);
- scc_smf_t* smf;
- if(!fd) {
- scc_log(LOG_ERR,"Failed to open %s: %s\n",file,strerror(errno));
- return NULL;
- }
- smf = scc_smf_parse(fd);
- scc_fd_close(fd);
- return smf;
- }
-
- void scc_smf_free(scc_smf_t* smf) {
- if(smf->track) {
- int i;
- for(i = 0 ; i < smf->num_track ; i++) {
- scc_smf_event_t* ev;
- SCC_LIST_FREE(smf->track[i].events,ev);
- }
- free(smf->track);
- }
- free(smf);
- }
-
-
- unsigned scc_smf_get_int_size(unsigned v) {
- int r = sizeof(v)*8/7;
- if(!v) return 1;
- while(!(v & (0x7F<<(7*r))))
- r--;
- return r+1;
- }
-
- int scc_smf_track_get_size(scc_smf_track_t* track) {
- unsigned size = 8; // Header
- scc_smf_event_t* ev, *last = NULL;
-
- for(ev = track->events ; ev ; last = ev, ev = ev->next) {
- // Delta
- size += scc_smf_get_int_size(last ? ev->time - last->time : 0);
- // Events
- switch(ev->cmd) {
- case SMF_META_EVENT:
- size++; // Meta event type
- case SMF_SYSEX_EVENT:
- case SMF_SYSEX_CONTINUATION:
- size++; // No runaway
- size += scc_smf_get_int_size(ev->args_size);
- break;
- default:
- // Event (if it is not a runaway)
- if(!last || last->cmd != ev->cmd)
- size++;
- }
- // Arguments
- size += ev->args_size;
- }
-
- return size;
- }
-
- int scc_smf_get_size(scc_smf_t* smf) {
- unsigned size = 8+6; // Header
- unsigned i;
-
- for(i = 0 ; i < smf->num_track ; i++)
- size += scc_smf_track_get_size(&smf->track[i]);
-
- return size;
- }
-
- int scc_smf_track_write(scc_smf_track_t* track, scc_fd_t* fd) {
- scc_smf_event_t* ev, *last = NULL;
- // Write the header
- scc_fd_w32(fd,MKID('M','T','r','k'));
- scc_fd_w32be(fd,track->size-8);
- for(ev = track->events ; ev ; last = ev, ev = ev->next) {
- scc_fd_write_smf_int(fd,last ? ev->time - last->time : 0);
- switch(ev->cmd) {
- case SMF_META_EVENT:
- case SMF_SYSEX_EVENT:
- case SMF_SYSEX_CONTINUATION:
- scc_fd_w8(fd,ev->cmd);
- if(ev->cmd == SMF_META_EVENT)
- scc_fd_w8(fd,ev->meta);
- scc_fd_write_smf_int(fd,ev->args_size);
- break;
- default:
- if(!last || last->cmd != ev->cmd)
- scc_fd_w8(fd,ev->cmd);
-
- }
- if(ev->args_size > 0)
- scc_fd_write(fd,ev->args,ev->args_size);
- }
- return 1;
- }
-
- int scc_smf_write(scc_smf_t* smf, scc_fd_t* fd) {
- int i;
- // Write the header
- scc_fd_w32(fd,MKID('M','T','h','d'));
- scc_fd_w32be(fd,6);
- scc_fd_w16be(fd,smf->type);
- scc_fd_w16be(fd,smf->num_track);
- scc_fd_w16be(fd,smf->division);
- // Write the tracks
- for(i = 0 ; i < smf->num_track ; i++)
- scc_smf_track_write(&smf->track[i],fd);
- return 1;
- }
-
- int scc_smf_write_file(scc_smf_t* smf, char* path) {
- scc_fd_t* fd = new_scc_fd(path,O_WRONLY|O_CREAT|O_TRUNC,0);
-
- if(!fd) {
- printf("Failed to open %s for writing.\n",path);
- return 0;
- }
-
- if(!scc_smf_write(smf,fd)) {
- printf("Failed to write MIDI file %s.\n",path);
- scc_fd_close(fd);
- return 0;
- }
-
- scc_fd_close(fd);
- return 1;
- }
-
-
- int scc_smf_remove_track(scc_smf_t* smf, unsigned track) {
- scc_smf_event_t* ev;
-
- if(track >= smf->num_track) return 0;
-
- SCC_LIST_FREE(smf->track[track].events,ev);
- smf->size -= smf->track[track].size;
-
- if(track < smf->num_track-1)
- memmove(&smf->track[track],&smf->track[track+1],
- (smf->num_track-track-1)*sizeof(scc_smf_track_t));
-
- smf->num_track--;
-
- return 1;
- }
-
- int scc_smf_merge_track(scc_smf_t* smf, unsigned trackA, unsigned trackB) {
- scc_smf_event_t *evA,*evB = NULL,*evBnext = NULL,*evEnd = NULL;
-
- if(trackA == trackB ||
- trackA >= smf->num_track ||
- trackB >= smf->num_track)
- return 0;
-
- if(smf->track[trackA].last_event->cmd == SMF_META_EVENT &&
- smf->track[trackA].last_event->meta == 0x2F) {
- evEnd = smf->track[trackA].last_event;
- for(evA = smf->track[trackA].events ; evA->next ; evB = evA, evA = evA->next);
- evB->next = NULL;
- smf->track[trackA].last_event = evB;
- }
- if(smf->track[trackB].last_event->cmd == SMF_META_EVENT &&
- smf->track[trackB].last_event->meta == 0x2F) {
- for(evA = smf->track[trackB].events ; evA->next ; evB = evA, evA = evA->next);
- if(evB) {
- evB->next = NULL;
- smf->track[trackB].last_event = evB;
- } else
- smf->track[trackB].last_event = smf->track[trackB].events = NULL;
- if(!evEnd) evEnd = evA->next;
- else free(evA);
- }
-
- evA = smf->track[trackA].events;
- for(evB = smf->track[trackB].events ; evB ; evB = evBnext) {
- evBnext = evB->next;
- if(!evA) {
- evB->next = NULL;
- smf->track[trackA].last_event = smf->track[trackA].events = evB;
- } else {
- while(evA->next && evA->next->time <= evB->time)
- evA = evA->next;
- evB->next = evA->next;
- evA->next = evB;
- if(!evB->next)
- smf->track[trackA].last_event = evB;
- }
- }
-
- if(evEnd) {
- while(evA->next)
- evA = evA->next;
- evA->next = evEnd;
- smf->track[trackA].last_event = evEnd;
- }
-
- smf->track[trackA].size = scc_smf_track_get_size(&smf->track[trackA]);
- smf->track[trackB].events = smf->track[trackB].last_event = NULL;
-
- return scc_smf_remove_track(smf,trackB);
- }
-
- void scc_smf_dump(scc_smf_t* smf) {
- int i,j;
- scc_smf_event_t* ev;
-
- scc_log(LOG_MSG,"Total size: %d\n",smf->size);
- scc_log(LOG_MSG,"Type: %d\n",smf->type);
- scc_log(LOG_MSG,"Division: %d\n",smf->division);
-
- for(i = 0 ; i < smf->num_track ; i++) {
- scc_log(LOG_MSG,"Track %d (%d):\n",i,smf->track[i].size);
- for(ev = smf->track[i].events ; ev ; ev = ev->next) {
- scc_log(LOG_MSG,"%6" PRIu64 " : %02X - ",ev->time, ev->cmd);
- switch(ev->cmd & 0xF0) {
- case 0x80:
- scc_log(LOG_MSG,"Note off (chan: %d)",ev->cmd & 0x0F);
- break;
- case 0x90:
- scc_log(LOG_MSG,"Note on (chan: %d)",ev->cmd & 0x0F);
- break;
- case 0xA0:
- scc_log(LOG_MSG,"Aftertouch (chan: %d)",ev->cmd & 0x0F);
- break;
- case 0xB0:
- if(ev->args[0] > 0x77) {
- scc_log(LOG_MSG,"Chan mode (%02X)",ev->cmd & 0x0F);
- switch(ev->args[0]) {
- case 0x79:
- scc_log(LOG_MSG," [Reset All Controllers]");
- break;
- case 0x7A:
- scc_log(LOG_MSG," [Local Control]");
- break;
- case 0x7B:
- scc_log(LOG_MSG," [All Notes Off]");
- break;
- case 0x7C:
- scc_log(LOG_MSG," [Omni Mode Off]");
- break;
- case 0x7D:
- scc_log(LOG_MSG," [Omni Mode On]");
- break;
- case 0x7E:
- scc_log(LOG_MSG," [Mono Mode On]");
- break;
- case 0x7F:
- scc_log(LOG_MSG," [Poly Mode On]");
- break;
- }
- } else {
- scc_log(LOG_MSG,"Control change (chan: %d)",ev->cmd & 0x0F);
- switch(ev->args[0]) {
- case 0x01:
- scc_log(LOG_MSG," [Mod Wheel]");
- break;
- case 0x02:
- scc_log(LOG_MSG," [Breath ctrl]");
- break;
- case 0x04:
- scc_log(LOG_MSG," [Foot ctrl]");
- break;
- case 0x05:
- scc_log(LOG_MSG," [Portamento Time]");
- break;
- case 0x06:
- scc_log(LOG_MSG," [Data Entry MSB]");
- break;
- case 0x07:
- scc_log(LOG_MSG," [Volume]");
- break;
- case 0x08:
- scc_log(LOG_MSG," [Balance]");
- break;
- case 0x0A:
- scc_log(LOG_MSG," [Pan]");
- break;
- case 0x0B:
- scc_log(LOG_MSG," [Expression ctrl]");
- break;
- case 0x10:
- scc_log(LOG_MSG," [General Purpose 1]");
- break;
- case 0x11:
- scc_log(LOG_MSG," [General Purpose 2]");
- break;
- case 0x12:
- scc_log(LOG_MSG," [General Purpose 3]");
- break;
- case 0x13:
- scc_log(LOG_MSG," [General Purpose 4]");
- break;
- case 0x40:
- scc_log(LOG_MSG," [Sustain]");
- break;
- case 0x41:
- scc_log(LOG_MSG," [Portamento]");
- break;
- case 0x42:
- scc_log(LOG_MSG," [Sustenuto]");
- break;
- case 0x43:
- scc_log(LOG_MSG," [Soft Pedal]");
- break;
- case 0x45:
- scc_log(LOG_MSG," [Hold 2]");
- break;
- case 0x50:
- scc_log(LOG_MSG," [General Purpose 5]");
- break;
- case 0x51:
- scc_log(LOG_MSG," [Temp Change (General Purpose 6)]");
- break;
- case 0x52:
- scc_log(LOG_MSG," [General Purpose 7]");
- break;
- case 0x53:
- scc_log(LOG_MSG," [General Purpose 8]");
- break;
- case 0x5B:
- scc_log(LOG_MSG," [Ext Effects Depth]");
- break;
- case 0x5C:
- scc_log(LOG_MSG," [Tremelo Depth]");
- break;
- case 0x5D:
- scc_log(LOG_MSG," [Chorus Depth]");
- break;
- case 0x5E:
- scc_log(LOG_MSG," [Detune Depth (Celeste Depth)]");
- break;
- case 0x5F:
- scc_log(LOG_MSG," [Phaser Depth]");
- break;
- case 0x60:
- scc_log(LOG_MSG," [Data Increment]");
- break;
- case 0x61:
- scc_log(LOG_MSG," [Data Decrement]");
- break;
- case 0x62:
- scc_log(LOG_MSG," [Non-Registered Param LSB]");
- break;
- case 0x63:
- scc_log(LOG_MSG," [Non-Registered Param MSB]");
- break;
- case 0x64:
- scc_log(LOG_MSG," [Registered Param LSB]");
- break;
- case 0x65:
- scc_log(LOG_MSG," [Registered Param MSB]");
- break;
- }
- if(ev->args[0] >= 0x20 &&
- ev->args[0] < 0x40)
- scc_log(LOG_MSG," [Set MSB]");
- }
- break;
- case 0xC0:
- scc_log(LOG_MSG,"Program Change (chan: %d)",ev->cmd & 0x0F);
- break;
- case 0xD0:
- scc_log(LOG_MSG,"Chan. Pressure (chan: %d)",ev->cmd & 0x0F);
- break;
- case 0xE0:
- scc_log(LOG_MSG,"Pitch Wheel (chan: %d)",ev->cmd & 0x0F);
- break;
- case 0xF0:
- switch(ev->cmd & 0x0F) {
- case 0x00:
- case 0x07:
- scc_log(LOG_MSG,"SysEx");
- switch(ev->args[0]) {
- case 0x41:
- scc_log(LOG_MSG," - Roland");
- break;
- case 0x7C:
- scc_log(LOG_MSG," - YM2612");
- break;
- case 0x7D:
- scc_log(LOG_MSG," - iMUSE - ");
- switch(ev->args[1]) {
- case 0x00:
- scc_log(LOG_MSG,"Allocate new part");
- break;
- case 0x01:
- scc_log(LOG_MSG,"Shut down a part");
- break;
- case 0x02:
- scc_log(LOG_MSG,"Start of song");
- break;
- case 0x10:
- scc_log(LOG_MSG,"Adlib instrument definition (Part)");
- break;
- case 0x11:
- scc_log(LOG_MSG,"Adlib instrument definition (Global)");
- break;
- case 0x21:
- scc_log(LOG_MSG,"Parameter adjust");
- break;
- case 0x30:
- scc_log(LOG_MSG,"Hook - jump");
- break;
- case 0x31:
- scc_log(LOG_MSG,"Hook - global transpose");
- break;
- case 0x32:
- scc_log(LOG_MSG,"Hook - part on/off");
- break;
- case 0x33:
- scc_log(LOG_MSG,"Hook - set volume");
- break;
- case 0x34:
- scc_log(LOG_MSG,"Hook - set program");
- break;
- case 0x35:
- scc_log(LOG_MSG,"Hook - set transpose");
- break;
- case 0x40:
- scc_log(LOG_MSG,"Marker");
- break;
- case 0x50:
- scc_log(LOG_MSG,"Set loop");
- break;
- case 0x51:
- scc_log(LOG_MSG,"Clear loop");
- break;
- case 0x60:
- scc_log(LOG_MSG,"Set instrument");
- break;
- }
- }
- break;
- case 0x08:
- scc_log(LOG_MSG,"Timing clock");
- break;
- case 0x09:
- scc_log(LOG_MSG,"Reserved");
- break;
- case 0x0A:
- scc_log(LOG_MSG,"Start");
- break;
- case 0x0B:
- scc_log(LOG_MSG,"Continue");
- break;
- case 0x0C:
- scc_log(LOG_MSG,"Stop");
- break;
- case 0x0D:
- scc_log(LOG_MSG,"Reserved");
- break;
- case 0x0E:
- scc_log(LOG_MSG,"Active Sensing");
- break;
- case 0x0F:
- scc_log(LOG_MSG,"\b\b\b\b\b%02X - Meta event ",ev->meta);
- switch(ev->meta) {
- case 0x00:
- scc_log(LOG_MSG,"(Seq Num)");
- break;
- case 0x01:
- scc_log(LOG_MSG,"(Text)");
- break;
- case 0x02:
- scc_log(LOG_MSG,"(Copyright)");
- break;
- case 0x03:
- scc_log(LOG_MSG,"(Seq name)");
- break;
- case 0x04:
- scc_log(LOG_MSG,"(Instrument name)");
- break;
- case 0x05:
- scc_log(LOG_MSG,"(Lyric)");
- break;
- case 0x06:
- scc_log(LOG_MSG,"(Marker)");
- break;
- case 0x07:
- scc_log(LOG_MSG,"(Cue point)");
- break;
- case 0x20:
- scc_log(LOG_MSG,"(Chan prefix)");
- break;
- case 0x21:
- scc_log(LOG_MSG,"(Port prefix)");
- break;
- case 0x2F:
- scc_log(LOG_MSG,"(End of track)");
- break;
- case 0x51:
- scc_log(LOG_MSG,"(Set tempo)");
- break;
- case 0x54:
- scc_log(LOG_MSG,"(SMPTE Offset)");
- break;
- case 0x58:
- scc_log(LOG_MSG,"(Time sig)");
- break;
- case 0x59:
- scc_log(LOG_MSG,"(Key sig)");
- break;
- case 0x7F:
- scc_log(LOG_MSG,"(Sequencer-Specific event)");
- break;
- default:
- scc_log(LOG_MSG,"(%02X)",ev->meta);
- }
- default:
- // Fallback
- ; // kill gcc warning
- }
- break;
- default:
- scc_log(LOG_MSG,"%02X (%d)",ev->cmd,ev->args_size);
- }
- for(j = 0 ; j < ev->args_size ; j++) {
- if(j % 16 == 0) scc_log(LOG_MSG,"\n ");
- scc_log(LOG_MSG," %02X",ev->args[j]);
- }
- scc_log(LOG_MSG,"\n");
- }
- scc_log(LOG_MSG,"\n");
- }
-
- }
-