static void SplitImplicitSlides(List <Bar> bars) { // Unfortunately, for targeted slides, Rocksmith does not usually follow the sliding note // with a target note, so the target note may be implied only. Of course, this does not // work for our export, so if we find such a case, we need to split the sliding note into // two and set the second one to the target. Chord lastChord = null; for (int b = 0; b < bars.Count; ++b) { var bar = bars[b]; var nextBar = (b < bars.Count - 1) ? bars[b + 1] : null; for (int i = 0; i < bar.Chords.Count; ++i) { var chord = bar.Chords[i]; var nextChord = (i < bar.Chords.Count - 1) ? bar.Chords[i + 1] : ((nextBar != null) ? nextBar.Chords.FirstOrDefault() : null); // see if there's an unmatched slide in the current chord. foreach (var kvp in chord.Notes) { var note = kvp.Value; if (note.Slide == Note.SlideType.ToNext) { if (!note.LinkNext || nextChord == null || !nextChord.Notes.ContainsKey(kvp.Key) || nextChord.Notes[kvp.Key].Fret != note.SlideTarget) { // found an instance where we need to set the slide up manually if (note._Extended && lastChord != null && lastChord.Notes.ContainsKey(kvp.Key)) { // this note was already split previously, so move the // slide one step back and change this to the target var prevNote = lastChord.Notes[kvp.Key]; prevNote.Slide = note.Slide; prevNote.SlideTarget = note.SlideTarget; note.Fret = note.SlideTarget; note.Slide = Note.SlideType.None; note.SlideTarget = -1; } else { // split the chord, ideally in half, but take care to use durations // that are valid int duration = chord.Duration / 2 + 1; int remaining = chord.Duration - duration; while (duration >= 2) { if (RhythmDetector.PrintableDurations.Contains(duration) && RhythmDetector.PrintableDurations.Contains(remaining)) { break; } --duration; ++remaining; } if (!RhythmDetector.PrintableDurations.Contains(duration) || !RhythmDetector.PrintableDurations.Contains(remaining)) { Console.WriteLine(" WARNING: Cannot split slide! Leaving incomplete as is..."); continue; } var newChord = SplitChord(chord, bar.GetDurationLength(chord.Start, duration)); foreach (var kvp2 in newChord.Notes) { if (kvp2.Value.Slide == Note.SlideType.ToNext) { kvp2.Value.Fret = kvp2.Value.SlideTarget; var prevNote = chord.Notes[kvp2.Key]; prevNote.Slide = Note.SlideType.ToNext; prevNote.SlideTarget = kvp2.Value.Fret; kvp2.Value.Slide = Note.SlideType.None; kvp2.Value.SlideTarget = -1; } } newChord.Duration = remaining; chord.Duration = duration; bar.Chords.Insert(i + 1, newChord); nextChord = newChord; } } } } lastChord = chord; } } }
private void WriteBeat(Chord chord, int trackNumber, int numStrings, bool changeTempo, int newTempo) { const Byte DOTTED_NOTE = 1; const Byte CHORD_DIAGRAM = (1 << 1); const Byte TEXT = (1 << 2); const Byte BEAT_EFFECTS = (1 << 3); const Byte MIX_TABLE = (1 << 4); const Byte TUPLET = (1 << 5); const Byte REST = (1 << 6); const short STRING_EFFECTS = (1 << 5); const Byte TAPPING = 1; const Byte SLAPPING = 2; const Byte POPPING = 3; // figure out beat duration bool dotted = false; bool triplet = false; SByte duration = 0; switch (chord.Duration) { case 192: duration = -2; break; case 144: duration = -1; dotted = true; break; case 96: duration = -1; break; case 72: duration = 0; dotted = true; break; case 48: duration = 0; break; case 36: duration = 1; dotted = true; break; case 32: duration = 0; triplet = true; break; case 24: duration = 1; break; case 18: duration = 2; dotted = true; break; case 16: duration = 1; triplet = true; break; case 12: duration = 2; break; case 9: duration = 3; dotted = true; break; case 8: duration = 2; triplet = true; break; case 6: duration = 3; break; case 4: duration = 3; triplet = true; break; case 3: duration = 4; break; case 2: duration = 4; triplet = true; break; default: Console.WriteLine(" Warning: Rhythm Duration {0} not handled, defaulting to quarter note.", chord.Duration); duration = 0; break; } Byte flags = 0; if (chord.Notes.Count == 0) { flags |= REST; } if (dotted) { flags |= DOTTED_NOTE; } if (triplet) { flags |= TUPLET; } if (changeTempo) { flags |= MIX_TABLE; } if (chord.Section != null) { flags |= TEXT; } bool tapped = false; foreach (var kvp in chord.Notes) { if (kvp.Value.Tapped) { tapped = true; } } if (chord.Popped || chord.Slapped || tapped) { flags |= BEAT_EFFECTS; } var chordTemplates = score.Tracks[trackNumber].ChordTemplates; if (chord.ChordId != -1 && chord.ChordId != prevChordId[trackNumber] && chordTemplates.ContainsKey(chord.ChordId) && chordTemplates[chord.ChordId].Name != string.Empty) { flags |= CHORD_DIAGRAM; } prevChordId[trackNumber] = chord.ChordId; writer.Write(flags); if (chord.Notes.Count == 0) { writer.Write((Byte)2); // 2 is an actual rest, 0 is silent } writer.Write(duration); if (triplet) { writer.Write((Int32)3); // declare a triplet beat } // chord diagram if ((flags & CHORD_DIAGRAM) != 0) { var chordTemplate = chordTemplates[chord.ChordId]; WriteChordTemplate(chordTemplate); } // section names if ((flags & TEXT) != 0) { WriteDoublePrefixedString(chord.Section); } // beat effects if ((flags & BEAT_EFFECTS) != 0) { short effectsFlag = 0; if (chord.Popped || chord.Slapped || tapped) { effectsFlag |= STRING_EFFECTS; } writer.Write(effectsFlag); if (tapped) { writer.Write(TAPPING); } else if (chord.Slapped) { writer.Write(SLAPPING); } else if (chord.Popped) { writer.Write(POPPING); } } // mix table (used for changing tempo) if ((flags & MIX_TABLE) != 0) { for (int i = 0; i < 23; ++i) { writer.Write((Byte)0xff); } WriteDoublePrefixedString(""); // tempo string writer.Write((Int32)newTempo); writer.Write((Byte)0); // means new tempo takes effect immediately writer.Write((Byte)1); writer.Write((Byte)0xff); } // now write the actual notes. a flag indicates which strings are being played Byte stringFlags = 0; int stringOffset = 7 - numStrings; foreach (var kvp in chord.Notes) { stringFlags |= (Byte)(1 << (kvp.Key + stringOffset)); } writer.Write(stringFlags); var notes = chord.Notes.Values.OrderByDescending(x => x.String); foreach (var note in notes) { WriteNote(note, trackNumber); } // there seem to be a few accidental ties set in the Rocksmith XMLs // so unset the tie status on any strings that weren't in the current chord. for (int i = 0; i < 6; ++i) { if (!chord.Notes.ContainsKey(i)) { tieNotes[trackNumber][i] = false; } } short noteTranspose = 0; writer.Write(noteTranspose); }
static Chord CreateChord(SongChord2014 rsChord, Dictionary <int, ChordTemplate> chordTemplates, int capo) { var chord = new Chord(); chord.Start = rsChord.Time; chord.ChordId = rsChord.ChordId; chord.Tremolo = false; if (rsChord.ChordNotes != null) { foreach (var note in rsChord.ChordNotes) { chord.Notes.Add(note.String, CreateNote(note, capo)); } } if (chordTemplates.ContainsKey(chord.ChordId)) { // need to determine chords from the chord template var template = chordTemplates[chord.ChordId]; for (int i = 0; i < 6; ++i) { if (template.Frets[i] >= 0 && !chord.Notes.ContainsKey(i)) { var note = new Note() { Fret = template.Frets[i], String = i, LeftFingering = template.Fingers[i], RightFingering = -1, }; chord.Notes.Add(i, note); } } } if (chord.Notes.Count == 0) { Console.WriteLine(" Warning: Empty chord. Cannot find chord with chordId {0}.", chord.ChordId); } // some properties set on the chord in Rocksmith need to be passed down to the individual notes // and vice versa foreach (var kvp in chord.Notes) { if (rsChord.PalmMute != 0) { kvp.Value.PalmMuted = true; } if (rsChord.FretHandMute != 0) { kvp.Value.Muted = true; } if (rsChord.Accent != 0) { kvp.Value.Accent = true; } if (kvp.Value.Tremolo) { chord.Tremolo = true; } if (kvp.Value.Slapped) { chord.Slapped = true; } if (kvp.Value.Popped) { chord.Popped = true; } } // we will show a strum hint for all chords played with an up-stroke, // and a down-stroke hint for all chords with more than 3 notes (to exclude power-chords) //if (rsChord.Strum.ToLower() == "up") // chord.BrushDirection = Chord.BrushType.Up; //else if (chord.Notes.Count > 3 && rsChord.Strum.ToLower() == "down") // chord.BrushDirection = Chord.BrushType.Down; // disabled, since apparently the strum hints aren't really useful. I might have // misunderstood the parameter. return(chord); }
int ExportOrFindBeat(Chord chord) { var beat = new Beat(); beat.Id = gpif.Beats.Count; foreach (var note in chord.Notes) { int id = ExportOrFindNote(note.Value); beat.Notes.Add(id); } // there seem to be a few accidental ties set in the Rocksmith XMLs // so unset the tie status on any strings that weren't in the current chord. for (int i = 0; i < 6; ++i) { if (!chord.Notes.ContainsKey(i)) { link[i] = false; } } // should we display a strum hint? if (chord.BrushDirection != Chord.BrushType.None) { var brushProp = new Property() { Name = "Brush" }; if (chord.BrushDirection == Chord.BrushType.Down) { brushProp.Direction = "Down"; } else { brushProp.Direction = "Up"; } if (beat.Properties == null) { beat.Properties = new List <Property>(); } beat.Properties.Add(brushProp); } // tremolo picking if (chord.Tremolo) { // 32nd notes tremolo picking (should be appropriate) beat.Tremolo = "1/8"; } // slap/pop notes if (chord.Slapped) { if (beat.Properties == null) { beat.Properties = new List <Property>(); } beat.Properties.Add(new Property() { Name = "Slapped", Enable = new Property.EnableType() }); } if (chord.Popped) { if (beat.Properties == null) { beat.Properties = new List <Property>(); } beat.Properties.Add(new Property() { Name = "Popped", Enable = new Property.EnableType() }); } // construct rhythm var rhythm = new Rhythm(); rhythm.Id = gpif.Rhythms.Count; switch (chord.Duration) { case 192: rhythm.NoteValue = "Whole"; break; case 168: // should avoid this, split note instead (TODO) rhythm.NoteValue = "Half"; rhythm.AugmentationDot = new Rhythm.Dot() { Count = 2 }; break; case 144: rhythm.NoteValue = "Half"; rhythm.AugmentationDot = new Rhythm.Dot() { Count = 1 }; break; case 96: rhythm.NoteValue = "Half"; break; case 84: // should avoid this, split note instead (TODO) rhythm.NoteValue = "Quarter"; rhythm.AugmentationDot = new Rhythm.Dot() { Count = 2 }; break; case 72: rhythm.NoteValue = "Quarter"; rhythm.AugmentationDot = new Rhythm.Dot() { Count = 1 }; break; case 48: rhythm.NoteValue = "Quarter"; break; case 36: rhythm.NoteValue = "Eighth"; rhythm.AugmentationDot = new Rhythm.Dot() { Count = 1 }; break; case 32: rhythm.NoteValue = "Quarter"; rhythm.PrimaryTuplet = new Rhythm.Tuplet() { Den = 2, Num = 3 }; break; case 24: rhythm.NoteValue = "Eighth"; break; case 18: rhythm.NoteValue = "16th"; rhythm.AugmentationDot = new Rhythm.Dot() { Count = 1 }; break; case 16: rhythm.NoteValue = "Eighth"; rhythm.PrimaryTuplet = new Rhythm.Tuplet() { Den = 2, Num = 3 }; break; case 12: rhythm.NoteValue = "16th"; break; case 9: rhythm.NoteValue = "32nd"; rhythm.AugmentationDot = new Rhythm.Dot() { Count = 1 }; break; case 8: rhythm.NoteValue = "16th"; rhythm.PrimaryTuplet = new Rhythm.Tuplet() { Den = 2, Num = 3 }; break; case 6: rhythm.NoteValue = "32nd"; break; case 4: rhythm.NoteValue = "32nd"; rhythm.PrimaryTuplet = new Rhythm.Tuplet() { Den = 2, Num = 3 }; break; case 3: rhythm.NoteValue = "64th"; break; case 2: rhythm.NoteValue = "64th"; rhythm.PrimaryTuplet = new Rhythm.Tuplet() { Den = 2, Num = 3 }; break; case 1: rhythm.NoteValue = "128th"; rhythm.PrimaryTuplet = new Rhythm.Tuplet() { Den = 2, Num = 3 }; break; default: Console.WriteLine(" Warning: Rhythm Duration {0} not handled, defaulting to quarter note.", chord.Duration); rhythm.NoteValue = "Quarter"; break; } // see if this rhythm already exists, otherwise add var searchRhythm = gpif.Rhythms.Find(x => x.Equals(rhythm)); if (searchRhythm != null) { rhythm = searchRhythm; } else { gpif.Rhythms.Add(rhythm); } beat.Rhythm.Ref = rhythm.Id; // should we display a chord name? if (chord.ChordId != -1 && chord.ChordId != prevChordId) { beat.Chord = chord.ChordId.ToString(); } prevChordId = chord.ChordId; if (chord.Section != null) { beat.FreeText = chord.Section; } // see if this beat already exists, otherwise add var searchBeat = gpif.Beats.Find(x => x.Equals(beat)); if (searchBeat != null) { beat = searchBeat; } else { gpif.Beats.Add(beat); } return(beat.Id); }