Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Rocksmith SNG file to guitar ASCII TAB converter

1,908 views
Skip to first unread message

Anonymous

unread,
Feb 28, 2013, 9:49:05 AM2/28/13
to
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

Anonymous

unread,
Mar 2, 2013, 3:23:25 PM3/2/13
to
Author of the first version has not considered information published in
http://code.google.com/p/rocksmith-custom-song-creator/wiki/DefiningCustomSongXmlFiles
The first version generates tabs for each SongLevel separately.
However SongLevel has different meaning for different phrases.

Therefore some phrases are lacking in the tab generated for the highest SongLevel.

To generate tab for the full version of the song we just need to select the highest
SongLevel for each phrase separately, and this is what my version does.

Enjoy, and please remember that this code is to help you learning with Rocksmith,
not to allow you to steal tabs from Rocksmith songs!


#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.81.3).
# 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=_sh05409
# Made on 2013-03-02 6:23 GMT by <nob...@somewhere.in.the.heaven>.
# Source directory was `/opt/sng2py'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 14896 -rwxr--r-- sng2tab.py
# with TabWriter class added
X print "song title: "+self._songTitle.strip("\x00")
X self._arrangement = sf.br.ReadBytes(32)
X print "arrangement: "+self._arrangement.strip("\x00")
X self._artist = sf.br.ReadBytes(32)
X print "artist: "+self._artist.strip("\x00")
X print
#Read the song
s=sng_file(sys.argv[1])
#Prepare lists of object to be put into tab
objs=s._beats
#Now we iterate through the SongLevels and add only notes
#belonging to the highest difficulty level for the particular phrase
for sl in s.SongLevels:
X for note in sl.Notes:
X if sl.Difficulty == s._phrases[note.PhraseId].MaxDifficulty:
X objs.append(note)
#Sort objects (to align notes and measure boundaries)
objs.sort(cmp=lambda x,y: cmp(x.Time, y.Time))
#Now generate the tab
t=TabWriter(s)
for n in objs:
X t.add(n)
t.close()
SHAR_EOF
(set 20 13 03 02 13 22 11 '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
8db767677b4319d4ad8e74d3e66ea110 sng2tab.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'sng2tab.py'` -ne 14896 && \
${echo} "restoration warning: size of 'sng2tab.py' is not 14896"

Anonymous

unread,
Mar 17, 2013, 7:03:59 AM3/17/13
to
These are sources of a tool which helps you to learn difficult songs
in Rocksmith http://rocksmith.ubi.com

This tool converts songs defined in a SNG file into ASCII TAB.
Additionally it extracts chord definitions and writes them at the begining
of the TAB.
Each chord is written in two forms. First - as it appears in the tab, and 2nd
as a "chord map" with fingering - if available.

Below are examples of two chords:
Cm7
3 4 5 1 2 3 4
3 |-1-|---|---| - X||---|---|---|---|
4 |---|-2-|---| 4 ||---|---|---|-o-|
3 |-1-|---|---| 2 ||---|-o-|---|---|
5 |---|---|-3-| - X||---|---|---|---|
3 |-1-|---|---| - X||---|---|---|---|
- X|---|---|---| - X||---|---|---|---|

In the second chord neither chord name nor fingering was defined, and therefore
no chord name and finger numbers are written.

To prepare tabs of songs included in an additional DLC, you have to unpack
the DLC with RocksmithCustomSongToolkit
http://code.google.com/p/rocksmith-custom-song-creator/
to produce the SNG file.

Code of the tool is based on the C# code from Rocksmith Custom Song project
http://code.google.com/p/rocksmith-custom-song-creator/
It is translated from C# to Python and supplemented with TabWriter and
ChordWriter classes.

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
Of course you can also write the generated tab to the file:
$ sng2tab.py /path/to/file.sng max_line_length > output_file.tab

Please remember that this tool is created to help legitimate Rocksmith users
to learn difficult songs, not to steal tabs from Rocksmith songs and DLCs.




#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.35.3).
# 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=_sh04361
# Made on 2013-03-16 12:36 GMT by <crazy...@my.home.is.my.castle>.
# Source directory was `/opt/rocksmith'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 19308 -rwxr--r-- sng2tab.py
# with TabWriter and ChordWriter classes added
# The code is licensed under GNU GPL v3 (as the original
# SngFile.cs )
#
import struct
import sys
#Read the maximum length of input line
max_len = int(sys.argv[2])
X self.Fret=[]
X self.Fret.append(sf.br.ReadInt32()) #0
X self.Fret.append(sf.br.ReadInt32()) #1
X self.Fret.append(sf.br.ReadInt32()) #2
X self.Fret.append(sf.br.ReadInt32()) #3
X self.Fret.append(sf.br.ReadInt32()) #4
X self.Fret.append(sf.br.ReadInt32()) #5
X self.Finger=[]
X self.Finger.append(sf.br.ReadInt32()) #0
X self.Finger.append(sf.br.ReadInt32()) #1
X self.Finger.append(sf.br.ReadInt32()) #2
X self.Finger.append(sf.br.ReadInt32()) #3
X self.Finger.append(sf.br.ReadInt32()) #4
X self.Finger.append(sf.br.ReadInt32()) #5
X self.Note=[]
X self.Note.append(sf.br.ReadInt32()) #0
X self.Note.append(sf.br.ReadInt32()) #1
X self.Note.append(sf.br.ReadInt32()) #2
X self.Note.append(sf.br.ReadInt32()) #3
X self.Note.append(sf.br.ReadInt32()) #4
X self.Note.append(sf.br.ReadInt32()) #5
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 self._arrangement = sf.br.ReadBytes(32)
X
class ChordWriter:
X def __init__(self, sf):
X self.sf = sf
X self.line = ["","","","","","","",""]
X def add(self, c):
X #Find the range of frets used in the chord (excluding 0 - empty string
X # and -1 - muted string)
X n_line = " "+ c._name.strip("\x00") # Name of the chord (if provided)
X min_fret = 1000 # Number higher than reasonable number of frets
X max_fret = -1
X empty_string = False
X #First we print the chord as it appears in the tab
X c_lines = ["","","","","",""]
X for i in range(0,6):
X if c.Fret[i] != -1:
X c_lines[i]+="%2.1d " % c.Fret[i]
X else:
X c_lines[i]+=" - "
X for i in range(0, 6):
X f=c.Fret[i]
X if (f > 0) and (f >max_fret):
X max_fret = f
X if f==0:
X empty_string = True
X if (f>0) and (f < min_fret):
X min_fret = f
X # Now we can decide how to print the chord
X if max_fret < 5:
X # Print starting from the 1st fret
X # Print at least 3 frets
X if max_fret < 3:
X max_fret = 3
X if min_fret >= 1:
X min_fret = 1
X #Now min_fret and max_fret are adjusted, and we can
X #prepare the chord image
X #In the first column we mark muted and empty strings
X for i in range(0, 6):
X if c.Fret[i]==-1:
X c_lines[i]+="X"
X elif c.Fret[i]==0:
X c_lines[i]+="O"
X else:
X c_lines[i]+=" "
X #Now we put either the nut mark "||" or the fret marks
X if min_fret == 1:
X for i in range(0, 6):
X c_lines[i]+="||"
X fn_line = " " # No fret numbers are printed
X else:
X for i in range(0, 6):
X c_lines[i]+="|"
X fn_line = " "
X #Now we put the finger marks (if finger numbers are given)
X #or simply "o" marks at proper frets
X for f in range(min_fret, max_fret+1):
X if min_fret != 0:
X #Add fret numbers
X fn_line += " %2.1d" % f
X for i in range(0, 6):
X if f == c.Fret[i]:
X if c.Finger[i]==-1:
X c_lines[i]+="-o-|"
X else:
X c_lines[i]+="-"+str(c.Finger[i])+"-|"
X else:
X c_lines[i]+="---|"
X #Check that all lines have the same length
X cl = len(c_lines[0])
X for i in range(1, 6):
X if len(c_lines[i]) != cl:
X print "Error in writing of chord!"
X sys.exit(1)
X #Chord image is ready. Now we should adjust lines.
X #We check if the length of the chord name is longer than
X #the chord definition, and enlarge definition if necessary
X if len(n_line) > cl:
X adj_len = len(n_line)-cl
X for i in range(0,6):
X c_lines[i] += adj_len * " "
X cl += adj_len
X #It may happen, that n_line, and/or fn_line
X #are shorter, so fill them with spaces
X n_line += (cl-len(n_line))*" "
X fn_line += (cl-len(fn_line))*" "
X #Now check, if the chord fits in the output line
X if len(self.line[0])+cl+2 < max_len:
X self.line[0]+=" "+n_line
X self.line[1]+=" "+fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i]+= " "+c_lines[i]
X else:
X for i in range(0, 8):
X print self.line[i]
X print ""
X self.line[0] = n_line
X self.line[1] = fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i] = c_lines[i]
X def close(self):
X if len(self.line[0]):
X for i in range(0, 8):
X print self.line[i]
X print ""
X
# Class below generates the tab
# We add notes/chords as long, as the tab is not longer
# then the limit defined as max_len
X if nt.Tremolo==1:
X n_str += "T"
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 for i in range(0,6):
X if chrd.Fret[i] != -1:
X n_strs[i]+=str(chrd.Fret[i])
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
#Read the song
s=sng_file(sys.argv[1])
#Write song description
md=s.Metadata
print "song title: "+md._songTitle.strip("\x00")
print "arrangement: "+md._arrangement.strip("\x00")
print "artist: "+md._artist.strip("\x00")
if md.Tuning == 0:
X print "Tuning: Standard E"
elif md.Tuning == 1:
X print "Tuning: Drop D"
else:
X print "Tuning undecoded - number: "+str(md.Tuning)
print
print "Chord definitions:"
#First print the chord templates
#Prepare the chord writer object
cwr=ChordWriter(s)
for c in s._chordTemplates:
X cwr.add(c)
#Close the writer to print the last line
cwr.close()
print
print "Tab:"
#Prepare the tab writer object
t=TabWriter(s)
#Prepare lists of object to be put into tab
objs=s._beats
#Now we iterate through the SongLevels and add only notes
#belonging to the highest difficulty level for the particular phrase
for sl in s.SongLevels:
X for note in sl.Notes:
X if sl.Difficulty == s._phrases[note.PhraseId].MaxDifficulty:
X objs.append(note)
#Sort objects (to align notes and measure boundaries)
objs.sort(cmp=lambda x,y: cmp(x.Time, y.Time))
for n in objs:
X t.add(n)
t.close()
SHAR_EOF
(set 20 13 03 16 19 26 47 '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
7ca42271316c3a020f14c38b84285b5d sng2tab.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'sng2tab.py'` -ne 19308 && \
${echo} "restoration warning: size of 'sng2tab.py' is not 19308"

Anonymous

unread,
Mar 17, 2013, 7:04:00 AM3/17/13
to
These are sources of a tool which helps you to learn difficult songs
in Rocksmith http://rocksmith.ubi.com

This tool converts songs defined in a SNG file into ASCII TAB.
Additionally it extracts chord definitions and writes them at the begining
of the TAB.
Each chord is written in two forms. First - as it appears in the tab, and 2nd
as a "chord map" with fingering - if available.

Below are examples of two chords:
Cm7
3 4 5 1 2 3 4
3 |-1-|---|---| - X||---|---|---|---|
4 |---|-2-|---| 4 ||---|---|---|-o-|
3 |-1-|---|---| 2 ||---|-o-|---|---|
5 |---|---|-3-| - X||---|---|---|---|
3 |-1-|---|---| - X||---|---|---|---|
- X|---|---|---| - X||---|---|---|---|

In the second chord neither chord name nor fingering was defined, and therefore
no chord name and no finger numbers are written.

To prepare tabs of songs included in an additional DLC, you have to unpack
the DLC with RocksmithCustomSongToolkit
http://code.google.com/p/rocksmith-custom-song-creator/
to produce the SNG file.

Code of the toolis 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 and
ChordWriter classes.

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
Of course you can also write the generated tab to the file:
$ sng2tab.py /path/to/file.sng max_line_length > output_file.tab

Please remember that this tool is created to help legitimate Rocksmith users
to learn difficult songs, not to steal tabs from Rocksmith songs and DLCs.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.31.5).
# 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=_sh10421
# Made on 2013-03-16 17:29 GMT by <some...@my.home.is.my.castle.org>.
# Source directory was `/opt/rocksmith'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 19308 -rwxr--r-- sng2tab.py
# with TabWriter and ChordWriter classes added
# The code is licensed under GNU GPL v3 (as the original
# SngFile.cs )
#
import struct
import sys
#Read the maximum length of input line
max_len = int(sys.argv[2])
X self.Fret=[]
X self.Fret.append(sf.br.ReadInt32()) #0
X self.Fret.append(sf.br.ReadInt32()) #1
X self.Fret.append(sf.br.ReadInt32()) #2
X self.Fret.append(sf.br.ReadInt32()) #3
X self.Fret.append(sf.br.ReadInt32()) #4
X self.Fret.append(sf.br.ReadInt32()) #5
X self.Finger=[]
X self.Finger.append(sf.br.ReadInt32()) #0
X self.Finger.append(sf.br.ReadInt32()) #1
X self.Finger.append(sf.br.ReadInt32()) #2
X self.Finger.append(sf.br.ReadInt32()) #3
X self.Finger.append(sf.br.ReadInt32()) #4
X self.Finger.append(sf.br.ReadInt32()) #5
X self.Note=[]
X self.Note.append(sf.br.ReadInt32()) #0
X self.Note.append(sf.br.ReadInt32()) #1
X self.Note.append(sf.br.ReadInt32()) #2
X self.Note.append(sf.br.ReadInt32()) #3
X self.Note.append(sf.br.ReadInt32()) #4
X self.Note.append(sf.br.ReadInt32()) #5
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 self._arrangement = sf.br.ReadBytes(32)
X self._artist = sf.br.ReadBytes(32)
class ChordWriter:
X def __init__(self, sf):
X self.sf = sf
X self.line = ["","","","","","","",""]
X def add(self, c):
X #Find the range of frets used in the chord (excluding 0 - empty string
X # and -1 - muted string)
X n_line = " "+ c._name.strip("\x00") # Name of the chord (if provided)
X min_fret = 1000 # Number higher than reasonable number of frets
X max_fret = -1
X empty_string = False
X #First we print the chord as it appears in the tab
X c_lines = ["","","","","",""]
X for i in range(0,6):
X if c.Fret[i] != -1:
X c_lines[i]+="%2.1d " % c.Fret[i]
X else:
X c_lines[i]+=" - "
X for i in range(0, 6):
X f=c.Fret[i]
X if (f > 0) and (f >max_fret):
X max_fret = f
X if f==0:
X empty_string = True
X if (f>0) and (f < min_fret):
X min_fret = f
X # Now we can decide how to print the chord
X if max_fret < 5:
X # Print starting from the 1st fret
X # Print at least 3 frets
X if max_fret < 3:
X max_fret = 3
X if min_fret >= 1:
X min_fret = 1
X #Now min_fret and max_fret are adjusted, and we can
X #prepare the chord image
X #In the first column we mark muted and empty strings
X for i in range(0, 6):
X if c.Fret[i]==-1:
X c_lines[i]+="X"
X elif c.Fret[i]==0:
X c_lines[i]+="O"
X else:
X c_lines[i]+=" "
X #Now we put either the nut mark "||" or the fret marks
X if min_fret == 1:
X for i in range(0, 6):
X c_lines[i]+="||"
X fn_line = " " # No fret numbers are printed
X else:
X for i in range(0, 6):
X c_lines[i]+="|"
X fn_line = " "
X #Now we put the finger marks (if finger numbers are given)
X #or simply "o" marks at proper frets
X for f in range(min_fret, max_fret+1):
X if min_fret != 0:
X #Add fret numbers
X fn_line += " %2.1d" % f
X for i in range(0, 6):
X if f == c.Fret[i]:
X if c.Finger[i]==-1:
X c_lines[i]+="-o-|"
X else:
X c_lines[i]+="-"+str(c.Finger[i])+"-|"
X else:
X c_lines[i]+="---|"
X #Check that all lines have the same length
X cl = len(c_lines[0])
X for i in range(1, 6):
X if len(c_lines[i]) != cl:
X print "Error in writing of chord!"
X sys.exit(1)
X #Chord image is ready. Now we should adjust lines.
X #We check if the length of the chord name is longer than
X #the chord definition, and enlarge definition if necessary
X if len(n_line) > cl:
X adj_len = len(n_line)-cl
X for i in range(0,6):
X c_lines[i] += adj_len * " "
X cl += adj_len
X #It may happen, that n_line, and/or fn_line
X #are shorter, so fill them with spaces
X n_line += (cl-len(n_line))*" "
X fn_line += (cl-len(fn_line))*" "
X #Now check, if the chord fits in the output line
X if len(self.line[0])+cl+2 < max_len:
X self.line[0]+=" "+n_line
X self.line[1]+=" "+fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i]+= " "+c_lines[i]
X else:
X for i in range(0, 8):
X print self.line[i]
X print ""
X self.line[0] = n_line
X self.line[1] = fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i] = c_lines[i]
X def close(self):
X if len(self.line[0]):
X for i in range(0, 8):
X print self.line[i]
X print ""
X
# Class below generates the tab
# We add notes/chords as long, as the tab is not longer
# then the limit defined as max_len
X if nt.Tremolo==1:
X n_str += "T"
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 for i in range(0,6):
X if chrd.Fret[i] != -1:
X n_strs[i]+=str(chrd.Fret[i])
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
#Read the song
s=sng_file(sys.argv[1])
#Write song description
md=s.Metadata
print "song title: "+md._songTitle.strip("\x00")
print "arrangement: "+md._arrangement.strip("\x00")
print "artist: "+md._artist.strip("\x00")
if md.Tuning == 0:
X print "Tuning: Standard E"
elif md.Tuning == 1:
X print "Tuning: Drop D"
else:
X print "Tuning undecoded - number: "+str(md.Tuning)
print
print "Chord definitions:"
#First print the chord templates
#Prepare the chord writer object
cwr=ChordWriter(s)
for c in s._chordTemplates:
X cwr.add(c)
#Close the writer to print the last line
cwr.close()
print
print "Tab:"
#Prepare the tab writer object
t=TabWriter(s)
#Prepare lists of object to be put into tab
objs=s._beats
#Now we iterate through the SongLevels and add only notes
#belonging to the highest difficulty level for the particular phrase
for sl in s.SongLevels:
X for note in sl.Notes:
X if sl.Difficulty == s._phrases[note.PhraseId].MaxDifficulty:
X objs.append(note)
#Sort objects (to align notes and measure boundaries)
objs.sort(cmp=lambda x,y: cmp(x.Time, y.Time))
for n in objs:
X t.add(n)
t.close()
SHAR_EOF
(set 20 13 03 16 19 26 47 '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
7ca42271316c3a020f14c38b84285b5d sng2tab.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'sng2tab.py'` -ne 19308 && \
${echo} "restoration warning: size of 'sng2tab.py' is not 19308"

Anonymous

unread,
Mar 21, 2013, 7:29:00 PM3/21/13
to
As some people were complaining about inconvenient use of the converter,
this is a new version with simplistic GUI.
Now you can select multiple files, and convert them to the notepad-like
window, in which you can view the tab, but also select a part, copy it,
and paste into another window.

These are sources of a tool which helps you to learn difficult songs
in Rocksmith http://rocksmith.ubi.com

This tool converts songs defined in a SNG file into ASCII TAB.
Additionally it extracts chord definitions and writes them at the begining
of the TAB.
Each chord is written in two forms. First - as it appears in the tab, and 2nd
as a "chord map" with fingering - if available.

Below are examples of two chords:
Cm7
3 4 5 1 2 3 4
3 |-1-|---|---| - X||---|---|---|---|
4 |---|-2-|---| 4 ||---|---|---|-o-|
3 |-1-|---|---| 2 ||---|-o-|---|---|
5 |---|---|-3-| - X||---|---|---|---|
3 |-1-|---|---| - X||---|---|---|---|
- X|---|---|---| - X||---|---|---|---|

In the second chord neither chord name nor fingering was defined, and therefore
no chord name and finger numbers are written.

To prepare tabs of songs included in an additional DLC, you have to unpack
the DLC with RocksmithCustomSongToolkit
http://code.google.com/p/rocksmith-custom-song-creator/
to produce the SNG file.

Code of the tool is based on the C# code from Rocksmith Custom Song project
http://code.google.com/p/rocksmith-custom-song-creator/
It is translated from C# to Python and supplemented with TabWriter and
ChordWriter classes.

This code is licensed under GNU GPL v3, so I license my code under the same
license.

Please remember that this tool is created to help legitimate Rocksmith users
to learn difficult songs, not to steal tabs from Rocksmith songs and DLCs.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.36.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=_sh13342
# Made on 2013-03-20 15:30 GMT by <some...@wherever.could.it.be>.
# Source directory was `/opt/rocksmith'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 21028 -rwxr--r-- sng2tab_gui.py
# ============= sng2tab_gui.py ==============
if test -n "${keep_file}" && test -f 'sng2tab_gui.py'
then
${echo} "x - SKIPPING sng2tab_gui.py (file already exists)"
else
${echo} "x - extracting sng2tab_gui.py (text)"
sed 's/^X//' << 'SHAR_EOF' > 'sng2tab_gui.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/
# with TabWriter and ChordWriter classes added
# The code is licensed under GNU GPL v3 (as the original
# SngFile.cs )
#
import struct
import sys
#Read the maximum length of input line
#max_len = int(sys.argv[2])
X self.Fret=[]
X self.Fret.append(sf.br.ReadInt32()) #0
X self.Fret.append(sf.br.ReadInt32()) #1
X self.Fret.append(sf.br.ReadInt32()) #2
X self.Fret.append(sf.br.ReadInt32()) #3
X self.Fret.append(sf.br.ReadInt32()) #4
X self.Fret.append(sf.br.ReadInt32()) #5
X self.Finger=[]
X self.Finger.append(sf.br.ReadInt32()) #0
X self.Finger.append(sf.br.ReadInt32()) #1
X self.Finger.append(sf.br.ReadInt32()) #2
X self.Finger.append(sf.br.ReadInt32()) #3
X self.Finger.append(sf.br.ReadInt32()) #4
X self.Finger.append(sf.br.ReadInt32()) #5
X self.Note=[]
X self.Note.append(sf.br.ReadInt32()) #0
X self.Note.append(sf.br.ReadInt32()) #1
X self.Note.append(sf.br.ReadInt32()) #2
X self.Note.append(sf.br.ReadInt32()) #3
X self.Note.append(sf.br.ReadInt32()) #4
X self.Note.append(sf.br.ReadInt32()) #5
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 self._arrangement = sf.br.ReadBytes(32)
X
class ChordWriter:
X def __init__(self, sf):
X self.sf = sf
X self.line = ["","","","","","","",""]
X def add(self, c):
X #Find the range of frets used in the chord (excluding 0 - empty string
X # and -1 - muted string)
X n_line = " "+ c._name.strip("\x00") # Name of the chord (if provided)
X min_fret = 1000 # Number higher than reasonable number of frets
X max_fret = -1
X empty_string = False
X #First we print the chord as it appears in the tab
X c_lines = ["","","","","",""]
X for i in range(0,6):
X if c.Fret[i] != -1:
X c_lines[i]+="%2.1d " % c.Fret[i]
X else:
X c_lines[i]+=" - "
X for i in range(0, 6):
X f=c.Fret[i]
X if (f > 0) and (f >max_fret):
X max_fret = f
X if f==0:
X empty_string = True
X if (f>0) and (f < min_fret):
X min_fret = f
X # Now we can decide how to print the chord
X if max_fret < 5:
X # Print starting from the 1st fret
X # Print at least 3 frets
X if max_fret < 3:
X max_fret = 3
X if min_fret >= 1:
X min_fret = 1
X #Now min_fret and max_fret are adjusted, and we can
X #prepare the chord image
X #In the first column we mark muted and empty strings
X for i in range(0, 6):
X if c.Fret[i]==-1:
X c_lines[i]+="X"
X elif c.Fret[i]==0:
X c_lines[i]+="O"
X else:
X c_lines[i]+=" "
X #Now we put either the nut mark "||" or the fret marks
X if min_fret == 1:
X for i in range(0, 6):
X c_lines[i]+="||"
X fn_line = " " # No fret numbers are printed
X else:
X for i in range(0, 6):
X c_lines[i]+="|"
X fn_line = " "
X #Now we put the finger marks (if finger numbers are given)
X #or simply "o" marks at proper frets
X for f in range(min_fret, max_fret+1):
X if min_fret != 0:
X #Add fret numbers
X fn_line += " %2.1d" % f
X for i in range(0, 6):
X if f == c.Fret[i]:
X if c.Finger[i]==-1:
X c_lines[i]+="-o-|"
X else:
X c_lines[i]+="-"+str(c.Finger[i])+"-|"
X else:
X c_lines[i]+="---|"
X #Check that all lines have the same length
X cl = len(c_lines[0])
X for i in range(1, 6):
X if len(c_lines[i]) != cl:
X my_print("Error in writing of chord!")
X sys.exit(1)
X #Chord image is ready. Now we should adjust lines.
X #We check if the length of the chord name is longer than
X #the chord definition, and enlarge definition if necessary
X if len(n_line) > cl:
X adj_len = len(n_line)-cl
X for i in range(0,6):
X c_lines[i] += adj_len * " "
X cl += adj_len
X #It may happen, that n_line, and/or fn_line
X #are shorter, so fill them with spaces
X n_line += (cl-len(n_line))*" "
X fn_line += (cl-len(fn_line))*" "
X #Now check, if the chord fits in the output line
X if len(self.line[0])+cl+2 < max_len:
X self.line[0]+=" "+n_line
X self.line[1]+=" "+fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i]+= " "+c_lines[i]
X else:
X for i in range(0, 8):
X my_print(self.line[i])
X my_print("")
X self.line[0] = n_line
X self.line[1] = fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i] = c_lines[i]
X def close(self):
X if len(self.line[0]):
X for i in range(0, 8):
X my_print(self.line[i])
X my_print("")
X
# Class below generates the tab
# We add notes/chords as long, as the tab is not longer
# then the limit defined as max_len
# 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 my_print(self.line[i][0:(self.last_measure_pos+1)])
X my_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.Tremolo==1:
X n_str += "T"
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 for i in range(0,6):
X if chrd.Fret[i] != -1:
X n_strs[i]+=str(chrd.Fret[i])
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 my_print(self.line[i])
X
def process_file(file_name):
X #Read the song
X s=sng_file(file_name)
X #Write song description
X md=s.Metadata
X my_print("song title: "+md._songTitle.strip("\x00"))
X my_print("arrangement: "+md._arrangement.strip("\x00"))
X my_print("artist: "+md._artist.strip("\x00"))
X if md.Tuning == 0:
X my_print("Tuning: Standard E")
X elif md.Tuning == 1:
X my_print("Tuning: Drop D")
X else:
X my_print("Tuning undecoded - number: "+str(md.Tuning))
X my_print("")
X my_print("Chord definitions:")
X #First print the chord templates
X #Prepare the chord writer object
X cwr=ChordWriter(s)
X for c in s._chordTemplates:
X cwr.add(c)
X #Close the writer to print the last line
X cwr.close()
X my_print("")
X my_print("Tab:")
X #Prepare the tab writer object
X t=TabWriter(s)
X #Prepare lists of object to be put into tab
X objs=s._beats
X #Now we iterate through the SongLevels and add only notes
X #belonging to the highest difficulty level for the particular phrase
X for sl in s.SongLevels:
X for note in sl.Notes:
X if sl.Difficulty == s._phrases[note.PhraseId].MaxDifficulty:
X objs.append(note)
X #Sort objects (to align notes and measure boundaries)
X objs.sort(cmp=lambda x,y: cmp(x.Time, y.Time))
X for n in objs:
X t.add(n)
X t.close()
X my_print("")
X my_print("")
X
# Below is implementation of a simple GUI. It allows you to select one or a few SNG files and generates
# tab in a notepad-like window, from which you can later copy the text to another program
#
import Tkinter as t
import tkMessageBox as tb
import tkFileDialog as td
X
def my_print(line):
X text.insert( t.END, line+"\n")
def clear():
X text.delete("1.0", t.END)
def process():
X global max_len
X max_len = int(v_width.get())
X file_list = td.askopenfilename(multiple=True)
X if file_list:
X for f in file_list:
X process_file(f)
X
root = t.Tk()
root.title("Rocksmith SNG file to ASCII TAB converter")
frame = t.Frame(root)
frame.pack(fill="both", expand=1)
frame2 = t.Frame(frame)
frame2.pack(side=t.TOP, fill="both")
label = t.Label(frame2, text="Tab width", relief=t.RAISED )
label.pack(side=t.LEFT)
v_width=t.StringVar()
v_width.set("80")
width=t.Entry(frame2,textvariable=v_width)
width.pack(side=t.LEFT)
filesel=t.Button(frame2, text ="Select files to convert", command = process)
filesel.pack(side=t.LEFT)
bclean=t.Button(frame2, text ="Clear output", command = clear)
bclean.pack(side=t.LEFT)
frame3=t.Frame(frame)
frame3.pack(side=t.TOP, fill="both", expand=1)
scroller = t.Scrollbar(frame3)
scroller.pack(side=t.RIGHT, fill="both")
text = t.Text(frame3, yscrollcommand=scroller.set)
text.pack(side=t.LEFT, fill="both", expand=1)
scroller.config(command=text.yview)
root.mainloop()
SHAR_EOF
(set 20 13 03 21 20 45 25 'sng2tab_gui.py'
eval "${shar_touch}") && \
chmod 0744 'sng2tab_gui.py'
if test $? -ne 0
then ${echo} "restore of sng2tab_gui.py failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sng2tab_gui.py': 'MD5 check failed'
) << \SHAR_EOF
5a86fd3654e6a59c76aca2e01e6e4016 sng2tab_gui.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'sng2tab_gui.py'` -ne 21028 && \
${echo} "restoration warning: size of 'sng2tab_gui.py' is not 21028"

Anonymous

unread,
Mar 21, 2013, 8:29:14 PM3/21/13
to
The version with GUI published earlier appeared to work correctly in Linux
and in Windows/Cygwin, but didn't work in Python on raw Windows.
The reason is that the askopenfilename(multiple=True) function
in Windows returns the string containing space separated filepaths, instead
of a tuple containing those filepaths.
To correct operation of the program in Windows it was necessary
to change the "process" function. Instead of:

def process():
global max_len
max_len = int(v_width.get())
file_list = td.askopenfilename(multiple=True)
if file_list:
for f in file_list:
process_file(f)

it is necessary to implent it as follows:

def process():
global max_len
max_len = int(v_width.get())
file_list = td.askopenfilename(multiple=True)
if file_list:
for f in file_list.split(" "):
process_file(f)

I'm afraid that the above correction will not work for filepaths containing spaces,
so any better solution is welcome.
Code of the tool is based on the C# code from Rocksmith Custom Song project
http://code.google.com/p/rocksmith-custom-song-creator/
It is translated from C# to Python and supplemented with TabWriter and
ChordWriter classes.

This code is licensed under GNU GPL v3, so I license my code under the same
license.

Please remember that this tool is created to help legitimate Rocksmith users
to learn difficult songs, not to steal tabs from Rocksmith songs and DLCs.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.3.12).
# 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=_sh04844
# Made on 2013-03-21 17:21 GMT by <nob...@nowhere.com>.
# Source directory was `/opt/rocksmith'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 21039 -rwxrwxrwx sng2tab_gui.py
# ============= sng2tab_gui.py ==============
if test -n "${keep_file}" && test -f 'sng2tab_gui.py'
then
${echo} "x - SKIPPING sng2tab_gui.py (file already exists)"
else
${echo} "x - extracting sng2tab_gui.py (text)"
sed 's/^X//' << 'SHAR_EOF' > 'sng2tab_gui.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/
# with TabWriter and ChordWriter classes added
# The code is licensed under GNU GPL v3 (as the original
# SngFile.cs )
#
import struct
import sys
#Read the maximum length of input line
#max_len = int(sys.argv[2])
X self.Fret=[]
X self.Fret.append(sf.br.ReadInt32()) #0
X self.Fret.append(sf.br.ReadInt32()) #1
X self.Fret.append(sf.br.ReadInt32()) #2
X self.Fret.append(sf.br.ReadInt32()) #3
X self.Fret.append(sf.br.ReadInt32()) #4
X self.Fret.append(sf.br.ReadInt32()) #5
X self.Finger=[]
X self.Finger.append(sf.br.ReadInt32()) #0
X self.Finger.append(sf.br.ReadInt32()) #1
X self.Finger.append(sf.br.ReadInt32()) #2
X self.Finger.append(sf.br.ReadInt32()) #3
X self.Finger.append(sf.br.ReadInt32()) #4
X self.Finger.append(sf.br.ReadInt32()) #5
X self.Note=[]
X self.Note.append(sf.br.ReadInt32()) #0
X self.Note.append(sf.br.ReadInt32()) #1
X self.Note.append(sf.br.ReadInt32()) #2
X self.Note.append(sf.br.ReadInt32()) #3
X self.Note.append(sf.br.ReadInt32()) #4
X self.Note.append(sf.br.ReadInt32()) #5
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 self._arrangement = sf.br.ReadBytes(32)
X
class ChordWriter:
X def __init__(self, sf):
X self.sf = sf
X self.line = ["","","","","","","",""]
X def add(self, c):
X #Find the range of frets used in the chord (excluding 0 - empty string
X # and -1 - muted string)
X n_line = " "+ c._name.strip("\x00") # Name of the chord (if provided)
X min_fret = 1000 # Number higher than reasonable number of frets
X max_fret = -1
X empty_string = False
X #First we print the chord as it appears in the tab
X c_lines = ["","","","","",""]
X for i in range(0,6):
X if c.Fret[i] != -1:
X c_lines[i]+="%2.1d " % c.Fret[i]
X else:
X c_lines[i]+=" - "
X for i in range(0, 6):
X f=c.Fret[i]
X if (f > 0) and (f >max_fret):
X max_fret = f
X if f==0:
X empty_string = True
X if (f>0) and (f < min_fret):
X min_fret = f
X # Now we can decide how to print the chord
X if max_fret < 5:
X # Print starting from the 1st fret
X # Print at least 3 frets
X if max_fret < 3:
X max_fret = 3
X if min_fret >= 1:
X min_fret = 1
X #Now min_fret and max_fret are adjusted, and we can
X #prepare the chord image
X #In the first column we mark muted and empty strings
X for i in range(0, 6):
X if c.Fret[i]==-1:
X c_lines[i]+="X"
X elif c.Fret[i]==0:
X c_lines[i]+="O"
X else:
X c_lines[i]+=" "
X #Now we put either the nut mark "||" or the fret marks
X if min_fret == 1:
X for i in range(0, 6):
X c_lines[i]+="||"
X fn_line = " " # No fret numbers are printed
X else:
X for i in range(0, 6):
X c_lines[i]+="|"
X fn_line = " "
X #Now we put the finger marks (if finger numbers are given)
X #or simply "o" marks at proper frets
X for f in range(min_fret, max_fret+1):
X if min_fret != 0:
X #Add fret numbers
X fn_line += " %2.1d" % f
X for i in range(0, 6):
X if f == c.Fret[i]:
X if c.Finger[i]==-1:
X c_lines[i]+="-o-|"
X else:
X c_lines[i]+="-"+str(c.Finger[i])+"-|"
X else:
X c_lines[i]+="---|"
X #Check that all lines have the same length
X cl = len(c_lines[0])
X for i in range(1, 6):
X if len(c_lines[i]) != cl:
X my_print("Error in writing of chord!")
X sys.exit(1)
X #Chord image is ready. Now we should adjust lines.
X #We check if the length of the chord name is longer than
X #the chord definition, and enlarge definition if necessary
X if len(n_line) > cl:
X adj_len = len(n_line)-cl
X for i in range(0,6):
X c_lines[i] += adj_len * " "
X cl += adj_len
X #It may happen, that n_line, and/or fn_line
X #are shorter, so fill them with spaces
X n_line += (cl-len(n_line))*" "
X fn_line += (cl-len(fn_line))*" "
X #Now check, if the chord fits in the output line
X if len(self.line[0])+cl+2 < max_len:
X self.line[0]+=" "+n_line
X self.line[1]+=" "+fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i]+= " "+c_lines[i]
X else:
X for i in range(0, 8):
X my_print(self.line[i])
X my_print("")
X self.line[0] = n_line
X self.line[1] = fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i] = c_lines[i]
X def close(self):
X if len(self.line[0]):
X for i in range(0, 8):
X my_print(self.line[i])
X my_print("")
X
# Class below generates the tab
# We add notes/chords as long, as the tab is not longer
# then the limit defined as max_len
# 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 my_print(self.line[i][0:(self.last_measure_pos+1)])
X my_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.Tremolo==1:
X n_str += "T"
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 for i in range(0,6):
X if chrd.Fret[i] != -1:
X n_strs[i]+=str(chrd.Fret[i])
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 objs.sort(cmp=lambda x,y: cmp(x.Time, y.Time))
X for n in objs:
X t.add(n)
X t.close()
X my_print("")
X my_print("")
X
# Below is implementation of a simple GUI. It allows you to select one or a few SNG files and generates
# tab in a notepad-like window, from which you can later copy the text to another program
#
import Tkinter as t
import tkMessageBox as tb
import tkFileDialog as td
X
def my_print(line):
X text.insert( t.END, line+"\n")
def clear():
X text.delete("1.0", t.END)
def process():
X global max_len
X max_len = int(v_width.get())
X file_list = td.askopenfilename(multiple=True)
X if file_list:
X for f in file_list.split(" "):
(set 20 13 03 21 23 03 22 'sng2tab_gui.py'
eval "${shar_touch}") && \
chmod 0777 'sng2tab_gui.py'
if test $? -ne 0
then ${echo} "restore of sng2tab_gui.py failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sng2tab_gui.py': 'MD5 check failed'
) << \SHAR_EOF
cb26c9d44663f8b782f1cc72dea6e221 sng2tab_gui.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'sng2tab_gui.py'` -ne 21039 && \
${echo} "restoration warning: size of 'sng2tab_gui.py' is not 21039"

Wojciech Zabołotny

unread,
Mar 22, 2013, 1:01:16 PM3/22/13
to
The above really doesn't work with filepaths containing spaces. The correct implementation
should be as follows:

def process():
global max_len
max_len = int(v_width.get())
file_list = td.askopenfilename(multiple=True)
file_list = root.splitlist(file_list)
if file_list:
for f in file_list:
process_file(f)

I've verified that the above works in Linux, Windows alone and Windows with Cygwin.

--
HTH & Regards,
Wojtek

Anonymous

unread,
Mar 24, 2013, 8:46:24 PM3/24/13
to
I have prepared yet another version of the SNG to tab converter.
I have an e-book reader, which unfortunately does not display correctly
texts with fixed size font.
So the only way to display tabs on it is to convert them to PDF files
with fixed size font.
I tried to convert text tabs to PDF using the a2ps and ps2pdf programs,
but results were not satisfactory (tabs were too small, margins were too big).
Finally I have decided to write a converter which uses the reportlab
library to prepare PDF files well suited for my reader.

To convert all SNG files to PDFs, you may run the following command:
$ for i in *.sng; do ./sng2tab_pdf.py $i `basename $i .sng`.pdf ; done

You may customize the converter changing the size of the page:
line 540: self.pagesize=landscape(A4)
The font - lines 543 and 544:
pdfmetrics.registerFont(TTFont("MyCourier","Courier_New_Bold.ttf"))
self.fontname="MyCourier"
And the fontsize - line 545: self.fontsize=16

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.

Feel free to change or extend this program according to your needs.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.25.131).
# 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=_sh06275
# Made on 2013-03-23 14:12 GMT by <nice...@home.sweet.home>.
# Source directory was `/opt/rocksmith'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 22366 -rwxr--r-- sng2tab_pdf.py
# ============= sng2tab_pdf.py ==============
if test -n "${keep_file}" && test -f 'sng2tab_pdf.py'
then
${echo} "x - SKIPPING sng2tab_pdf.py (file already exists)"
else
${echo} "x - extracting sng2tab_pdf.py (text)"
sed 's/^X//' << 'SHAR_EOF' > 'sng2tab_pdf.py' &&
#!/usr/bin/python
# The code below is a Rocksmith SNG to ASCII TAB in PDF converter
# it is port of SngFile.cs from Rocksmith Custom Song Project
# http://code.google.com/p/rocksmith-custom-song-creator/
# with TabWriter and ChordWriter classes added
X self.Fret=[]
X self.Fret.append(sf.br.ReadInt32()) #0
X self.Fret.append(sf.br.ReadInt32()) #1
X self.Fret.append(sf.br.ReadInt32()) #2
X self.Fret.append(sf.br.ReadInt32()) #3
X self.Fret.append(sf.br.ReadInt32()) #4
X self.Fret.append(sf.br.ReadInt32()) #5
X self.Finger=[]
X self.Finger.append(sf.br.ReadInt32()) #0
X self.Finger.append(sf.br.ReadInt32()) #1
X self.Finger.append(sf.br.ReadInt32()) #2
X self.Finger.append(sf.br.ReadInt32()) #3
X self.Finger.append(sf.br.ReadInt32()) #4
X self.Finger.append(sf.br.ReadInt32()) #5
X self.Note=[]
X self.Note.append(sf.br.ReadInt32()) #0
X self.Note.append(sf.br.ReadInt32()) #1
X self.Note.append(sf.br.ReadInt32()) #2
X self.Note.append(sf.br.ReadInt32()) #3
X self.Note.append(sf.br.ReadInt32()) #4
X self.Note.append(sf.br.ReadInt32()) #5
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 self._arrangement = sf.br.ReadBytes(32)
X
class ChordWriter:
X def __init__(self, sf, pdfwr):
X self.sf = sf
X self.pw=pdfwr
X self.line = ["","","","","","","",""]
X def add(self, c):
X #Find the range of frets used in the chord (excluding 0 - empty string
X # and -1 - muted string)
X n_line = " "+ c._name.strip("\x00") # Name of the chord (if provided)
X min_fret = 1000 # Number higher than reasonable number of frets
X max_fret = -1
X empty_string = False
X #First we print the chord as it appears in the tab
X c_lines = ["","","","","",""]
X for i in range(0,6):
X if c.Fret[i] != -1:
X c_lines[i]+="%2.1d " % c.Fret[i]
X else:
X c_lines[i]+=" - "
X for i in range(0, 6):
X f=c.Fret[i]
X if (f > 0) and (f >max_fret):
X max_fret = f
X if f==0:
X empty_string = True
X if (f>0) and (f < min_fret):
X min_fret = f
X # Now we can decide how to print the chord
X if max_fret < 5:
X # Print starting from the 1st fret
X # Print at least 3 frets
X if max_fret < 3:
X max_fret = 3
X if min_fret >= 1:
X min_fret = 1
X #Now min_fret and max_fret are adjusted, and we can
X #prepare the chord image
X #In the first column we mark muted and empty strings
X for i in range(0, 6):
X if c.Fret[i]==-1:
X c_lines[i]+="X"
X elif c.Fret[i]==0:
X c_lines[i]+="O"
X else:
X c_lines[i]+=" "
X #Now we put either the nut mark "||" or the fret marks
X if min_fret == 1:
X for i in range(0, 6):
X c_lines[i]+="||"
X fn_line = " " # No fret numbers are printed
X else:
X for i in range(0, 6):
X c_lines[i]+="|"
X fn_line = " "
X #Now we put the finger marks (if finger numbers are given)
X #or simply "o" marks at proper frets
X for f in range(min_fret, max_fret+1):
X if min_fret != 0:
X #Add fret numbers
X fn_line += " %2.1d" % f
X for i in range(0, 6):
X if f == c.Fret[i]:
X if c.Finger[i]==-1:
X c_lines[i]+="-o-|"
X else:
X c_lines[i]+="-"+str(c.Finger[i])+"-|"
X else:
X c_lines[i]+="---|"
X #Check that all lines have the same length
X cl = len(c_lines[0])
X for i in range(1, 6):
X if len(c_lines[i]) != cl:
X self.pw.lprint("Error in writing of chord!")
X sys.exit(1)
X #Chord image is ready. Now we should adjust lines.
X #We check if the length of the chord name is longer than
X #the chord definition, and enlarge definition if necessary
X if len(n_line) > cl:
X adj_len = len(n_line)-cl
X for i in range(0,6):
X c_lines[i] += adj_len * " "
X cl += adj_len
X #It may happen, that n_line, and/or fn_line
X #are shorter, so fill them with spaces
X n_line += (cl-len(n_line))*" "
X fn_line += (cl-len(fn_line))*" "
X #Now check, if the chord fits in the output line
X if len(self.line[0])+cl+2 < self.pw.max_len:
X self.line[0]+=" "+n_line
X self.line[1]+=" "+fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i]+= " "+c_lines[i]
X else:
X #Check if the chord fits vertically in the page
X self.pw.ensure_lines(8)
X for i in range(0, 8):
X self.pw.lprint(self.line[i])
X self.pw.lprint("")
X self.line[0] = n_line
X self.line[1] = fn_line
X for i in range(0, 6):
X #Remember, that strings are printed in inverted order
X self.line[7-i] = c_lines[i]
X def close(self):
X #Check if the chord fits vertically in the page
X self.pw.ensure_lines(8)
X if len(self.line[0]):
X for i in range(0, 8):
X self.pw.lprint(self.line[i])
X self.pw.lprint("")
X
# Class below generates the tab
# We add notes/chords as long, as the tab is not longer
# then the limit defined as max_len
# 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, pdfwr):
X self.sf = sf
X self.pw = pdfwr
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 > self.pw.max_len:
X # Print notes to the end of the previous measure
X self.pw.ensure_lines(6)
X for i in range(5,-1,-1):
X self.pw.lprint(self.line[i][0:(self.last_measure_pos+1)])
X self.pw.lprint("")
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.Tremolo==1:
X n_str += "T"
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 for i in range(0,6):
X if chrd.Fret[i] != -1:
X n_strs[i]+=str(chrd.Fret[i])
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 self.pw.ensure_lines(6)
X for i in range(5,-1,-1):
X self.pw.lprint(self.line[i])
X
def process_file(pdfwr, file_name):
X #We always start at the begining of the page
X #Read the song
X s=sng_file(file_name)
X #Write song description
X md=s.Metadata
X pdfwr.lprint("song title: "+md._songTitle.strip("\x00"))
X pdfwr.lprint("arrangement: "+md._arrangement.strip("\x00"))
X pdfwr.lprint("artist: "+md._artist.strip("\x00"))
X if md.Tuning == 0:
X pdfwr.lprint("Tuning: Standard E")
X elif md.Tuning == 1:
X pdfwr.lprint("Tuning: Drop D")
X else:
X pdfwr.lprint("Tuning undecoded - number: "+str(md.Tuning))
X pdfwr.lprint("")
X pdfwr.lprint("Chord definitions:")
X #First print the chord templates
X #Prepare the chord writer object
X cwr=ChordWriter(s, pdfwr)
X for c in s._chordTemplates:
X cwr.add(c)
X #Close the writer to print the last line
X cwr.close()
X pdfwr.ensure_lines(8)
X pdfwr.lprint("")
X pdfwr.lprint("Tab:")
X #Prepare the tab writer object
X t=TabWriter(s, pdfwr)
X #Prepare lists of object to be put into tab
X objs=s._beats
X #Now we iterate through the SongLevels and add only notes
X #belonging to the highest difficulty level for the particular phrase
X for sl in s.SongLevels:
X for note in sl.Notes:
X if sl.Difficulty == s._phrases[note.PhraseId].MaxDifficulty:
X objs.append(note)
X #Sort objects (to align notes and measure boundaries)
X objs.sort(cmp=lambda x,y: cmp(x.Time, y.Time))
X for n in objs:
X t.add(n)
X t.close()
X pdfwr.close()
# Below is implementation of a simple GUI. It allows you to select one or a few SNG files and generates
# tab in a notepad-like window, from which you can later copy the text to another program
#
X
X
X
X
#Max len is now calculated from the font parameters and page size
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4, legal, landscape
from reportlab.lib.fontfinder import FontFinder
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
X
class PDFWriter:
X def __init__(self, filename):
X self.c = canvas.Canvas(sys.argv[2])
X # You may need to adjust the pagesize!
X self.pagesize=landscape(A4)
X self.c.setPageSize(self.pagesize)
X # You may need to adjust the font
X pdfmetrics.registerFont(TTFont("MyCourier","Courier_New_Bold.ttf"))
X self.fontname="MyCourier"
X self.fontsize=16
X ft=pdfmetrics.getFont(self.fontname)
X f=ft.face
X self.c.setFont(self.fontname,self.fontsize)
X descent=self.fontsize*f.descent/1000.0
X ascent=self.fontsize*f.ascent/1000.0
X self.lineheight=ascent-descent
X #Maximum length
X self.max_len = int(self.pagesize[0]/float(self.c.stringWidth("A"))-1)
X self.max_lines = int(self.pagesize[1]/self.lineheight-1)
X #Starting position for each page
X self.xpos0=self.c.stringWidth("A")/2.0
X self.ypos0=self.pagesize[1]-ascent-self.fontsize/2.0
X self.printed=False
X self.NewPage() #ensure initialization of position pointers
X
X def lprint(self, line):
X self.c.drawString(self.xpos0,self.ypos,line)
X self.ypos-=self.lineheight
X self.lines_avail -=1
X self.printed = True
X
X def NewPage(self):
X if self.printed:
X self.c.showPage()
X self.c.setFont(self.fontname,self.fontsize)
X self.xpos = self.xpos0
X self.ypos = self.ypos0
X self.lines_avail = self.max_lines
X self.printed=False
X
X def ensure_lines(self, n):
X # make sure that at least n lines will fit on current page
X if self.lines_avail < n:
X self.NewPage()
X
X def close(self):
X if self.printed:
X self.c.showPage()
X self.c.save()
X
X
filename=sys.argv[1]
pw=PDFWriter(sys.argv[2])
process_file(pw, filename)
SHAR_EOF
(set 20 13 03 24 22 33 14 'sng2tab_pdf.py'
eval "${shar_touch}") && \
chmod 0744 'sng2tab_pdf.py'
if test $? -ne 0
then ${echo} "restore of sng2tab_pdf.py failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sng2tab_pdf.py': 'MD5 check failed'
) << \SHAR_EOF
3abc70c6583c5e92bec6210877348065 sng2tab_pdf.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'sng2tab_pdf.py'` -ne 22366 && \
${echo} "restoration warning: size of 'sng2tab_pdf.py' is not 22366"

fabiano...@gmail.com

unread,
Apr 1, 2013, 3:51:23 PM4/1/13
to
Here have a clone of toolkit that have a tab to convert SNG to Text TAB:

http://code.google.com/r/thierrysteebierg-sng-to-tab-converter/

I tested and works fine.
0 new messages