| 1 | package com.github.dakusui.symfonion.cli.subcommands; | |
| 2 | ||
| 3 | import com.github.dakusui.symfonion.cli.Cli; | |
| 4 | import com.github.dakusui.symfonion.cli.Subcommand; | |
| 5 | import com.github.dakusui.symfonion.core.Symfonion; | |
| 6 | import com.github.dakusui.symfonion.exceptions.SymfonionException; | |
| 7 | import com.github.dakusui.symfonion.song.Song; | |
| 8 | import com.github.dakusui.symfonion.utils.midi.MidiDeviceScanner; | |
| 9 | import com.github.dakusui.symfonion.utils.midi.MidiUtils; | |
| 10 | ||
| 11 | import javax.sound.midi.*; | |
| 12 | import java.io.IOException; | |
| 13 | import java.io.InputStream; | |
| 14 | import java.io.PrintStream; | |
| 15 | import java.util.*; | |
| 16 | import java.util.function.Function; | |
| 17 | import java.util.regex.Pattern; | |
| 18 | ||
| 19 | import static com.github.dakusui.symfonion.exceptions.ExceptionThrower.*; | |
| 20 | import static com.github.dakusui.symfonion.exceptions.ExceptionThrower.ContextKey.SOURCE_FILE; | |
| 21 | ||
| 22 | public class Play implements Subcommand { | |
| 23 | ||
| 24 | @Override | |
| 25 | public void invoke(Cli cli, PrintStream ps, InputStream inputStream) throws IOException { | |
| 26 | try (Context ignored = context($(SOURCE_FILE, cli.source()))) { | |
| 27 | Symfonion symfonion = cli.symfonion(); | |
| 28 | ||
| 29 | Song song = symfonion.load(cli.source().getAbsolutePath()); | |
| 30 | Map<String, Sequence> sequences = symfonion.compile(song); | |
| 31 |
1
1. invoke : removed call to java/io/PrintStream::println → NO_COVERAGE |
ps.println(); |
| 32 | Map<String, MidiDevice> midiOutDevices = prepareMidiOutDevices(ps, cli.midiOutRegexPatterns()); | |
| 33 |
1
1. invoke : removed call to java/io/PrintStream::println → NO_COVERAGE |
ps.println(); |
| 34 |
1
1. invoke : removed call to com/github/dakusui/symfonion/cli/subcommands/Play::play → NO_COVERAGE |
play(midiOutDevices, sequences); |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | public static Map<String, MidiDevice> prepareMidiOutDevices(PrintStream ps, Map<String, Pattern> portDefinitions) { | |
| 39 | Map<String, MidiDevice> devices = new HashMap<>(); | |
| 40 | for (String portName : portDefinitions.keySet()) { | |
| 41 | Pattern regex = portDefinitions.get(portName); | |
| 42 | //// | |
| 43 | // BEGIN: Trying to find an output device whose name matches the given regex | |
| 44 | MidiDeviceScanner scanner = MidiUtils.chooseOutputDevices(ps, regex); | |
| 45 |
1
1. prepareMidiOutDevices : removed call to com/github/dakusui/symfonion/utils/midi/MidiDeviceScanner::scan → NO_COVERAGE |
scanner.scan(); |
| 46 | MidiDevice.Info[] matchedInfos = MidiUtils.getInfos(portName, scanner, regex); | |
| 47 | // END | |
| 48 | //// | |
| 49 | try { | |
| 50 | devices.put(portName, MidiSystem.getMidiDevice(matchedInfos[0])); | |
| 51 | } catch (MidiUnavailableException e) { | |
| 52 | throw failedToAccessMidiDevice("out", e, matchedInfos); | |
| 53 | } | |
| 54 | } | |
| 55 |
1
1. prepareMidiOutDevices : replaced return value with Collections.emptyMap for com/github/dakusui/symfonion/cli/subcommands/Play::prepareMidiOutDevices → NO_COVERAGE |
return devices; |
| 56 | } | |
| 57 | ||
| 58 | private static Map<String, Sequencer> prepareSequencers(List<String> portNames, Map<String, MidiDevice> midiOutDevices, Map<String, Sequence> sequences) throws MidiUnavailableException, InvalidMidiDataException { | |
| 59 | Map<String, Sequencer> ret = new HashMap<>(); | |
| 60 | final List<Sequencer> playingSequencers = new LinkedList<>(); | |
| 61 | for (String portName : portNames) { | |
| 62 | MidiDevice midiOutDevice = midiOutDevices.get(portName); | |
| 63 | Sequence sequence = sequences.get(portName); | |
| 64 |
1
1. lambda$prepareSequencers$0 : replaced return value with null for com/github/dakusui/symfonion/cli/subcommands/Play::lambda$prepareSequencers$0 → NO_COVERAGE |
final Sequencer sequencer = prepareSequencer(midiOutDevice, sequence, seq -> createMetaEventListener(playingSequencers, seq)); |
| 65 | playingSequencers.add(sequencer); | |
| 66 | ret.put(portName, sequencer); | |
| 67 | } | |
| 68 |
1
1. prepareSequencers : replaced return value with Collections.emptyMap for com/github/dakusui/symfonion/cli/subcommands/Play::prepareSequencers → NO_COVERAGE |
return ret; |
| 69 | } | |
| 70 | ||
| 71 | private static Sequencer prepareSequencer(MidiDevice midiOutDevice, Sequence sequence, Function<Sequencer, MetaEventListener> metaEventListenerFactory) throws MidiUnavailableException, InvalidMidiDataException { | |
| 72 | final Sequencer sequencer = MidiSystem.getSequencer(); | |
| 73 |
1
1. prepareSequencer : removed call to javax/sound/midi/Sequencer::open → NO_COVERAGE |
sequencer.open(); |
| 74 |
1
1. prepareSequencer : removed call to com/github/dakusui/symfonion/cli/subcommands/Play::connectMidiDeviceToSequencer → NO_COVERAGE |
connectMidiDeviceToSequencer(midiOutDevice, sequencer); |
| 75 |
1
1. prepareSequencer : removed call to javax/sound/midi/Sequencer::setSequence → NO_COVERAGE |
sequencer.setSequence(sequence); |
| 76 | sequencer.addMetaEventListener(metaEventListenerFactory.apply(sequencer)); | |
| 77 |
1
1. prepareSequencer : replaced return value with null for com/github/dakusui/symfonion/cli/subcommands/Play::prepareSequencer → NO_COVERAGE |
return sequencer; |
| 78 | } | |
| 79 | ||
| 80 | private static void connectMidiDeviceToSequencer(MidiDevice midiOutDevice, Sequencer sequencer) throws MidiUnavailableException { | |
| 81 |
1
1. connectMidiDeviceToSequencer : negated conditional → NO_COVERAGE |
if (midiOutDevice != null) { |
| 82 |
1
1. connectMidiDeviceToSequencer : removed call to javax/sound/midi/MidiDevice::open → NO_COVERAGE |
midiOutDevice.open(); |
| 83 |
1
1. connectMidiDeviceToSequencer : removed call to com/github/dakusui/symfonion/cli/subcommands/Play::assignDeviceReceiverToSequencer → NO_COVERAGE |
assignDeviceReceiverToSequencer(sequencer, midiOutDevice); |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 87 | private static void assignDeviceReceiverToSequencer(Sequencer sequencer, MidiDevice dev) throws MidiUnavailableException { | |
| 88 | for (Transmitter tr : sequencer.getTransmitters()) { | |
| 89 |
1
1. assignDeviceReceiverToSequencer : removed call to javax/sound/midi/Transmitter::setReceiver → NO_COVERAGE |
tr.setReceiver(null); |
| 90 | } | |
| 91 |
1
1. assignDeviceReceiverToSequencer : removed call to javax/sound/midi/Transmitter::setReceiver → NO_COVERAGE |
sequencer.getTransmitter().setReceiver(dev.getReceiver()); |
| 92 | } | |
| 93 | ||
| 94 | private static MetaEventListener createMetaEventListener(List<Sequencer> playingSequencers, Sequencer sequencer) { | |
| 95 |
1
1. createMetaEventListener : replaced return value with null for com/github/dakusui/symfonion/cli/subcommands/Play::createMetaEventListener → NO_COVERAGE |
return new MetaEventListener() { |
| 96 | final Sequencer seq = sequencer; | |
| 97 | ||
| 98 | @Override | |
| 99 | public void meta(MetaMessage meta) { | |
| 100 |
1
1. meta : negated conditional → NO_COVERAGE |
if (meta.getType() == 0x2f) { |
| 101 | synchronized (Play.class) { | |
| 102 | try { | |
| 103 |
1
1. meta : removed call to java/lang/Thread::sleep → NO_COVERAGE |
Thread.sleep(1000); |
| 104 | } catch (InterruptedException e) { | |
| 105 | throw interrupted(e); | |
| 106 | } | |
| 107 | playingSequencers.remove(this.seq); | |
| 108 |
1
1. meta : negated conditional → NO_COVERAGE |
if (playingSequencers.isEmpty()) { |
| 109 |
1
1. meta : removed call to java/lang/Object::notifyAll → NO_COVERAGE |
Play.class.notifyAll(); |
| 110 | } | |
| 111 | } | |
| 112 | } | |
| 113 | } | |
| 114 | }; | |
| 115 | } | |
| 116 | ||
| 117 | private static void startSequencers(List<String> portNames, Map<String, Sequencer> sequencers) { | |
| 118 | for (String portName : portNames) { | |
| 119 |
1
1. startSequencers : removed call to java/io/PrintStream::println → NO_COVERAGE |
System.out.println("Start playing on " + portName + "(" + System.currentTimeMillis() + ")"); |
| 120 |
1
1. startSequencers : removed call to javax/sound/midi/Sequencer::start → NO_COVERAGE |
sequencers.get(portName).start(); |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | private static void cleanUpSequencers(List<String> portNames, Map<String, MidiDevice> midiOutDevices, Map<String, Sequencer> sequencers) { | |
| 125 | List<String> tmp = new LinkedList<>(portNames); | |
| 126 |
1
1. cleanUpSequencers : removed call to java/util/Collections::reverse → NO_COVERAGE |
Collections.reverse(portNames); |
| 127 | for (String portName : tmp) { | |
| 128 | MidiDevice dev = midiOutDevices.get(portName); | |
| 129 |
1
1. cleanUpSequencers : negated conditional → NO_COVERAGE |
if (dev != null) { |
| 130 |
1
1. cleanUpSequencers : removed call to javax/sound/midi/MidiDevice::close → NO_COVERAGE |
dev.close(); |
| 131 | } | |
| 132 | Sequencer sequencer = sequencers.get(portName); | |
| 133 |
1
1. cleanUpSequencers : negated conditional → NO_COVERAGE |
if (sequencer != null) { |
| 134 |
1
1. cleanUpSequencers : removed call to javax/sound/midi/Sequencer::close → NO_COVERAGE |
sequencer.close(); |
| 135 | } | |
| 136 | } | |
| 137 | } | |
| 138 | ||
| 139 | private static synchronized void play(Map<String, MidiDevice> midiOutDevices, Map<String, Sequence> sequences) throws SymfonionException { | |
| 140 | List<String> portNames = new LinkedList<>(sequences.keySet()); | |
| 141 | Map<String, Sequencer> sequencers; | |
| 142 | try { | |
| 143 | sequencers = prepareSequencers(portNames, midiOutDevices, sequences); | |
| 144 | try { | |
| 145 |
1
1. play : removed call to com/github/dakusui/symfonion/cli/subcommands/Play::startSequencers → NO_COVERAGE |
startSequencers(portNames, sequencers); |
| 146 |
1
1. play : removed call to java/lang/Object::wait → NO_COVERAGE |
Play.class.wait(); |
| 147 | } finally { | |
| 148 |
1
1. play : removed call to java/io/PrintStream::println → NO_COVERAGE |
System.out.println("Finished playing."); |
| 149 |
1
1. play : removed call to com/github/dakusui/symfonion/cli/subcommands/Play::cleanUpSequencers → NO_COVERAGE |
cleanUpSequencers(portNames, midiOutDevices, sequencers); |
| 150 | } | |
| 151 | } catch (MidiUnavailableException e) { | |
| 152 | throw deviceException("Midi device was not available.", e); | |
| 153 | } catch (InvalidMidiDataException e) { | |
| 154 | throw deviceException("Data was invalid.", e); | |
| 155 | } catch (InterruptedException e) { | |
| 156 | throw deviceException("Operation was interrupted.", e); | |
| 157 | } | |
| 158 | } | |
| 159 | } | |
Mutations | ||
| 31 |
1.1 |
|
| 33 |
1.1 |
|
| 34 |
1.1 |
|
| 45 |
1.1 |
|
| 55 |
1.1 |
|
| 64 |
1.1 |
|
| 68 |
1.1 |
|
| 73 |
1.1 |
|
| 74 |
1.1 |
|
| 75 |
1.1 |
|
| 77 |
1.1 |
|
| 81 |
1.1 |
|
| 82 |
1.1 |
|
| 83 |
1.1 |
|
| 89 |
1.1 |
|
| 91 |
1.1 |
|
| 95 |
1.1 |
|
| 100 |
1.1 |
|
| 103 |
1.1 |
|
| 108 |
1.1 |
|
| 109 |
1.1 |
|
| 119 |
1.1 |
|
| 120 |
1.1 |
|
| 126 |
1.1 |
|
| 129 |
1.1 |
|
| 130 |
1.1 |
|
| 133 |
1.1 |
|
| 134 |
1.1 |
|
| 145 |
1.1 |
|
| 146 |
1.1 |
|
| 148 |
1.1 |
|
| 149 |
1.1 |