adds a MIDI component; closes #228

This commit is contained in:
hneemann 2019-02-15 15:16:31 +01:00
commit ae070dddaf
14 changed files with 3554 additions and 15 deletions

View File

@ -105,6 +105,13 @@
<include>*.dig</include> <include>*.dig</include>
</includes> </includes>
</fileSet> </fileSet>
<fileSet>
<directory>${basedir}/src/main/dig/misc</directory>
<outputDirectory>/examples/misc/</outputDirectory>
<includes>
<include>*.dig</include>
</includes>
</fileSet>
<fileSet> <fileSet>
<directory>${basedir}/src/main/dig/hdl</directory> <directory>${basedir}/src/main/dig/hdl</directory>
<outputDirectory>/examples/hdl/</outputDirectory> <outputDirectory>/examples/hdl/</outputDirectory>

View File

@ -7,6 +7,7 @@ HEAD, planned as v0.22
- Now its possible to create circuits using lookup tables. - Now its possible to create circuits using lookup tables.
- More consistent handling of the initial state in the FSM editor. - More consistent handling of the initial state in the FSM editor.
- Added a rectangle to visually group elements. - Added a rectangle to visually group elements.
- Added a MIDI component.
- Added Portuguese translation. Special thanks to Theldo Cruz Franqueira who - Added Portuguese translation. Special thanks to Theldo Cruz Franqueira who
provided the translation. provided the translation.

File diff suppressed because it is too large Load Diff

View 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>

View File

@ -16,7 +16,7 @@ public enum ModelEvent {
STARTED, STARTED,
/** /**
* The model had stopped. * The model has stopped.
*/ */
STOPPED, STOPPED,

View File

@ -735,4 +735,24 @@ public final class Keys {
public static final Key<Boolean> RECT_RIGHT public static final Key<Boolean> RECT_RIGHT
= new Key<>("RectRight", false).setSecondary(); = 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);
} }

View 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);
}
}

View 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};
}
}
}

View File

@ -140,7 +140,8 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
.add(DummyElement.DATADESCRIPTION) .add(DummyElement.DATADESCRIPTION)
.add(RotEncoder.DESCRIPTION) .add(RotEncoder.DESCRIPTION)
.add(Keyboard.DESCRIPTION) .add(Keyboard.DESCRIPTION)
.add(Terminal.DESCRIPTION))) .add(Terminal.DESCRIPTION)
.add(MIDI.DESCRIPTION)))
.add(new LibraryNode(Lang.get("lib_wires")) .add(new LibraryNode(Lang.get("lib_wires"))
.add(Ground.DESCRIPTION) .add(Ground.DESCRIPTION)
.add(VDD.DESCRIPTION) .add(VDD.DESCRIPTION)

View File

@ -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.MIDI_INSTRUMENT)
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);
}
}
} }

View File

@ -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_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_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 --> <!-- 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_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 <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> 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">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>
@ -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_LEFTCENTER">links mitte</string>
<string name="key_textOrientation_CENTERCENTER">mitte 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_insertWire">Leitung eingefügt.</string>
<string name="mod_insertCopied">Aus Zwischenablage 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> <string name="mod_setKey_N0_in_element_N1">Wert ''{0}'' in Element ''{1}'' verändert.</string>

View File

@ -263,6 +263,15 @@
<string name="elem_Terminal_pin_D">The data to write to the terminal</string> <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_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 --> <!-- 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_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 <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> 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">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>
@ -1279,6 +1292,14 @@
<string name="key_textOrientation_LEFTCENTER">left center</string> <string name="key_textOrientation_LEFTCENTER">left center</string>
<string name="key_textOrientation_CENTERCENTER">center 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_insertWire">Inserted wire.</string>
<string name="mod_insertCopied">Insert from clipboard.</string> <string name="mod_insertCopied">Insert from clipboard.</string>
<string name="mod_setKey_N0_in_element_N1">Value ''{0}'' in component ''{1}'' modified.</string> <string name="mod_setKey_N0_in_element_N1">Value ''{0}'' in component ''{1}'' modified.</string>

View File

@ -32,7 +32,7 @@ public class TestExamples extends TestCase {
*/ */
public void testDistExamples() throws Exception { public void testDistExamples() throws Exception {
File examples = new File(Resources.getRoot().getParentFile().getParentFile(), "/main/dig"); 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); assertEquals(170, testCasesInFiles);
} }

View File

@ -579,6 +579,31 @@
<elementAttributes/> <elementAttributes/>
<pos x="120" y="900"/> <pos x="120" y="900"/>
</visualElement> </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> </visualElements>
<wires> <wires>
<wire> <wire>
@ -625,6 +650,10 @@
<p1 x="400" y="960"/> <p1 x="400" y="960"/>
<p2 x="420" y="960"/> <p2 x="420" y="960"/>
</wire> </wire>
<wire>
<p1 x="280" y="960"/>
<p2 x="300" y="960"/>
</wire>
<wire> <wire>
<p1 x="240" y="260"/> <p1 x="240" y="260"/>
<p2 x="260" y="260"/> <p2 x="260" y="260"/>
@ -773,6 +802,10 @@
<p1 x="400" y="400"/> <p1 x="400" y="400"/>
<p2 x="440" y="400"/> <p2 x="440" y="400"/>
</wire> </wire>
<wire>
<p1 x="440" y="400"/>
<p2 x="460" y="400"/>
</wire>
<wire> <wire>
<p1 x="460" y="400"/> <p1 x="460" y="400"/>
<p2 x="480" y="400"/> <p2 x="480" y="400"/>
@ -781,10 +814,6 @@
<p1 x="480" y="400"/> <p1 x="480" y="400"/>
<p2 x="500" y="400"/> <p2 x="500" y="400"/>
</wire> </wire>
<wire>
<p1 x="440" y="400"/>
<p2 x="460" y="400"/>
</wire>
<wire> <wire>
<p1 x="1080" y="1040"/> <p1 x="1080" y="1040"/>
<p2 x="1120" y="1040"/> <p2 x="1120" y="1040"/>
@ -805,6 +834,14 @@
<p1 x="240" y="660"/> <p1 x="240" y="660"/>
<p2 x="260" y="660"/> <p2 x="260" y="660"/>
</wire> </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> <wire>
<p1 x="380" y="920"/> <p1 x="380" y="920"/>
<p2 x="420" y="920"/> <p2 x="420" y="920"/>
@ -973,6 +1010,10 @@
<p1 x="1140" y="360"/> <p1 x="1140" y="360"/>
<p2 x="1160" y="360"/> <p2 x="1160" y="360"/>
</wire> </wire>
<wire>
<p1 x="280" y="1000"/>
<p2 x="300" y="1000"/>
</wire>
<wire> <wire>
<p1 x="1080" y="940"/> <p1 x="1080" y="940"/>
<p2 x="1100" y="940"/> <p2 x="1100" y="940"/>
@ -985,6 +1026,10 @@
<p1 x="400" y="940"/> <p1 x="400" y="940"/>
<p2 x="420" y="940"/> <p2 x="420" y="940"/>
</wire> </wire>
<wire>
<p1 x="280" y="940"/>
<p2 x="300" y="940"/>
</wire>
<wire> <wire>
<p1 x="1080" y="620"/> <p1 x="1080" y="620"/>
<p2 x="1100" y="620"/> <p2 x="1100" y="620"/>
@ -1105,6 +1150,14 @@
<p1 x="180" y="180"/> <p1 x="180" y="180"/>
<p2 x="240" y="180"/> <p2 x="240" y="180"/>
</wire> </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> <wire>
<p1 x="400" y="180"/> <p1 x="400" y="180"/>
<p2 x="580" y="180"/> <p2 x="580" y="180"/>
@ -1113,10 +1166,6 @@
<p1 x="780" y="180"/> <p1 x="780" y="180"/>
<p2 x="900" y="180"/> <p2 x="900" y="180"/>
</wire> </wire>
<wire>
<p1 x="1120" y="180"/>
<p2 x="1140" y="180"/>
</wire>
<wire> <wire>
<p1 x="1080" y="180"/> <p1 x="1080" y="180"/>
<p2 x="1120" y="180"/> <p2 x="1120" y="180"/>
@ -1129,10 +1178,6 @@
<p1 x="240" y="180"/> <p1 x="240" y="180"/>
<p2 x="400" y="180"/> <p2 x="400" y="180"/>
</wire> </wire>
<wire>
<p1 x="580" y="180"/>
<p2 x="780" y="180"/>
</wire>
<wire> <wire>
<p1 x="1080" y="760"/> <p1 x="1080" y="760"/>
<p2 x="1100" y="760"/> <p2 x="1100" y="760"/>
@ -1193,6 +1238,10 @@
<p1 x="940" y="380"/> <p1 x="940" y="380"/>
<p2 x="960" y="380"/> <p2 x="960" y="380"/>
</wire> </wire>
<wire>
<p1 x="280" y="1020"/>
<p2 x="300" y="1020"/>
</wire>
<wire> <wire>
<p1 x="640" y="500"/> <p1 x="640" y="500"/>
<p2 x="640" y="520"/> <p2 x="640" y="520"/>
@ -1461,6 +1510,14 @@
<p1 x="920" y="100"/> <p1 x="920" y="100"/>
<p2 x="920" y="620"/> <p2 x="920" y="620"/>
</wire> </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> <wire>
<p1 x="220" y="220"/> <p1 x="220" y="220"/>
<p2 x="220" y="300"/> <p2 x="220" y="300"/>
@ -1613,6 +1670,10 @@
<p1 x="240" y="340"/> <p1 x="240" y="340"/>
<p2 x="240" y="420"/> <p2 x="240" y="420"/>
</wire> </wire>
<wire>
<p1 x="240" y="880"/>
<p2 x="240" y="980"/>
</wire>
<wire> <wire>
<p1 x="240" y="420"/> <p1 x="240" y="420"/>
<p2 x="240" y="500"/> <p2 x="240" y="500"/>