Blitzmax MIDI IN

BlitzMax Forums/BlitzMax Programming/Blitzmax MIDI IN

Ok a quick hack, hope this helps. Really needs someone to make this into a module Brucey?

Download portmidi28aug06 and place it inside your project.

main.bmx
Strict

Import "portmidi28aug06/porttime/*.h"
Import "portmidi28aug06/porttime/porttime.c"

Import "portmidi28aug06/pm_common/*.h"
Import "portmidi28aug06/pm_common/portmidi.c"


?Win32

	Import "portmidi28aug06/porttime/ptwinmm.c"

	Import "portmidi28aug06/pm_win/*.h"
	Import "portmidi28aug06/pm_win/pmwin.c"
	Import "portmidi28aug06/pm_win/pmdll.c"
	Import "portmidi28aug06/pm_win/pmwinmm.c"

	Import "sysex.cpp"

Framework		brl.d3d7max2d

?MacOS
?Linux
?

Extern "C"		' sysex.cpp

	Global	OutputDevice:Int
	Global	InputDevice:Int
	Global	LoopbackInputDevice:Int
	Global	LoopbackOutputDevice:Int

	Global	midi_message:Byte[]

	Function 	midi_open:Int()
	Function 	midi_close:Int()
	Function 	midi_test:Byte Ptr()

	Function 	loopback_open:Int()
	Function 	loopback_close:Int()
	Function 	loopback_test:Int()

	Function 	get_midi_in()
	Function 	get_midi_out()

	Function 	sysex_receive_open:Int()
	Function 	sysex_receive_get:Byte Ptr()
	Function 	sysex_receive_close:Int()

	Function 	sysex_send( message:Byte Ptr )
	Function 	sysex_send_receive:Byte Ptr( message:Byte Ptr )

	Function 	sysex_initalise()
	Function 	sysex_set_device:Int( device:Byte Ptr, dir:Int )
	Function 	sysex_get_next_device:String( nxt:Int, dir:Int )
	Function 	sysex_get_number_of_devices:Int()

	Function 	sysex_finalise()

End Extern

Global device_enquiry:Byte[]=[ Byte($F0),Byte($7E),Byte($00),Byte($06),Byte($01),Byte($F7) ]

	sysex_initalise()
	Delay(1000)

	' List input / output devices
	For Local i:Int=0 To sysex_get_number_of_devices()-1
	
		If sysex_get_next_device(i,True) = ""
		Else
			DebugLog "Output Device [" + i + "] = " + sysex_get_next_device(i,True)
		EndIf
	
		If sysex_get_next_device(i,False) = ""
		Else
			DebugLog "Input Device [" + i + "] = " + sysex_get_next_device(i,False)
		EndIf
		
	Next

	' Change these to correct values for your setup.
	InputDevice = 2
	OutputDevice = 5

    '
    If sysex_get_next_device( InputDevice , False ) = ""
        If Not Confirm( "No compatible MIDI input device selected, Continue?", True )
            Return False
        EndIf
    EndIf

    '
    If sysex_get_next_device( OutputDevice, True ) = ""
        If Not Confirm( "No compatible MIDI output device selected, Continue?", True )
            Return False
        EndIf
    EndIf


    Global bp:Byte Ptr

    bp = sysex_send_receive(device_enquiry)

	'
	For Local i:Int=0 To 4
		DebugLog "bp[i] = " + bp[i]
	Next
	'
    If bp[0]=$F0 And bp[1]=$7E And bp[2]=$00 And bp[3]=$06 And bp[4]=$02	' Valid responce to device enquiry?
		'
		Notify( "Correct responce from device enquiry" )

    Else
		'
        Notify( "Invalid response from DeviceEnquiry(), Retry?", False )
    EndIf


sysex_finalise()

End


sysex.cpp
#include "stdio.h"
#include "stdlib.h"
#include "assert.h"
#include "string.h"

#include "portmidi28aug06/pm_common/portmidi.h"
#include "portmidi28aug06/porttime/porttime.h"

extern "C" {

#define MIDI_SYSEX 0xf0
#define MIDI_EOX 0xf7

#define STRING_MAX 80
#define MIDI_MESSAGE_SIZE 1024

#define is_real_time_msg(msg) ((0xF0 & Pm_MessageStatus(msg)) == 0xF8)

int InputDevice;
int OutputDevice;

int	 LoopbackInputDevice;
int	 LoopbackOutputDevice;

unsigned char midi_message[MIDI_MESSAGE_SIZE];

#define INPUT_BUFFER_SIZE 100
#define OUTPUT_BUFFER_SIZE 0
#define DRIVER_INFO NULL
#define TIME_PROC ((long (*)(void *)) Pt_Time)
#define TIME_INFO NULL
#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */

PmStream *midiIn = NULL; 
PmStream *midiOut = NULL;


//char dummy[256];

PmStream *midi_in_stream;

// ----------------------------------------------------------
// Reset the message buffer
// ----------------------------------------------------------
void	reset_message( void )
// ----------------------------------------------------------
{
//return;

int		i;

	for ( i=0; i<MIDI_MESSAGE_SIZE; i++ ) {
		midi_message[i] = 0;
	}
}

// ----------------------------------------------------------
// Open the MIDI ports
// ----------------------------------------------------------
bool midi_open( void )
// ----------------------------------------------------------
{
//return true;

	//
	Pm_OpenInput( &midi_in_stream, InputDevice, DRIVER_INFO, INPUT_BUFFER_SIZE, TIME_PROC, TIME_INFO );

	//
	if ( midi_in_stream != NULL ) {
		return true;
	}

	return false;	
}


// ----------------------------------------------------------
// Close the MIDI ports
// ----------------------------------------------------------
bool midi_close( void )
// ----------------------------------------------------------
{
//return true;
		
	//
	if ( midi_in_stream != NULL) {
		Pm_Close(midi_in_stream);
		midi_in_stream = NULL;			
		return true;
	} else {
		return false;
	}
}


// ----------------------------------------------------------
// Read a short message
// ----------------------------------------------------------
signed char *midi_test( void )
// ----------------------------------------------------------
{
//return true;

	PmEvent buffer[1];
	PmError status, length;

	//
	status = Pm_Poll( midi_in_stream );
	if ( status == true ) {

		reset_message();

		length = Pm_Read( midi_in_stream, buffer, 1 );
		if ( length > 0 ) {

			midi_message[0] = Pm_MessageStatus( buffer[0].message );
			midi_message[1] = Pm_MessageData1( buffer[0].message );			
			midi_message[2] = Pm_MessageData2( buffer[0].message );
			
//			printf("Got message: time %ld, %2lx %2lx %2lx\n",
	//					buffer[0].timestamp,
		//				midi_message[0] = Pm_MessageStatus( buffer[0].message ),
			//			Pm_MessageData1( buffer[0].message ),
				//		Pm_MessageData2( buffer[0].message ));

//			if ( Pm_MessageData1( buffer[0].message ) == 31 && Pm_MessageData2(buffer[0].message ) == 64 ) return true;			

			return (signed char *)midi_message;
		}
	}

	return NULL;
}


// ----------------------------------------------------------
// Open the MIDI loopback ports
// ----------------------------------------------------------
bool loopback_open()
// ----------------------------------------------------------
{
//return true;

	//
	Pm_OpenOutput( &midiOut, LoopbackOutputDevice, DRIVER_INFO, OUTPUT_BUFFER_SIZE, NULL, NULL, 0 );

	//
	Pm_OpenInput( &midiIn, LoopbackInputDevice, DRIVER_INFO, INPUT_BUFFER_SIZE, TIME_PROC, TIME_INFO );

	//
	if ( ( midiOut != NULL ) && ( midiIn != NULL ) ) {

		//
//		Pm_SetFilter( midiIn, PM_FILT_ACTIVE | PM_FILT_CLOCK );

	   // empty the buffer after setting filter, just in case anything got through.
//	    while (Pm_Poll(midiIn)) {
//	        Pm_Read( midiIn, buffer, 1 );
//	    }

		return true;
	}

	return false;	
}


// ----------------------------------------------------------
// Close the MIDI loopback ports
// ----------------------------------------------------------
bool loopback_close()
// ----------------------------------------------------------
{
//return true;

	//	
	if ( midiOut != NULL ) {
		Pm_Close(midiOut);
		midiOut = NULL;
		
		if ( midiIn != NULL) {
			Pm_Close(midiIn);
			midiIn = NULL;
			
			return true;
		} else {
			return false;
		}
	} else {
		return false;
	}
}


// ----------------------------------------------------------
// Write a short message then poll for a returned message
// ----------------------------------------------------------
bool loopback_test()
// ----------------------------------------------------------
{
//return true;

	PmEvent buffer[1];
	PmError status, length;

	// Note on
	Pm_WriteShort( midiOut, TIME_PROC(TIME_INFO), Pm_Message(0x90, 31, 64) );

	// Note off
//	Pm_WriteShort( midiOut, TIME_PROC(TIME_INFO), Pm_Message(0x80, 31, 40) );

//	buffer[0].timestamp = TIME_PROC(TIME_INFO);
//	buffer[0].message = Pm_Message(0x90,31,64);
//	Pm_Write( midiOut, buffer, 1);

	//
	status = Pm_Poll( midiIn );
	if ( status == true ) {

		length = Pm_Read( midiIn, buffer, 1 );
		if (length > 0) {

//			printf("Got message: time %ld, %2lx %2lx %2lx\n",
//						buffer[0].timestamp,
//						Pm_MessageStatus( buffer[0].message ),
//						Pm_MessageData1( buffer[0].message ),
//						Pm_MessageData2( buffer[0].message ));

			if ( Pm_MessageData1( buffer[0].message ) == 31 && Pm_MessageData2(buffer[0].message ) == 64 ) return true;			
		}
	}

	return false;
}


// ----------------------------------------------------------
// ----------------------------------------------------------
int		get_midi_in( void )
// ----------------------------------------------------------
{
//return true;

return InputDevice;
}


// ----------------------------------------------------------
// ----------------------------------------------------------
int		get_midi_out( void )
// ----------------------------------------------------------
{
//return true;

return OutputDevice;
}


// ----------------------------------------------------------
// ----------------------------------------------------------
void send_sysex( unsigned char *sysex_data ) 
// ----------------------------------------------------------
{
//return;

	PmStream *midi_out;
	
	//
	Pm_OpenOutput( &midi_out, OutputDevice, DRIVER_INFO, OUTPUT_BUFFER_SIZE, NULL, NULL, 0 );

	if ( midi_out != NULL ) {

		Pm_WriteSysEx( midi_out, 0, sysex_data );
		Pm_Close( midi_out );
		midi_out = NULL;
	}
}


// ----------------------------------------------------------
void receive_sysex( PmStream *midi )
// ----------------------------------------------------------
{
//return;

	int shift = 0;
	unsigned char data = 0;
	int bytes_on_line = 0;
	PmEvent msg;
	PmError count;

	// read data and write to file
	while ( data != MIDI_EOX ) {
		
		count = Pm_Read(midi, &msg, 1);
			
		// CAUTION: this causes busy waiting. It would be better to 
		// be in a polling loop to avoid being compute bound. PortMidi
		// does not support a blocking read since this is so seldom useful.
		
		if (count == 0) continue;
		
		// ignore real-time messages
		if (is_real_time_msg(Pm_MessageStatus(msg.message))) continue;

		// write 4 bytes of data until you reach an eox
		bool done = 0;
		for ( shift = 0; (shift < 32) && !done /*(data != MIDI_EOX)*/; shift += 8) {
			data = (msg.message >> shift) & 0xFF;
			
			// if this is a status byte that's not MIDI_EOX, the sysex
			// message is incomplete and there is no more sysex data
			
			midi_message[ bytes_on_line ] = data;
			++bytes_on_line;
			
			done = ( data == MIDI_EOX );
		}
	}
}


// ----------------------------------------------------------
unsigned char *sysex_send_receive( unsigned char *sysex_data ) 
// ----------------------------------------------------------
{
//return;

	PmStream *midi_in;
	PmStream *midi_out;

	Pm_OpenInput( &midi_in, InputDevice, DRIVER_INFO, INPUT_BUFFER_SIZE, NULL, NULL);
	Pm_OpenOutput( &midi_out, OutputDevice, DRIVER_INFO, OUTPUT_BUFFER_SIZE, NULL, NULL, 0);

	if ( midi_out != NULL ) {

		Pm_WriteSysEx( midi_out, 0, sysex_data );

		if ( midi_in != NULL) {
			reset_message();
			receive_sysex( midi_in );
			Pm_Close(midi_in);
			midi_in = NULL;
		}

		Pm_Close(midi_out);
		midi_out = NULL;
	}
	
	return midi_message;
}


// ----------------------------------------------------------
void sysex_send( unsigned char *message )
// ----------------------------------------------------------
{
//return;
int i;

	PmStream *midi_in;
	PmStream *midi_out;

//	fprintf( stderr, "opening output\n" );

	Pm_OpenOutput( &midi_out, OutputDevice, DRIVER_INFO, OUTPUT_BUFFER_SIZE, NULL, NULL, 0 );

//	fprintf( stderr, "opened output\n" );


	if ( midi_out != NULL ) {
	
		//
		Pm_WriteSysEx( midi_out, 0, message );
		
//		for( i=0; ( message[i] != MIDI_EOX ); i++ ) {
//			fprintf( stderr, "message[%d] = %.2x\n", i, message[i] );
//		}


		Pm_Close(midi_out);
		midi_out = NULL;

//		fprintf( stderr, "closed output\n" );
	}
	else {
	
		fprintf( stderr, "midi_out == NULL\n" );
	}
}


PmStream *midi_in = NULL;

// ----------------------------------------------------------
bool sysex_receive_open( void ) 
// ----------------------------------------------------------
{
//return true;

	Pm_OpenInput( &midi_in, InputDevice, NULL, 512, NULL, NULL );

	if ( midi_in != NULL) {
		return true;
	}

	return false;	
}


// ----------------------------------------------------------
unsigned char *sysex_receive_get( void ) 
// ----------------------------------------------------------
{
//return dummy;

	if ( midi_in != NULL) {

		if ( Pm_Poll( midi_in ) > 0 ) {
			reset_message();
			receive_sysex( midi_in );
			return midi_message;
		} else {
			return NULL;
		}
	} else {
		return NULL;
	}
}


// ----------------------------------------------------------
bool sysex_receive_close( void ) 
// ----------------------------------------------------------
{
//return true;

	if ( midi_in != NULL) {
		Pm_Close(midi_in);
		midi_in = NULL;
		return true;
	}
	return false;
}


#if 0
// ----------------------------------------------------------
char *sysex_send_receive( unsigned char *message )
// ----------------------------------------------------------
{
//return dummy;

	reset_message();

	send_receive_sysex( message );

	return midi_message;
}
#endif


// ----------------------------------------------------------
void	sysex_initalise( void )
// ----------------------------------------------------------
{
//return;

	int i;

	//
	Pm_Initialize();
	
#if 0	

	// list device information
	for (i = 0; i < Pm_CountDevices(); i++) {

        const PmDeviceInfo *info = Pm_GetDeviceInfo(i);

        printf("%d: %s, %s", i, info->interf, info->name);
        if (info->input) printf(" (input)");
        if (info->output) printf(" (output)");
        printf("\n");

		if ( ( strcmp( info->name, "USB Audio Device" ) == 0 ) && info->output ) {
			printf( "found output device %d\n", i );
			OutputDevice = i;
		}

		if ( ( strcmp( info->name, "USB Audio Device" ) == 0 ) && info->input ) {
			printf( "found input device @ %d\n", i );
			InputDevice = i;
		}


		if ( ( strcmp( info->name, "USB Audio Device [3]" ) == 0 ) && info->input ) {
			printf( "found intput loopback device @ %d\n", i );
			LoopbackInputDevice = i;
		}
	
		if ( ( strcmp( info->name, "USB Audio Device [2]" ) == 0 ) && info->output ) {
			printf( "found output loopback device @ %d\n", i );
			LoopbackOutputDevice = i;
		}		
	}

#endif

	//
	reset_message();
}


// ----------------------------------------------------------
int		sysex_set_device( char *select, int dir )
// ----------------------------------------------------------
{
	int i;

	// list device information
	for (i = 0; i < Pm_CountDevices(); i++) {
	
		const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
		
//		printf("%d: %s, %s", i, info->interf, info->name);
//		if (info->input) printf(" (input)");
//		if (info->output) printf(" (output)");
//		printf("\n");

		if ( dir == true ) {
			if ( ( strcmp( info->name, select ) == 0 ) && info->output ) {
				return i;
			}
		} else {
			if ( ( strcmp( info->name, select ) == 0 ) && info->input ) {
				return i;
			}
		}
	}
	return -1;
}


#include <brl.mod/blitz.mod/blitz_string.h>

// ----------------------------------------------------------
BBString *sysex_get_next_device( int nxt, int dir )
// ----------------------------------------------------------
{	
	const PmDeviceInfo *info = Pm_GetDeviceInfo( nxt );
	
//	printf( "pointer = %p", info );		
//	printf("%d: %s, %s", nxt, info->interf, info->name);
//	if (info->input) printf(" (input)");
//	if (info->output) printf(" (output)");
//	printf("\n");

	if ( info != NULL ) {

		if ( (dir == true) && info->output ) {
			return bbStringFromCString(info->name);
		}
	
		if ( (dir == false) && info->input ) {
			return bbStringFromCString(info->name);
		}
	}

	return bbStringFromCString("");
}


// ----------------------------------------------------------
int sysex_get_device_dir( int nxt )
// ----------------------------------------------------------
{	
int dir = 0;

	const PmDeviceInfo *info = Pm_GetDeviceInfo( nxt );

	if ( info != NULL ) {

		if ( info->output ) dir |= 1;
		if ( info->input ) dir |= 2;
		return dir;
	}
}


// ----------------------------------------------------------
int sysex_get_number_of_devices( void ) {
// ----------------------------------------------------------
	return Pm_CountDevices();
}


// ----------------------------------------------------------
void	sysex_finalise( void )
// ----------------------------------------------------------
{
//return;

	Pm_Terminate();
}

} // extern "C" }