I enjoy playing Rocksmith. It is very nice game teaching you to play guitar.
Unfortunately it does not provide possibility to study different riffs
and phrases slowly.
To help learning difficult passages I have created small program allowing
you to create ASCII tabs from the Rocksmith SNG file.
Code is based on the C# code from Rocsmith Custom Song project
http://code.google.com/p/rocksmith-custom-song-creator/
It is translated from C# to Python and supplemented with TabWriter class.
This code is licensed under GNU GPL v3, so I license my code under the same
license.
To display the tab you should call:
$ sng2tab.py /path/to/file.sng max_line_length
for example:
$ sng2tab.py /opt/Rocksmith/songs/Angela_Combo.sng 96
Of course you can also write the generated tab to the file:
$ sng2tab.py /path/to/file.sng max_line_length > output_file.tab
It should be possible to change the program to write the TuxGuitar
or GuitarPro files, but I just had no time to investigate those formats.
Feel free to change or extend this program according to your needs.
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.11.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `#!/bin/sh' line above, then type `sh FILE'.
#
lock_dir=_sh05371
# Made on 2013-02-28 5:04 GMT by <
nob...@nowhere.com>.
# Source directory was `/opt/secrets/sng2py'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 14604 -rwxr--r-- sng2tab.py
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false
for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done
if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done
if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901
if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU `touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= sng2tab.py ==============
if test -n "${keep_file}" && test -f 'sng2tab.py'
then
${echo} "x - SKIPPING sng2tab.py (file already exists)"
else
${echo} "x - extracting sng2tab.py (text)"
sed 's/^X//' << 'SHAR_EOF' > 'sng2tab.py' &&
#!/usr/bin/python
# The code below is a Rocksmith SNG to ASCII TAB converter
# it is port of SngFile.cs from Rocksmith Custom Song Project
#
http://code.google.com/p/rocksmith-custom-song-creator/
# to Python with TabWriter class added
# The code is licensed under GNU GPL v3 (as the original
# SngFile.cs )
#
import struct
import sys
X
class reader:
X def __init__(self,fname):
X
self.fi = open(fname,"rb")
X def ReadByte(self):
X res = ord(self.fi.read(1))
X return res
X def ReadBytes(self,n):
X res = self.fi.read(n)
X return res
X def ReadInt16(self):
X s=self.fi.read(2)
X res=struct.unpack("<h",s)[0]
X return res
X def ReadInt32(self):
X s=self.fi.read(4)
X res=struct.unpack("<i",s)[0]
X return res
X def ReadSingle(self):
X s=self.fi.read(4)
X res=struct.unpack("<f",s)[0]
X return res
X def ReadDouble(self):
X s=self.fi.read(8)
X res=struct.unpack("<d",s)[0]
X return res
X
class Ebeat:
X def __init__(self,sf):
X self.Time = sf.br.ReadSingle()
X self.Measure = sf.br.ReadInt16()
X self.Beat = sf.br.ReadInt16()
X self.IsFirstBeatInMeasure = sf.br.ReadInt32()
X #print "meas: "+str(self.Measure)+" time: "+str(self.Time)+" is first: "+str(self.IsFirstBeatInMeasure)
X
class Phrase:
X def __init__(self,sf):
X self.Solo = sf.br.ReadByte()
X self.Disparity = sf.br.ReadByte()
X self.Ignore = sf.br.ReadByte()
X self.Unknown = sf.br.ReadByte()
X self.MaxDifficulty = sf.br.ReadInt32()
X self.PhraseIterationCount = sf.br.ReadInt32()
X self.Name = sf.br.ReadBytes(32)
X
class PhraseIteration:
X def __init__(self,sf):
X self.Id = sf.br.ReadInt32()
X self.StartTime = sf.br.ReadSingle()
X self.EndTime = sf.br.ReadSingle()
X
class Control:
X def __init__(self,sf):
X self.Time = sf.br.ReadSingle()
X self._code = sf.br.ReadBytes(256)
X
class SongEvent:
X def __init__(self,sf):
X self.Time = sf.br.ReadSingle()
X self._code = sf.br.ReadBytes(256)
X
class SongSection:
X def __init__(self,sf):
X self._name = sf.br.ReadBytes(32)
X #print "song section:"+self._name
X self.Number = sf.br.ReadInt32()
X self.StartTime = sf.br.ReadSingle()
X self.EndTime = sf.br.ReadSingle()
X self.StartPhraseIteration = sf.br.ReadInt32()
X self.EndPhraseIteration = sf.br.ReadInt32()
X self.Bit1 = sf.br.ReadByte()
X self.Bit2 = sf.br.ReadByte()
X self.Bit3 = sf.br.ReadByte()
X self.Bit4 = sf.br.ReadByte()
X self.Bit5 = sf.br.ReadByte()
X self.Bit6 = sf.br.ReadByte()
X self.Bit7 = sf.br.ReadByte()
X self.Bit8 = sf.br.ReadByte()
X
class SongLevel:
X def __init__(self,sf):
X self.Difficulty = sf.br.ReadInt32()
X #print "difficulty: "+str(self.Difficulty)
X AnchorCount = sf.br.ReadInt32()
X #print "anchors: "+str(AnchorCount)
X self.Anchors = [ Anchor(sf) for i in range(0,AnchorCount)]
X SlideCount = sf.br.ReadInt32()
X #print "slides: "+str(SlideCount)
X self.Slides = [ Slide(sf) for i in range(0,SlideCount)]
X HandShapeCount = sf.br.ReadInt32()
X #print "hand shapes: "+str(HandShapeCount)
X self.HandShapes = [ HandShape(sf) for i in range(0,HandShapeCount)]
X NoteCount = sf.br.ReadInt32()
X #print "Notes: "+str(NoteCount)
X self.Notes = [ Note(sf) for i in range(0,NoteCount)]
X PhraseCount = sf.br.ReadInt32()
X #print "Phrases: "+str(PhraseCount)
X self.AverageNotesPerPhrase = [ sf.br.ReadSingle() for i in range(0,PhraseCount)]
X PhraseIterationCount = sf.br.ReadInt32()
X #print "Phrase iterations 1 : "+str(PhraseIterationCount)
X self.NotesPerIteration1 = [ sf.br.ReadInt32() for i in range(0,PhraseIterationCount)]
X PhraseIterationCount = sf.br.ReadInt32()
X #print "Phrase iterations 2 : "+str(PhraseIterationCount)
X self.NotesPerIteration2 = [ sf.br.ReadInt32() for i in range(0,PhraseIterationCount)]
X
class Anchor:
X def __init__(self,sf):
X self.StartTime = sf.br.ReadSingle()
X self.EndTime = sf.br.ReadSingle()
X self.MidTime = sf.br.ReadSingle()
X self.Fret = sf.br.ReadInt32()
X self.PhraseIteration = sf.br.ReadInt32()
X
class Note:
X def __init__(self,sf):
X self.sf = sf
X self.Time = sf.br.ReadSingle()
X self.String = sf.br.ReadInt32()
X self.Fret = sf.br.ReadInt32()
X self.ChordId = sf.br.ReadInt32() # equal to -1 when this is a plain note
X self.Unknown1 = sf.br.ReadInt32()
X self.SustainTime = sf.br.ReadSingle()
X self.Bend = sf.br.ReadInt32()
X self.SlideTo = sf.br.ReadInt32()
X self.Tremolo = sf.br.ReadByte()
X self.Harmonic = sf.br.ReadByte()
X self.PalmMute = sf.br.ReadByte()
X self.Hopo = sf.br.ReadByte()
X if sf.Version == 51:
X self.Slap = sf.br.ReadInt32()
X self.Pluck = sf.br.ReadInt32()
X self.HammerOn = sf.br.ReadByte()
X self.PullOff = sf.br.ReadByte()
X self.Ignore = sf.br.ReadByte()
X self.HighDensity = sf.br.ReadByte()
X self.Unknown2 = sf.br.ReadInt32()
X self.IterationId = sf.br.ReadInt32()
X self.PhraseId = sf.br.ReadInt32()
X
class Slide:
X def __init__(self,sf):
X self.Time = sf.br.ReadSingle()
X self.EndingFret = sf.br.ReadInt32()
X
class HandShape:
X def __init__(self,sf):
X self.StartTime = sf.br.ReadSingle()
X self.EndTime = sf.br.ReadSingle()
X self.Unknown1 = sf.br.ReadSingle()
X self.Unknown2 = sf.br.ReadSingle()
X self.ChordId = sf.br.ReadSingle()
X self.FirstChordInHandShapeTime = sf.br.ReadSingle()
X self.LastChordInHandShapeTime = sf.br.ReadSingle()
X
class ChordTemplate:
X def __init__(self,sf):
X self.Fret0 = sf.br.ReadInt32()
X self.Fret1 = sf.br.ReadInt32()
X self.Fret2 = sf.br.ReadInt32()
X self.Fret3 = sf.br.ReadInt32()
X self.Fret4 = sf.br.ReadInt32()
X self.Fret5 = sf.br.ReadInt32()
X self.Finger0 = sf.br.ReadInt32()
X self.Finger1 = sf.br.ReadInt32()
X self.Finger2 = sf.br.ReadInt32()
X self.Finger3 = sf.br.ReadInt32()
X self.Finger4 = sf.br.ReadInt32()
X self.Finger5 = sf.br.ReadInt32()
X self.Note0 = sf.br.ReadInt32()
X self.Note1 = sf.br.ReadInt32()
X self.Note2 = sf.br.ReadInt32()
X self.Note3 = sf.br.ReadInt32()
X self.Note4 = sf.br.ReadInt32()
X self.Note5 = sf.br.ReadInt32()
X self._name = sf.br.ReadBytes(32)
X #print "chord name: "+self._name
X
class Vocal:
X def __init__(self,sf):
X self.Time = sf.br.ReadSingle()
X self.Note = sf.br.ReadInt32()
X self.Length = sf.br.ReadSingle()
X self._lyric = sf.br.ReadBytes(32)
X
class Metadata:
X def __init__(self,sf):
X self.MaxScore = sf.br.ReadDouble()
X self.TotalNotes = sf.br.ReadDouble()
X self.PointsPerNote = sf.br.ReadDouble()
X self.BeatTiming = sf.br.ReadSingle()
X self.FirstBeat = sf.br.ReadSingle()
X self._lastConversion = sf.br.ReadBytes(32)
X self._songTitle = sf.br.ReadBytes(64)
X #print "song title: "+self._songTitle
X self._arrangement = sf.br.ReadBytes(32)
X #print "arrangement: "+self._arrangement
X self._artist = sf.br.ReadBytes(32)
X self.SongPart = sf.br.ReadInt16()
X self.Length = sf.br.ReadSingle()
X self.Tuning = sf.br.ReadInt32()
X self.Difficulty = sf.br.ReadSingle()
X self.Unknown1 = sf.br.ReadSingle()
X self.Unknown2 = sf.br.ReadSingle()
X self.MaxDifficulty = sf.br.ReadInt32()
X len1 = sf.br.ReadInt32()
X self.UnknownSection1 = [ UnknownSection1(sf) for i in range(0,len1)]
X len2 = sf.br.ReadInt32()
X self.UnknownSection2 = [ UnknownSection2(sf) for i in range(0,len2)]
X
class UnknownSection1:
X def __init__(self,sf):
X self.Unknown1 = sf.br.ReadBytes(4)
X self.Unknown2 = sf.br.ReadBytes(4)
X self.Unknown3 = sf.br.ReadInt32()
X
class UnknownSection2:
X def __init__(self,sf):
X self.Unknown1 = sf.br.ReadBytes(64)
X self.Unknown2 = sf.br.ReadInt32()
X self.Unknown3 = sf.br.ReadInt32()
X
class PhraseProperty:
X def __init__(self,sf):
X self.PhraseId = sf.br.ReadInt32()
X self.Difficulty = sf.br.ReadInt32()
X self.Empty = sf.br.ReadInt32()
X self.LevelJump = sf.br.ReadInt16()
X self.Redundant = sf.br.ReadInt16()
X
class LinkedDiff:
X def __init__(self,sf):
X self.ParentId = sf.br.ReadInt32()
X self.ChildId = sf.br.ReadInt32()
X self.Unknown = sf.br.ReadInt32()
X
X
class sng_file:
X def __init__(self, fn):
X
self.br = reader(fn)
X self.Version = self.br.ReadInt32()
X _beatCount = self.br.ReadInt32()
X self._beats = [ Ebeat(self) for i in range(0,_beatCount)]
X _PhraseCount = self.br.ReadInt32()
X self._phrases = [ Phrase(self) for i in range(0,_PhraseCount)]
X _chordTemplateCount = self.br.ReadInt32()
X #print "chord templates: "+str(_chordTemplateCount)
X self._chordTemplates = [ ChordTemplate(self) for i in range(0,_chordTemplateCount)]
X _fretHandMuteTemplateCount = self.br.ReadInt32(); # always 0?
X _vocalsCount = self.br.ReadInt32();
X #print "vocals: "+str(_vocalsCount)
X self._vocals = [ Vocal(self) for i in range(0,_vocalsCount)]
X _phraseIterationCount = self.br.ReadInt32();
X #print "PHRASE iterations: "+str(_phraseIterationCount)
X self._phraseIterations = [ PhraseIteration(self) for i in range(0,_phraseIterationCount)]
X PhrasePropertyCount = self.br.ReadInt32();
X #print "PHRASE PROPERTIES: "+str(PhrasePropertyCount)
X self.PhraseProperties = [ PhraseProperty(self) for i in range(0,PhrasePropertyCount)]
X LinkedDiffCount = self.br.ReadInt32();
X #print "Linked Diffs: "+str(LinkedDiffCount)
X self.LinkedDiffs = [LinkedDiff(self) for i in range(0,LinkedDiffCount)]
X ControlCount = self.br.ReadInt32();
X #print "Controls: "+str(ControlCount)
X self.Controls = [Control(self) for i in range(0,ControlCount)]
X _songEventCount = self.br.ReadInt32();
X self.SongEvents = [SongEvent(self) for i in range(0,_songEventCount)]
X SongSectionCount = self.br.ReadInt32();
X self.SongSections = [SongSection(self) for i in range(0,SongSectionCount)]
X _songLevelCount = self.br.ReadInt32();
X self.SongLevels = [SongLevel(self) for i in range(0,_songLevelCount)]
X self.Metadata = Metadata(self);
X #The original code reads to the end of the file...
X
# Class below generates the tab
# We add notes/chords as long, as the tab is not longer
# then the limit defined below
max_len = int(sys.argv[2])
# We always divide tab on the end of measure
# Now we do not take into consideration time positions
# of particular notes, but it can be done.
X
class TabWriter:
X def __init__(self,sf):
X self.sf = sf
X self.line=["|","|","|","|","|","|"]
X self.line_cnt=1
X self.last_measure_pos=0
X def add(self,nt):
X if isinstance(nt, Ebeat):
X if nt.IsFirstBeatInMeasure == 1:
X measure_pos=self.line_cnt
X if measure_pos > max_len:
X # Print notes to the end of the previous measure
X for i in range(5,-1,-1):
X print self.line[i][0:(self.last_measure_pos+1)]
X print ""
X # Remove the printed part except of the end of measure mark
X for i in range(0,6):
X self.line[i]=self.line[i][self.last_measure_pos:]
X # Correct the indices to the begining of the measure
X # and line length
X measure_pos -= self.last_measure_pos
X self.line_cnt -= self.last_measure_pos
X # Update the begining of the measure position
X self.last_measure_pos = measure_pos
X for i in range(0, 6):
X self.line[i]+="|-"
X self.line_cnt += 2
X else:
X if nt.ChordId==-1:
X #regular note
X #Create note representation
X n_str=""
X #Below we code special features of the note
X #some of them are ignored - add them yourself
X if nt.PullOff==1:
X n_str += "P"
X if nt.Bend==1:
X n_str += "B"
X if nt.HammerOn==1:
X n_str += "H"
X n_str+=str(nt.Fret)
X if nt.SlideTo != -1:
X n_str+="/"+str(nt.SlideTo)
X n_str+="-"
X #Check length of the note representation
X n_len=len(n_str)
X #Add note or hyphens to string lines
X for i in range(0, 6):
X if i==nt.String:
X self.line[i]+=n_str
X else:
X self.line[i]+=n_len*"-"
X self.line_cnt += n_len
X else:
X #Chord
X n_strs=["","","","","",""]
X #Look-up the chord definition
X chrd=self.sf._chordTemplates[nt.ChordId]
X if chrd.Fret0 != -1:
X n_strs[0]+=str(chrd.Fret0)
X if chrd.Fret1 != -1:
X n_strs[1]+=str(chrd.Fret1)
X if chrd.Fret2 != -1:
X n_strs[2]+=str(chrd.Fret2)
X if chrd.Fret3 != -1:
X n_strs[3]+=str(chrd.Fret3)
X if chrd.Fret4 != -1:
X n_strs[4]+=str(chrd.Fret4)
X if chrd.Fret5 != -1:
X n_strs[5]+=str(chrd.Fret5)
X n_len=max([len(st) for st in n_strs])
X #Fill lines with hyphens
X for i in range(0,6):
X self.line[i] += n_strs[i]+(n_len+1-len(n_strs[i]))*"-"
X self.line_cnt += n_len+1
X def close(self):
X for i in range(5,-1,-1):
X print self.line[i]
X
s=sng_file(sys.argv[1])
for sl in s.SongLevels:
X print ""
X print "Song level "+str(sl.Difficulty)
X t=TabWriter(s)
X #Generate list of objects to be printed in a tab
X objs=s._beats+sl.Notes
X #Sort objects according to time
X objs.sort(cmp=lambda x,y: cmp(x.Time, y.Time))
X for n in objs:
X t.add(n)
X t.close()
SHAR_EOF
(set 20 13 02 28 10 04 03 'sng2tab.py'
eval "${shar_touch}") && \
chmod 0744 'sng2tab.py'
if test $? -ne 0
then ${echo} "restore of sng2tab.py failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sng2tab.py': 'MD5 check failed'
) << \SHAR_EOF
c77c3e7fe81d8facd023d0d73f8a695f sng2tab.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'sng2tab.py'` -ne 14604 && \
${echo} "restoration warning: size of 'sng2tab.py' is not 14604"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0