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

37

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

38

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

49

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

59

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

68

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

72

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

77

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

78

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

79

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

81

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

85

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

86

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

87

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

93

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

95

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

99

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

104

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

105

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

106

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

109

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

114

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

115

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

125

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

126

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

132

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

135

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

136

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

139

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

140

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

151

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

152

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

154

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

155

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.19.1