google-site-verification: google61fe8ba583a51912.html
MIDI Music Generation in Java

Introduction

MIDI (Musical Instrument Digital Interface) music generation in Java allows developers to programmatically create, manipulate, and play musical compositions. This guide covers comprehensive MIDI programming using Java's built-in MIDI API and external libraries.

Setup and Dependencies

Maven Dependencies

<dependencies> <!-- Java Sound API (included in JDK) --> <!-- No additional dependencies needed for basic MIDI --> <!-- For advanced MIDI features --> <dependency> <groupId>com.googlecode.soundlibs</groupId> <artifactId>jlayer</artifactId> <version>1.0.1</version> </dependency> <!-- For MIDI file manipulation --> <dependency> <groupId>org.jfugue</groupId> <artifactId>jfugue</artifactId> <version>5.0.9</version> </dependency> </dependencies>

Basic MIDI Synthesis

Simple MIDI Player

import javax.sound.midi.*; import java.util.ArrayList; import java.util.List; public class BasicMIDIPlayer { private Synthesizer synthesizer; private MidiChannel[] channels; private final int DEFAULT_VELOCITY = 64; private final int DEFAULT_CHANNEL = 0; public BasicMIDIPlayer() throws MidiUnavailableException { initializeSynthesizer(); } private void initializeSynthesizer() throws MidiUnavailableException { synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); channels = synthesizer.getChannels(); // Set default instrument (Acoustic Grand Piano) channels[DEFAULT_CHANNEL].programChange(0); } public void playNote(int note, int duration) throws InterruptedException { playNote(note, duration, DEFAULT_VELOCITY); } public void playNote(int note, int duration, int velocity) throws InterruptedException { channels[DEFAULT_CHANNEL].noteOn(note, velocity); Thread.sleep(duration); channels[DEFAULT_CHANNEL].noteOff(note); } public void playChord(int[] notes, int duration) throws InterruptedException { playChord(notes, duration, DEFAULT_VELOCITY); } public void playChord(int[] notes, int duration, int velocity) throws InterruptedException { // Play all notes simultaneously for (int note : notes) { channels[DEFAULT_CHANNEL].noteOn(note, velocity); } Thread.sleep(duration); // Turn off all notes for (int note : notes) { channels[DEFAULT_CHANNEL].noteOff(note); } } public void changeInstrument(int program) { channels[DEFAULT_CHANNEL].programChange(program); } public void setVolume(int volume) { channels[DEFAULT_CHANNEL].controlChange(7, volume); // 7 = Volume controller } public void playScale(int[] scale, int noteDuration) throws InterruptedException { for (int note : scale) { playNote(note, noteDuration); } } public void close() { if (synthesizer != null && synthesizer.isOpen()) { synthesizer.close(); } } // Demo method public static void demo() throws Exception { BasicMIDIPlayer player = new BasicMIDIPlayer(); // C Major scale int[] cMajorScale = {60, 62, 64, 65, 67, 69, 71, 72}; // C4 to C5 System.out.println("Playing C Major Scale..."); player.playScale(cMajorScale, 300); Thread.sleep(500); // C Major chord int[] cMajorChord = {60, 64, 67}; // C, E, G System.out.println("Playing C Major Chord..."); player.playChord(cMajorChord, 1000); player.close(); } }

MIDI Note Constants and Music Theory

Music Theory Utilities

public class MusicTheory { // MIDI note numbers for C4 scale (Middle C = 60) public static final int C4 = 60; public static final int CS4 = 61; // C#4/Db4 public static final int D4 = 62; public static final int DS4 = 63; // D#4/Eb4 public static final int E4 = 64; public static final int F4 = 65; public static final int FS4 = 66; // F#4/Gb4 public static final int G4 = 67; public static final int GS4 = 68; // G#4/Ab4 public static final int A4 = 69; public static final int AS4 = 70; // A#4/Bb4 public static final int B4 = 71; public static final int C5 = 72; // Common scales public static int[] getMajorScale(int root) { // Major scale pattern: W-W-H-W-W-W-H return new int[]{ root, root + 2, // Whole step root + 4, // Whole step root + 5, // Half step root + 7, // Whole step root + 9, // Whole step root + 11, // Whole step root + 12 // Half step (octave) }; } public static int[] getMinorScale(int root) { // Natural minor scale pattern: W-H-W-W-H-W-W return new int[]{ root, root + 2, // Whole step root + 3, // Half step root + 5, // Whole step root + 7, // Whole step root + 8, // Half step root + 10, // Whole step root + 12 // Whole step (octave) }; } public static int[] getPentatonicMajor(int root) { return new int[]{ root, root + 2, root + 4, root + 7, root + 9 }; } public static int[] getPentatonicMinor(int root) { return new int[]{ root, root + 3, root + 5, root + 7, root + 10 }; } // Common chords public static int[] getMajorChord(int root) { return new int[]{root, root + 4, root + 7}; } public static int[] getMinorChord(int root) { return new int[]{root, root + 3, root + 7}; } public static int[] getDominant7th(int root) { return new int[]{root, root + 4, root + 7, root + 10}; } public static int[] getMajor7th(int root) { return new int[]{root, root + 4, root + 7, root + 11}; } // Rhythm patterns (duration in milliseconds) public static class Rhythm { public static final int WHOLE_NOTE = 2000; public static final int HALF_NOTE = 1000; public static final int QUARTER_NOTE = 500; public static final int EIGHTH_NOTE = 250; public static final int SIXTEENTH_NOTE = 125; public static int[] getRockBeat() { return new int[]{QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE}; } public static int[] getWaltzBeat() { return new int[]{HALF_NOTE, QUARTER_NOTE, QUARTER_NOTE}; } } // Instrument programs (General MIDI) public static class Instruments { public static final int ACOUSTIC_GRAND_PIANO = 0; public static final int BRIGHT_ACOUSTIC_PIANO = 1; public static final int ELECTRIC_GRAND_PIANO = 2; public static final int HONKY_TONK_PIANO = 3; public static final int ELECTRIC_PIANO_1 = 4; public static final int ELECTRIC_PIANO_2 = 5; public static final int HARPSICHORD = 6; public static final int CLAVINET = 7; public static final int CELESTA = 8; public static final int GLOCKENSPIEL = 9; public static final int MUSIC_BOX = 10; public static final int VIBRAPHONE = 11; public static final int MARIMBA = 12; public static final int XYLOPHONE = 13; public static final int TUBULAR_BELLS = 14; public static final int DULCIMER = 15; public static final int ORGAN = 16; public static final int GUITAR = 24; public static final int VIOLIN = 40; public static final int TRUMPET = 56; public static final int SAXOPHONE = 66; public static final int FLUTE = 73; // ... and many more } public static String getNoteName(int midiNote) { String[] noteNames = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; int octave = (midiNote / 12) - 1; int noteIndex = midiNote % 12; return noteNames[noteIndex] + octave; } }

Advanced MIDI Sequence Generation

MIDI Sequence Composer

import javax.sound.midi.*; import java.util.ArrayList; import java.util.List; public class MIDIComposer { private Sequence sequence; private Track track; private int currentTick; private final int DEFAULT_RESOLUTION = 480; // ticks per quarter note private final int DEFAULT_TEMPO = 120; // BPM public MIDIComposer() throws InvalidMidiDataException { this.sequence = new Sequence(Sequence.PPQ, DEFAULT_RESOLUTION); this.track = sequence.createTrack(); this.currentTick = 0; setTempo(DEFAULT_TEMPO); } public MIDIComposer(int resolution) throws InvalidMidiDataException { this.sequence = new Sequence(Sequence.PPQ, resolution); this.track = sequence.createTrack(); this.currentTick = 0; setTempo(DEFAULT_TEMPO); } public void setTempo(int bpm) throws InvalidMidiDataException { int mpq = 60000000 / bpm; // microseconds per quarter note MetaMessage tempoMessage = new MetaMessage(); byte[] data = new byte[3]; data[0] = (byte) ((mpq >> 16) & 0xFF); data[1] = (byte) ((mpq >> 8) & 0xFF); data[2] = (byte) (mpq & 0xFF); tempoMessage.setMessage(0x51, data, data.length); MidiEvent tempoEvent = new MidiEvent(tempoMessage, currentTick); track.add(tempoEvent); } public void addNote(int channel, int note, int velocity, int duration, int tick) throws InvalidMidiDataException { // Note On event ShortMessage noteOn = new ShortMessage(); noteOn.setMessage(ShortMessage.NOTE_ON, channel, note, velocity); MidiEvent onEvent = new MidiEvent(noteOn, tick); track.add(onEvent); // Note Off event ShortMessage noteOff = new ShortMessage(); noteOff.setMessage(ShortMessage.NOTE_OFF, channel, note, velocity); MidiEvent offEvent = new MidiEvent(noteOff, tick + duration); track.add(offEvent); } public void addChord(int channel, int[] notes, int velocity, int duration, int tick) throws InvalidMidiDataException { for (int note : notes) { addNote(channel, note, velocity, duration, tick); } } public void addProgramChange(int channel, int program) throws InvalidMidiDataException { ShortMessage programChange = new ShortMessage(); programChange.setMessage(ShortMessage.PROGRAM_CHANGE, channel, program, 0); MidiEvent programEvent = new MidiEvent(programChange, currentTick); track.add(programEvent); } public void addControlChange(int channel, int controller, int value) throws InvalidMidiDataException { ShortMessage controlChange = new ShortMessage(); controlChange.setMessage(ShortMessage.CONTROL_CHANGE, channel, controller, value); MidiEvent controlEvent = new MidiEvent(controlChange, currentTick); track.add(controlEvent); } public void addRest(int duration) { currentTick += duration; } public void play() throws MidiUnavailableException, InvalidMidiDataException { Sequencer sequencer = MidiSystem.getSequencer(); sequencer.open(); sequencer.setSequence(sequence); sequencer.start(); // Wait for playback to complete while (sequencer.isRunning()) { try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } sequencer.close(); } public void saveToFile(String filename) throws java.io.IOException { int[] supportedTypes = MidiSystem.getMidiFileTypes(sequence); if (supportedTypes.length == 0) { throw new java.io.IOException("No supported MIDI file types"); } java.io.File file = new java.io.File(filename); MidiSystem.write(sequence, supportedTypes[0], file); System.out.println("MIDI file saved: " + filename); } // Composition helper methods public void composeMelody(int channel, int[] notes, int[] durations, int velocity) throws InvalidMidiDataException { for (int i = 0; i < notes.length; i++) { addNote(channel, notes[i], velocity, durations[i], currentTick); currentTick += durations[i]; } } public void composeChordProgression(int channel, int[][] chords, int duration, int velocity) throws InvalidMidiDataException { for (int[] chord : chords) { addChord(channel, chord, velocity, duration, currentTick); currentTick += duration; } } public void addArpeggio(int channel, int[] notes, int pattern, int noteDuration, int velocity) throws InvalidMidiDataException { switch (pattern) { case 1: // Up for (int note : notes) { addNote(channel, note, velocity, noteDuration, currentTick); currentTick += noteDuration; } break; case 2: // Down for (int i = notes.length - 1; i >= 0; i--) { addNote(channel, notes[i], velocity, noteDuration, currentTick); currentTick += noteDuration; } break; case 3: // Up-Down for (int note : notes) { addNote(channel, note, velocity, noteDuration, currentTick); currentTick += noteDuration; } for (int i = notes.length - 2; i > 0; i--) { addNote(channel, notes[i], velocity, noteDuration, currentTick); currentTick += noteDuration; } break; } } }

Algorithmic Music Generation

Markov Chain Music Generator

import java.util.*; import java.util.stream.Collectors; public class MarkovMusicGenerator { private Map<String, List<String>> markovChain; private Random random; public MarkovMusicGenerator() { this.markovChain = new HashMap<>(); this.random = new Random(); } public void train(List<String> sequences) { for (String sequence : sequences) { String[] notes = sequence.split(" "); for (int i = 0; i < notes.length - 1; i++) { String currentNote = notes[i]; String nextNote = notes[i + 1]; markovChain.computeIfAbsent(currentNote, k -> new ArrayList<>()).add(nextNote); } } } public List<String> generateSequence(String startNote, int length) { List<String> sequence = new ArrayList<>(); String currentNote = startNote; sequence.add(currentNote); for (int i = 1; i < length; i++) { List<String> possibleNextNotes = markovChain.get(currentNote); if (possibleNextNotes == null || possibleNextNotes.isEmpty()) { break; } // Randomly select next note based on probabilities String nextNote = possibleNextNotes.get(random.nextInt(possibleNextNotes.size())); sequence.add(nextNote); currentNote = nextNote; } return sequence; } public void trainFromMIDI(List<int[]> noteSequences) { for (int[] sequence : noteSequences) { List<String> stringSequence = Arrays.stream(sequence) .mapToObj(MusicTheory::getNoteName) .collect(Collectors.toList()); train(Collections.singletonList(String.join(" ", stringSequence))); } } public static class GeneticMusicComposer { private static final int POPULATION_SIZE = 50; private static final double MUTATION_RATE = 0.1; private static final int MAX_GENERATIONS = 1000; public static class Melody { private int[] notes; private double fitness; public Melody(int length, Random random) { this.notes = new int[length]; for (int i = 0; i < length; i++) { this.notes[i] = 60 + random.nextInt(13); // C4 to C5 } } public Melody(int[] notes) { this.notes = notes.clone(); } public double calculateFitness() { // Fitness based on musical rules double fitness = 0.0; // Prefer melodies that stay in key fitness += calculateKeyConsistency(); // Prefer smooth transitions fitness += calculateSmoothness(); // Prefer interesting rhythms (variation in note lengths) // fitness += calculateRhythmicInterest(); this.fitness = fitness; return fitness; } private double calculateKeyConsistency() { // Simple: count notes in C major scale int[] cMajor = {0, 2, 4, 5, 7, 9, 11}; // Scale degrees int inKeyCount = 0; for (int note : notes) { int noteInOctave = note % 12; if (Arrays.stream(cMajor).anyMatch(x -> x == noteInOctave)) { inKeyCount++; } } return (double) inKeyCount / notes.length; } private double calculateSmoothness() { double smoothness = 0.0; for (int i = 1; i < notes.length; i++) { int interval = Math.abs(notes[i] - notes[i - 1]); if (interval <= 2) { // Stepwise motion smoothness += 1.0; } else if (interval <= 5) { // Small leaps smoothness += 0.5; } // Large leaps reduce smoothness } return smoothness / (notes.length - 1); } public Melody crossover(Melody other, Random random) { int crossoverPoint = random.nextInt(notes.length); int[] childNotes = new int[notes.length]; System.arraycopy(notes, 0, childNotes, 0, crossoverPoint); System.arraycopy(other.notes, crossoverPoint, childNotes, crossoverPoint, notes.length - crossoverPoint); return new Melody(childNotes); } public void mutate(Random random) { for (int i = 0; i < notes.length; i++) { if (random.nextDouble() < MUTATION_RATE) { // Mutate this note int mutation = random.nextInt(3) - 1; // -1, 0, or 1 notes[i] = Math.max(0, Math.min(127, notes[i] + mutation)); } } } public int[] getNotes() { return notes.clone(); } public double getFitness() { return fitness; } } public static Melody evolveMelody(int length, Random random) { List<Melody> population = new ArrayList<>(); // Initialize population for (int i = 0; i < POPULATION_SIZE; i++) { population.add(new Melody(length, random)); } // Evolution loop for (int generation = 0; generation < MAX_GENERATIONS; generation++) { // Evaluate fitness population.forEach(Melody::calculateFitness); // Sort by fitness population.sort((m1, m2) -> Double.compare(m2.getFitness(), m1.getFitness())); // Check for convergence if (population.get(0).getFitness() > 0.9) { System.out.println("Converged at generation " + generation); break; } // Create new generation List<Melody> newPopulation = new ArrayList<>(); // Elitism: keep best individuals int eliteCount = POPULATION_SIZE / 10; for (int i = 0; i < eliteCount; i++) { newPopulation.add(population.get(i)); } // Crossover and mutation while (newPopulation.size() < POPULATION_SIZE) { Melody parent1 = selectParent(population, random); Melody parent2 = selectParent(population, random); Melody child = parent1.crossover(parent2, random); child.mutate(random); newPopulation.add(child); } population = newPopulation; if (generation % 100 == 0) { System.out.printf("Generation %d: Best fitness = %.3f%n", generation, population.get(0).getFitness()); } } return population.get(0); } private static Melody selectParent(List<Melody> population, Random random) { // Tournament selection int tournamentSize = 5; Melody best = null; for (int i = 0; i < tournamentSize; i++) { Melody candidate = population.get(random.nextInt(population.size())); if (best == null || candidate.getFitness() > best.getFitness()) { best = candidate; } } return best; } } }

Real-Time MIDI Performance

Interactive MIDI Controller

import javax.sound.midi.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.HashMap; import java.util.Map; public class InteractiveMIDIController extends JFrame { private Synthesizer synthesizer; private MidiChannel[] channels; private Map<Integer, Long> activeNotes; private int currentInstrument; private int currentOctave; public InteractiveMIDIController() throws MidiUnavailableException { initializeMIDI(); initializeUI(); this.activeNotes = new HashMap<>(); this.currentInstrument = 0; this.currentOctave = 4; } private void initializeMIDI() throws MidiUnavailableException { synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); channels = synthesizer.getChannels(); channels[0].programChange(currentInstrument); } private void initializeUI() { setTitle("Interactive MIDI Controller"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); // Piano keyboard JPanel pianoPanel = createPianoKeyboard(); add(pianoPanel, BorderLayout.CENTER); // Control panel JPanel controlPanel = createControlPanel(); add(controlPanel, BorderLayout.NORTH); pack(); setLocationRelativeTo(null); } private JPanel createPianoKeyboard() { JPanel pianoPanel = new JPanel(); pianoPanel.setLayout(new GridLayout(1, 12)); String[] whiteKeys = {"C", "D", "E", "F", "G", "A", "B"}; String[] blackKeys = {"C#", "D#", "", "F#", "G#", "A#", ""}; for (int i = 0; i < 7; i++) { // White key JButton whiteKey = new JButton(whiteKeys[i]); whiteKey.setBackground(Color.WHITE); whiteKey.setPreferredSize(new Dimension(60, 200)); whiteKey.addMouseListener(new PianoKeyListener( MusicTheory.C4 + i * 2 - (i >= 3 ? 1 : 0) // Adjust for E-F and B-C )); pianoPanel.add(whiteKey); // Black key (if exists) if (!blackKeys[i].isEmpty()) { JButton blackKey = new JButton(blackKeys[i]); blackKey.setBackground(Color.BLACK); blackKey.setForeground(Color.WHITE); blackKey.setPreferredSize(new Dimension(40, 120)); blackKey.addMouseListener(new PianoKeyListener( MusicTheory.C4 + i * 2 - (i >= 3 ? 1 : 0) + 1 )); // Position black key above white key blackKey.setBounds(0, 0, 40, 120); pianoPanel.add(blackKey); } } // Add high C JButton highC = new JButton("C"); highC.setBackground(Color.WHITE); highC.setPreferredSize(new Dimension(60, 200)); highC.addMouseListener(new PianoKeyListener(MusicTheory.C5)); pianoPanel.add(highC); return pianoPanel; } private JPanel createControlPanel() { JPanel controlPanel = new JPanel(new FlowLayout()); // Instrument selector JComboBox<String> instrumentCombo = new JComboBox<>(new String[]{ "Piano", "Guitar", "Violin", "Trumpet", "Flute", "Drums" }); instrumentCombo.addActionListener(e -> { int[] instrumentPrograms = {0, 24, 40, 56, 73, 0}; currentInstrument = instrumentPrograms[instrumentCombo.getSelectedIndex()]; channels[0].programChange(currentInstrument); }); // Octave selector JSpinner octaveSpinner = new JSpinner(new SpinnerNumberModel(4, 1, 7, 1)); octaveSpinner.addChangeListener(e -> { currentOctave = (Integer) octaveSpinner.getValue(); }); // Volume slider JSlider volumeSlider = new JSlider(0, 127, 64); volumeSlider.addChangeListener(e -> { channels[0].controlChange(7, volumeSlider.getValue()); }); controlPanel.add(new JLabel("Instrument:")); controlPanel.add(instrumentCombo); controlPanel.add(new JLabel("Octave:")); controlPanel.add(octaveSpinner); controlPanel.add(new JLabel("Volume:")); controlPanel.add(volumeSlider); return controlPanel; } private class PianoKeyListener extends MouseAdapter { private final int baseNote; public PianoKeyListener(int baseNote) { this.baseNote = baseNote; } @Override public void mousePressed(MouseEvent e) { int note = baseNote + (currentOctave - 4) * 12; channels[0].noteOn(note, 64); activeNotes.put(note, System.currentTimeMillis()); } @Override public void mouseReleased(MouseEvent e) { int note = baseNote + (currentOctave - 4) * 12; channels[0].noteOff(note); activeNotes.remove(note); } } public void playArpeggio(int[] notes, int pattern, int tempo) { new Thread(() -> { try { switch (pattern) { case 1: // Up for (int note : notes) { channels[0].noteOn(note + currentOctave * 12, 64); Thread.sleep(tempo); channels[0].noteOff(note + currentOctave * 12); } break; case 2: // Down for (int i = notes.length - 1; i >= 0; i--) { channels[0].noteOn(notes[i] + currentOctave * 12, 64); Thread.sleep(tempo); channels[0].noteOff(notes[i] + currentOctave * 12); } break; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); } public static void main(String[] args) { try { SwingUtilities.invokeLater(() -> { try { new InteractiveMIDIController().setVisible(true); } catch (MidiUnavailableException e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } }

Drum Machine and Rhythm Generation

Drum Pattern Sequencer

public class DrumMachine { private static final int DRUM_CHANNEL = 9; // Channel 10 in MIDI (0-based) // General MIDI drum notes public static class DrumNotes { public static final int BASS_DRUM_2 = 35; public static final int BASS_DRUM_1 = 36; public static final int SIDE_STICK = 37; public static final int SNARE_DRUM_1 = 38; public static final int HAND_CLAP = 39; public static final int SNARE_DRUM_2 = 40; public static final int LOW_TOM_2 = 41; public static final int CLOSED_HI_HAT = 42; public static final int LOW_TOM_1 = 43; public static final int PEDAL_HI_HAT = 44; public static final int MID_TOM_2 = 45; public static final int OPEN_HI_HAT = 46; public static final int MID_TOM_1 = 47; public static final int HIGH_TOM_2 = 48; public static final int CRASH_CYMBAL_1 = 49; public static final int HIGH_TOM_1 = 50; public static final int RIDE_CYMBAL_1 = 51; public static final int CHINESE_CYMBAL = 52; public static final int RIDE_BELL = 53; public static final int TAMBOURINE = 54; public static final int SPLASH_CYMBAL = 55; public static final int COWBELL = 56; public static final int CRASH_CYMBAL_2 = 57; public static final int VIBRASLAP = 58; public static final int RIDE_CYMBAL_2 = 59; public static final int HIGH_BONGO = 60; public static final int LOW_BONGO = 61; public static final int MUTE_HIGH_CONGA = 62; public static final int OPEN_HIGH_CONGA = 63; public static final int LOW_CONGA = 64; public static final int HIGH_TIMBALE = 65; public static final int LOW_TIMBALE = 66; public static final int HIGH_AGOGO = 67; public static final int LOW_AGOGO = 68; public static final int CABASA = 69; public static final int MARACAS = 70; public static final int SHORT_WHISTLE = 71; public static final int LONG_WHISTLE = 72; public static final int SHORT_GUIRO = 73; public static final int LONG_GUIRO = 74; public static final int CLAVES = 75; public static final int HIGH_WOOD_BLOCK = 76; public static final int LOW_WOOD_BLOCK = 77; public static final int MUTE_CUICA = 78; public static final int OPEN_CUICA = 79; public static final int MUTE_TRIANGLE = 80; public static final int OPEN_TRIANGLE = 81; } public static class DrumPattern { private boolean[][] pattern; private int steps; private int instruments; public DrumPattern(int steps, int instruments) { this.steps = steps; this.instruments = instruments; this.pattern = new boolean[instruments][steps]; } public void setHit(int instrument, int step) { if (instrument >= 0 && instrument < instruments && step >= 0 && step < steps) { pattern[instrument][step] = true; } } public void clearHit(int instrument, int step) { if (instrument >= 0 && instrument < instruments && step >= 0 && step < steps) { pattern[instrument][step] = false; } } public boolean isHit(int instrument, int step) { return pattern[instrument][step]; } public int getSteps() { return steps; } public int getInstruments() { return instruments; } } public static void playDrumPattern(MIDIComposer composer, DrumPattern pattern, int[] drumNotes, int stepDuration) throws InvalidMidiDataException { for (int step = 0; step < pattern.getSteps(); step++) { for (int instrument = 0; instrument < pattern.getInstruments(); instrument++) { if (pattern.isHit(instrument, step)) { composer.addNote(DRUM_CHANNEL, drumNotes[instrument], 64, stepDuration / 2, step * stepDuration); } } } } // Common drum patterns public static DrumPattern createRockBeat(int steps) { DrumPattern pattern = new DrumPattern(steps, 3); int[] bassDrum = {0, 4, 8, 12}; int[] snareDrum = {4, 12}; int[] hiHat = {0, 2, 4, 6, 8, 10, 12, 14}; for (int step : bassDrum) pattern.setHit(0, step); for (int step : snareDrum) pattern.setHit(1, step); for (int step : hiHat) pattern.setHit(2, step); return pattern; } public static DrumPattern createJazzBeat(int steps) { DrumPattern pattern = new DrumPattern(steps, 4); // Ride cymbal pattern for (int i = 0; i < steps; i += 2) { pattern.setHit(0, i); } // Comping pattern pattern.setHit(1, 4); pattern.setHit(1, 12); pattern.setHit(2, 8); pattern.setHit(3, 0); return pattern; } }

Complete Music Generation Example

Song Composer Demo

public class SongComposerDemo { public static void main(String[] args) { try { // Demo 1: Simple melody demoSimpleMelody(); // Demo 2: Chord progression demoChordProgression(); // Demo 3: Algorithmic composition demoAlgorithmicComposition(); // Demo 4: Full song with drums demoFullSong(); } catch (Exception e) { e.printStackTrace(); } } private static void demoSimpleMelody() throws Exception { System.out.println("=== Simple Melody Demo ==="); MIDIComposer composer = new MIDIComposer(); // C Major scale melody int[] melodyNotes = { MusicTheory.C4, MusicTheory.E4, MusicTheory.G4, MusicTheory.C5, MusicTheory.G4, MusicTheory.E4, MusicTheory.C4 }; int[] durations = { composer.getResolution(), composer.getResolution(), composer.getResolution(), composer.getResolution() * 2, composer.getResolution(), composer.getResolution(), composer.getResolution() * 2 }; composer.composeMelody(0, melodyNotes, durations, 80); composer.saveToFile("simple_melody.mid"); composer.play(); } private static void demoChordProgression() throws Exception { System.out.println("=== Chord Progression Demo ==="); MIDIComposer composer = new MIDIComposer(); composer.setTempo(100); // Common chord progression: I - V - vi - IV int[][] chords = { MusicTheory.getMajorChord(MusicTheory.C4), // C Major MusicTheory.getMajorChord(MusicTheory.G4), // G Major MusicTheory.getMinorChord(MusicTheory.A4), // A Minor MusicTheory.getMajorChord(MusicTheory.F4) // F Major }; composer.composeChordProgression(0, chords, composer.getResolution() * 4, 80); composer.saveToFile("chord_progression.mid"); composer.play(); } private static void demoAlgorithmicComposition() throws Exception { System.out.println("=== Algorithmic Composition Demo ==="); // Generate melody using genetic algorithm MarkovMusicGenerator.GeneticMusicComposer.Melody evolvedMelody = MarkovMusicGenerator.GeneticMusicComposer.evolveMelody(16, new Random()); MIDIComposer composer = new MIDIComposer(); int[] melody = evolvedMelody.getNotes(); int[] durations = new int[melody.length]; Arrays.fill(durations, composer.getResolution()); composer.composeMelody(0, melody, durations, 80); composer.saveToFile("algorithmic_melody.mid"); composer.play(); } private static void demoFullSong() throws Exception { System.out.println("=== Full Song Demo ==="); MIDIComposer composer = new MIDIComposer(); composer.setTempo(120); // Set instruments composer.addProgramChange(0, MusicTheory.Instruments.ACOUSTIC_GRAND_PIANO); // Melody composer.addProgramChange(1, MusicTheory.Instruments.ELECTRIC_BASS_FINGER); // Bass composer.addProgramChange(2, MusicTheory.Instruments.ORCHESTRAL_STRINGS); // Strings // Chord progression int[][] chords = { MusicTheory.getMajorChord(MusicTheory.C4), MusicTheory.getMajorChord(MusicTheory.G4), MusicTheory.getMinorChord(MusicTheory.A4), MusicTheory.getMajorChord(MusicTheory.F4) }; // Melody based on chords int[][] melodyNotes = { {MusicTheory.C5, MusicTheory.E5, MusicTheory.G5}, {MusicTheory.B4, MusicTheory.D5, MusicTheory.G5}, {MusicTheory.C5, MusicTheory.E5, MusicTheory.A5}, {MusicTheory.A4, MusicTheory.C5, MusicTheory.F5} }; // Bass line (root notes) int[] bassNotes = { MusicTheory.C3, MusicTheory.G3, MusicTheory.A3, MusicTheory.F3 }; // Compose 4 bars for (int bar = 0; bar < 4; bar++) { int tick = bar * composer.getResolution() * 4; // Chords (piano) composer.addChord(0, chords[bar % chords.length], 70, composer.getResolution() * 4, tick); // Melody (strings) for (int i = 0; i < 4; i++) { int melodyTick = tick + i * composer.getResolution(); int note = melodyNotes[bar % melodyNotes.length] [i % melodyNotes[bar % melodyNotes.length].length]; composer.addNote(2, note, 80, composer.getResolution(), melodyTick); } // Bass composer.addNote(1, bassNotes[bar % bassNotes.length], 90, composer.getResolution() * 4, tick); } // Add drums DrumMachine.DrumPattern drumPattern = DrumMachine.createRockBeat(16); int[] drumInstruments = { DrumMachine.DrumNotes.BASS_DRUM_1, DrumMachine.DrumNotes.SNARE_DRUM_1, DrumMachine.DrumNotes.CLOSED_HI_HAT }; DrumMachine.playDrumPattern(composer, drumPattern, drumInstruments, composer.getResolution()); composer.saveToFile("full_song.mid"); composer.play(); } }

Key Features Covered

  1. Basic MIDI Synthesis: Note playing, chords, and instrument control
  2. Music Theory Integration: Scales, chords, and rhythm patterns
  3. MIDI File Generation: Creating and saving MIDI sequences
  4. Algorithmic Composition: Markov chains and genetic algorithms
  5. Real-time Performance: Interactive MIDI controllers
  6. Drum Programming: Rhythm patterns and drum machines
  7. Multi-track Composition: Layering melodies, chords, and rhythms

This comprehensive MIDI music generation framework provides everything needed to create algorithmic music, interactive performances, and professional-quality MIDI compositions in Java.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper