home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C++ Games Programming
/
CPPGAMES.ISO
/
mt
/
mtrc4.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-03-06
|
11KB
|
398 lines
/* mtrc4.c */
/* `MIDI Sequencing In C', Jim Conger, M&T Books, 1989 */
/* #define TURBOC 1 Define if using TURBOC, leave out for Microsoft */
#include <stdio.h>
#include <conio.h>
#include <string.h>
#ifdef TURBOC
#include <alloc.h>
#else
#include <malloc.h>
#endif
#include "screenf.h"
#include "standard.h"
#include "mpu401.h"
#include "mt.h"
#include "video.h"
#include "mtdeclar.h"
/* allocate a block of far memory big enough to hold one event. */
/* returns pointer to memory area. */
struct event far
*eventalloc(void)
{
#ifdef TURBOC
return((struct event far *)farmalloc(sizeof(struct event)));
#else
return((struct event far *)_fmalloc(sizeof(struct event)));
#endif
}
/* store an event in far memory , return pointer to next event. */
/* node is the last (empty) event created. */
struct event far
*store(struct event far *node, int nbytes, int b0, int b1, int b2, int b3)
{
node->next = eventalloc();
(node->next)->next = NULL; /* null pointer in next node to mark end */
node->nbytes = nbytes;
node->b[0] = b0;
node->b[1] = b1;
node->b[2] = b2;
node->b[3] = b3;
return(node->next);
}
/* This block contains the communications functions to/from the MPU-401 */
/* At the lowest level these use the MIO401.ASM assembly language calls. */
/* Only get401(), sendcmd401(), repeat_cmd401() and putdata401() are used */
/* outside of this block. The remainder handle data buffering. */
#define BUFSIZE 100
int cmdbuf[BUFSIZE]; /* global buffer for pending MPU commands */
int cmdbufp = 0; /* next free position in cmdbuf */
/* get a possibly pushed back command from MPU or cmdbuf */
int
getnext401(void)
{
return((cmdbufp > 0) ? cmdbuf[--cmdbufp] : getdata());
}
void
ungetnext401(int n) /* push a command back on input */
{
if (cmdbufp > BUFSIZE)
printf("\nungetnext401 ran out of buffer space.");
else
cmdbuf[cmdbufp++] = n;
}
int
get401(void) /* get next byte from mpu401 (or pending buffer) */
{ /* try forever, stop on keypress */
int i;
while (1){
if (kbhit())
return(-1);
i = getnext401();
if (g_trace_on){
if (i != -1)
printf(" rc=%0x", i);
}
if (i != -1)
return(i);
}
return(-1);
}
void
putdata401(int n) /* send data byte to MPU, print it if trace is on */
{
putdata(n);
if (g_trace_on)
printf(" td=%0x", n);
}
int /* send a command, check for ACK, if not save MPU data */
sendcmd401(int n) /* until it stops sending */
{
int ans;
if (g_trace_on)
printf(" tc=%0x", n);
ans = putcmd(n);
if (ans == ACK){
if (g_trace_on)
fputs("(ACK)", stdout);
return(ans);
}
else if (ans != -1){
if (g_trace_on)
fputs("(No ACK)", stdout);
ungetnext401(ans); /* put pending data on stack */
while (1){ /* check for more incoming data */
ans = getdata();
if (ans == ACK || ans == -1)
return(ans);
else
ungetnext401(ans);
}
}
return(ans);
}
int
repeat_cmd401(int n) /* determined command send - max tries = 10 */
{
int i, j, m;
char buf[SCRNWIDE], nbuf[10];
for (i = 0; i < 10; i++){
m = sendcmd401(n);
if (m != -1)
return(m);
if ((m = getdata()) != -1) /* check for ack */
return(m) ;
}
if (!g_trace_on)
{
strcpy(buf, "Error in sending command ");
itoa(n, nbuf, 16);
strcat(buf, nbuf);
strcat(buf, " hex. (repeat_cmd401)");
writeword(buf, 1, g_text_char_v, g_emph_attrib);
}
return(-1);
}
/* end of MPU communications function block */
/* Move all track counters to given measure. Returns measure number of */
/* the highest measure reached before running off end of longest track. */
int
goto_measure(int meas)
{
int end_meas, count, i;
struct event far *ep;
struct event far *lep;
struct event far *startp;
end_meas = 0;
for (i = 0; i < NTRACK; i++){
startp = ep = g_trackarray[i].first;
lep = g_trackarray[i].last;
count = 0;
while (count != meas){
if (ep == lep){ /* if reached end of track */
break;
}
if ((ep->b[1]) == MES_END){ /* found measure end mark */
if (ep != startp){ /* don't count first event */
count++;
}
}
ep = ep->next;
}
if (count > end_meas)
end_meas = count;
g_trackarray[i].current = ep;
}
return(end_meas);
}
/* adjust all MIDI data on track to channel specified */
void
change_channel(int track, int channel)
{
unsigned int data, root;
struct event far *ep;
ep = g_trackarray[track].first;
while (ep->next != NULL){
data = ep->b[1]; /* get second byte of data */
/* check if it is a MIDI message */
if (data >= 0x90 && data <= 0xEF){
root = data - (data % 0x10);
ep->b[1] = root + channel;
}
ep = ep->next;
}
}
/* put default values in track data array. Allocate first event for each */
/* track and set it to a MES_END. Called on startup from main(). */
void
init_tracks(void)
{
int i, j;
struct event far *ep;
for (i = 0; i < NTRACK; i++){
strcpy(g_trackarray[i].name, "< >");
g_trackarray[i].midichan = i;
g_trackarray[i].numevents = 1;
g_trackarray[i].active = 0;
g_trackarray[i].midivol = 100;
ep = eventalloc();
g_trackarray[i].first = ep;
g_trackarray[i].current = ep;
g_trackarray[i].last = ep;
ep->next = NULL; /* starting event is a mes_end */
ep->nbytes = 2;
ep->b[1] = MES_END;
ep->b[0] = ep->b[2] = ep->b[3] = 0;
}
g_trackarray[0].active = 1; /* start with track 1 active */
}
/* Find the number of K bytes free in memory on startup. Allocates 10K */
/* blocks until not further space. Can leave smaller space unnoticed. */
int
free_memory(void)
{
void far *node[64];
int i, j;
i = 0;
do{
#ifdef TURBOC
node[i] = farmalloc(1024 * 10); /* allocate one 10 K byte */
#else
node[i] = _fmalloc(1024 * 10); /* allocate one 10 K byte */
#endif
}while (node[i++] != NULL);
for (j = 0; j < i; j++){ /* free memory for use */
#ifdef TURBOC
farfree(node[j]);
#else
_ffree(node[j]);
#endif
}
return(10 * i);
}
/* returns the number of K bytes used by all track data. */
int
used_memory(void)
{
int i;
long bytes, ln;
bytes = 0;
for (i = 0; i < NTRACK; i++){
ln = g_trackarray[i].numevents;
bytes += ln * (sizeof(struct event) + FMALLOC_BYTES);
}
i = bytes/1024; /* convert to integer K bytes */
return(i);
}
/* Display track data until esc key is pressed. This is a menu item on */
/* both the Record and MLE screens. */
void
data_dump(void)
{
int i, j, k;
struct event far *ep[NTRACK];
for (i = 0; i < NTRACK; i++){
ep[i] = g_trackarray[i].current;
}
while (1){
clearscreen(g_norm_attrib);
fputs("MIDI data in hex for tracks 1-8:\n\n", stdout);
for (i = 0; i < 20; i++){
for (j = 0; j < NTRACK; j++){
for (k = 0; k < 4; k++){
if (ep[j] != NULL)
printf("%02x", ep[j]->b[k]);
else
fputs(" ", stdout);
}
fputs("-", stdout);
if (ep[j] != NULL)
ep[j] = ep[j]->next;
}
fputs("\n",stdout);
}
while(kbhit())
getch();
fputs("\nHit space to continue, ESC to quit.", stdout);
while(!kbhit())
;
if (getch() == ESC)
return;
}
}
/* Erases all track data forward of the current measure number. Prompts */
/* for track number. Sticks new track terminator event at end. */
void
clear_forward(void)
{
int ans, track;
char nbuf[10], buf[SCRNWIDE];
struct event far *ep;
struct event far *np;
ans = getint(g_text_char_v - 1,
"Enter the track number to clear forward ->",
&track, 1, 8, g_norm_attrib, g_emph_attrib);
if (ans){
strcpy(buf, "Clear track number ");
itoa(track, nbuf, 10);
strcat(buf, nbuf);
strcat(buf, " from current measure to end? (Y/N)->");
writeword(buf, 1, g_text_char_v - 1, g_norm_attrib);
ans = getche();
if (toupper(ans) == 'Y'){
track--;
ep = advance_to_measure(track, g_current_measure);
np = ep->next; /* point to event past meas end */
clear_events(np);
np = ep->next = eventalloc();
np->next = NULL; /* add track terminator to end */
np->nbytes = 2;
np->b[1] = ALL_END;
np->b[0] = np->b[2] = np->b[3] = 0;
clean_track(track);
g_trackarray[track].current = advance_to_measure(track,
g_current_measure);
}
}
}
/* Does the work of clearing all events from start to end of event list */
/* from memory. */
void
clear_events(struct event far *start)
{
struct event far *nextevent;
while (start != NULL){
nextevent = start->next;
#if TURBOC
farfree(start);
#else
_ffree(start);
#endif
start = nextevent;
}
}
void
wait_for_key(void) /* pause until a key is pressed */
{
while (kbhit()) /* clear key buffer */
getch();
while (!kbhit()) /* wait for key */
;
getch();
}