fredc@petsd.UUCP (01/25/87)
Recently there has been quite a bit of interest in MIDI on the AMIGA. I have a Yamaha DX-27 and have put together a few programs that will play and record thru the MIDI I/F. They aren't anything spectacular, as a matter of fact all they do is play and record, placing the output in an ASCII hex file (stdout). I hope to add more midi tools to this collection, maybe this posting will prompt a few more. What it does: 1) Record - Records midi data and TIMING between midi events and writes it to stdout. The timing is done using TICKS (60th's of a second) via the DateStamp AMIGADOS call. To do this I've introduced a "new" midi message number, #defined as "TIMETAG". All other data recorded is pure midi data. When a TIMETAG is found in the stream, a 16 bit value is assumed to follow. I know it's probably not the best way to do it, but that's the way I did it. 2) Play - The complement of "Record". Play will take as input the ASCII hex file created by "Record" and send it out the serial port (hopefully via a MIDI I/F). Play will interpret the TIMETAG and wait between the MIDI commands so as to ATTEMPT to reproduce the original timings. Under 1.2 you must set your serial port (via pref.) to be 31250 baud. I am using the 512 byte buffer size, I don't think you need more then this as the programs are using the lowest level of I/O. The one problem that could possibly arise is due to the buffering of the serial device. So far I haven't had any problems, but I think if you were to run a few other tasks while using play/record, you may cause the serial device to start buffering it's input. In this case I think the timing's would start to get out of sync with real-time, since the TIMETAG wouldn't reflect when the data was actually read (record seems to keep up with the serial device when there's nothing else going on). As an aside, I usually run the programs under Matt Dillon's shell, with the output going to RAM:, haven't tried it with output going directly to disk, but I would think it may make a difference it how accurate the timings are, whenever the disk flushes the serial buffer may start to back up, not sure. I normally run them using a pipe like: % record | play and hit ^C to exit the record and start the playback. Currently I'm working on a composite of both play and record that allow you to control the entire DX-27 (DX-7 .. 100 etc) from the AMIGA. I was having some problems getting the DX to send the presets, but I may have that solved. If anyone has any problems, I'll try to help, but I'm new at this stuff too. I'd like to talk to anyone interested in writing midi tools that can be used together, maybe we can start some kind of a library ... Fred Cassirer # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # getmidi.c play.c record.c echo x - getmidi.c cat > "getmidi.c" << '//E*O*F getmidi.c//' /**************************************************************************** Author: Fred Cassirer Date: January 24, 1987 This program is placed in the public domain, and from what I understand that means anyone can do whatever they want with it. That's fine because they are fairly trivial anyway ... If you can get someone to pay you for them, you must be doing something right. ****************************************************************************/ /* compiler directives to fetch the necessary header files */ #include <libraries/dos.h> #include <exec/types.h> #include <exec/exec.h> #include <devices/serial.h> #include <stdio.h> #include <ctype.h> #include <functions.h> #include "midi.h" #undef NULL #define NULL ((void *)0) extern long SetSignal(); breakcheck() { if (SetSignal(0L,0L) & SIGBREAKF_CTRL_C ) return ( 1 ); else return( 0 ); } breakreset() { SetSignal(0L, SIGBREAKF_CTRL_C); } /* this routine causes manx to use this Chk_Abort() rather than it's own */ /* otherwise it resets our ^C when doing any I/O (even when Enable_Abort */ /* is zero). Since we want to check for our own ^C's */ Chk_Abort() { return(0); } /* declarations for the serial stuff */ extern struct MsgPort *CreatePort(); struct IOExtSer *Read_Request; static unsigned char rs_in[2]; struct IOExtSer *Write_Request; static unsigned char rs_out[2]; void initmidi() { Read_Request = (struct IOExtSer *)AllocMem((long)sizeof(*Read_Request),MEMF_PUBLIC|MEMF_CLEAR); Read_Request->io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE; Read_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Read_RS",0); if(OpenDevice(SERIALNAME,NULL,Read_Request,NULL)) { puts("Cant open Read device\n"); DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort); FreeMem(Read_Request,(long)sizeof(*Read_Request)); exit(TRUE); } Read_Request->IOSer.io_Length = 1; Read_Request->IOSer.io_Data = (APTR) &rs_in[0]; Read_Request->io_Baud = 31250; Read_Request->io_ReadLen = 8; Read_Request->io_WriteLen = 8; Read_Request->io_CtlChar = 1L; Read_Request->IOSer.io_Command = SDCMD_SETPARAMS; DoIO(Read_Request); Read_Request->IOSer.io_Command = CMD_READ; Write_Request = (struct IOExtSer *)AllocMem((long)sizeof(*Write_Request),MEMF_PUBLIC|MEMF_CLEAR); Write_Request->io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE; Write_Request->io_StopBits = 1; Write_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Write_RS",0); if(OpenDevice(SERIALNAME,NULL,Write_Request,NULL)) { puts("Can't open Write device\n"); DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort); FreeMem(Write_Request,(long)sizeof(*Write_Request)); DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort); FreeMem(Read_Request,(long)sizeof(*Read_Request)); exit(TRUE); } Write_Request->IOSer.io_Command = CMD_WRITE; Write_Request->IOSer.io_Length = 1; Write_Request->IOSer.io_Data = (APTR) &rs_out[0]; Write_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED; BeginIO(Read_Request); breakreset(); } void cleanup() { CloseDevice(Read_Request); DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort); FreeMem(Read_Request,(long)sizeof(*Read_Request)); CloseDevice(Write_Request); DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort); FreeMem(Write_Request,(long)sizeof(*Write_Request)); } /************************************************************/ /* send midi data to the serial port */ /************************************************************/ int putmidi(ch) int ch; { rs_out[0] = ch; DoIO(Write_Request); return(ch); } /******************************************************/ /* This routine will return an unsigned */ /* byte of midi data. */ /******************************************************/ int getmidi() { int c; Wait( (1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) | (SIGBREAKF_CTRL_C) ); if(CheckIO(Read_Request)) { WaitIO(Read_Request); c=rs_in[0]; BeginIO(Read_Request); } else c = NON_MIDI_EVENT; return(c); } //E*O*F getmidi.c// echo x - play.c cat > "play.c" << '//E*O*F play.c//' /**************************************************************************** Author: Fred Cassirer Date: January 24, 1987 This program is placed in the public domain, and from what I understand that means anyone can do whatever they want with it. That's fine because they are fairly trivial anyway ... If you can get someone to pay you for them, you must be doing something right. ****************************************************************************/ #include "stdio.h" #include "exec/types.h" #include "midi.h" #undefine GETMIDI #define GETMIDI(t) if (scanf("%x",&t) != 1) t = NON_MIDI_EVENT; main() { FILE *fp; int i=0; int midi,timetag; if ( (fp=fopen("ser:31250,n,8,1","w")) == NULL) { printf("Could not open serial port for write!/n"); exit(1); } GETMIDI(midi); /* First stuff is bogus timetag */ GETMIDI(midi); /* Eat timetag .. munch .. munch crunch */ GETMIDI(midi); /* First true midi stuff */ while (midi != NON_MIDI_EVENT) { if ( midi == TIMETAG ) { GETMIDI(timetag); if ( timetag ) { Delay( (long) timetag ); /* 60th's of second */ } GETMIDI(midi); } else do { putc(midi,fp); GETMIDI(midi); } while ( ( midi != TIMETAG) && ( midi != NON_MIDI_EVENT) ); } } //E*O*F play.c// echo x - record.c cat > "record.c" << '//E*O*F record.c//' /**************************************************************************** Author: Fred Cassirer Date: January 24, 1987 This program is placed in the public domain, and from what I understand that means anyone can do whatever they want with it. That's fine because they are fairly trivial anyway ... If you can get someone to pay you for them, you must be doing something right. ****************************************************************************/ #include "stdio.h" #include "exec/types.h" #include "libraries/dos.h" #include "midi.h" extern int getmidi(); extern void initmidi(); extern void cleanup(); /* Return time in ticks since last call. For the 1st call the result is undefined. */ ULONG duration() { struct DateStamp dstmp; ULONG curticks,retval; static ULONG lastics; DateStamp(&dstmp); curticks = dstmp.ds_Tick + dstmp . ds_Minute * 60 * TICKS_PER_SECOND; retval = curticks - lastics; lastics = curticks; return(retval); /* 60th's of a second */ } main() { int i=0; int midi_status,midi_note,midi_velocity; int midi_sysdata,midi_parmno,midi_setting; initmidi(); GETMIDI(midi_status); duration(); /* Toss the 1st duration to the wind */ for(;;) { switch (midi_status & 0xff0) { case NON_MIDI_EVENT: cleanup(); exit(FALSE); case NOTE_OFF : case NOTE_ON : GETMIDI(midi_note); do { GETMIDI(midi_velocity); printf("%02x %04lx %02x %02x %02x ",TIMETAG ,duration() ,midi_status ,midi_note ,midi_velocity); i += 5; GETMIDI(midi_note); if ((i % 20) == 0) printf("\n"); } while (!(midi_note & 0x80)); midi_status = midi_note; break; case PITCHWHEEL: case PARAMETER: printf("%02x %04lx %02x ",TIMETAG,duration(),midi_status); i += 3; if ((i%20) == 0) printf("\n"); GETMIDI(midi_parmno); do { GETMIDI(midi_setting); printf("%02x %02x ",midi_parmno,midi_setting); i += 2; GETMIDI(midi_parmno); if ((i % 20) == 0) printf("\n"); } while (!(midi_parmno & 0x80)); midi_status = midi_parmno; break; case SYSTEM_EXCLUSIVE: switch (midi_status) { case SYSTEM_EXCLUSIVE: /* Remember we masked off low nibble */ printf("%02x %04lx %02x ",TIMETAG,duration(),midi_status); i += 3; if ((i%20) == 0) printf("\n"); do { GETMIDI(midi_sysdata); printf("%02x ",midi_sysdata); } while ( (midi_sysdata != EOX) && (midi_sysdata != ACTIVE_SENSING)); default: GETMIDI(midi_status); break; } /* end inner switch */ break; case PROGRAM: /* These two are followed by a single data byte */ case CHANNEL_PRESSURE: case KEY_PRESSURE: /* Whereas this is followed by two. MIDI_DATA next time around will pick up the slack. */ printf("%02x %04lx %02x ",TIMETAG,duration(),midi_status); i += 3; if ((i%20) == 0) printf("\n"); GETMIDI(midi_sysdata); printf("%02x ",midi_sysdata); GETMIDI(midi_status); break; case MIDI_DATA: /* Data bytes for any of the above */ default: printf("%02x ",midi_status); GETMIDI(midi_status); break; } } } //E*O*F record.c// exit 0