| 1 | package com.github.dakusui.symfonion.core; | |
| 2 | ||
| 3 | import com.github.dakusui.logias.Logias; | |
| 4 | import com.github.dakusui.logias.lisp.Context; | |
| 5 | import com.github.dakusui.logias.lisp.s.Literal; | |
| 6 | import com.github.dakusui.logias.lisp.s.Sexp; | |
| 7 | import com.github.dakusui.symfonion.exceptions.ExceptionThrower; | |
| 8 | import com.github.dakusui.symfonion.utils.Fraction; | |
| 9 | import com.github.dakusui.symfonion.exceptions.SymfonionException; | |
| 10 | import com.github.dakusui.symfonion.utils.Utils; | |
| 11 | import com.github.dakusui.symfonion.song.*; | |
| 12 | import com.github.dakusui.symfonion.song.Pattern.Parameters; | |
| 13 | import com.google.gson.JsonArray; | |
| 14 | ||
| 15 | import javax.sound.midi.*; | |
| 16 | import java.io.ByteArrayOutputStream; | |
| 17 | import java.io.IOException; | |
| 18 | import java.util.HashMap; | |
| 19 | import java.util.Iterator; | |
| 20 | import java.util.List; | |
| 21 | import java.util.Map; | |
| 22 | ||
| 23 | import static com.github.dakusui.symfonion.exceptions.ExceptionThrower.*; | |
| 24 | import static com.github.dakusui.symfonion.exceptions.ExceptionThrower.ContextKey.JSON_ELEMENT_ROOT; | |
| 25 | ||
| 26 | public class MidiCompiler { | |
| 27 | ||
| 28 | private final Context logiasContext; | |
| 29 | ||
| 30 | public MidiCompiler(Context logiasContext) { | |
| 31 | this.logiasContext = (logiasContext); | |
| 32 | } | |
| 33 | ||
| 34 | /** | |
| 35 | * Compiles a {@link Song} object into a map from a port name to {@link Sequence} object. | |
| 36 | * | |
| 37 | * @param song An object that holds user-provided music data. | |
| 38 | * @return A map from a port name to {@code Sequence} object. | |
| 39 | * @throws InvalidMidiDataException Won't be thrown. | |
| 40 | * @throws SymfonionException Undefined part name is referenced by a bar. | |
| 41 | */ | |
| 42 | public Map<String, Sequence> compile(Song song) throws InvalidMidiDataException, SymfonionException { | |
| 43 |
1
1. compile : removed call to java/io/PrintStream::println → SURVIVED |
System.err.println("Now compiling..."); |
| 44 | int resolution = 384; | |
| 45 | Map<String, Sequence> ret = new HashMap<>(); | |
| 46 | Map<String, Track> tracks; | |
| 47 | tracks = new HashMap<>(); | |
| 48 | for (String partName : song.partNames()) { | |
| 49 | Part part = song.part(partName); | |
| 50 | String portName = part.portName(); | |
| 51 | Sequence sequence = ret.get(portName); | |
| 52 |
1
1. compile : negated conditional → KILLED |
if (sequence == null) { |
| 53 |
1
1. compile : Replaced integer division with multiplication → SURVIVED |
sequence = new Sequence(Sequence.PPQ, resolution / 4); |
| 54 | ret.put(portName, sequence); | |
| 55 | } | |
| 56 | tracks.put(partName, sequence.createTrack()); | |
| 57 | } | |
| 58 | ||
| 59 | //// | |
| 60 | // position is the offset of a bar from the beginning of the sequence. | |
| 61 | // Giving the sequencer a grace period to initialize its internal state. | |
| 62 | long barPositionInTicks = 0; //= resolution / 4; | |
| 63 | int barid = 0; | |
| 64 | for (Bar bar : song.bars()) { | |
| 65 | try (var ignored = context($(JSON_ELEMENT_ROOT, bar.rootJsonObject()))) { | |
| 66 |
1
1. compile : removed call to com/github/dakusui/symfonion/core/MidiCompiler::barStarted → SURVIVED |
barStarted(barid); |
| 67 | Groove groove = bar.groove(); | |
| 68 | for (String partName : bar.partNames()) { | |
| 69 |
1
1. compile : removed call to com/github/dakusui/symfonion/core/MidiCompiler::partStarted → SURVIVED |
partStarted(partName); |
| 70 | Track track = tracks.get(partName); | |
| 71 |
1
1. compile : negated conditional → KILLED |
if (track == null) { |
| 72 |
1
1. compile : removed call to com/github/dakusui/symfonion/core/MidiCompiler::aborted → SURVIVED |
aborted(); |
| 73 | throw partNotFound(bar.lookUpJsonNode(partName), partName); | |
| 74 | } | |
| 75 | int channel = song.part(partName).channel(); | |
| 76 | for (List<Pattern> patterns : bar.part(partName)) { | |
| 77 | //// | |
| 78 | // relativePosition is a relative position from the beginning | |
| 79 | // of the bar the pattern belongs to. | |
| 80 | Fraction relPosInBar = Fraction.zero; | |
| 81 | for (Pattern each : patterns) { | |
| 82 | Parameters params = each.parameters(); | |
| 83 |
1
1. compile : removed call to com/github/dakusui/symfonion/core/MidiCompiler::patternStarted → SURVIVED |
patternStarted(); |
| 84 | for (Stroke stroke : each.strokes()) { | |
| 85 | try { | |
| 86 | Fraction endingPos = Fraction.add(relPosInBar, stroke.length()); | |
| 87 | ||
| 88 |
1
1. compile : removed call to com/github/dakusui/symfonion/song/Stroke::compile → KILLED |
stroke.compile( |
| 89 | this, | |
| 90 | new MidiCompilerContext( | |
| 91 | track, | |
| 92 | channel, | |
| 93 | params, | |
| 94 | relPosInBar, | |
| 95 | barPositionInTicks, | |
| 96 | groove | |
| 97 | ) | |
| 98 | ); | |
| 99 | ||
| 100 | relPosInBar = endingPos; | |
| 101 | //// | |
| 102 | // Breaks if relative position goes over the length of the bar. | |
| 103 |
2
1. compile : changed conditional boundary → SURVIVED 2. compile : negated conditional → KILLED |
if (Fraction.compare(relPosInBar, bar.beats()) >= 0) { |
| 104 | break; | |
| 105 | } | |
| 106 | } finally { | |
| 107 |
1
1. compile : removed call to com/github/dakusui/symfonion/core/MidiCompiler::strokeEnded → SURVIVED |
strokeEnded(); |
| 108 | } | |
| 109 | } | |
| 110 |
1
1. compile : removed call to com/github/dakusui/symfonion/core/MidiCompiler::patternEnded → SURVIVED |
patternEnded(); |
| 111 | } | |
| 112 | } | |
| 113 |
1
1. compile : removed call to com/github/dakusui/symfonion/core/MidiCompiler::partEnded → SURVIVED |
partEnded(); |
| 114 | } | |
| 115 |
1
1. compile : removed call to com/github/dakusui/symfonion/core/MidiCompiler::barEnded → SURVIVED |
barEnded(); |
| 116 |
1
1. compile : Changed increment from 1 to -1 → SURVIVED |
barid++; |
| 117 |
2
1. compile : Replaced double multiplication with division → SURVIVED 2. compile : Replaced long addition with subtraction → SURVIVED |
barPositionInTicks += (long) (bar.beats().doubleValue() * resolution); |
| 118 | } | |
| 119 | } | |
| 120 |
1
1. compile : removed call to java/io/PrintStream::println → SURVIVED |
System.err.println("Compilation finished."); |
| 121 |
1
1. compile : replaced return value with Collections.emptyMap for com/github/dakusui/symfonion/core/MidiCompiler::compile → KILLED |
return ret; |
| 122 | } | |
| 123 | ||
| 124 | public MidiEvent createNoteOnEvent(int ch, int nKey, int velocity, long lTick) throws InvalidMidiDataException { | |
| 125 |
1
1. createNoteOnEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createNoteOnEvent → KILLED |
return createNoteEvent(ShortMessage.NOTE_ON, |
| 126 | ch, | |
| 127 | nKey, | |
| 128 | velocity, | |
| 129 | lTick); | |
| 130 | } | |
| 131 | ||
| 132 | public MidiEvent createNoteOffEvent(int ch, int nKey, long lTick) throws InvalidMidiDataException { | |
| 133 |
1
1. createNoteOffEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createNoteOffEvent → KILLED |
return createNoteEvent(ShortMessage.NOTE_OFF, |
| 134 | ch, | |
| 135 | nKey, | |
| 136 | 0, | |
| 137 | lTick); | |
| 138 | } | |
| 139 | ||
| 140 | protected MidiEvent createNoteEvent(int nCommand, | |
| 141 | int ch, | |
| 142 | int nKey, | |
| 143 | int nVelocity, | |
| 144 | long lTick) throws InvalidMidiDataException { | |
| 145 | ShortMessage message = new ShortMessage(); | |
| 146 |
1
1. createNoteEvent : removed call to javax/sound/midi/ShortMessage::setMessage → KILLED |
message.setMessage(nCommand, |
| 147 | ch, | |
| 148 | nKey, | |
| 149 | nVelocity); | |
| 150 |
1
1. createNoteEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createNoteEvent → KILLED |
return new MidiEvent(message, |
| 151 | lTick); | |
| 152 | } | |
| 153 | ||
| 154 | public MidiEvent createProgramChangeEvent(int ch, int pgnum, long lTick) throws InvalidMidiDataException { | |
| 155 | ShortMessage message = new ShortMessage(); | |
| 156 |
1
1. createProgramChangeEvent : removed call to javax/sound/midi/ShortMessage::setMessage → KILLED |
message.setMessage(ShortMessage.PROGRAM_CHANGE, ch, pgnum, 0); |
| 157 | ||
| 158 |
1
1. createProgramChangeEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createProgramChangeEvent → KILLED |
return new MidiEvent(message, lTick); |
| 159 | } | |
| 160 | ||
| 161 | public MidiEvent createSysexEvent(int ch, JsonArray arr, long lTick) throws InvalidMidiDataException { | |
| 162 | SysexMessage message = new SysexMessage(); | |
| 163 | Context lctxt = this.logiasContext.createChild(); | |
| 164 | Sexp channel = new Literal(ch); | |
| 165 | lctxt.bind("channel", channel); | |
| 166 | Logias logias = new Logias(lctxt); | |
| 167 | Sexp sysexsexp = logias.buildSexp(arr); | |
| 168 | Sexp sexp = logias.run(sysexsexp); | |
| 169 |
1
1. createSysexEvent : negated conditional → NO_COVERAGE |
if (Sexp.nil.equals(sexp)) { |
| 170 | return null; | |
| 171 | } | |
| 172 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
| 173 |
1
1. createSysexEvent : removed call to java/io/ByteArrayOutputStream::write → NO_COVERAGE |
baos.write(SysexMessage.SYSTEM_EXCLUSIVE); // status: SysEx start |
| 174 | Iterator<Sexp> i = sexp.iterator().assumeList(); | |
| 175 |
1
1. createSysexEvent : negated conditional → NO_COVERAGE |
while (i.hasNext()) { |
| 176 | Sexp cur = i.next(); | |
| 177 |
1
1. createSysexEvent : removed call to java/io/ByteArrayOutputStream::write → NO_COVERAGE |
baos.write((byte) cur.asAtom().longValue()); |
| 178 | } | |
| 179 |
1
1. createSysexEvent : removed call to java/io/ByteArrayOutputStream::write → NO_COVERAGE |
baos.write(SysexMessage.SPECIAL_SYSTEM_EXCLUSIVE); // End of exclusive |
| 180 | try { | |
| 181 |
1
1. createSysexEvent : removed call to java/io/ByteArrayOutputStream::close → NO_COVERAGE |
baos.close(); |
| 182 | } catch (IOException e) { | |
| 183 | throw ExceptionThrower.runtimeException(e.getMessage(), e); | |
| 184 | } | |
| 185 | byte[] data = baos.toByteArray(); | |
| 186 |
1
1. createSysexEvent : removed call to javax/sound/midi/SysexMessage::setMessage → NO_COVERAGE |
message.setMessage(data, data.length); |
| 187 |
1
1. createSysexEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createSysexEvent → NO_COVERAGE |
return new MidiEvent(message, lTick); |
| 188 | } | |
| 189 | ||
| 190 | public MidiEvent createControlChangeEvent(int ch, int controllernum, int param, long lTick) throws InvalidMidiDataException { | |
| 191 | ShortMessage message = new ShortMessage(); | |
| 192 |
1
1. createControlChangeEvent : removed call to javax/sound/midi/ShortMessage::setMessage → KILLED |
message.setMessage(ShortMessage.CONTROL_CHANGE, ch, controllernum, param); |
| 193 |
1
1. createControlChangeEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createControlChangeEvent → KILLED |
return new MidiEvent(message, lTick); |
| 194 | } | |
| 195 | ||
| 196 | public MidiEvent createBankSelectMSBEvent(int ch, int bkmsb, long lTick) throws InvalidMidiDataException { | |
| 197 |
1
1. createBankSelectMSBEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createBankSelectMSBEvent → KILLED |
return createControlChangeEvent(ch, 0, bkmsb, lTick); |
| 198 | } | |
| 199 | ||
| 200 | public MidiEvent createBankSelectLSBEvent(int ch, int bklsb, long lTick) throws InvalidMidiDataException { | |
| 201 |
1
1. createBankSelectLSBEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createBankSelectLSBEvent → KILLED |
return createControlChangeEvent(ch, 32, bklsb, lTick); |
| 202 | } | |
| 203 | ||
| 204 | public MidiEvent createVolumeChangeEvent(int ch, int volume, long lTick) throws InvalidMidiDataException { | |
| 205 |
1
1. createVolumeChangeEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createVolumeChangeEvent → KILLED |
return createControlChangeEvent(ch, 7, volume, lTick); |
| 206 | } | |
| 207 | ||
| 208 | public MidiEvent createPanChangeEvent(int ch, int pan, long lTick) throws InvalidMidiDataException { | |
| 209 |
1
1. createPanChangeEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createPanChangeEvent → SURVIVED |
return createControlChangeEvent(ch, 10, pan, lTick); |
| 210 | } | |
| 211 | ||
| 212 | public MidiEvent createReverbEvent(int ch, int depth, long lTick) throws InvalidMidiDataException { | |
| 213 |
1
1. createReverbEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createReverbEvent → SURVIVED |
return createControlChangeEvent(ch, 91, depth, lTick); |
| 214 | } | |
| 215 | ||
| 216 | public MidiEvent createChorusEvent(int ch, int depth, long lTick) throws InvalidMidiDataException { | |
| 217 |
1
1. createChorusEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createChorusEvent → SURVIVED |
return createControlChangeEvent(ch, 93, depth, lTick); |
| 218 | } | |
| 219 | ||
| 220 | public MidiEvent createPitchBendEvent(int ch, int depth, long lTick) throws InvalidMidiDataException { | |
| 221 | ShortMessage message = new ShortMessage(); | |
| 222 |
1
1. createPitchBendEvent : removed call to javax/sound/midi/ShortMessage::setMessage → SURVIVED |
message.setMessage(ShortMessage.PITCH_BEND, ch, 0, depth); |
| 223 |
1
1. createPitchBendEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createPitchBendEvent → SURVIVED |
return new MidiEvent(message, lTick); |
| 224 | } | |
| 225 | ||
| 226 | public MidiEvent createModulationEvent(int ch, int depth, long lTick) throws InvalidMidiDataException { | |
| 227 |
1
1. createModulationEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createModulationEvent → SURVIVED |
return createControlChangeEvent(ch, 1, depth, lTick); |
| 228 | } | |
| 229 | ||
| 230 | public MidiEvent createAfterTouchChangeEvent(int ch, int v, long lTick) throws InvalidMidiDataException { | |
| 231 | ShortMessage message = new ShortMessage(); | |
| 232 |
1
1. createAfterTouchChangeEvent : removed call to javax/sound/midi/ShortMessage::setMessage → NO_COVERAGE |
message.setMessage(ShortMessage.CHANNEL_PRESSURE, ch, v, 0); |
| 233 |
1
1. createAfterTouchChangeEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createAfterTouchChangeEvent → NO_COVERAGE |
return new MidiEvent(message, lTick); |
| 234 | } | |
| 235 | ||
| 236 | public MidiEvent createTempoEvent(int tempo, long lTick) throws InvalidMidiDataException { | |
| 237 |
1
1. createTempoEvent : Replaced integer division with multiplication → NO_COVERAGE |
int mpqn = 60000000 / tempo; |
| 238 | MetaMessage mm = new MetaMessage(); | |
| 239 | byte[] data = Utils.getIntBytes(mpqn); | |
| 240 |
1
1. createTempoEvent : removed call to javax/sound/midi/MetaMessage::setMessage → NO_COVERAGE |
mm.setMessage(0x51, data, data.length); |
| 241 |
1
1. createTempoEvent : replaced return value with null for com/github/dakusui/symfonion/core/MidiCompiler::createTempoEvent → NO_COVERAGE |
return new MidiEvent(mm, lTick); |
| 242 | } | |
| 243 | ||
| 244 | public void noteProcessed() { | |
| 245 |
1
1. noteProcessed : removed call to java/io/PrintStream::print → SURVIVED |
System.out.print("."); |
| 246 | } | |
| 247 | ||
| 248 | public void controlEventProcessed() { | |
| 249 |
1
1. controlEventProcessed : removed call to java/io/PrintStream::print → SURVIVED |
System.out.print("*"); |
| 250 | } | |
| 251 | ||
| 252 | public void sysexEventProcessed() { | |
| 253 |
1
1. sysexEventProcessed : removed call to java/io/PrintStream::print → NO_COVERAGE |
System.out.print("X"); |
| 254 | } | |
| 255 | ||
| 256 | public void barStarted(int barid) { | |
| 257 |
1
1. barStarted : removed call to java/io/PrintStream::println → SURVIVED |
System.out.println("bar[" + barid + "]"); |
| 258 | } | |
| 259 | ||
| 260 | public void patternStarted() { | |
| 261 |
1
1. patternStarted : removed call to java/io/PrintStream::print → SURVIVED |
System.out.print("["); |
| 262 | } | |
| 263 | ||
| 264 | public void patternEnded() { | |
| 265 |
1
1. patternEnded : removed call to java/io/PrintStream::print → SURVIVED |
System.out.print("]"); |
| 266 | } | |
| 267 | ||
| 268 | public void barEnded() { | |
| 269 | } | |
| 270 | ||
| 271 | public void partStarted(String partName) { | |
| 272 |
1
1. partStarted : removed call to java/io/PrintStream::print → SURVIVED |
System.out.print(" " + partName + ":"); |
| 273 | } | |
| 274 | ||
| 275 | public void strokeEnded() { | |
| 276 |
1
1. strokeEnded : removed call to java/io/PrintStream::print → SURVIVED |
System.out.print("|"); |
| 277 | } | |
| 278 | ||
| 279 | public void partEnded() { | |
| 280 |
1
1. partEnded : removed call to java/io/PrintStream::println → SURVIVED |
System.out.println(); |
| 281 | } | |
| 282 | ||
| 283 | public void aborted() { | |
| 284 |
1
1. aborted : removed call to java/io/PrintStream::println → SURVIVED |
System.out.println("aborted."); |
| 285 | } | |
| 286 | ||
| 287 | public void noteSetProcessed() { | |
| 288 |
1
1. noteSetProcessed : removed call to java/io/PrintStream::print → SURVIVED |
System.out.print(";"); |
| 289 | } | |
| 290 | } | |
Mutations | ||
| 43 |
1.1 |
|
| 52 |
1.1 |
|
| 53 |
1.1 |
|
| 66 |
1.1 |
|
| 69 |
1.1 |
|
| 71 |
1.1 |
|
| 72 |
1.1 |
|
| 83 |
1.1 |
|
| 88 |
1.1 |
|
| 103 |
1.1 2.2 |
|
| 107 |
1.1 |
|
| 110 |
1.1 |
|
| 113 |
1.1 |
|
| 115 |
1.1 |
|
| 116 |
1.1 |
|
| 117 |
1.1 2.2 |
|
| 120 |
1.1 |
|
| 121 |
1.1 |
|
| 125 |
1.1 |
|
| 133 |
1.1 |
|
| 146 |
1.1 |
|
| 150 |
1.1 |
|
| 156 |
1.1 |
|
| 158 |
1.1 |
|
| 169 |
1.1 |
|
| 173 |
1.1 |
|
| 175 |
1.1 |
|
| 177 |
1.1 |
|
| 179 |
1.1 |
|
| 181 |
1.1 |
|
| 186 |
1.1 |
|
| 187 |
1.1 |
|
| 192 |
1.1 |
|
| 193 |
1.1 |
|
| 197 |
1.1 |
|
| 201 |
1.1 |
|
| 205 |
1.1 |
|
| 209 |
1.1 |
|
| 213 |
1.1 |
|
| 217 |
1.1 |
|
| 222 |
1.1 |
|
| 223 |
1.1 |
|
| 227 |
1.1 |
|
| 232 |
1.1 |
|
| 233 |
1.1 |
|
| 237 |
1.1 |
|
| 240 |
1.1 |
|
| 241 |
1.1 |
|
| 245 |
1.1 |
|
| 249 |
1.1 |
|
| 253 |
1.1 |
|
| 257 |
1.1 |
|
| 261 |
1.1 |
|
| 265 |
1.1 |
|
| 272 |
1.1 |
|
| 276 |
1.1 |
|
| 280 |
1.1 |
|
| 284 |
1.1 |
|
| 288 |
1.1 |