Nowonos Posted October 4, 2012 Report Share Posted October 4, 2012 Have a project to do for my final year in a computing degree, it can be pretty much anything so I've decided to try my hand at java programming for Android. I have an idea to create a simple interface which can send MIDI signals from the Phone via USB back to a DAW (Basically a clone of this http://createdigitalmusic.com/2009/06/android-midi-controller-musical-app-updates-with-midi-over-wifi/). I've been learning to program with Java for the last 3 years so I hopefully know enough to get by. Does anyone have any experience with MIDI programming on Android? Cheers! Thanks Haha Confused Sad Facepalm Burger Farnsworth Big Brain Like × Quote Link to comment https://forum.watmm.com/topic/75950-programming-a-midi-interface-for-android/ Share on other sites More sharing options...
BCM Posted October 4, 2012 Report Share Posted October 4, 2012 not sure about this but isn't there some fundamental problem with Android and MIDI? in that it can't process it or something? Thanks Haha Confused Sad Facepalm Burger Farnsworth Big Brain Like × Quote Hide BCM's signature Hide all signatures Bandcamp | Spotify | SoundCloud | Amazon | Apple Music | YouTube | YouTube Music | Deezer | Google Play Music Link to comment https://forum.watmm.com/topic/75950-programming-a-midi-interface-for-android/#findComment-1886499 Share on other sites More sharing options...
mcbpete Posted October 4, 2012 Report Share Posted October 4, 2012 You can go a bit of a long way round using OSC i.e. Android to OSC to MIDI to PC, check out TouchOSC - http://hexler.net/software/touchosc-android . It's a bit of a faff but it works all the same (though I never did anything of use - couldn't get PureData, which happily (and wirelessly) accepted the OSC data from my HTC desire, to talk to Ableton) Thanks Haha Confused Sad Facepalm Burger Farnsworth Big Brain Like × Quote Hide all signatures I haven't eaten a Wagon Wheel since 07/11/07... ilovecubus.co.uk - 25ml of mp3 taken twice daily. Link to comment https://forum.watmm.com/topic/75950-programming-a-midi-interface-for-android/#findComment-1886502 Share on other sites More sharing options...
Guest Posted October 4, 2012 Report Share Posted October 4, 2012 (edited) I believe that Android has fundamental problems with DSP, but MIDI should be okay? Dunno if you can host USB-MIDI interfaces (as you can with the iPad & Camera Connection Kit), but even if there's no MIDI API already, you should be able to use the USB serial port to send and listen to a arbitrary byte stream. Then maybe clone the Arduino MIDI library: http://arduino.cc/pl...ain/MIDILibrary Parsing MIDI is not rocket science, but a bit tricky nonetheless. Here's my convoluted ObjC implementation: Reveal hidden contents #define MD_MIDI_STATUS_SYSEX_BEGIN (0xF0) #define MD_MIDI_STATUS_SYSEX_END (0xF7) #define MD_MIDI_STATUS_CLOCK (0xF8) #define MD_MIDI_STATUS_ACTIVESENSE (0xFE) #define MD_MIDI_STATUS_NOTE_ON (0x90) #define MD_MIDI_STATUS_NOTE_OFF (0x80) #define MD_MIDI_STATUS_AFTERTOUCH (0xA0) #define MD_MIDI_STATUS_CONTROL_CHANGE (0xB0) #define MD_MIDI_STATUS_PROGRAM_CHANGE (0xC0) #define MD_MIDI_STATUS_ANY (0x80) #define sysexBufferSize (1024*1024) uint8_t sysexBuffer[sysexBufferSize]; NSUInteger sysexBufferIndex = 0; uint8_t noteOnBuffer[3]; NSUInteger noteOnBufferIndex = 0; uint8_t noteOffBuffer[3]; NSUInteger noteOffBufferIndex = 0; uint8_t aftertouchBuffer[3]; NSUInteger aftertouchBufferIndex = 0; uint8_t ccBuffer[3]; NSUInteger ccBufferIndex = 0; uint8_t programChangeBuffer[2]; NSUInteger programChangeBufferIndex = 0; typedef enum receiveState { receiveState_None, receiveState_Sysex, receiveState_NoteOn, receiveState_NoteOff, receiveState_Aftertouch, receiveState_ControlChange, receiveState_ProgramChange, } receiveState; receiveState _receiveState = receiveState_None; @implementation MidiInputParser - (void)midiSource:(PGMidiSource *)input midiReceived:(const MIDIPacketList *)list { //DLog(@"midi received.."); const MIDIPacket *currentPacket = &list->packet[0]; for (NSUInteger currentPacketIndex = 0; currentPacketIndex < list->numPackets; currentPacketIndex++) { for (NSUInteger currentByteIndex = 0; currentByteIndex < currentPacket->length; currentByteIndex++) { if(self.delegate) { unsigned char byteValue = currentPacket->data[currentByteIndex]; if(byteValue & MD_MIDI_STATUS_ANY) // got status byte { uint8_t hiNib = byteValue & 0xf0; if(byteValue == MD_MIDI_STATUS_CLOCK) // check for realtime messages { // handle clock } else if(byteValue == MD_MIDI_STATUS_ACTIVESENSE) { // handle activesense } else if(byteValue == MD_MIDI_STATUS_SYSEX_BEGIN) { //DLog(@"sysex receive begin"); _receiveState = receiveState_Sysex; // discard sysex buffer sysexBufferIndex = 0; sysexBuffer[sysexBufferIndex++] = byteValue; } else if (_receiveState == receiveState_Sysex && byteValue == MD_MIDI_STATUS_SYSEX_END) // sysex end (expected) { _receiveState = receiveState_None; sysexBuffer[sysexBufferIndex++] = byteValue; NSUInteger sysexDataLength = sysexBufferIndex; sysexBufferIndex = 0; if([self.delegate respondsToSelector:@selector(midiReceivedSysexData:fromSource:)]) { @autoreleasepool { NSData *d = [NSData dataWithBytes:&sysexBuffer length:sysexDataLength]; [self.delegate midiReceivedSysexData:d fromSource:self.source]; } } } else if (byteValue == MD_MIDI_STATUS_SYSEX_END) // unexpected sysex end (haven't started sysex receive) { ; } else if (hiNib == MD_MIDI_STATUS_NOTE_ON) { _receiveState = receiveState_NoteOn; noteOnBufferIndex = 0; noteOnBuffer[noteOnBufferIndex++] = byteValue; } else if (hiNib == MD_MIDI_STATUS_NOTE_OFF) { _receiveState = receiveState_NoteOff; noteOffBufferIndex = 0; noteOffBuffer[noteOffBufferIndex++] = byteValue; } else if (hiNib == MD_MIDI_STATUS_CONTROL_CHANGE) { _receiveState = receiveState_ControlChange; ccBufferIndex = 0; ccBuffer[ccBufferIndex++] = byteValue; } else if (hiNib == MD_MIDI_STATUS_PROGRAM_CHANGE) { _receiveState = receiveState_ProgramChange; programChangeBufferIndex = 0; programChangeBuffer[programChangeBufferIndex++] = byteValue; } else if (hiNib == MD_MIDI_STATUS_AFTERTOUCH) { _receiveState = receiveState_Aftertouch; aftertouchBufferIndex = 0; aftertouchBuffer[aftertouchBufferIndex++] = byteValue; } else { NSString *type = @"unknown"; uint8_t loNib = byteValue & 0x0f; if(hiNib == 0xD0) type = [NSString stringWithFormat:@"chan aftertouch @ chan %d", loNib]; else if(hiNib == 0xE0) type = [NSString stringWithFormat:@"pitch wheel range %d", loNib]; DLog(@"received unimplemented status byte: 0x%x type: %@", byteValue, type); } } else { if(_receiveState == receiveState_Sysex) { // fill buffer sysexBuffer[sysexBufferIndex++] = byteValue; if(sysexBufferIndex >= sysexBufferSize) { DLog(@"overflowing sysex buffer, cancelling sysex receive."); sysexBufferIndex = 0; _receiveState = receiveState_None; } } else if(_receiveState == receiveState_NoteOn) { //DLog(@"filling note on buffer.."); noteOnBuffer[noteOnBufferIndex++] = byteValue; if(noteOnBufferIndex == 3) { _receiveState = receiveState_None; noteOnBufferIndex = 0; if((noteOnBuffer[2] & 0x7f) == 0 && [self.delegate respondsToSelector:@selector(midiReceivedNoteOff:fromSource:)]) // zero velocity note off { @autoreleasepool { MidiNoteOff *noteOff = [MidiNoteOff new]; noteOff.channel = noteOnBuffer[0] & 0x0f; noteOff.note = noteOnBuffer[1] & 0x7f; noteOff.velocity = noteOnBuffer[2] & 0x7f; [self.delegate midiReceivedNoteOff:noteOff fromSource:self.source]; } } else if([self.delegate respondsToSelector:@selector(midiReceivedNoteOn:fromSource:)]) { @autoreleasepool { MidiNoteOn *noteOn = [MidiNoteOn new]; noteOn.channel = noteOnBuffer[0] & 0x0f; noteOn.note = noteOnBuffer[1] & 0x7f; noteOn.velocity = noteOnBuffer[2] & 0x7f; [self.delegate midiReceivedNoteOn:noteOn fromSource:self.source]; } } } } else if(_receiveState == receiveState_NoteOff) { //DLog(@"filling note on buffer.."); noteOffBuffer[noteOffBufferIndex++] = byteValue; if(noteOffBufferIndex == 3) { _receiveState = receiveState_None; noteOffBufferIndex = 0; if([self.delegate respondsToSelector:@selector(midiReceivedNoteOff:fromSource:)]) { @autoreleasepool { MidiNoteOff *noteOff = [MidiNoteOff new]; noteOff.channel = noteOffBuffer[0] & 0x0f; noteOff.note = noteOffBuffer[1] & 0x7f; noteOff.velocity = noteOffBuffer[2] & 0x7f; [self.delegate midiReceivedNoteOff:noteOff fromSource:self.source]; } } } } else if(_receiveState == receiveState_ControlChange) { ccBuffer[ccBufferIndex++] = byteValue; if(ccBufferIndex == 3) { _receiveState = receiveState_None; ccBufferIndex = 0; if([self.delegate respondsToSelector:@selector(midiReceivedControlChange:fromSource:)]) { @autoreleasepool { MidiControlChange *cc = [MidiControlChange new]; cc.channel = ccBuffer[0] & 0x0f; cc.parameter = ccBuffer[1] & 0x7f; cc.ccValue = ccBuffer[2] & 0x7f; [self.delegate midiReceivedControlChange:cc fromSource:self.source]; } } } } else if(_receiveState == receiveState_ProgramChange) { programChangeBuffer[programChangeBufferIndex++] = byteValue; if(programChangeBufferIndex == 2) { _receiveState = receiveState_None; programChangeBufferIndex = 0; if([self.delegate respondsToSelector:@selector(midiReceivedProgramChange:fromSource:)]) { @autoreleasepool { MidiProgramChange *pc = [MidiProgramChange new]; pc.channel = programChangeBuffer[0] & 0x0f; pc.program = programChangeBuffer[1] & 0x7f; [self.delegate midiReceivedProgramChange:pc fromSource:self.source]; } } } } else if(_receiveState == receiveState_Aftertouch) { //DLog(@"filling note on buffer.."); aftertouchBuffer[aftertouchBufferIndex++] = byteValue; if(aftertouchBufferIndex == 3) { _receiveState = receiveState_None; aftertouchBufferIndex = 0; if([self.delegate respondsToSelector:@selector(midiReceivedAftertouch:fromSource:)]) { @autoreleasepool { MidiAftertouch *aftertouch = [MidiAftertouch new]; aftertouch.channel = aftertouchBuffer[0] & 0x0f; aftertouch.note = aftertouchBuffer[1] & 0x7f; aftertouch.pressure = aftertouchBuffer[2] & 0x7f; [self.delegate midiReceivedAftertouch:aftertouch fromSource:self.source]; } } } } } } } currentPacket = MIDIPacketNext(currentPacket); } } @end It could probably be optimised a little, but when you look at the Arduino MIDI parser it looks just as messy, and it works so yeah :] good luck & have fun. edit: lol formatting Edited October 4, 2012 by Guest Quote Link to comment https://forum.watmm.com/topic/75950-programming-a-midi-interface-for-android/#findComment-1886503 Share on other sites More sharing options...
oscillik Posted October 5, 2012 Report Share Posted October 5, 2012 audio code for DSP tasks was improved in Android 4.0, if that helps any... Thanks Haha Confused Sad Facepalm Burger Farnsworth Big Brain Like × Quote Hide oscillik's signature Hide all signatures Link to comment https://forum.watmm.com/topic/75950-programming-a-midi-interface-for-android/#findComment-1886937 Share on other sites More sharing options...
Recommended Posts