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
@ -741,6 +741,12 @@ public final class Keys {
|
|||||||
*/
|
*/
|
||||||
public static final Key.KeyInteger MIDICHANNEL =
|
public static final Key.KeyInteger MIDICHANNEL =
|
||||||
new Key.KeyInteger("midiChannel", 0)
|
new Key.KeyInteger("midiChannel", 0)
|
||||||
.setMin(0)
|
.setMin(0)
|
||||||
.setMax(15);
|
.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.ElementAttributes;
|
||||||
import de.neemann.digital.core.element.ElementTypeDescription;
|
import de.neemann.digital.core.element.ElementTypeDescription;
|
||||||
import de.neemann.digital.core.element.Keys;
|
import de.neemann.digital.core.element.Keys;
|
||||||
import de.neemann.digital.lang.Lang;
|
|
||||||
|
|
||||||
import javax.sound.midi.MidiChannel;
|
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;
|
import static de.neemann.digital.core.element.PinInfo.input;
|
||||||
|
|
||||||
@ -33,10 +29,12 @@ public class MIDI extends Node implements Element {
|
|||||||
input("V"),
|
input("V"),
|
||||||
input("OnOff"),
|
input("OnOff"),
|
||||||
input("C").setClock())
|
input("C").setClock())
|
||||||
|
.addAttribute(Keys.ROTATE)
|
||||||
.addAttribute(Keys.MIDICHANNEL)
|
.addAttribute(Keys.MIDICHANNEL)
|
||||||
.addAttribute(Keys.ROTATE);
|
.addAttribute(Keys.MIDIINSTRUMENT);
|
||||||
|
|
||||||
private final int chanNum;
|
private final int chanNum;
|
||||||
|
private final String instrument;
|
||||||
private ObservableValue note;
|
private ObservableValue note;
|
||||||
private ObservableValue volume;
|
private ObservableValue volume;
|
||||||
private ObservableValue clock;
|
private ObservableValue clock;
|
||||||
@ -51,6 +49,7 @@ public class MIDI extends Node implements Element {
|
|||||||
*/
|
*/
|
||||||
public MIDI(ElementAttributes attributes) {
|
public MIDI(ElementAttributes attributes) {
|
||||||
chanNum = attributes.get(Keys.MIDICHANNEL);
|
chanNum = attributes.get(Keys.MIDICHANNEL);
|
||||||
|
instrument = attributes.get(Keys.MIDIINSTRUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -86,27 +85,7 @@ public class MIDI extends Node implements Element {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Model model) throws NodeException {
|
public void init(Model model) throws NodeException {
|
||||||
try {
|
MIDIHelper.getInstance().open(model);
|
||||||
Synthesizer synth = MidiSystem.getSynthesizer();
|
channel = MIDIHelper.getInstance().getChannel(chanNum, instrument);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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.Application;
|
||||||
import de.neemann.digital.core.extern.PortDefinition;
|
import de.neemann.digital.core.extern.PortDefinition;
|
||||||
import de.neemann.digital.core.io.InValue;
|
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.DataField;
|
||||||
import de.neemann.digital.core.memory.ROM;
|
import de.neemann.digital.core.memory.ROM;
|
||||||
import de.neemann.digital.core.memory.importer.Importer;
|
import de.neemann.digital.core.memory.importer.Importer;
|
||||||
@ -94,6 +95,9 @@ public final class EditorFactory {
|
|||||||
* @return the editor
|
* @return the editor
|
||||||
*/
|
*/
|
||||||
public <T> Editor<T> create(Key<T> key, T value) {
|
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());
|
Class<? extends Editor> fac = map.get(key.getValueClass());
|
||||||
if (fac == null)
|
if (fac == null)
|
||||||
throw new RuntimeException("no editor found for " + key.getValueClass().getSimpleName());
|
throw new RuntimeException("no editor found for " + key.getValueClass().getSimpleName());
|
||||||
@ -1037,4 +1041,34 @@ public final class EditorFactory {
|
|||||||
romManager = value;
|
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>
|
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_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_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">Adress-Bits</string><!-- ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM -->
|
||||||
<string name="key_AddrBits_tt">Anzahl der Adress-Bits, die verwendet werden.</string>
|
<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">MIDI-Kanal</string>
|
||||||
<string name="key_midiChannel_tt">Legt den MIDI-Kanal fest.</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_insertWire">Leitung eingefügt.</string>
|
||||||
<string name="mod_insertCopied">Aus Zwischenablage 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>
|
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_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_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">Address Bits</string><!-- ROM, RAMDualPort, RAMSinglePort, RAMSinglePortSel, EEPROM -->
|
||||||
<string name="key_AddrBits_tt">Number of address bits used.</string>
|
<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">MIDI channel</string>
|
||||||
<string name="key_midiChannel_tt">Selects the MIDI channel to use.</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_insertWire">Inserted wire.</string>
|
||||||
<string name="mod_insertCopied">Insert from clipboard.</string>
|
<string name="mod_insertCopied">Insert from clipboard.</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user