Play.java

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
Location : invoke
Killed by : none
removed call to java/io/PrintStream::println → NO_COVERAGE

33

1.1
Location : invoke
Killed by : none
removed call to java/io/PrintStream::println → NO_COVERAGE

34

1.1
Location : invoke
Killed by : none
removed call to com/github/dakusui/symfonion/cli/subcommands/Play::play → NO_COVERAGE

45

1.1
Location : prepareMidiOutDevices
Killed by : none
removed call to com/github/dakusui/symfonion/utils/midi/MidiDeviceScanner::scan → NO_COVERAGE

55

1.1
Location : prepareMidiOutDevices
Killed by : none
replaced return value with Collections.emptyMap for com/github/dakusui/symfonion/cli/subcommands/Play::prepareMidiOutDevices → NO_COVERAGE

64

1.1
Location : lambda$prepareSequencers$0
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/cli/subcommands/Play::lambda$prepareSequencers$0 → NO_COVERAGE

68

1.1
Location : prepareSequencers
Killed by : none
replaced return value with Collections.emptyMap for com/github/dakusui/symfonion/cli/subcommands/Play::prepareSequencers → NO_COVERAGE

73

1.1
Location : prepareSequencer
Killed by : none
removed call to javax/sound/midi/Sequencer::open → NO_COVERAGE

74

1.1
Location : prepareSequencer
Killed by : none
removed call to com/github/dakusui/symfonion/cli/subcommands/Play::connectMidiDeviceToSequencer → NO_COVERAGE

75

1.1
Location : prepareSequencer
Killed by : none
removed call to javax/sound/midi/Sequencer::setSequence → NO_COVERAGE

77

1.1
Location : prepareSequencer
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/cli/subcommands/Play::prepareSequencer → NO_COVERAGE

81

1.1
Location : connectMidiDeviceToSequencer
Killed by : none
negated conditional → NO_COVERAGE

82

1.1
Location : connectMidiDeviceToSequencer
Killed by : none
removed call to javax/sound/midi/MidiDevice::open → NO_COVERAGE

83

1.1
Location : connectMidiDeviceToSequencer
Killed by : none
removed call to com/github/dakusui/symfonion/cli/subcommands/Play::assignDeviceReceiverToSequencer → NO_COVERAGE

89

1.1
Location : assignDeviceReceiverToSequencer
Killed by : none
removed call to javax/sound/midi/Transmitter::setReceiver → NO_COVERAGE

91

1.1
Location : assignDeviceReceiverToSequencer
Killed by : none
removed call to javax/sound/midi/Transmitter::setReceiver → NO_COVERAGE

95

1.1
Location : createMetaEventListener
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/cli/subcommands/Play::createMetaEventListener → NO_COVERAGE

100

1.1
Location : meta
Killed by : none
negated conditional → NO_COVERAGE

103

1.1
Location : meta
Killed by : none
removed call to java/lang/Thread::sleep → NO_COVERAGE

108

1.1
Location : meta
Killed by : none
negated conditional → NO_COVERAGE

109

1.1
Location : meta
Killed by : none
removed call to java/lang/Object::notifyAll → NO_COVERAGE

119

1.1
Location : startSequencers
Killed by : none
removed call to java/io/PrintStream::println → NO_COVERAGE

120

1.1
Location : startSequencers
Killed by : none
removed call to javax/sound/midi/Sequencer::start → NO_COVERAGE

126

1.1
Location : cleanUpSequencers
Killed by : none
removed call to java/util/Collections::reverse → NO_COVERAGE

129

1.1
Location : cleanUpSequencers
Killed by : none
negated conditional → NO_COVERAGE

130

1.1
Location : cleanUpSequencers
Killed by : none
removed call to javax/sound/midi/MidiDevice::close → NO_COVERAGE

133

1.1
Location : cleanUpSequencers
Killed by : none
negated conditional → NO_COVERAGE

134

1.1
Location : cleanUpSequencers
Killed by : none
removed call to javax/sound/midi/Sequencer::close → NO_COVERAGE

145

1.1
Location : play
Killed by : none
removed call to com/github/dakusui/symfonion/cli/subcommands/Play::startSequencers → NO_COVERAGE

146

1.1
Location : play
Killed by : none
removed call to java/lang/Object::wait → NO_COVERAGE

148

1.1
Location : play
Killed by : none
removed call to java/io/PrintStream::println → NO_COVERAGE

149

1.1
Location : play
Killed by : none
removed call to com/github/dakusui/symfonion/cli/subcommands/Play::cleanUpSequencers → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.15.3