How do I play java.sound.midi sequences to a virtual midi port?

edited March 2015 in Questions about Code

I have been trying to do a little project for a long time but my programming skills have made it difficult to complete. I need to be able to play a midi file in real time and use the note +volume or velocity data to act as a variable to alter graphics on the screen. I want to sort out the midi issues and think Im close thanks to some online code I found.

Basically this code seems to get processing to play a midi file:

import javax.sound.midi.*; void setup() { try { File midiFile = new File(dataPath("Me.mid")); Sequencer sequencer = MidiSystem.getSequencer(); sequencer.open(); Sequence sequence = MidiSystem.getSequence(midiFile); sequencer.setSequence(sequence); sequencer.start(); } catch(Exception e) {} }

This tutorial I found on youtube by Alex Pilkevych can listen to midi songs and pick out the note data in real time by using a music sequencer to play the midi song and redirect it to processing via a virtual midi port.

import javax.sound.midi.Sequencer;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.InvalidMidiDataException;
import java.io.IOException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Track;
import javax.sound.midi.ShortMessage;

MidiSystem midiSystem;
Sequencer midiSequencer;

String[] Notes = {
  "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};

class Note
{
  float m_fTime;
  String m_sNote;
}

class Song
{
  Song(String sName)
  {
    m_sName = sName;
  }
  String m_sName = "";
  ArrayList m_Notes = new ArrayList();
}

int g_iTicksPerBeat = 96;
ArrayList loadMidi(String fileName, float fBPM)
{
  String sketchPath = this.sketchPath;
  println(sketchPath);

  File midiFile = new File(sketchPath + "/" + fileName);

  println(midiFile.getAbsolutePath());

  if (!midiFile.exists() || midiFile.isDirectory() || !midiFile.canRead())
  {
    println("Error reading file");
  }
  Sequence midiSequence = null;

  try
  {
    midiSequence = midiSystem.getSequence(midiFile);
  }
  catch (Exception e)
  {
    e.printStackTrace();
  }

  if (midiSequence == null)
  {
    return null;
  }

  Track[] midiTracks = midiSequence.getTracks();  
  Track midiTrack = midiTracks[0];
  int iNumEvents = midiTrack.size();
  ArrayList myNotes = new ArrayList();

  for (int i=0; i<iNumEvents; i++)
  {
    MidiEvent event = midiTrack.get(i);
    MidiMessage message = event.getMessage();
    if (message.getLength()-1 == 2) //message length - staus byte = 2 (short message)
    {
      int iMessage = (int)(message.getMessage()[0] & 0xFF);
      if (iMessage == ShortMessage.NOTE_ON)
      {    
        int iFullNote = (int)(message.getMessage()[1] & 0xFF);
        int iNote = iFullNote % 12;
        int iOctave = floor(iFullNote/12) - 2;
        float fBPS = fBPM / 60.0;
        float fSPB = 1.0 / fBPS;
        float fTime = event.getTick() * fSPB / g_iTicksPerBeat;
        //println("Note On: " + Notes[iNote] + "-" + iOctave + " @" + fTime + "[" + event.getTick() + "]");
        Note n = new Note();
        n.m_sNote = Notes[iNote];
        n.m_fTime = fTime;
        myNotes.add(n);
        //384 ticks per bar?
      }
    }
  }
  return myNotes;
}

What Id like to do is combine the two sketches or at least have two independent sketches which communicate with each other so that I dont need a giant complicated music sequencer program like ProTools just to do the simple task of playing a midi file..... the problem I see is that the java.sound.midi sketch plays the midi song through the audio port and does not have an option to redirect it to the virtual midi port instead (dont need to hear the song, just need to use the song data).

Looking at the java.sound.midi documentation : [http://docs.oracle.com/javase/7/docs/api/javax/sound/midi/package-summary.html]

it looks like its possible to assign output to a different port, but my knowledge of Object oriented programming is not there yet and the docs dont really show me how to implement the added commands....

If any of you midi guys and gals could help guide me through this it would be huge!!!

Answers

  • I never dabled with true java, but is there maybe a good java forum where I can repost the question? I realize its a processing project but the code is straight Java...

  • You can use stack overflow, there they have a wider variety of programmers, that could also help with your issue. I personally do not know how to help you, but if the basic idea of your program is to sync playing notes with LED lights, I could help you with that.

  • Basically I need a midi file to play and for specific midi events like note on and velocity to be extracted in real time to use them to do something like control leds....note value = led#, velocity=led brightness pm me if it's something you could do

  • I know this is old, but since none answered... This link is a bit difficult to find so you might have missed it. I think you should use an array MidiDevice.Info to check for available devices and send it through the virtual port you've created.

  • edited September 2016 Answer ✓

    I know you can use MINIM library to play your music and then you can use controlP5 library both from processing to control it with a midi controller. this code works with my MIDI MIX controller, you will just have to change the name of the device (arduino) you are using. the first code is the processing sketch and the second is the java class I assume you know how to make a new tab for the class.

    '

    // Map controller with Midi import java.util.HashMap; import java.util.Map; import controlP5.*; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.Transmitter; ControlP5 cp5; MidiSimple midi; int theIndex; Map midimapper = new HashMap(); void setup() { size( 600, 400 ); cp5 = new ControlP5( this ); cp5.begin(cp5.addTab("a")); cp5.addSlider("a-1").setPosition(20, 120).setSize(200, 20) .setRange(0,255);//.setValue(200); cp5.addSlider("a-2").setPosition(20, 160).setSize(200, 20) .setRange(0,255);//.setValue(200); cp5.addSlider("a-3").setPosition(20, 200).setSize(200, 20) .setRange(0,255);//.setValue(200); cp5.addToggle("a-4").setPosition(280, 120).setSize(100, 20); cp5.addButton("a-5").setPosition(280, 160).setSize(100, 20); cp5.addBang("a-6").setPosition(280, 200).setSize(100, 20); cp5.end(); cp5.begin(cp5.addTab("b")); cp5.addSlider("b-1").setPosition(20, 120).setSize(200, 20); cp5.addSlider("b-2").setPosition(20, 160).setSize(200, 20); cp5.addSlider("b-3").setPosition(20, 200).setSize(200, 20); cp5.end(); final String device = "MIDI Mix"; //midimapper.clear(); pushMatrix(); for (int i =0; i < 127; i ++){ midimapper.put( ref( device, i ), "a-1" ); } popMatrix(); pushMatrix(); for (int i =0; i < 127; i ++){ midimapper.put( ref( device, i ), "a-2" ); } popMatrix(); pushMatrix(); for (int i =0; i < 127; i ++){ midimapper.put( ref( device, i ), "a-3" ); } popMatrix(); midimapper.put( ref( device, 32 ), "a-4" ); midimapper.put( ref( device, 48 ), "a-5" ); midimapper.put( ref( device, 64 ), "a-6" ); midimapper.put( ref( device, 16 ), "b-1" ); midimapper.put( ref( device, 17 ), "b-2" ); midimapper.put( ref( device, 18 ), "b-3" ); boolean DEBUG = true; if (DEBUG) { new MidiSimple( device ); } midi = new MidiSimple( device , new Receiver() { @Override public void send( MidiMessage msg, long timeStamp ) { byte[] b = msg.getMessage(); if ( b[ 0 ] != -48 ) { Object index = ( midimapper.get( ref( device , b[ 2 ] ) ) ); if ( index != null ) { Controller c = cp5.getController(index.toString()); if (c instanceof Slider ) { float min = c.getMin(); float max = c.getMax(); c.setValue(map(b[ 2 ], 0, 127, min, max) ); } else if ( c instanceof Button ) { if ( b[ 2 ] > 0 ) { c.setValue( c.getValue( ) ); c.setColorBackground( 0xff08a2cf ); } else { c.setColorBackground( 0xff003652 ); } } else if ( c instanceof Bang ) { if ( b[ 2 ] > 0 ) { c.setValue( c.getValue( ) ); c.setColorForeground( 0xff08a2cf ); } else { c.setColorForeground( 0xff00698c ); } } else if ( c instanceof Toggle ) { if ( b[ 2 ] > 0 ) { ( ( Toggle ) c ).toggle( ); } } } } } @Override public void close( ) { } } ); } String ref(String theDevice, int theIndex) { return theDevice+"-"+theIndex; } void draw() { float s1 = cp5.getController("a-1").getValue(); float s2 = cp5.getController("a-2").getValue(); float s3 = cp5.getController("a-3").getValue(); background( s1 + theIndex, s2, s3 ); }

    '

    '

    import java.util.HashMap; import java.util.Map; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.Transmitter; public class MidiSimple { public MidiSimple( String theDeviceName , Receiver theReceiver ) { MidiDevice.Info[] aInfos = MidiSystem.getMidiDeviceInfo(); for ( int i = 0; i < aInfos.length; i++ ) { try { MidiDevice device = MidiSystem.getMidiDevice( aInfos[ i ] ); boolean bAllowsInput = ( device.getMaxTransmitters() != 0 ); boolean bAllowsOutput = ( device.getMaxReceivers() != 0 ); System.out.println( "" + i + " " + ( bAllowsInput ? "IN " : " " ) + ( bAllowsOutput ? "OUT " : " " ) + aInfos[ i ].getName() + ", " + aInfos[ i ].getVendor() + ", " + aInfos[ i ].getVersion() + ", " + aInfos[ i ].getDescription() + ", " + aInfos[ i ].toString() + ", " ); } catch ( MidiUnavailableException e ) { // device is obviously not available... // out(e); } } try { MidiDevice device; device = MidiSystem.getMidiDevice( getMidiDeviceInfo( theDeviceName, false ) ); device.open(); Transmitter conTrans = device.getTransmitter(); conTrans.setReceiver( theReceiver ); } catch ( MidiUnavailableException e ) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NullPointerException e) { //System.out.println("No Midi device ( "+theDeviceName+" ) is available."); } } public MidiSimple( String theDeviceName ) { new MidiSimple(theDeviceName , new MidiInputReceiver( theDeviceName ) ); } public MidiDevice.Info getMidiDeviceInfo( String strDeviceName, boolean bForOutput ) { MidiDevice.Info[] aInfos = MidiSystem.getMidiDeviceInfo(); for ( int i = 0; i < aInfos.length; i++ ) { if ( aInfos[ i ].getName().equals( strDeviceName ) ) { try { MidiDevice device = MidiSystem.getMidiDevice( aInfos[ i ] ); boolean bAllowsInput = ( device.getMaxTransmitters() != 0 ); boolean bAllowsOutput = ( device.getMaxReceivers() != 0 ); if ( ( bAllowsOutput && bForOutput ) || ( bAllowsInput && !bForOutput ) ) { return aInfos[ i ]; } } catch ( MidiUnavailableException e ) { // TODO: } } } return null; } class MidiInputReceiver implements Receiver { public String name; Map< Byte, String > commandMap = new HashMap< Byte, String >(); public MidiInputReceiver( String name ) { this.name = name; commandMap.put( ( byte ) -112, "Note On" ); commandMap.put( ( byte ) -128, "Note Off" ); commandMap.put( ( byte ) -48, "Channel Pressure" ); commandMap.put( ( byte ) -80, "Continuous Controller" ); } public void send( MidiMessage msg, long timeStamp ) { System.out.println( "midi received (" + name + ")" ); System.out.println( "Timestamp: " + timeStamp ); byte[] b = msg.getMessage(); if ( b[ 0 ] != -48 ) { System.out.println("Message length: " + msg.getLength()); System.out.println( "Note command: " + commandMap.get( b[ 0 ] ) ); System.out.println( "Which note: " + b[ 1 ] ); System.out.println( "Note pressure: " + b[ 2 ] ); System.out.println( "---------------------" ); } else { System.out.println("Message length: " + msg.getLength()); System.out.println( "Note command: " + commandMap.get( b[ 0 ] ) ); System.out.println( "Note Pressure: " + b[ 1 ] ); System.out.println( "---------------------" ); } } public void close( ) { } } }

    '

Sign In or Register to comment.