Created
August 24, 2022 09:23
-
-
Save lacan/38e2110c561108c198a97c30200b9aef 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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#@ 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