private SongPlayer() { time = new TimeBarrier(); thread = new Thread(Tick) { Name = "SongPlayer Tick" }; thread.Start(); for (byte i = 0; i < 0x10; i++) { Tracks[i] = new Track(i); } for (int i = 0; i < soundVars.Length; i++) { soundVars[i] = new SoundVar(); } }
void ExecuteNext(Track track) { ArgType argOverrideType = 0; bool useOverrideType = false; bool doCmdWork = true; byte cmd = sseq.Data[track.DataOffset++]; again: if (cmd == 0xA0) // Rand: [New Super Mario Bros (BGM_AMB_CHIKA)] { cmd = sseq.Data[track.DataOffset++]; argOverrideType = ArgType.Rand; useOverrideType = true; goto again; } else if (cmd == 0xA1) // Var: [New Super Mario Bros (BGM_AMB_SABAKU)] { cmd = sseq.Data[track.DataOffset++]; argOverrideType = ArgType.SoundVar; useOverrideType = true; goto again; } else if (cmd == 0xA2) // If: [Mario Kart DS (75)] { cmd = sseq.Data[track.DataOffset++]; doCmdWork = track.VariableFlag; goto again; } if (cmd < 0x80) // Notes { byte velocity = sseq.Data[track.DataOffset++]; int length = ReadArg(track, useOverrideType ? argOverrideType : ArgType.VarLen); if (doCmdWork) { byte key = (byte)(cmd + track.KeyShift).Clamp(0x0, 0x7F); PlayNote(track, key, velocity, Math.Max(-1, length)); track.PortamentoKey = key; if (track.ShouldWaitForNotesToFinish) { track.Delay = length; if (length == 0) { track.WaitingForNoteToFinishBeforeContinuingXD = true; } } } } else { int cmdGroup = cmd & 0xF0; if (cmdGroup == 0x80) { int arg = ReadArg(track, useOverrideType ? argOverrideType : ArgType.VarLen); if (doCmdWork) { if (cmd == 0x80) // Rest { track.Delay = arg; } else if (cmd == 0x81 && arg <= byte.MaxValue) // Program Change { track.Voice = (byte)arg; } } } else if (cmdGroup == 0x90) { switch (cmd) { case 0x93: // Open Track { int index = sseq.Data[track.DataOffset++]; int offset24bit = sseq.Data[track.DataOffset++] | (sseq.Data[track.DataOffset++] << 8) | (sseq.Data[track.DataOffset++] << 16); if (doCmdWork) { Tracks[index].DataOffset = offset24bit; } break; } case 0x94: // Goto { int offset24bit = sseq.Data[track.DataOffset++] | (sseq.Data[track.DataOffset++] << 8) | (sseq.Data[track.DataOffset++] << 16); if (doCmdWork) { track.DataOffset = offset24bit; } break; } case 0x95: // Call { int offset24bit = sseq.Data[track.DataOffset++] | (sseq.Data[track.DataOffset++] << 8) | (sseq.Data[track.DataOffset++] << 16); if (doCmdWork && track.CallStackDepth < 3) { track.CallStack[track.CallStackDepth] = track.DataOffset; track.CallStackDepth += 1; track.DataOffset = offset24bit; } break; } } } else if (cmdGroup == 0xB0) { byte varIndex = sseq.Data[track.DataOffset++]; short mathArg = (short)ReadArg(track, useOverrideType ? argOverrideType : ArgType.Short); if (doCmdWork) { SoundVar var = soundVars[varIndex]; switch (cmd) { case 0xB0: { Console.Write("Setvar {0} {1} (Old: {2}", varIndex, mathArg, var.Value); var.Value = mathArg; Console.WriteLine(", New: {0})", var.Value); break; } case 0xB1: { Console.Write("Addvar {0} {1} (Old: {2}", varIndex, mathArg, var.Value); var.Value += mathArg; Console.WriteLine(", New: {0})", var.Value); break; } case 0xB2: { Console.Write("Subvar {0} {1} (Old: {2}", varIndex, mathArg, var.Value); var.Value -= mathArg; Console.WriteLine(", New: {0})", var.Value); break; } case 0xB3: { Console.Write("Mulvar {0} {1} (Old: {2}", varIndex, mathArg, var.Value); var.Value *= mathArg; Console.WriteLine(", New: {0})", var.Value); break; } case 0xB4: { Console.Write("Divvar {0} {1} (Old: {2}", varIndex, mathArg, var.Value); if (mathArg != 0) { var.Value /= mathArg; } Console.WriteLine(", New: {0})", var.Value); break; } case 0xB5: { Console.Write("Shiftvar {0} {1} (Old: {2}", varIndex, mathArg, var.Value); if (mathArg < 0) { var.Value = (short)(var.Value >> -mathArg); } else { var.Value = (short)(var.Value << mathArg); } Console.WriteLine(", New: {0})", var.Value); break; } case 0xB6: // [Mario Kart DS (75)] { Console.Write("Randvar {0} {1} (Old: {2}", varIndex, mathArg, var.Value); bool negate = false; if (mathArg < 0) { negate = true; mathArg = (short)-mathArg; } short val = (short)Utils.RNG.Next(mathArg + 1); if (negate) { val = (short)-val; } var.Value = val; Console.WriteLine(", New: {0})", var.Value); break; } case 0xB8: { Console.WriteLine("CMPVar {0} == {1} (Value: {2})", varIndex, mathArg, var.Value); track.VariableFlag = var.Value == mathArg; break; } case 0xB9: { Console.WriteLine("CMPVar {0} >= {1} (Value: {2})", varIndex, mathArg, var.Value); track.VariableFlag = var.Value >= mathArg; break; } case 0xBA: { Console.WriteLine("CMPVar {0} > {1} (Value: {2})", varIndex, mathArg, var.Value); track.VariableFlag = var.Value > mathArg; break; } case 0xBB: { Console.WriteLine("CMPVar {0} <= {1} (Value: {2})", varIndex, mathArg, var.Value); track.VariableFlag = var.Value <= mathArg; break; } case 0xBC: { Console.WriteLine("CMPVar {0} < {1} (Value: {2})", varIndex, mathArg, var.Value); track.VariableFlag = var.Value < mathArg; break; } case 0xBD: { Console.WriteLine("CMPVar {0} != {1} (Value: {2})", varIndex, mathArg, var.Value); track.VariableFlag = var.Value != mathArg; break; } } } } else if (cmdGroup == 0xC0 || cmdGroup == 0xD0) { int cmdArg = ReadArg(track, useOverrideType ? argOverrideType : ArgType.Byte); if (doCmdWork) { switch (cmd) { case 0xC0: // Panpot { track.Pan = (sbyte)(cmdArg - 0x40); break; } case 0xC1: // Volume { track.Volume = (byte)cmdArg; break; } case 0xC2: // Player Volume { Volume = (byte)cmdArg; break; } case 0xC3: // Key Shift { track.KeyShift = (sbyte)cmdArg; break; } case 0xC4: // Pitch Bend { track.Bend = (sbyte)cmdArg; break; } case 0xC5: // Pitch Bend Range { track.BendRange = (byte)cmdArg; break; } case 0xC6: // Priority { track.Priority = (byte)cmdArg; break; } case 0xC7: // Mono/Poly { track.ShouldWaitForNotesToFinish = cmdArg == 1; break; } case 0xC8: // Tie { track.Tie = cmdArg == 1; track.CloseAllChannels(); break; } case 0xC9: // Portamento Control { track.PortamentoKey = (byte)(cmdArg + track.KeyShift); track.Portamento = true; break; } case 0xCA: // LFO Depth { track.LFODepth = (byte)cmdArg; break; } case 0xCB: // LFO Speed { track.LFOSpeed = (byte)cmdArg; break; } case 0xCC: // LFO Type { track.LFOType = (LFOType)cmdArg; break; } case 0xCD: // LFO Range { track.LFORange = (byte)cmdArg; break; } case 0xCE: // Portamento Toggle { track.Portamento = cmdArg == 1; break; } case 0xCF: // Portamento Time { track.PortamentoTime = (byte)cmdArg; break; } case 0xD0: // Forced Attack { track.Attack = (byte)cmdArg; break; } case 0xD1: // Forced Decay { track.Decay = (byte)cmdArg; break; } case 0xD2: // Forced Sustain { track.Sustain = (byte)cmdArg; break; } case 0xD3: // Forced Release { track.Release = (byte)cmdArg; break; } case 0xD4: // Call { if (track.CallStackDepth < 3) { track.CallStack[track.CallStackDepth] = track.DataOffset; track.CallStackLoops[track.CallStackDepth] = (byte)cmdArg; track.CallStackDepth += 1; } break; } case 0xD5: // Expression { track.Expression = (byte)cmdArg; break; } case 0xD6: // Print { Console.WriteLine("Track {0}, Var {1}, Value{2}", track.Index, cmdArg, soundVars[cmdArg].Value); break; } } } } else if (cmdGroup == 0xE0) { int cmdArg = ReadArg(track, useOverrideType ? argOverrideType : ArgType.Short); if (doCmdWork) { switch (cmd) { case 0xE0: // LFO Delay { track.LFODelay = (ushort)cmdArg; break; } case 0xE1: // Tempo { tempo = (ushort)cmdArg; break; } case 0xE3: // Sweep Pitch { track.SweepPitch = (short)cmdArg; break; } } } } else if (cmdGroup == 0xF0) { if (doCmdWork) { switch (cmd) { case 0xFC: // Loop End { if (track.CallStackDepth != 0) { byte count = track.CallStackLoops[track.CallStackDepth - 1]; if (count != 0) { count--; if (count == 0) { track.CallStackDepth -= 1; break; } } track.CallStackLoops[track.CallStackDepth - 1] = count; track.DataOffset = track.CallStack[track.CallStackDepth - 1]; } break; } case 0xFD: // Return { if (track.CallStackDepth != 0) { track.CallStackDepth -= 1; track.DataOffset = track.CallStack[track.CallStackDepth]; } break; } case 0xFF: // End { track.Stopped = true; break; } } } } } }