Skip to content

Commit

Permalink
support more djh2: tempomap, chart author
Browse files Browse the repository at this point in the history
  • Loading branch information
shockdude committed Jun 28, 2019
1 parent cac7959 commit c44deed
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 44 deletions.
4 changes: 2 additions & 2 deletions Sharktooth/Mub/Mub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private static Mub FromStream(Stream stream)
float length = ar.ReadSingle();
int wordOffset = ar.ReadInt32();

mub.Entries.Add(new MubEntry(start, mod, length, wordOffset > 0 && words.ContainsKey(wordOffset) ? words[wordOffset] : ""));
mub.Entries.Add(new MubEntry(start, mod, length, wordOffset, wordOffset > 0 && words.ContainsKey(wordOffset) ? words[wordOffset] : ""));
}

return mub;
Expand Down Expand Up @@ -148,7 +148,7 @@ private byte[] CreateData()
aw.Write((float)entry.Start);
aw.Write((int)entry.Modifier);
aw.Write((float)entry.Length);
aw.Write(stringOffsets.ContainsKey(entry.Text) ? stringOffsets[entry.Text] : 0);
aw.Write(stringOffsets.ContainsKey(entry.Text) ? stringOffsets[entry.Text] : entry.Data);
}

// Writes string blob
Expand Down
6 changes: 4 additions & 2 deletions Sharktooth/Mub/MubEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@ namespace Sharktooth.Mub
{
public struct MubEntry
{
public MubEntry(float start, int mod, float length, string text = "")
public MubEntry(float start, int mod, float length, int data = 0, string text = "")
{
Start = start;
Modifier = mod;
Length = length;
Data = data;
Text = text;
}

public float Start { get; set; } // Measure percentage, 0-index
public int Modifier { get; set; }
public float Length { get; set; }
public int Data { get; set; }
public string Text { get; set; }

public override string ToString() => $"{Start:0.000}, 0x{Modifier:X8}, {Length:0.000}, \"{Text}\"";
public override string ToString() => $"{Start:0.000}, 0x{Modifier:X8}, {Length:0.000}, 0x{Data:X8}, \"{Text}\"";
}
}
133 changes: 115 additions & 18 deletions Sharktooth/Mub/MubExport.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
using System;
using NAudio.Midi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NAudio.Midi;

namespace Sharktooth.Mub
{
public class MubExport
{
private const int DELTA_TICKS_PER_QUARTER = 480;
private Mub _mub;
private List<MubTempoMarker> tempoMarkers;

public MubExport(Mub mub)
{
Expand All @@ -29,14 +28,91 @@ public void Export(string path)
MidiFile.Export(path, mid);
}

private long NotePosToTicks(double pos, bool useTempoMarkers = true)
{
int ticksPerMeasure = DELTA_TICKS_PER_QUARTER * 4; // Assume 4/4

if (useTempoMarkers && tempoMarkers != null)
{
for (int i = 0; i < tempoMarkers.Count; ++i)
{
if (i == tempoMarkers.Count - 1 || tempoMarkers[i + 1].AbsolutePos > pos)
{
return (long)Math.Round(tempoMarkers[i].GetBeatPos(pos) * ticksPerMeasure);
}
}
throw new Exception("Error converting note position using tempo markers");
}
return (long)Math.Round(pos * ticksPerMeasure);
}

private List<MidiEvent> CreateTempoTrack()
{
List<MidiEvent> track = new List<MidiEvent>();

float chartBPM = 0;
int chartUsPerQuarterNote = 500000; // 120 BPM

// These are redundant but ehh
track.Add(new NAudio.Midi.TextEvent("mubTempo", MetaEventType.SequenceTrackName, 0));
track.Add(new TimeSignatureEvent(0, 4, 2, 24, 8)); // 4/4 ts
track.Add(new TempoEvent(500000, 0)); // 120 bpm

// get chart BPM
foreach (var entry in _mub.Entries)
{
if (entry.Modifier == 0x0B000002)
{
if (chartBPM > 0)
{
throw new Exception("Mub has more than one Chart BPM!");
}
chartBPM = BitConverter.ToSingle(BitConverter.GetBytes(entry.Data), 0);
}
}

// without a chart BPM, can't do an accurate tempomap so don't even try without one.
if (chartBPM > 0)
{
chartUsPerQuarterNote = (int)Math.Round(60000000 / chartBPM);

foreach (var entry in _mub.Entries)
{
if (entry.Modifier == 0x0B000001)
{
long start = NotePosToTicks(entry.Start, false);
track.Add(new TempoEvent(entry.Data, start));
if (tempoMarkers == null)
{
tempoMarkers = new List<MubTempoMarker>();
}
tempoMarkers.Add(new MubTempoMarker(entry.Start, entry.Data, chartUsPerQuarterNote));
}
}
if (tempoMarkers != null)
{
tempoMarkers.Sort((x, y) =>
{
if (x.BeatPos < y.BeatPos)
return -1;
else if (x.BeatPos > y.BeatPos)
return 1;
else
return 0;
});
MubTempoMarker temp;
for (int i=1; i<tempoMarkers.Count; ++i)
{
temp = tempoMarkers[i];
temp.AbsolutePos = tempoMarkers[i - 1].GetAbsolutePos(tempoMarkers[i].BeatPos);
tempoMarkers[i] = temp;
}
}
}

if (tempoMarkers == null)
{
track.Add(new TempoEvent(chartUsPerQuarterNote, 0));
}

// Adds end track
track.Add(new MetaEvent(MetaEventType.EndTrack, 0, track.Last().AbsoluteTime));
Expand All @@ -48,21 +124,43 @@ private List<MidiEvent> CreateTrack()
List<MidiEvent> track = new List<MidiEvent>();
track.Add(new NAudio.Midi.TextEvent("NOTES", MetaEventType.SequenceTrackName, 0));

int ticksPerMeasure = DELTA_TICKS_PER_QUARTER * 4; // Assume 4/4
foreach (var entry in _mub.Entries)
{
long start = (long)(entry.Start * ticksPerMeasure);
long end = (long)(start + (entry.Length * ticksPerMeasure));
long start = NotePosToTicks(entry.Start);
long end = NotePosToTicks(entry.Start + entry.Length);

// DJH2 effect type - 0x05FFFFFF through 0x06000009
// Should not be added to the notes track
if ((entry.Modifier & 0xFF000000) == 0x06000000 || entry.Modifier == 0x05FFFFFF) continue;
if ((entry.Modifier & 0xFF000000) == 0x06000000
|| entry.Modifier == 0x05FFFFFF) continue;

// BPM type - 0x0Bxxxxxx
if ((entry.Modifier & 0xFF000000) == 0x0B000000)
{
if (entry.Modifier == 0x0B000002)
{
float chartBpm = BitConverter.ToSingle(BitConverter.GetBytes(entry.Data), 0);
track.Add(new NAudio.Midi.TextEvent(chartBpm.ToString(), MetaEventType.CuePoint, 0));
}
continue;
}

if ((entry.Modifier & 0xFFFFFF) == 0xFFFFFF)
{
// Text event?
// Text Event?
if (!string.IsNullOrEmpty(entry.Text))
track.Add(new NAudio.Midi.TextEvent(entry.Text, MetaEventType.TextEvent, start));
{
// Author?
if ((entry.Modifier & 0xFF000000) == 0x0A000000)
{
track.Add(new NAudio.Midi.TextEvent(entry.Text, MetaEventType.Copyright, 0));
}
// Just a section
else
{
track.Add(new NAudio.Midi.TextEvent(entry.Text, MetaEventType.TextEvent, start));
}
}
continue;
}

Expand All @@ -76,8 +174,8 @@ private List<MidiEvent> CreateTrack()
track.Add(new NAudio.Midi.TextEvent(entry.Text, MetaEventType.TextEvent, start));
}

track.Add(new NoteEvent(start, 1, MidiCommandCode.NoteOn, entry.Modifier & 0xFF, 100));
track.Add(new NoteEvent(end, 1, MidiCommandCode.NoteOff, entry.Modifier & 0xFF, 100));
track.Add(new NoteEvent(start, 1, MidiCommandCode.NoteOn, entry.Modifier & 0xFF, entry.Data + 1));
track.Add(new NoteEvent(end, 1, MidiCommandCode.NoteOff, entry.Modifier & 0xFF, entry.Data + 1));
}

track.Sort((x, y) =>
Expand All @@ -99,11 +197,10 @@ private List<MidiEvent> CreateEffectsTrack()
{
List<MidiEvent> effects = null;

int ticksPerMeasure = DELTA_TICKS_PER_QUARTER * 4; // Assume 4/4
foreach (var entry in _mub.Entries)
{
long start = (long)(entry.Start * ticksPerMeasure);
long end = (long)(start + (entry.Length * ticksPerMeasure));
long start = NotePosToTicks(entry.Start);
long end = NotePosToTicks(entry.Start + entry.Length);

if ((entry.Modifier & 0xFF000000) == 0x06000000)
{
Expand All @@ -112,8 +209,8 @@ private List<MidiEvent> CreateEffectsTrack()
effects = new List<MidiEvent>();
effects.Add(new NAudio.Midi.TextEvent("EFFECTS", MetaEventType.SequenceTrackName, 0));
}
effects.Add(new NoteEvent(start, 1, MidiCommandCode.NoteOn, entry.Modifier & 0xFF, 100));
effects.Add(new NoteEvent(end, 1, MidiCommandCode.NoteOff, entry.Modifier & 0xFF, 100));
effects.Add(new NoteEvent(start, 1, MidiCommandCode.NoteOn, entry.Modifier & 0xFF, entry.Data + 1));
effects.Add(new NoteEvent(end, 1, MidiCommandCode.NoteOff, entry.Modifier & 0xFF, entry.Data + 1));
continue;
}
}
Expand Down
Loading

0 comments on commit c44deed

Please sign in to comment.