seqfile.c++ 7.21 KB
//====================================================================
// seqfile.c++
//
// Synopsis:
//   Implementation for sequence file object
//
// Author(s)
//  Steve Shepard
//
// 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 "seqfile.h"

SeqFile::SeqFile(int div)
{
    file = 0;
    division = div;
    eventList.PointToFirst(curIter);
}

SeqFile::~SeqFile()
{
    if (file)
        fclose(file);
}

int SeqFile::Open(const char *name, const char *rw)
{
    file = fopen(name, rw);
    if (!file)
        return -1;
}

void SeqFile::Close()
{
    if (file) {
        fclose(file);
        file = 0;
    }
}

void SeqFile::AddEvent(Event *event)
{
    ListIter	iter = 0;
    ListIter    thisIter;
    Event       *thisEvent;
    Time        thisTime;
    Time        eventTime;

    //--------------------------------------------------
    // filter out the events we don't want
    //--------------------------------------------------
    char type = event->GetType();
    if (type == 0xff) {
        if (event->GetSubtype() != 0x51)
            return;     // only want set tempo events
    } else if ((type & 0xf0) == 0xf0) {
        return; // don't want sysex events
    }
    
    //--------------------------------------------------
    // insert in the event list.
    //--------------------------------------------------
    eventList.PointToFirst( thisIter );
    eventList.PointToFirst( iter );
    
    thisEvent = eventList.GetIteratorValue(curIter);
    if (thisEvent) {        
        thisEvent->GetTime(thisTime);
        event->GetTime(eventTime);    
        if (eventTime > thisTime) {
            thisIter = curIter;
        }
    }
    
    while((thisEvent = eventList.GetIteratorValue( thisIter )) != NULL) {

        if (testEvent(event, thisEvent))
            break;
            
        iter = thisIter;

        eventList.PointToNext( thisIter );
    }

    eventList.AppendData(iter, event);
    curIter = iter;
}

//------------------------------------------------------------
// Brute force sort. Returns true if event comes before
// beforeEvent
//------------------------------------------------------------
int SeqFile::testEvent(Event *event, Event *thisEvent)
{
    Time        thisTime;
    Time        eventTime;

    event->GetTime(eventTime);
    thisEvent->GetTime(thisTime);

    // first check event times
    if (eventTime < thisTime) {
        return 1;
    } else if (eventTime == thisTime) {
        char eventType      = event->GetType();
        char thisType       = thisEvent->GetType();
        char eventStatus    = (eventType >> 4) & 0xf;
        char thisStatus     = (thisType >> 4) & 0xf;
        if (eventStatus > thisStatus) {
            return 1;
        } else if (eventStatus == thisStatus) {  /* same status */
            char eventChannel = eventType & 0x0f;
            char thisChannel = eventType & 0x0f;
            if (eventChannel < thisChannel) {
                return 1;
            }                
        }
    }
    return 0;
}

int SeqFile::RemoveEvent(Event *event)
{
    ListIter iter;
    
    if (eventList.FindByKey(iter, event)) {
	eventList.RemoveData(iter);
        return 0;
    } else {
        return -1;
    }
}
    
void SeqFile::Print()
{
    ListIter	iter;
    Event       *event;

    printf("Sequence file\n");

    for (eventList.PointToFirst( iter );
         (event = eventList.GetIteratorValue( iter )) != NULL;
	 eventList.PointToNext( iter )) {

        event->Print();
    }
}

void SeqFile::Write()
{
    ListIter	iter;
    Event       *event;
    Time        lastTime(0, division);
    Time        thisTime;
    Time        delta;
    char        lastStatus = 0;
    int         rv;
    float       qnpt;

    qnpt = 1.0/(float)division;

    printf("division = %d\n", division);
    printf("qnpt = %f\n", qnpt);
    
    rv = fwrite(&qnpt, sizeof(qnpt), 1, file);
    assert(rv == 1);
    
    for (eventList.PointToFirst( iter );
         (event = eventList.GetIteratorValue( iter )) != NULL;
	 eventList.PointToNext( iter )) {

        //------------------------------------------------------------
        // write out delta ticks
        //------------------------------------------------------------
        event->GetTime(thisTime);
        delta = thisTime - lastTime;
        short ts = delta.ticks;
        
        int cnt = fwrite ((void *)&ts, sizeof(short), 1, file);
        assert(cnt == 1);
        lastTime = thisTime;
        
        //------------------------------------------------------------
        // write out event
        //------------------------------------------------------------
        char type = event->GetType();
        char subtype = event->GetSubtype();

        if ((type == 0xff) && (subtype == 0x51)) { // tempo event
            rv = putc(0xff, file);
            assert(rv != EOF);
            rv = putc(0x51, file);
            assert(rv != EOF);
            rv = putc(0x03, file);
            assert(rv != EOF);

            long tempo = ((MSetTempo *)event)->GetTempo();
            unsigned char b1 = (unsigned char)(tempo >> 16) & 0xff;
            unsigned char b2 = (unsigned char)(tempo >>  8) & 0xff;
            unsigned char b3 = (unsigned char)(tempo >>  0) & 0xff;
            
            rv =putc(b1, file);
            assert(rv != EOF);
            putc(b2, file);
            assert(rv != EOF);
            putc(b3, file);           
            assert(rv != EOF);

            lastStatus = 0;
            
        } else if ((type & 0xf0) != 0xf0) { // MIDI event
            if (type != lastStatus) {       // running status
                rv = putc(type, file);
                assert(rv != EOF);
            }

            char byte1 = ((MIDIEvent *)event)->GetByte1();
            char byte2 = ((MIDIEvent *)event)->GetByte2();
            
            rv = putc(byte1, file);
            assert(rv != EOF);

            if (((type & 0xf0) != 0xc0) && ((type& 0xf0) != 0xd0)) {
                rv = putc(byte2, file);
                assert(rv != EOF);
            }
            
            lastStatus = type;

        } else {
            printf("Warning: bogus event in seqence file\n");
        }
    }

    //------------------------------------------------------------
    // append End of Track
    //------------------------------------------------------------
    printf("writing End of Track\n");

    putc(0, file);      // timestamp
    putc(0, file);
    
    putc(0xff, file);   // ### dummy for now, should use real event
    putc(0x2f, file);   // ### and actually put it in the list
    putc(0, file);
}