/*
 * Copyrighted as an unpublished work.
 * (c) Copyright 1991 Brian Smith
 * All rights reserved.
 *
 * Read the LICENSE file for details on distribution and use.
 *
 */

#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <sys/sb.h>
#include <stdio.h>

#define lobyte(X)   (((unsigned char *)&X)[0])
#define hibyte(X)   (((unsigned char *)&X)[1])

/* Globals */
int fm_herz;        /* clock ticks per second */
int tempo;          /* clock ticks per quarter note */
int fm_fd;

int main(argc, argv)
int argc;
char **argv;
{
    int cmf_fd;

    if (argc != 2)
    {
        printf("usage: %s <cmf file>\n", argv[0]);
        exit(-1);
    }

    /* open cmf file */
    cmf_fd = open(argv[1], O_RDONLY);
    if (cmf_fd == -1)
    {
        printf("usage: %s <cmf file>\n", argv[0]);
        exit(-1);
    }

    /* verify that file is a cmf file */
    if (!verify_cmf(cmf_fd))
    {
        printf("file was not a cmf file\n");
        printf("usage: %s <cmf file>\n", argv[0]);
        exit(-1);
    }

    /* read and set instruments from cmf file */
    set_instruments(cmf_fd);

    /* play song */
    play_song(cmf_fd);

    return(0);
}


/* check for "CTMF" in first four bytes of file */
int verify_cmf(fd)
int fd;
{
    char idbuf[5];

    /* get id */
    lseek(fd, 0, SEEK_SET);
    if (read(fd, idbuf, 4) != 4)
        return(FALSE);
    
    /* compare to standard id */
    idbuf[4] = (char)0;
    if (strcmp(idbuf, "CTMF") != 0)
        return(FALSE);
    
    return(TRUE);
}

int set_instruments(fd)
int fd;
{
    int offset;
    int num_instruments;
    int i;
    int rc;
    int fnum, block, note;
    unsigned char tmp_byte;
    sb_fm_character note_character;

    /* open soundblaster fm chips */
    fm_fd = open("/dev/sbfm", O_WRONLY);
    if (fm_fd == -1)
    {
        perror("opening fm chips");
        exit(-1);
    }

    /* get offset of instrument block */
    offset = 0;
    lseek(fd, 0x06, SEEK_SET);
    read(fd, &tmp_byte, 1);
    lobyte(offset) = tmp_byte;
    read(fd, &tmp_byte, 1);
    hibyte(offset) = tmp_byte;

    /* get number of instruments */
    num_instruments = 0;
    lseek(fd, 0x24, SEEK_SET);
    read(fd, &tmp_byte, 1);
    lobyte(num_instruments) = tmp_byte;
    read(fd, &tmp_byte, 1);
    hibyte(num_instruments) = tmp_byte;

    if (num_instruments > 9)
        num_instruments = 9;
    printf("loading %d instruments\n", num_instruments);

    /* read each instrument */
    lseek(fd, offset, SEEK_SET);
    for (i=0; i< num_instruments; i++)
    {
        /* set instrument characteristics */
        note_character.voice_num = i;
        read(fd, note_character.data, 16);
        printf("loading instrument: 0x%02x\n", i);
        rc = ioctl(fm_fd, FM_IOCTL_SET_VOICE, (int)&note_character);
        if (rc == -1)
        {
            perror("fm chips voice set");
            exit(-1);
        }

        /* set note to regular C note */
        fnum = 686;
        block = 4;
        note_num(note) = i;
        fnum_low(note) = fnum & 0xFF;
        keyon_blk_fnum(note) = 0;
        keyon_blk_fnum(note) |= 1<<5;                   /* KEYON bit */
        keyon_blk_fnum(note) |= (block & 7) << 2;       /* block/octave */
        keyon_blk_fnum(note) |= (fnum & 0x3FF) >> 8;    /* top 2 bits of fnum */
        ioctl(fm_fd, FM_IOCTL_NOTE_ON, note);
        sleep(1);
        ioctl(fm_fd, FM_IOCTL_NOTE_ON, i);
    }

    return(0);
}


/*
 * get and set timing parameters
 */
void set_timing(fd)
int fd;
{
    unsigned char tmp_byte;

    /* get tempo */
    tempo = 0;
    lseek(fd, 0x0C, SEEK_SET);
    read(fd, &tmp_byte, 1);
    tempo = (unsigned int)tmp_byte;
    read(fd, &tmp_byte, 1);
    tempo += (unsigned int)tmp_byte << 8;

    /* get herz of timing clock */
    fm_herz = 0;
    lseek(fd, 0x0C, SEEK_SET);
    read(fd, &tmp_byte, 1);
    tempo = (unsigned int)fm_herz;
    read(fd, &tmp_byte, 1);
    tempo += (unsigned int)fm_herz << 8;
    
    return;
}


/*
 * seek to the midi stream and handle midi events for the song
 */
int play_song(fd)
int fd;
{
    return(0);
}
