Skip to content

Instantly share code, notes, and snippets.

@mutterer
Forked from lacan/Data_To_Midi.groovy
Created August 25, 2022 11:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mutterer/1a4a43f36c4af187463909781c483fb8 to your computer and use it in GitHub Desktop.
Save mutterer/1a4a43f36c4af187463909781c483fb8 to your computer and use it in GitHub Desktop.
[Data to MIDI sound] Use results from a Results table as notes to form a melody #midi #imagej
#@ File saveLocation (label="Location of MIDI save file")
/**
* Testing Spots to Sound using MIDI
* Each hit is given a unique note based on some metric and intensity is related to volume
*
* = AUTHOR INFORMATION =
* Code written by Olivier Burri, EPFL - SV - PTECH - BIOP
* for Samuel Vernon, McCabe Lab
* 2022.08.23
*/
// Prepare the data
def results = ResultsTable.getResultsTable( "Hits Results" )
// Map notes to X Coordinate
def for_notes = results.getColumn( "X Center") as List
// Define intensities for volume
def for_volume = results.getColumn( "Intensity" ) as List
// Make a SoundMaker (Class is below)
int instrument = 1 // grand piano
//int instrument = 123 // birds tweeting
SoundMaker player = new SoundMaker( instrument )
// Provide intensities and note min and max values for normalization
player.setUp( for_notes.min(), for_notes.max(), for_volume.min(), for_volume.max(), )
// go through the results and add the notes
(0..results.getCounter() - 1).each{ row ->
def intensity = results.getValue( "Intensity", row )
def xCoord = results.getValue( "X Center", row )
// Need to know when to play the note
def timepoint = results.getValue( "Timepoint", row ) as int
player.addNote( xCoord, intensity, timepoint )
}
// Play the sequence
player.play()
// Save the sequence
player.save( saveLocation )
// Simple class to make sounds
public class SoundMaker {
int delay = 4 // Delay between the note ON and note OFF
Sequencer sequencer
Sequence sequence
Track track
int normMin
int normMax
int noteMin
int noteMax
int instrument
public SoundMaker( int instrument ) { this.instrument = instrument }
public void setUp( def noteMin, def noteMax, def normMin, def normMax ) {
this.normMin = normMin
this.normMax = normMax
this.noteMin = noteMin
this.noteMax = noteMax
// A static method of MidiSystem that returns
// a sequencer instance.
sequencer = MidiSystem.getSequencer()
sequencer.open()
// Creating a sequence.
sequence = new Sequence(Sequence.PPQ, 4)
// PPQ(Pulse per ticks) is used to specify timing
// type and 4 is the timing resolution.
track = sequence.createTrack()
// This will set the instrument
ShortMessage sm = new ShortMessage( )
sm.setMessage(ShortMessage.PROGRAM_CHANGE, 1, instrument, 0);
// Inform the track of the instrument change
track.add( new MidiEvent( sm, 0 ) )
}
public void addNote( double map2Note, double intensity, int timepoint ) {
// Normalize the note between 0 and 88, then shift it to make it less deep
def note = (int) Math.round( ( map2Note - noteMin ) / ( noteMax - noteMin ) * 88 + 5+(3*8) )
// Mormalize the intensity (volume) so that the smallest is 20% and the highest is 100%
def intNorm = (int) Math.round( ( intensity - normMin ) / ( normMax - normMin ) * 80 + 20 )
// Build a short message for the note
ShortMessage sm1 = new ShortMessage( )
// Play the note, 1 is the channel
sm1.setMessage( ShortMessage.NOTE_ON, 1, note, intNorm )
track.add( new MidiEvent( sm1, timepoint ) )
// Stop playing the note
ShortMessage sm2 = new ShortMessage( )
sm2.setMessage( ShortMessage.NOTE_OFF, 1, note, intNorm )
track.add( new MidiEvent( sm2, timepoint + delay ) )
}
public void play() {
// Setting our sequence so that the sequencer can
// run it on synthesizer
sequencer.setSequence(sequence)
// Specifies the beat rate in beats per minute.
sequencer.setTempoInBPM(220)
// Sequencer starts to play notes
sequencer.start();
def done = false
while (!done) {
// Exit the program when sequencer has stopped playing.
if (!sequencer.isRunning()) {
sequencer.stop()
sequencer.close()
done = true
}
}
sequencer.close()
}
public void save( File file ) {
def fileTypes = MidiSystem.getMidiFileTypes(sequence)
MidiSystem.write(sequence, fileTypes[0], file)
}
}
import javax.sound.midi.*
import java.util.*
import ij.IJ
import ij.measure.ResultsTable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment