/*
 * midiapi.c -- MIDI API for application programs.   This code wraps all of
 *              the lower level code, and abstract whatever special protocol
 *              the MIDI interface hardware has.
 *
 * Copyright 1998, John E. Stone
 *                 j.stone@acm.org
 *                 johns@cs.umr.edu
 *
 * $Id: midiapi.c,v 1.11 1998/02/18 10:01:22 johns Exp $
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>

#include "seq.h"
#include "midiapi.h"
#include "msgq.h"

typedef struct {
  QHandle outq;
  QHandle inq;
  int bpm;
  int ppqn;
  int nsecpertick;
} MidiHandle;      


void MidiTimeToRealTime(void * voidhandle, long stamp, miditime *t) {
  MidiHandle * handle = voidhandle;
  long i;

  t->tv_sec = 0;
  t->tv_nsec = 0;
  for (i=0; i<stamp; i++) {
    t->tv_nsec += handle->nsecpertick;
    if (t->tv_nsec >= 1000000000) {
      t->tv_sec += 1;
      t->tv_nsec -= 1000000000;
    }
  }
}


void RealTimeToMidiTime(void * voidhandle, miditime *t, long *stamp) {
  MidiHandle * handle = voidhandle;

  *stamp = 0;
}



void * MidiOpen(void) {
  MidiHandle * handle;
  
  handle = (MidiHandle *) malloc(sizeof(MidiHandle));
  
  if ((handle->outq = midiq_open(MIDIQ_OUT)) == NULL) {
    free(handle);
    return NULL;
  }

  if ((handle->inq = midiq_open(MIDIQ_IN)) != NULL) {
    midiq_set_nonblock(handle->inq);
  }

  handle->bpm = 60;
  handle->ppqn = 1000000;
 
  return (void *) handle;  
}

int MidiWrite(void * voidhandle, int port, unsigned char msg[4], long stamp, int msglen) {
  MidiHandle * handle = voidhandle;
  MidiIOEvent ev;

  if (handle->outq == NULL)
    return -1;

  memcpy(&ev.msg, msg, 4);

  MidiTimeToRealTime(voidhandle, stamp, &ev.delay);

  ev.msglen = msglen;
  ev.port = port; 

  if (midiq_write(handle->outq, &ev, MIDI_PRIORITY_NORMAL) == -1)
    printf("error sending message!\n");

  return 0;
}

int MidiRead(void * voidhandle, int * port, unsigned char * msg, long *stamp, int *msglen) {
  MidiHandle * handle = voidhandle;
  MidiIOEvent ev;

  if (handle->inq == NULL)
    return -1;

  if (midiq_read(handle->inq, &ev) == -1)
    return -1;

  memcpy(msg, &ev.msg, 4);
  *msglen = ev.msglen;
  *port = ev.port;
  RealTimeToMidiTime(voidhandle, &ev.delay, stamp);

  return 0;
}


void MidiPanic(void * voidhandle) {
  int i, port;
  unsigned char msg[4];

  for (port=0; port<MAX_DEVICES; port++) { 
    for(i=0; i<16; i++) { 
      /* all notes off and reset controllers*/
      msg[0] = 0xB0 + i;
      msg[1] = 0x7B;
      msg[2] = 0;
   
      /* Send all notes off */
      MidiWrite(voidhandle, port, msg, 0, 3);
               
      msg[0] = 0xB0 + i;
      msg[1] = 0x79;
      msg[2] = 0;

      /* Send reset all controllers  */
      MidiWrite(voidhandle, port, msg, 0, 3);
    } 
  }
}


void MidiClose(void * voidhandle) {
  MidiHandle * handle = voidhandle;

  if (handle->outq != NULL) 
    midiq_close(handle->outq);  

  if (handle->inq != NULL) 
    midiq_close(handle->inq);  

  free(handle);
}

void MidiSetTempo(void * voidhandle, int bpm, int ppqn) {
  MidiHandle * handle = voidhandle;

  handle->bpm = bpm;
  handle->ppqn = ppqn;

  handle->nsecpertick = 600000000 / (bpm * ppqn);
  handle->nsecpertick *= 100; 
}

