mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-14 15:26:52 -04:00
adds a MIDI component; closes #228
This commit is contained in:
commit
ae070dddaf
@ -105,6 +105,13 @@
|
||||
<include>*.dig</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${basedir}/src/main/dig/misc</directory>
|
||||
<outputDirectory>/examples/misc/</outputDirectory>
|
||||
<includes>
|
||||
<include>*.dig</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${basedir}/src/main/dig/hdl</directory>
|
||||
<outputDirectory>/examples/hdl/</outputDirectory>
|
||||
|
@ -7,6 +7,7 @@ HEAD, planned as v0.22
|
||||
- Now its possible to create circuits using lookup tables.
|
||||
- More consistent handling of the initial state in the FSM editor.
|
||||
- Added a rectangle to visually group elements.
|
||||
- Added a MIDI component.
|
||||
- Added Portuguese translation. Special thanks to Theldo Cruz Franqueira who
|
||||
provided the translation.
|
||||
|
||||
|
2666
src/main/dig/misc/BillieJean.dig
Normal file
2666
src/main/dig/misc/BillieJean.dig
Normal file
File diff suppressed because it is too large
Load Diff
244
src/main/dig/misc/MIDIexample.dig
Normal file
244
src/main/dig/misc/MIDIexample.dig
Normal file
@ -0,0 +1,244 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<circuit>
|
||||
<version>1</version>
|
||||
<attributes/>
|
||||
<visualElements>
|
||||
<visualElement>
|
||||
<elementName>In</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<string>Vol</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Bits</string>
|
||||
<int>7</int>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>InDefault</string>
|
||||
<value v="127" z="false"/>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="100" y="200"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Clock</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>runRealTime</string>
|
||||
<boolean>true</boolean>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<string>C</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Frequency</string>
|
||||
<int>15</int>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="100" y="260"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Counter</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>Bits</string>
|
||||
<int>6</int>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="280" y="120"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Ground</elementName>
|
||||
<elementAttributes/>
|
||||
<pos x="260" y="180"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Add</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>Bits</string>
|
||||
<int>7</int>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="480" y="120"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Ground</elementName>
|
||||
<elementAttributes/>
|
||||
<pos x="460" y="180"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Const</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<long>30</long>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Bits</string>
|
||||
<int>7</int>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>intFormat</string>
|
||||
<intFormat>dec</intFormat>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="460" y="140"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Splitter</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>Input Splitting</string>
|
||||
<string>6,1</string>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Output Splitting</string>
|
||||
<string>7</string>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="400" y="120"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Const</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>Value</string>
|
||||
<long>0</long>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="380" y="140"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>MIDI</elementName>
|
||||
<elementAttributes/>
|
||||
<pos x="580" y="180"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>D_FF</elementName>
|
||||
<elementAttributes/>
|
||||
<pos x="140" y="120"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Const</elementName>
|
||||
<elementAttributes/>
|
||||
<pos x="560" y="240"/>
|
||||
</visualElement>
|
||||
</visualElements>
|
||||
<wires>
|
||||
<wire>
|
||||
<p1 x="260" y="160"/>
|
||||
<p2 x="280" y="160"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="460" y="160"/>
|
||||
<p2 x="480" y="160"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="560" y="240"/>
|
||||
<p2 x="580" y="240"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="100" y="260"/>
|
||||
<p2 x="120" y="260"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="120" y="260"/>
|
||||
<p2 x="240" y="260"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="240" y="260"/>
|
||||
<p2 x="580" y="260"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="560" y="180"/>
|
||||
<p2 x="580" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="120" y="100"/>
|
||||
<p2 x="220" y="100"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="100" y="200"/>
|
||||
<p2 x="580" y="200"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="540" y="120"/>
|
||||
<p2 x="560" y="120"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="200" y="120"/>
|
||||
<p2 x="280" y="120"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="420" y="120"/>
|
||||
<p2 x="480" y="120"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="340" y="120"/>
|
||||
<p2 x="400" y="120"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="120" y="120"/>
|
||||
<p2 x="140" y="120"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="120" y="140"/>
|
||||
<p2 x="140" y="140"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="200" y="140"/>
|
||||
<p2 x="220" y="140"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="460" y="140"/>
|
||||
<p2 x="480" y="140"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="380" y="140"/>
|
||||
<p2 x="400" y="140"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="240" y="140"/>
|
||||
<p2 x="280" y="140"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="220" y="220"/>
|
||||
<p2 x="580" y="220"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="560" y="120"/>
|
||||
<p2 x="560" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="240" y="140"/>
|
||||
<p2 x="240" y="260"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="260" y="160"/>
|
||||
<p2 x="260" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="120" y="140"/>
|
||||
<p2 x="120" y="260"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="120" y="100"/>
|
||||
<p2 x="120" y="120"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="220" y="100"/>
|
||||
<p2 x="220" y="140"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="220" y="140"/>
|
||||
<p2 x="220" y="220"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="460" y="160"/>
|
||||
<p2 x="460" y="180"/>
|
||||
</wire>
|
||||
</wires>
|
||||
</circuit>
|
@ -16,7 +16,7 @@ public enum ModelEvent {
|
||||
STARTED,
|
||||
|
||||
/**
|
||||
* The model had stopped.
|
||||
* The model has stopped.
|
||||
*/
|
||||
STOPPED,
|
||||
|
||||
|
@ -735,4 +735,24 @@ public final class Keys {
|
||||
public static final Key<Boolean> RECT_RIGHT
|
||||
= new Key<>("RectRight", false).setSecondary();
|
||||
|
||||
|
||||
/**
|
||||
* Selects the midi channel
|
||||
*/
|
||||
public static final Key.KeyInteger MIDI_CHANNEL =
|
||||
new Key.KeyInteger("midiChannel", 1)
|
||||
.setMin(1)
|
||||
.setMax(16);
|
||||
|
||||
/**
|
||||
* Selects the midi channel
|
||||
*/
|
||||
public static final Key<String> MIDI_INSTRUMENT =
|
||||
new Key<>("midiInstrument", "");
|
||||
|
||||
/**
|
||||
* Enables Program change
|
||||
*/
|
||||
public static final Key<Boolean> MIDI_PROG_CHANGE =
|
||||
new Key<>("midiProgChange", false);
|
||||
}
|
||||
|
116
src/main/java/de/neemann/digital/core/io/MIDI.java
Normal file
116
src/main/java/de/neemann/digital/core/io/MIDI.java
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Helmut Neemann.
|
||||
* Use of this source code is governed by the GPL v3 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package de.neemann.digital.core.io;
|
||||
|
||||
import de.neemann.digital.core.*;
|
||||
import de.neemann.digital.core.element.*;
|
||||
|
||||
import javax.sound.midi.MidiChannel;
|
||||
|
||||
import static de.neemann.digital.core.element.PinInfo.input;
|
||||
|
||||
/**
|
||||
* The speaker.
|
||||
*/
|
||||
public class MIDI extends Node implements Element {
|
||||
|
||||
/**
|
||||
* The Speakers description
|
||||
*/
|
||||
public static final ElementTypeDescription DESCRIPTION
|
||||
= new ElementTypeDescription(MIDI.class) {
|
||||
public PinDescriptions getInputDescription(ElementAttributes elementAttributes) throws NodeException {
|
||||
if (elementAttributes.get(Keys.MIDI_PROG_CHANGE))
|
||||
return new PinDescriptions(input("N"),
|
||||
input("V"),
|
||||
input("OnOff"),
|
||||
input("PC"),
|
||||
input("en"),
|
||||
input("C").setClock()).setLangKey(getPinLangKey());
|
||||
else
|
||||
return new PinDescriptions(input("N"),
|
||||
input("V"),
|
||||
input("OnOff"),
|
||||
input("en"),
|
||||
input("C").setClock()).setLangKey(getPinLangKey());
|
||||
}
|
||||
}
|
||||
.addAttribute(Keys.ROTATE)
|
||||
.addAttribute(Keys.LABEL)
|
||||
.addAttribute(Keys.MIDI_CHANNEL)
|
||||
.addAttribute(Keys.MIDI_INSTRUMENT)
|
||||
.addAttribute(Keys.MIDI_PROG_CHANGE);
|
||||
|
||||
private final int chanNum;
|
||||
private final String instrument;
|
||||
private final boolean progChangeEnable;
|
||||
private ObservableValue note;
|
||||
private ObservableValue volume;
|
||||
private ObservableValue clock;
|
||||
private ObservableValue onOff;
|
||||
private ObservableValue en;
|
||||
private ObservableValue progChange;
|
||||
private MidiChannel channel;
|
||||
private boolean lastCl = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param attributes the elements attributes
|
||||
*/
|
||||
public MIDI(ElementAttributes attributes) {
|
||||
chanNum = attributes.get(Keys.MIDI_CHANNEL) - 1;
|
||||
instrument = attributes.get(Keys.MIDI_INSTRUMENT);
|
||||
progChangeEnable = attributes.get(Keys.MIDI_PROG_CHANGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputs(ObservableValues inputs) throws NodeException {
|
||||
note = inputs.get(0).checkBits(7, this, 0);
|
||||
volume = inputs.get(1).checkBits(7, this, 1);
|
||||
onOff = inputs.get(2).checkBits(1, this, 2);
|
||||
if (progChangeEnable) {
|
||||
progChange = inputs.get(3).checkBits(1, this, 3);
|
||||
en = inputs.get(4).checkBits(1, this, 4);
|
||||
clock = inputs.get(5).checkBits(1, this, 5).addObserverToValue(this);
|
||||
} else {
|
||||
en = inputs.get(3).checkBits(1, this, 3);
|
||||
clock = inputs.get(4).checkBits(1, this, 4).addObserverToValue(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableValues getOutputs() {
|
||||
return ObservableValues.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readInputs() throws NodeException {
|
||||
boolean cl = clock.getBool();
|
||||
if (!lastCl && cl && en.getBool()) {
|
||||
int note = (int) this.note.getValue();
|
||||
if (progChange != null && progChange.getBool()) {
|
||||
channel.programChange(note);
|
||||
} else {
|
||||
if (onOff.getBool()) {
|
||||
int v = (int) volume.getValue();
|
||||
channel.noteOn(note, v);
|
||||
} else
|
||||
channel.noteOff(note);
|
||||
}
|
||||
}
|
||||
lastCl = cl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeOutputs() throws NodeException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Model model) throws NodeException {
|
||||
channel = MIDIHelper.getInstance().getChannel(chanNum, instrument, model);
|
||||
}
|
||||
}
|
347
src/main/java/de/neemann/digital/core/io/MIDIHelper.java
Normal file
347
src/main/java/de/neemann/digital/core/io/MIDIHelper.java
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Helmut Neemann.
|
||||
* Use of this source code is governed by the GPL v3 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sound.midi.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Helper for MIDI functions
|
||||
*/
|
||||
public final class MIDIHelper {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MIDIHelper.class);
|
||||
private static MIDIHelper ourInstance = new MIDIHelper();
|
||||
|
||||
/**
|
||||
* @return the MIDIHelper
|
||||
*/
|
||||
public static MIDIHelper getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
private SynthesizerInterface synthesizer;
|
||||
private boolean isOpen;
|
||||
private TreeMap<String, Instrument> instrumentMap;
|
||||
|
||||
private MIDIHelper() {
|
||||
}
|
||||
|
||||
private SynthesizerInterface getSynthesizer() throws NodeException {
|
||||
if (synthesizer == null) {
|
||||
try {
|
||||
final Synthesizer synth = MidiSystem.getSynthesizer();
|
||||
if (synth == null)
|
||||
throw new NodeException(Lang.get("err_midiSystemNotAvailable"));
|
||||
synthesizer = new RealSynthesizer(synth);
|
||||
} 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
|
||||
*/
|
||||
private void ensureOpen(Model model) throws NodeException {
|
||||
if (!isOpen) {
|
||||
try {
|
||||
getSynthesizer().open();
|
||||
} catch (MidiUnavailableException e) {
|
||||
if (System.getProperty("testdata") == null) {
|
||||
throw new NodeException(Lang.get("err_midiSystemNotAvailable"), e);
|
||||
} else {
|
||||
LOGGER.info("Use fake MIDI interface!");
|
||||
synthesizer = new SynthesizerMock();
|
||||
}
|
||||
}
|
||||
|
||||
isOpen = true;
|
||||
|
||||
model.addObserver(event -> {
|
||||
if (event.equals(ModelEvent.STOPPED))
|
||||
close();
|
||||
}, ModelEvent.STOPPED);
|
||||
}
|
||||
}
|
||||
|
||||
private void close() {
|
||||
if (isOpen) {
|
||||
synthesizer.close();
|
||||
isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the channel to use
|
||||
*
|
||||
* @param num the channel number
|
||||
* @param instrument the instrument to use
|
||||
* @param model the model
|
||||
* @return the channel
|
||||
* @throws NodeException NodeException
|
||||
*/
|
||||
public MidiChannel getChannel(int num, String instrument, Model model) throws NodeException {
|
||||
ensureOpen(model);
|
||||
|
||||
Instrument instr = null;
|
||||
if (!instrument.isEmpty())
|
||||
instr = getInstrument(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<>();
|
||||
instrumentMap.put("", null);
|
||||
for (Instrument i : getSynthesizer().getAvailableInstruments()) {
|
||||
instrumentMap.put(i.getName(), i);
|
||||
}
|
||||
}
|
||||
return instrumentMap;
|
||||
}
|
||||
|
||||
private interface SynthesizerInterface {
|
||||
void open() throws MidiUnavailableException;
|
||||
|
||||
void close();
|
||||
|
||||
Instrument[] getAvailableInstruments();
|
||||
|
||||
MidiChannel[] getChannels();
|
||||
}
|
||||
|
||||
private static final class RealSynthesizer implements SynthesizerInterface {
|
||||
private final Synthesizer synthesizer;
|
||||
|
||||
private RealSynthesizer(Synthesizer synthesizer) {
|
||||
this.synthesizer = synthesizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws MidiUnavailableException {
|
||||
synthesizer.open();
|
||||
|
||||
Soundbank soundbank = synthesizer.getDefaultSoundbank();
|
||||
if (soundbank == null)
|
||||
throw new MidiUnavailableException(Lang.get("err_midiInstrumentsNotAvailable"));
|
||||
|
||||
if (!synthesizer.loadAllInstruments(soundbank))
|
||||
throw new MidiUnavailableException(Lang.get("err_midiInstrumentsNotAvailable"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synthesizer.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instrument[] getAvailableInstruments() {
|
||||
return synthesizer.getAvailableInstruments();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MidiChannel[] getChannels() {
|
||||
return synthesizer.getChannels();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SynthesizerMock implements SynthesizerInterface {
|
||||
@Override
|
||||
public void open() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instrument[] getAvailableInstruments() {
|
||||
return new Instrument[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public MidiChannel[] getChannels() {
|
||||
final MidiChannel dummy = new MidiChannel() {
|
||||
@Override
|
||||
public void noteOn(int i, int i1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noteOff(int i, int i1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noteOff(int i) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPolyPressure(int i, int i1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPolyPressure(int i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChannelPressure(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannelPressure() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controlChange(int i, int i1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getController(int i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programChange(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programChange(int i, int i1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProgram() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPitchBend(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPitchBend() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAllControllers() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allNotesOff() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allSoundOff() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean localControl(boolean b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMono(boolean b) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMono() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOmni(boolean b) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOmni() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMute(boolean b) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMute() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSolo(boolean b) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSolo() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
return new MidiChannel[]{
|
||||
dummy, dummy, dummy, dummy,
|
||||
dummy, dummy, dummy, dummy,
|
||||
dummy, dummy, dummy, dummy,
|
||||
dummy, dummy, dummy, dummy};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -140,7 +140,8 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
|
||||
.add(DummyElement.DATADESCRIPTION)
|
||||
.add(RotEncoder.DESCRIPTION)
|
||||
.add(Keyboard.DESCRIPTION)
|
||||
.add(Terminal.DESCRIPTION)))
|
||||
.add(Terminal.DESCRIPTION)
|
||||
.add(MIDI.DESCRIPTION)))
|
||||
.add(new LibraryNode(Lang.get("lib_wires"))
|
||||
.add(Ground.DESCRIPTION)
|
||||
.add(VDD.DESCRIPTION)
|
||||
|
@ -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.MIDI_INSTRUMENT)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,6 +258,15 @@
|
||||
<string name="elem_Terminal_pin_D">Über diesen Eingang werden die anzuzeigenden Daten an das Terminal weitergegeben.</string>
|
||||
<string name="elem_Terminal_pin_en">Ein High an diesem Eingang aktiviert den Takteingang.</string>
|
||||
|
||||
<string name="elem_MIDI">MIDI</string>
|
||||
<string name="elem_MIDI_tt">Nutzt das MIDI-System, um Noten abzuspielen.</string>
|
||||
<string name="elem_MIDI_pin_N">Note</string>
|
||||
<string name="elem_MIDI_pin_V">Lautstärke</string>
|
||||
<string name="elem_MIDI_pin_OnOff">Wenn gesetzt, enspricht das dem Drücken einer Keyboard-Taste (key down).
|
||||
Wenn nicht gesetzt, entspricht das einem Loslassen der Taste (key up).</string>
|
||||
<string name="elem_MIDI_pin_en">Wenn dieser eingang high ist, kann der Baustein angesprochen werden.</string>
|
||||
<string name="elem_MIDI_pin_PC">Wenn gesetzt, wird mit dem Wert an Eingang N das Programm (Instrument) gewechselt.</string>
|
||||
<string name="elem_MIDI_pin_C">Takteingang</string>
|
||||
|
||||
<!-- Leitungen -->
|
||||
|
||||
@ -993,6 +1002,10 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
|
||||
<string name="err_allMemoriesNeedToHaveTheSameByteWidth">Alle Speicher in welche Daten geladen werden sollen, benötigen die selbe Bitbreite.</string>
|
||||
<string name="err_ProgMemLabelsNotDifferent">Wenn Programme in mehrere RAMs geladen werden sollen, müssen alle RAMs
|
||||
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="err_midiInstrumentsNotAvailable">Die MIDI-Instrumente sind 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>
|
||||
@ -1291,6 +1304,14 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
|
||||
<string name="key_textOrientation_LEFTCENTER">links mitte</string>
|
||||
<string name="key_textOrientation_CENTERCENTER">mitte mitte</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="key_midiProgChange">Programmwechsel erlauben</string>
|
||||
<string name="key_midiProgChange_tt">Fügt einen weiteren Eingang PC hinzu. Wird dieser Eingang auf High gesetzt,
|
||||
wird mit dem Wert am Eingang N das Programm (Instrument) gewechselt.</string>
|
||||
|
||||
<string name="mod_insertWire">Leitung eingefügt.</string>
|
||||
<string name="mod_insertCopied">Aus Zwischenablage eingefügt.</string>
|
||||
<string name="mod_setKey_N0_in_element_N1">Wert ''{0}'' in Element ''{1}'' verändert.</string>
|
||||
|
@ -263,6 +263,15 @@
|
||||
<string name="elem_Terminal_pin_D">The data to write to the terminal</string>
|
||||
<string name="elem_Terminal_pin_en">A high at this input enables the clock input.</string>
|
||||
|
||||
<string name="elem_MIDI">MIDI</string>
|
||||
<string name="elem_MIDI_tt">Uses the MIDI system to play notes.</string>
|
||||
<string name="elem_MIDI_pin_N">Note</string>
|
||||
<string name="elem_MIDI_pin_V">Volume</string>
|
||||
<string name="elem_MIDI_pin_OnOff">If set, this translates to pressing a keyboard key (key down event),
|
||||
if not set, this translates to releasing the key (key up event).</string>
|
||||
<string name="elem_MIDI_pin_en">Enables the component</string>
|
||||
<string name="elem_MIDI_pin_PC">If high, the value at N is used to change the program (instrument).</string>
|
||||
<string name="elem_MIDI_pin_C">Clock</string>
|
||||
|
||||
<!-- Wires -->
|
||||
|
||||
@ -986,6 +995,10 @@
|
||||
<string name="err_allMemoriesNeedToHaveTheSameByteWidth">All memories into which data are to be loaded require the same bit width.</string>
|
||||
<string name="err_ProgMemLabelsNotDifferent">If programs are to be loaded into several RAMs, all RAMs must have
|
||||
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="err_midiInstrumentsNotAvailable">The MIDI instruments are 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>
|
||||
@ -1279,6 +1292,14 @@
|
||||
<string name="key_textOrientation_LEFTCENTER">left center</string>
|
||||
<string name="key_textOrientation_CENTERCENTER">center center</string>
|
||||
|
||||
<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="key_midiProgChange">Allow program change</string>
|
||||
<string name="key_midiProgChange_tt">Adds a new input PC. If this input is set to high,
|
||||
the value at input N is used to change te program (instrument).</string>
|
||||
|
||||
<string name="mod_insertWire">Inserted wire.</string>
|
||||
<string name="mod_insertCopied">Insert from clipboard.</string>
|
||||
<string name="mod_setKey_N0_in_element_N1">Value ''{0}'' in component ''{1}'' modified.</string>
|
||||
|
@ -32,7 +32,7 @@ public class TestExamples extends TestCase {
|
||||
*/
|
||||
public void testDistExamples() throws Exception {
|
||||
File examples = new File(Resources.getRoot().getParentFile().getParentFile(), "/main/dig");
|
||||
assertEquals(248, new FileScanner(this::check).scan(examples));
|
||||
assertEquals(250, new FileScanner(this::check).scan(examples));
|
||||
assertEquals(170, testCasesInFiles);
|
||||
}
|
||||
|
||||
|
@ -579,6 +579,31 @@
|
||||
<elementAttributes/>
|
||||
<pos x="120" y="900"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Const</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>Bits</string>
|
||||
<int>7</int>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="280" y="940"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Const</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>Bits</string>
|
||||
<int>7</int>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="280" y="960"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>MIDI</elementName>
|
||||
<elementAttributes/>
|
||||
<pos x="300" y="940"/>
|
||||
</visualElement>
|
||||
</visualElements>
|
||||
<wires>
|
||||
<wire>
|
||||
@ -625,6 +650,10 @@
|
||||
<p1 x="400" y="960"/>
|
||||
<p2 x="420" y="960"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="280" y="960"/>
|
||||
<p2 x="300" y="960"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="240" y="260"/>
|
||||
<p2 x="260" y="260"/>
|
||||
@ -773,6 +802,10 @@
|
||||
<p1 x="400" y="400"/>
|
||||
<p2 x="440" y="400"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="440" y="400"/>
|
||||
<p2 x="460" y="400"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="460" y="400"/>
|
||||
<p2 x="480" y="400"/>
|
||||
@ -781,10 +814,6 @@
|
||||
<p1 x="480" y="400"/>
|
||||
<p2 x="500" y="400"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="440" y="400"/>
|
||||
<p2 x="460" y="400"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="1080" y="1040"/>
|
||||
<p2 x="1120" y="1040"/>
|
||||
@ -805,6 +834,14 @@
|
||||
<p1 x="240" y="660"/>
|
||||
<p2 x="260" y="660"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="240" y="980"/>
|
||||
<p2 x="280" y="980"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="280" y="980"/>
|
||||
<p2 x="300" y="980"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="380" y="920"/>
|
||||
<p2 x="420" y="920"/>
|
||||
@ -973,6 +1010,10 @@
|
||||
<p1 x="1140" y="360"/>
|
||||
<p2 x="1160" y="360"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="280" y="1000"/>
|
||||
<p2 x="300" y="1000"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="1080" y="940"/>
|
||||
<p2 x="1100" y="940"/>
|
||||
@ -985,6 +1026,10 @@
|
||||
<p1 x="400" y="940"/>
|
||||
<p2 x="420" y="940"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="280" y="940"/>
|
||||
<p2 x="300" y="940"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="1080" y="620"/>
|
||||
<p2 x="1100" y="620"/>
|
||||
@ -1105,6 +1150,14 @@
|
||||
<p1 x="180" y="180"/>
|
||||
<p2 x="240" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="1120" y="180"/>
|
||||
<p2 x="1140" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="580" y="180"/>
|
||||
<p2 x="780" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="400" y="180"/>
|
||||
<p2 x="580" y="180"/>
|
||||
@ -1113,10 +1166,6 @@
|
||||
<p1 x="780" y="180"/>
|
||||
<p2 x="900" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="1120" y="180"/>
|
||||
<p2 x="1140" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="1080" y="180"/>
|
||||
<p2 x="1120" y="180"/>
|
||||
@ -1129,10 +1178,6 @@
|
||||
<p1 x="240" y="180"/>
|
||||
<p2 x="400" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="580" y="180"/>
|
||||
<p2 x="780" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="1080" y="760"/>
|
||||
<p2 x="1100" y="760"/>
|
||||
@ -1193,6 +1238,10 @@
|
||||
<p1 x="940" y="380"/>
|
||||
<p2 x="960" y="380"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="280" y="1020"/>
|
||||
<p2 x="300" y="1020"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="640" y="500"/>
|
||||
<p2 x="640" y="520"/>
|
||||
@ -1461,6 +1510,14 @@
|
||||
<p1 x="920" y="100"/>
|
||||
<p2 x="920" y="620"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="280" y="980"/>
|
||||
<p2 x="280" y="1000"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="280" y="1000"/>
|
||||
<p2 x="280" y="1020"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="220" y="220"/>
|
||||
<p2 x="220" y="300"/>
|
||||
@ -1613,6 +1670,10 @@
|
||||
<p1 x="240" y="340"/>
|
||||
<p2 x="240" y="420"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="240" y="880"/>
|
||||
<p2 x="240" y="980"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="240" y="420"/>
|
||||
<p2 x="240" y="500"/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user