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