mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-17 08:55:05 -04:00
improved the midi component
This commit is contained in:
parent
1590790a90
commit
b4d30bfe2e
@ -743,4 +743,10 @@ public final class Keys {
|
||||
new Key.KeyInteger("midiChannel", 0)
|
||||
.setMin(0)
|
||||
.setMax(15);
|
||||
|
||||
/**
|
||||
* Selects the midi channel
|
||||
*/
|
||||
public static final Key<String> MIDIINSTRUMENT =
|
||||
new Key<>("midiInstrument", "");
|
||||
}
|
||||
|
@ -10,12 +10,8 @@ import de.neemann.digital.core.element.Element;
|
||||
import de.neemann.digital.core.element.ElementAttributes;
|
||||
import de.neemann.digital.core.element.ElementTypeDescription;
|
||||
import de.neemann.digital.core.element.Keys;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
|
||||
import javax.sound.midi.MidiChannel;
|
||||
import javax.sound.midi.MidiSystem;
|
||||
import javax.sound.midi.MidiUnavailableException;
|
||||
import javax.sound.midi.Synthesizer;
|
||||
|
||||
import static de.neemann.digital.core.element.PinInfo.input;
|
||||
|
||||
@ -33,10 +29,12 @@ public class MIDI extends Node implements Element {
|
||||
input("V"),
|
||||
input("OnOff"),
|
||||
input("C").setClock())
|
||||
.addAttribute(Keys.ROTATE)
|
||||
.addAttribute(Keys.MIDICHANNEL)
|
||||
.addAttribute(Keys.ROTATE);
|
||||
.addAttribute(Keys.MIDIINSTRUMENT);
|
||||
|
||||
private final int chanNum;
|
||||
private final String instrument;
|
||||
private ObservableValue note;
|
||||
private ObservableValue volume;
|
||||
private ObservableValue clock;
|
||||
@ -51,6 +49,7 @@ public class MIDI extends Node implements Element {
|
||||
*/
|
||||
public MIDI(ElementAttributes attributes) {
|
||||
chanNum = attributes.get(Keys.MIDICHANNEL);
|
||||
instrument = attributes.get(Keys.MIDIINSTRUMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,27 +85,7 @@ public class MIDI extends Node implements Element {
|
||||
|
||||
@Override
|
||||
public void init(Model model) throws NodeException {
|
||||
try {
|
||||
Synthesizer synth = MidiSystem.getSynthesizer();
|
||||
synth.open();
|
||||
MidiChannel[] channels = synth.getChannels();
|
||||
if (chanNum >= channels.length) {
|
||||
synth.close();
|
||||
throw new NodeException(Lang.get("err_midiChannel_N_NotAvailable", chanNum));
|
||||
}
|
||||
|
||||
channel = channels[chanNum];
|
||||
if (channel == null) {
|
||||
synth.close();
|
||||
throw new NodeException(Lang.get("err_midiChannel_N_NotAvailable", chanNum));
|
||||
}
|
||||
|
||||
model.addObserver(event -> {
|
||||
if (event.equals(ModelEvent.STOPPED))
|
||||
synth.close();
|
||||
}, ModelEvent.STOPPED);
|
||||
} catch (MidiUnavailableException e) {
|
||||
throw new NodeException(Lang.get("err_midiSystemNotAvailable"), e);
|
||||
}
|
||||
MIDIHelper.getInstance().open(model);
|
||||
channel = MIDIHelper.getInstance().getChannel(chanNum, instrument);
|
||||
}
|
||||
}
|
||||
|
136
src/main/java/de/neemann/digital/core/io/MIDIHelper.java
Normal file
136
src/main/java/de/neemann/digital/core/io/MIDIHelper.java
Normal file
@ -0,0 +1,136 @@
|
||||
package de.neemann.digital.core.io;
|
||||
|
||||
import de.neemann.digital.core.Model;
|
||||
import de.neemann.digital.core.ModelEvent;
|
||||
import de.neemann.digital.core.NodeException;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
|
||||
import javax.sound.midi.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Helper for MIDI functions
|
||||
*/
|
||||
public final class MIDIHelper {
|
||||
private static MIDIHelper ourInstance = new MIDIHelper();
|
||||
|
||||
/**
|
||||
* @return the MIDIHelper
|
||||
*/
|
||||
public static MIDIHelper getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
private Synthesizer synthesizer;
|
||||
private boolean isOpen;
|
||||
private TreeMap<String, Instrument> instrumentMap;
|
||||
|
||||
private MIDIHelper() {
|
||||
}
|
||||
|
||||
private Synthesizer getSynthesizer() throws NodeException {
|
||||
if (synthesizer == null) {
|
||||
try {
|
||||
synthesizer = MidiSystem.getSynthesizer();
|
||||
if (synthesizer == null)
|
||||
throw new NodeException(Lang.get("err_midiSystemNotAvailable"));
|
||||
} catch (MidiUnavailableException e) {
|
||||
throw new NodeException(Lang.get("err_midiSystemNotAvailable"), e);
|
||||
}
|
||||
}
|
||||
return synthesizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the synthesizer
|
||||
*
|
||||
* @param model the mode used. If the model is closed also the synthesizer is closed
|
||||
* @throws NodeException NodeException
|
||||
*/
|
||||
public void open(Model model) throws NodeException {
|
||||
if (!isOpen) {
|
||||
try {
|
||||
getSynthesizer().open();
|
||||
} catch (MidiUnavailableException e) {
|
||||
throw new NodeException(Lang.get("err_midiSystemNotAvailable"), e);
|
||||
}
|
||||
isOpen = true;
|
||||
|
||||
model.addObserver(event -> {
|
||||
if (event.equals(ModelEvent.STOPPED))
|
||||
close();
|
||||
}, ModelEvent.STOPPED);
|
||||
}
|
||||
}
|
||||
|
||||
private void close() {
|
||||
if (!isOpen) {
|
||||
synthesizer.close();
|
||||
synthesizer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the channel to use
|
||||
*
|
||||
* @param num the channel number
|
||||
* @param instrument the instrument to use
|
||||
* @return the channel
|
||||
* @throws NodeException NodeException
|
||||
*/
|
||||
public MidiChannel getChannel(int num, String instrument) throws NodeException {
|
||||
Instrument instr = null;
|
||||
if (!instrument.isEmpty()) {
|
||||
instr = getInstrument(instrument);
|
||||
|
||||
if (!getSynthesizer().loadInstrument(instr))
|
||||
throw new NodeException(Lang.get("err_midiInstrument_N_NotAvailable", instrument));
|
||||
}
|
||||
|
||||
MidiChannel[] channels = getSynthesizer().getChannels();
|
||||
if (num >= channels.length) {
|
||||
close();
|
||||
throw new NodeException(Lang.get("err_midiChannel_N_NotAvailable", num));
|
||||
}
|
||||
|
||||
MidiChannel channel = channels[num];
|
||||
if (channel == null) {
|
||||
close();
|
||||
throw new NodeException(Lang.get("err_midiChannel_N_NotAvailable", num));
|
||||
}
|
||||
|
||||
if (instr != null) {
|
||||
final Patch patch = instr.getPatch();
|
||||
channel.programChange(patch.getBank(), patch.getProgram());
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of available instruments
|
||||
* @throws NodeException NodeException
|
||||
*/
|
||||
public String[] getInstruments() throws NodeException {
|
||||
return new ArrayList<>(getInstumentMap().keySet()).toArray(new String[0]);
|
||||
}
|
||||
|
||||
private Instrument getInstrument(String instrument) throws NodeException {
|
||||
Instrument i = getInstumentMap().get(instrument);
|
||||
if (i == null)
|
||||
throw new NodeException(Lang.get("err_midiInstrument_N_NotAvailable", instrument));
|
||||
return i;
|
||||
}
|
||||
|
||||
private TreeMap<String, Instrument> getInstumentMap() throws NodeException {
|
||||
if (instrumentMap == null) {
|
||||
instrumentMap = new TreeMap<>();
|
||||
for (Instrument i : getSynthesizer().getAvailableInstruments()) {
|
||||
instrumentMap.put(i.getName(), i);
|
||||
}
|
||||
}
|
||||
return instrumentMap;
|
||||
}
|
||||
|
||||
}
|
@ -13,6 +13,7 @@ import de.neemann.digital.core.element.*;
|
||||
import de.neemann.digital.core.extern.Application;
|
||||
import de.neemann.digital.core.extern.PortDefinition;
|
||||
import de.neemann.digital.core.io.InValue;
|
||||
import de.neemann.digital.core.io.MIDIHelper;
|
||||
import de.neemann.digital.core.memory.DataField;
|
||||
import de.neemann.digital.core.memory.ROM;
|
||||
import de.neemann.digital.core.memory.importer.Importer;
|
||||
@ -94,6 +95,9 @@ public final class EditorFactory {
|
||||
* @return the editor
|
||||
*/
|
||||
public <T> Editor<T> create(Key<T> key, T value) {
|
||||
if (key == Keys.MIDIINSTRUMENT)
|
||||
return (Editor<T>) new MidiInstrumentEditor(value.toString());
|
||||
|
||||
Class<? extends Editor> fac = map.get(key.getValueClass());
|
||||
if (fac == null)
|
||||
throw new RuntimeException("no editor found for " + key.getValueClass().getSimpleName());
|
||||
@ -1037,4 +1041,34 @@ public final class EditorFactory {
|
||||
romManager = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class MidiInstrumentEditor extends LabelEditor<String> {
|
||||
private JComboBox<String> comb;
|
||||
|
||||
private MidiInstrumentEditor(String instrument) {
|
||||
String[] instruments;
|
||||
try {
|
||||
instruments = MIDIHelper.getInstance().getInstruments();
|
||||
} catch (NodeException e) {
|
||||
instruments = new String[]{"MIDI not available"};
|
||||
}
|
||||
comb = new JComboBox<>(instruments);
|
||||
comb.setSelectedItem(instrument);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent getComponent(ElementAttributes elementAttributes) {
|
||||
return comb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return (String) comb.getSelectedItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
comb.setSelectedItem(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1002,6 +1002,7 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
|
||||
unterschiedliche Bezeichnungen haben. Die lexikalische Ordnung legt dann die Reihenfolge der RAMs fest.</string>
|
||||
<string name="err_midiSystemNotAvailable">Das MIDI-System ist nicht verfügbar.</string>
|
||||
<string name="err_midiChannel_N_NotAvailable">Der MIDI-Kanal {0} ist nicht verfügbar.</string>
|
||||
<string name="err_midiInstrument_N_NotAvailable">Das MIDI-Instrument {0} ist nicht verfügbar.</string>
|
||||
|
||||
<string name="key_AddrBits">Adress-Bits</string><!-- ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM -->
|
||||
<string name="key_AddrBits_tt">Anzahl der Adress-Bits, die verwendet werden.</string>
|
||||
@ -1302,6 +1303,8 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
|
||||
|
||||
<string name="key_midiChannel">MIDI-Kanal</string>
|
||||
<string name="key_midiChannel_tt">Legt den MIDI-Kanal fest.</string>
|
||||
<string name="key_midiInstrument">MIDI-Instrument</string>
|
||||
<string name="key_midiInstrument_tt">Das MIDI-Instrument, welches verwendet werden soll.</string>
|
||||
|
||||
<string name="mod_insertWire">Leitung eingefügt.</string>
|
||||
<string name="mod_insertCopied">Aus Zwischenablage eingefügt.</string>
|
||||
|
@ -995,6 +995,7 @@
|
||||
different names. The lexical order then determines the order of the RAMs.</string>
|
||||
<string name="err_midiSystemNotAvailable">The MIDI-System is not available.</string>
|
||||
<string name="err_midiChannel_N_NotAvailable">The MIDI channel {0} is not available.</string>
|
||||
<string name="err_midiInstrument_N_NotAvailable">The MIDI instrument {0} is not available.</string>
|
||||
|
||||
<string name="key_AddrBits">Address Bits</string><!-- ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM -->
|
||||
<string name="key_AddrBits_tt">Number of address bits used.</string>
|
||||
@ -1290,6 +1291,8 @@
|
||||
|
||||
<string name="key_midiChannel">MIDI channel</string>
|
||||
<string name="key_midiChannel_tt">Selects the MIDI channel to use.</string>
|
||||
<string name="key_midiInstrument">MIDI instrument</string>
|
||||
<string name="key_midiInstrument_tt">The MIDI instrument to use.</string>
|
||||
|
||||
<string name="mod_insertWire">Inserted wire.</string>
|
||||
<string name="mod_insertCopied">Insert from clipboard.</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user