Skip to content

Commit

Permalink
v0.1.2
Browse files Browse the repository at this point in the history
- Some refactoring in package `mpmToolbox.gui.syncPlayer`.
- The SyncPlayer's MIDI output can now be streamed to any other MIDI port available on the host system. Thus, users are no longer limited to the internal Gervill synthesizer and the soundbank loaded into it.
  • Loading branch information
axelberndt committed Apr 13, 2021
1 parent 2b5b9c4 commit a57a761
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 113 deletions.
Binary file modified externals/meico.jar
Binary file not shown.
5 changes: 5 additions & 0 deletions history.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
### Version History


#### v0.1.2
- Some refactoring in package `mpmToolbox.gui.syncPlayer`.
- The SyncPlayer's MIDI output can now be streamed to any other MIDI port available on the host system. Thus, users are no longer limited to the internal Gervill synthesizer and the soundbank loaded into it.


#### v0.1.1
- PDF score import added. The pages of a PDF file are extracted and stored as PNG files with 300dpi (following the [DFG Practical Guidelines on Digitisation](https://www.dfg.de/formulare/12_151/12_151_en.pdf)) in a subfolder of the source directory.
- The window title will now print the title of the currently opened project.
Expand Down
2 changes: 1 addition & 1 deletion src/mpmToolbox/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* @author Axel Berndt
*/
public class Main {
public static final String version = "0.1.1";
public static final String version = "0.1.2";

public static void main(String[] args) {
// read the application settings from file
Expand Down
2 changes: 0 additions & 2 deletions src/mpmToolbox/gui/MpmToolbox.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package mpmToolbox.gui;

import com.alee.extended.image.WebImage;
import com.alee.laf.WebLookAndFeel;
import com.alee.laf.label.WebLabel;
import com.alee.laf.menu.WebMenu;
Expand Down Expand Up @@ -30,7 +29,6 @@
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URI;
Expand Down
39 changes: 39 additions & 0 deletions src/mpmToolbox/gui/syncPlayer/AudioChooserItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package mpmToolbox.gui.syncPlayer;

import com.alee.api.annotations.NotNull;
import meico.audio.Audio;
import meico.supplementary.KeyValue;

/**
* This class represents an item in the audio chooser combobox of the SyncPlayer.
* @author Axel Berndt
*/
class AudioChooserItem extends KeyValue<String, Audio> {
/**
* This constructor creates a audio chooser item (String, Audio) pair out of a non-null audio object.
* @param audio
*/
public AudioChooserItem(@NotNull Audio audio) {
super(audio.getFile().getName(), audio);
}

/**
* This constructor creates a audio chooser item with the specified name key but null audio object.
* Basically, this is used to communicate to the SyncPlayer not to play audio.
* The string is typically something like "No audio recording".
* @param string
*/
public AudioChooserItem(String string) {
super(string, null);
}

/**
* All combobox items require this method. The overwrite here makes sure that the string being returned
* is the audio file's name instead of some Java Object ID.
* @return
*/
@Override
public String toString() {
return this.getKey();
}
}
39 changes: 39 additions & 0 deletions src/mpmToolbox/gui/syncPlayer/PerformanceChooserItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package mpmToolbox.gui.syncPlayer;

import com.alee.api.annotations.NotNull;
import meico.mpm.elements.Performance;
import meico.supplementary.KeyValue;

/**
* This class represents an item in the performance chooser combobox of the SyncPlayer.
* @author Axel Berndt
*/
class PerformanceChooserItem extends KeyValue<String, Performance> {
/**
* This constructor creates a performance chooser item (String, Performance) pair out of a non-null performance.
* @param performance
*/
public PerformanceChooserItem(@NotNull Performance performance) {
super(performance.getName(), performance);
}

/**
* This constructor creates a performance chooser item with the specified name key but null performance.
* Basically, this is used to communicate to the SyncPlayer not to play a performance rendering.
* The string is typically something like "No performance rendering".
* @param string
*/
public PerformanceChooserItem(String string) {
super(string, null);
}

/**
* All combobox items require this method. The overwrite here makes sure that the string being returned
* is the performance's name instead of some Java Object ID.
* @return
*/
@Override
public String toString() {
return this.getKey();
}
}
39 changes: 39 additions & 0 deletions src/mpmToolbox/gui/syncPlayer/SoundfontChooserItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package mpmToolbox.gui.syncPlayer;

import com.alee.api.annotations.NotNull;
import meico.supplementary.KeyValue;

import java.io.File;

/**
* This class represents an item in the soundfont chooser combobox of the SyncPlayer.
* @author Axel Berndt
*/
class SoundfontChooserItem extends KeyValue<String, File> {
/**
* This constructor creates a soundfont chooser item (String, File) pair out of a non-null file.
* @param soundfont
*/
public SoundfontChooserItem(@NotNull File soundfont) {
super(soundfont.getName(), soundfont);
}

/**
* This constructor creates a soundfont chooser item with the specified name key but null soundfont.
* Basically, this is used to communicate to the SyncPlayer to use the default soundfont.
* @param string
*/
public SoundfontChooserItem(String string) {
super(string, null);
}

/**
* All combobox items require this method. The overwrite here makes sure that the string being returned
* is the file name instead of some Java Object ID.
* @return
*/
@Override
public String toString() {
return this.getKey();
}
}
166 changes: 56 additions & 110 deletions src/mpmToolbox/gui/syncPlayer/SyncPlayer.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package mpmToolbox.gui.syncPlayer;

import com.alee.api.annotations.NotNull;
import com.alee.laf.button.WebButton;
import com.alee.laf.combobox.WebComboBox;
import com.alee.laf.label.WebLabel;
Expand All @@ -12,19 +11,18 @@
import meico.midi.Midi;
import meico.midi.MidiPlayer;
import meico.mpm.elements.Performance;
import meico.supplementary.KeyValue;
import mpmToolbox.gui.ProjectPane;
import mpmToolbox.gui.Settings;
import mpmToolbox.supplementary.Tools;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.*;
import javax.sound.sampled.Clip;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;

/**
* This class implements the Audio and MIDI player for MPM Toolbox.
Expand All @@ -45,6 +43,8 @@ public class SyncPlayer extends WebPanel {
private final WebComboBox performanceChooser = new WebComboBox();
private final PerformanceChooserItem rawPerformance = new PerformanceChooserItem(Performance.createPerformance("Play raw notes, no performance"));

private final WebComboBox midiPortChooser = new WebComboBox();

private final WebComboBox audioChooser = new WebComboBox();
private final WebSpinner skipMillisecondsInAudioPlayback = new WebSpinner(new SpinnerNumberModel(0L, 0L, 9999999999L, 1L));

Expand Down Expand Up @@ -90,14 +90,38 @@ private void makeGui() {
this.performanceChooser.setToolTip("Select the performance rendering to be played.");
Tools.addComponentToGridBagLayout(this, (GridBagLayout) this.getLayout(), this.performanceChooser, 0, 0, 1, 1, 1.0, 1.0, 0, 0, GridBagConstraints.BOTH, GridBagConstraints.LINE_START);

WebLabel midiPortLabel = new WebLabel("MIDI Out:");
midiPortLabel.setToolTip("Select the MIDI port to output performance rendering. Default is \"Gervill\".");
midiPortLabel.setHorizontalAlignment(WebLabel.RIGHT);
midiPortLabel.setPadding(Settings.paddingInDialogs / 4);
Tools.addComponentToGridBagLayout(this, (GridBagLayout) this.getLayout(), midiPortLabel, 1, 0, 1, 1, 1.0, 1.0, 0, 0, GridBagConstraints.BOTH, GridBagConstraints.LINE_START);

this.updateMidiPortList();
this.midiPortChooser.setPadding(Settings.paddingInDialogs / 4);
this.midiPortChooser.setToolTip("Select the MIDI port to output performance rendering. Default is \"Gervill\".");
this.midiPortChooser.addActionListener(actionEvent -> {
MidiDevice.Info item = (MidiDevice.Info) this.midiPortChooser.getSelectedItem();// get the device info from the chooser; it is required to get the corresponding device from the MidiSystem
if (item == null) // if nothing meaningful is chosen
return; // done
try {
if (item == this.midiPlayer.getSynthesizer().getDeviceInfo()) // ensure that we do not instantiate a new Gervill synth if that is chosen, but use the one that midiPlayer is already holding
this.midiPlayer.setMidiOutputPort(this.midiPlayer.getSynthesizer()); // Gervill was chosen, hence, use the midiPlayer's native one instead of a new instance
else // something else was chosen
this.midiPlayer.setMidiOutputPort(MidiSystem.getMidiDevice(item)); // switch to it
} catch (MidiUnavailableException e) {
e.printStackTrace();
}
});
Tools.addComponentToGridBagLayout(this, (GridBagLayout) this.getLayout(), this.midiPortChooser, 2, 0, 1, 1, 1.0, 1.0, 0, 0, GridBagConstraints.BOTH, GridBagConstraints.LINE_START);

this.updateAudioList();
this.audioChooser.setPadding(Settings.paddingInDialogs / 4);
this.audioChooser.setToolTip("Select the audio recording to be played.");
Tools.addComponentToGridBagLayout(this, (GridBagLayout) this.getLayout(), this.audioChooser, 0, 1, 1, 1, 1.0, 1.0, 0, 0, GridBagConstraints.BOTH, GridBagConstraints.LINE_START);

WebLabel skipLabel = new WebLabel("skip");
WebLabel skipLabel = new WebLabel("skip:");
skipLabel.setToolTip("Skip initial silence in the audio recording.");
skipLabel.setHorizontalAlignment(WebLabel.CENTER);
skipLabel.setHorizontalAlignment(WebLabel.RIGHT);
skipLabel.setPadding(Settings.paddingInDialogs / 4);
Tools.addComponentToGridBagLayout(this, (GridBagLayout) this.getLayout(), skipLabel, 1, 1, 1, 1, 1.0, 1.0, 0, 0, GridBagConstraints.BOTH, GridBagConstraints.LINE_START);

Expand All @@ -117,8 +141,8 @@ private void makeGui() {
}

/**
* This fills the performance chooser list
* It method should be called when the the available performances have changed.
* This fills the performance chooser list.
* The method should be called when the the available performances have changed.
*/
public void updatePerformanceList() {
PerformanceChooserItem selectedItem = (PerformanceChooserItem) this.performanceChooser.getSelectedItem(); // store the previously selected item
Expand All @@ -137,6 +161,29 @@ public void updatePerformanceList() {
}
}

/**
* This fills the MIDI port chooser list.
* The method should be called to initialize and update the list.
* See: https://humanwritescode.wordpress.com/2017/11/22/java-midi-basics-how-to-access-a-midi-device-or-midi-port-in-java/
*/
public void updateMidiPortList() {
this.midiPortChooser.removeAllItems();

for (MidiDevice.Info info : MidiSystem.getMidiDeviceInfo()) { // iterate the info of each device
// get the corresponding device
MidiDevice device;
try {
device = MidiSystem.getMidiDevice(info);
} catch (MidiUnavailableException e) {
continue;
}

// the device should be a MIDI port with receiver or a synthesizer (Gervill)
if (!(device instanceof Sequencer) && (device.getMaxReceivers() != 0))
this.midiPortChooser.addItem(info);
}
}

/**
* This fills the audio chooser list.
* It should be called when the list of audio recordings changes.
Expand Down Expand Up @@ -390,105 +437,4 @@ public void run() {
});
}
}

/**
* This class represents an item in the performance chooser combobox of the SyncPlayer.
* @author Axel Berndt
*/
private class PerformanceChooserItem extends KeyValue<String, Performance> {
/**
* This constructor creates a performance chooser item (String, Performance) pair out of a non-null performance.
* @param performance
*/
private PerformanceChooserItem(@NotNull Performance performance) {
super(performance.getName(), performance);
}

/**
* This constructor creates a performance chooser item with the specified name key but null performance.
* Basically, this is used to communicate to the SyncPlayer not to play a performance rendering.
* The string is typically something like "No performance rendering".
* @param string
*/
private PerformanceChooserItem(String string) {
super(string, null);
}

/**
* All combobox items require this method. The overwrite here makes sure that the string being returned
* is the performance's name instead of some Java Object ID.
* @return
*/
@Override
public String toString() {
return this.getKey();
}
}

/**
* This class represents an item in the soundfont chooser combobox of the SyncPlayer.
* @author Axel Berndt
*/
private class SoundfontChooserItem extends KeyValue<String, File> {
/**
* This constructor creates a soundfont chooser item (String, File) pair out of a non-null file.
* @param soundfont
*/
private SoundfontChooserItem(@NotNull File soundfont) {
super(soundfont.getName(), soundfont);
}

/**
* This constructor creates a soundfont chooser item with the specified name key but null soundfont.
* Basically, this is used to communicate to the SyncPlayer to use the default soundfont.
* @param string
*/
private SoundfontChooserItem(String string) {
super(string, null);
}

/**
* All combobox items require this method. The overwrite here makes sure that the string being returned
* is the file name instead of some Java Object ID.
* @return
*/
@Override
public String toString() {
return this.getKey();
}
}

/**
* This class represents an item in the audio chooser combobox of the SyncPlayer.
* @author Axel Berndt
*/
private class AudioChooserItem extends KeyValue<String, Audio> {
/**
* This constructor creates a audio chooser item (String, Audio) pair out of a non-null audio object.
* @param audio
*/
private AudioChooserItem(@NotNull Audio audio) {
super(audio.getFile().getName(), audio);
}

/**
* This constructor creates a audio chooser item with the specified name key but null audio object.
* Basically, this is used to communicate to the SyncPlayer not to play audio.
* The string is typically something like "No audio recording".
* @param string
*/
private AudioChooserItem(String string) {
super(string, null);
}

/**
* All combobox items require this method. The overwrite here makes sure that the string being returned
* is the audio file's name instead of some Java Object ID.
* @return
*/
@Override
public String toString() {
return this.getKey();
}
}
}

0 comments on commit a57a761

Please sign in to comment.