seq.c 6.23 KB
/*====================================================================
 * seq.c
 *
 * Copyright 1993, Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics,
 * Inc.; the contents of this file may not be disclosed to third
 * parties, copied or duplicated in any form, in whole or in part,
 * without the prior written permission of Silicon Graphics, Inc.
 *
 * RESTRICTED RIGHTS LEGEND:
 * Use, duplication or disclosure by the Government is subject to
 * restrictions as set forth in subdivision (c)(1)(ii) of the Rights
 * in Technical Data and Computer Software clause at DFARS
 * 252.227-7013, and/or in similar or successor clauses in the FAR,
 * DOD or NASA FAR Supplement. Unpublished - rights reserved under the
 * Copyright Laws of the United States.
 *====================================================================*/

#include <libaudio.h>

#define IFF_FILE_HDR    'MThd'
#define IFF_TRACK_HDR   'MTrk'

static s32 readVarLen(ALSeqMarker *m);
static s8 read8(ALSeqMarker *m);
static s16 read16(ALSeqMarker *m);
static s32 read32(ALSeqMarker *m);

void alSeqNew(ALSeq *seq, char *ptr, int len)
{
    /*
     * load the seqence pointed to by ptr
     */
    seq->base           = ptr;
    seq->len            = len;

    seq->trackStart.curPtr      = ptr;
    seq->trackStart.ticks       = 0;
    seq->trackStart.lastStatus  = 0;
    
    if (read32(&seq->trackStart) != IFF_FILE_HDR) {
        emPrintf("File 0x%x is not a MIDI file\n", ptr);
        return;
    }

    read32(&seq->trackStart);        /* skip the length field */

    if (read16(&seq->trackStart) != 0) {
        emPrintf("File 0x%x is not a Type 0 MIDI file\n", ptr);
        return;
    }

    if (read16(&seq->trackStart) != 1) {
        emPrintf("File 0x%x has more than 1 track\n", ptr);
        return;
    }

    seq->division = read16(&seq->trackStart);
    if (seq->division & 0x8000) {
        emPrintf("Error SMPTE delta times not supported\n", ptr);
        return;
    }
    
    seq->qnpt = 1.0/(float)seq->division;

    if (read32(&seq->trackStart) != IFF_TRACK_HDR) {
        emPrintf("Error parsing file 0x%x (no track header)\n", ptr);
        return;
    }

    read32(&seq->trackStart);        /* skip the length field */
}


void alSeqDelete(ALSeq *seq)
{}

void alSeqNextEvent(ALSeq *seq, ALEvent *event, ALSeqMarker *m)
{
    char        status;
    short       delta;
    long        len;
    int         i;
    
    m->ticks  = readVarLen(m);   /* read the delta time */
    status = read8(m);
    
#if AL_DEBUG
    /*
     * System exclusives are not supported, so just skip them and read
     * the next event
     */
    if ((status == 0xf0) || (status == 0xf7)) {
        emPrintf("warning: unsupported System Exclusive in sequence\n");
        len = readVarLen(m);
        for (i = 0; i < len; i++) {
            read8(m);
        }
        alSeqNextEvent(seq,event,m);
        return;
    }
#endif
    
    if (status == AL_MIDI_Meta) {
        char type = read8(m);
        
        if (type == AL_MIDI_META_TEMPO) {
            event->type = AL_TEMPO_EVT;
            event->msg.tempo.ticks = m->ticks;
            event->msg.tempo.status = status;
            event->msg.tempo.type = type;
            event->msg.tempo.len  = read8(m);
            event->msg.tempo.byte1 = read8(m);
            event->msg.tempo.byte2 = read8(m);
            event->msg.tempo.byte3 = read8(m);
        } else if (type == AL_MIDI_META_EOT) {
            event->type = AL_SEQ_END_EVT;
            event->msg.end.ticks  = m->ticks;
            event->msg.end.status = status;
            event->msg.end.type   = type;
            event->msg.end.len    = read8(m);
        } else {
#if AL_DEBUG            
            emPrintf("warning: unsupported MIDI meta event 0x%x in sequence\n",
                     type);
            len = readVarLen(m);
            for (i = 0; i < len; i++) {
                read8(m);
            }
            alSeqNextEvent(seq,event,m);
            return;
#endif            
        }

        m->lastStatus = 0;
        
    } else {
        event->type = AL_SEQ_MIDI_EVT;
        event->msg.midi.ticks = m->ticks;
        if (status & 0x80) {
            event->msg.midi.status = status;
            event->msg.midi.byte1 = read8(m);
            m->lastStatus = status;
        } else {
            /* running status */
            event->msg.midi.status = m->lastStatus;
            event->msg.midi.byte1 = status;
        }
        
        if (((event->msg.midi.status & 0xf0) != AL_MIDI_ProgramChange) &&
            ((event->msg.midi.status & 0xf0) != AL_MIDI_ChannelPressure)) {
            event->msg.midi.byte2 = read8(m);
        } else {
            event->msg.midi.byte2 = 0;
        }
    }
}

float alSeqTicksToSec(ALSeq *seq, s32 ticks, u32 tempo)
{
    return ((float) (((float)(ticks) * (float)(tempo)) /
                     ((float)(seq->division) * 1000000.0)));
}

u32 alSeqSecToTicks(ALSeq *seq, float sec, u32 tempo)
{
    return (u32)(((sec * 1000000.0) * seq->division) / tempo);
}

void alSeqNewMarker(ALSeq *seq, ALSeqMarker *m, u32 ticks)
{
    ALEvent     evt;
    
    if (!ticks) {
        alCopy(&seq->trackStart, m, sizeof(ALSeqMarker));
    } else {
        do {
            /*
             * this would be marginally more efficient if we just
             * coded the loop as in alSeqNextEvent, but with no
             * message setting.
             */
            alSeqNextEvent(seq, &evt, m);
        } while (evt.msg.midi.ticks < ticks);
    }        
}


/* non-aligned byte reading routines */
static s8 read8(ALSeqMarker *marker)
{
    return *marker->curPtr++;
}

static s16 read16(ALSeqMarker *marker)
{
    s16 tmp;

    tmp  = *marker->curPtr++ << 8;
    tmp |= *marker->curPtr++;
    
    return tmp;
}

static s32 read32(ALSeqMarker *marker)
{
    s32 tmp;

    tmp  = *marker->curPtr++ << 24;
    tmp |= *marker->curPtr++ << 16;
    tmp |= *marker->curPtr++ <<  8;
    tmp |= *marker->curPtr++;

    return tmp;
}

static s32 readVarLen(ALSeqMarker *marker)
{
    long value;
    int c;

    c = *marker->curPtr++;
    value = c;
    if ( c & 0x80 ) {
        value &= 0x7f;
        do {
            c = *marker->curPtr++;
            value = (value << 7) + (c & 0x7f);
        } while (c & 0x80);
    }
    return (value);
}