#8 [ Feature Request #3029 ] NWC import / export

after final release
open
nobody
5
2015-01-20
2011-10-31
No

Date: 2007-Jul-06 08:18
Sender: zz85
Logged In: YES
user_id=39268
Browser: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.4) Gecko/20070515
Firefox/2.0.0.4

Actually you take a look at the latest version (done a long
time ago), it could perhaps open compressed nwc files.
Anyway I have never used it much too, although last time
when I did it for the fun, it was good at exporting long
single line of nwc music to lilypond format.

http://lily4jedit.svn.sourceforge.net/viewvc/lily4jedit/trunk/LilyPondTool/otherpatch
es/nwc2ly/nwc2ly.py?view=markup

Date: 2007-May-11 13:08
Sender: bdumont
Logged In: YES
user_id=14181
Browser: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.10) Gecko/20060911
SUSE/1.5.0.10-0.2 Firefox/1.5.0.10

I think that NoteWorthy holds the file format closed. I
know of one open source (GPL) python script that was
reverse-engineered, nwc2ly.py. I guess it has some
limitations; it doesn't handle compressed nwc files and I've
heard that it sometimes has problems with multiple voices on
a staff. I haven't really used the script since I generally
work with noteedit and abc; neither of which accept lilypond
input. When I've had to convert, I've just used Noteworthy
viewer and re-entered the score by hand.

I'm including the script in question below:
-------------------------------------------------------------------------
import binascii, sys

shortcopyleft = """
nwc2ly - Converts NWC(v 1.75) to LY fileformat
Copyright (C) 2005 Joshua Koo (joshuakoo@myrealbox.com)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public
License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
"""

##
# most infomation obtained about the nwc format
# is by using noteworthycomposer and the french cafe method
# (http://samba.org/ftp/tridge/misc/french_cafe.txt)
#
#
##
# Revisions
# 0.1 7 april 2005 initial hex parsing;
# 0.2 13 april 2005 added multiple staff, keysig, dots,
durations
# 0.3 14 april 2005 clef, key sig detection, absolute notes
pitching
# 0.4 15 April 2005 Relative Pitchs, Durations,
Accidentals, Stem Up/Down, Beam, Tie
# 0.5 16 April 2005 Bug fixes, Generate ly score , Write
to file, Time Sig, Triplets, Experimental chords

##
# TODO
#
# appoggiatura/ acciaccatura / Grace notes
#
# Tenuto, Staccato
#
# Titles
# Piano Staff
# Chords
# Pedals
# Midi Instruments
# Visability
# Lyrics
##
# Compression format
#
# BUGS headers, text markups, chords
######

############
# Settings #
############
relativePitch = 1
relativeDuration = 1
barLinesComments = 25 # Comments for every x lines

args = len(sys.argv)
if args<2:
print "Syntax: python nwc2ly nwcfile [lyfile]"
sys.exit()
nwcfile = sys.argv[1]

if args<3:
lyfile = ''
else:
lyfile = sys.argv[2]

def findNoOfStaff(nwcData):
# Infomation on Staffs \x08 00 00 FF 00 00 n
data = 0;
while data!='\x08':
if (nwcData.read(1)!='\x00') :
continue
if (nwcData.read(1)!='\x00') :
continue
if (nwcData.read(1)!='\xFF') :
continue
if (nwcData.read(1)!='\x00') :
continue
if (nwcData.read(1)!='\x00') :
continue
if (nwcData.read(1)!='\x00') :
continue
noOfStaffs = ord(nwcData.read(1))
print noOfStaffs, " noOfStaffs found"
return noOfStaffs

def findNoOfTokens(nwcData):
# fast forward 7f 00 40
data = 0
while data!='\x7F':
data = nwcData.read(1)
# a new staff infomation starts at 7F0040
data = nwcData.read(12)
noOfTokens = ord(data[10])
noOfTokens += ord(data[11]) * 256
print noOfTokens, " Tokens found"
return noOfTokens

# lyrics
#F4 FF 0C
#F4 FF 10
# 00 00 01 00 01 00 02
# 04

def getDuration(data):
durationBit = ord(data[2]) & 7
durationDotBit = ord(data[6])

duration = durations[durationBit]
if (durationDotBit & 1<<2):
durationDot = '.'
elif (durationDotBit & 1):
durationDot = '..'
else :
durationDot = ''
return duration + durationDot

def getKey(data):
data = binascii.hexlify(data)

if (keysigs.has_key(data)):
return '\key ' + keysigs[data]
return '% unknown key'

def getLocation(data):
offset = ord(data[8]);
if offset > 127 :
return 256-offset
if (ord(data[9])>>3 & 1):
return -offset

return offset
#print 'offset ', offset
#print binascii.hexlify(data[8:9])

def getAccidental(data):
data = ord(data)
data = (data & 7 )
return acdts[data]
# Variables
keysigs = {
'00000000' : 'c \major % or a \minor' ,
'00000020' : 'g \major % or e \minor' ,
'00000024': 'd \major % or b \minor' ,
'00000064' : 'a \major % or fis \minor' ,
'0000006c' : 'e \major % or cis \minor' ,
'0000006d' : 'b \major % or gis \minor' ,
'0000007d' : 'fis \major % or dis \minor' ,
'0000007f' : 'cis \major % or ais \minor' ,
'00020000' : 'f \major % or d \minor' ,
'00120000' : 'bes \major % or g \minor' ,
'00130000' : 'ees \major % or c \minor' ,
'001b0000' : 'aes \major % or f \minor' ,
'005b0000' : 'des \major % or bes \minor' ,
'005f0000' : 'ges \major % or ees \minor' ,
'007f0000' : 'ces \major % or a \minor'
}

acdts = ( 'is', 'es', '' ,'isis', 'eses', 'auto' )

clefs = { 0 : "b'",
1 : "d",
2 : "c",
3 : "a",
}

clefNames = { 0: 'treble',
1: 'bass',
2: 'alto',
3: 'tenor',
}
octaves = { 0: 0, 1:7, 2:-7 }
scale = [ # this list is taken from lilycomp
"c,,,","d,,,","e,,,","f,,,","g,,
,","a,,,","b,,,",
"c,,","d,,","e,,","f,,","g,,&quo
t;,"a,,","b,,",
"c,","d,","e,","f,","g,",&q
uot;a,","b,",
"c","d","e","f","g","a
","b",
"c'","d'","e'","f'","g'",&q
uot;a'","b'",
"c''","d''","e''","f''","g''&quo
t;,"a''","b''",
"c'''","d'''","e'''","f'''","g''
'","a'''","b'''",
"c''''","d''''","e''''","f''''","
;g''''","a''''","b''''",
]

stems = [ '\stemNeutral ', '\stemUp ', '\stemDown ']

slurs = [ '', '(' , ')', '' ]

triplets = [
('' , '' ),
( '\\times 2/3 { ', '') ,
('' , '' ),
('' , ' }' ),
]

durations = ( '1','2','4','8','16','32','64' )

print "python nwc2ly is running..."
try:

nwcData = open( nwcfile,'rb')

#durationDots = {0:'', 4: '.'}

# check if its a readable nwc format
# compressed - [NWZ]
# uncompressed - [NoteWorthy ArtWare] [NoteWorthy Composer] K
format = nwcData.read(5)
if format== '[NWZ]':
print 'Compressed NWC unsupported, '
print 'Please save as an uncompressed NWC format and try
again.'
sys.exit()
if format!= '[Note':
print 'Unknown format, please use an uncompress NWC format
and try again.'
sys.exit()

noOfStaffs = findNoOfStaff(nwcData);
resultFile = """% Generated from python nwc2ly converter by
Joshua Koo (joshuakoo@myrealbox.com)
\\version "2.4.0"
"""
resultFile+= "\n\n\\score {"
resultFile+= "\n\t<<\n\t\t"

for staff in range(1,noOfStaffs+1):
print "\n\nWorking on Staff", staff

keysigCount =0
timesigCount=0
noteCount=0
restCount=0
staffCount=0
barlineCount=0
clefCount=0
textCount =0
dynamicCount = 0

lastPitch = scale.index('c')
lastDuration = 0
lastClef = scale.index(clefs[0]) # index referenced to
note of last clef
lastStem = 0

lastKey = { 'c': '', #c
'd': '', 'e': '', 'f': '', 'g': '', 'a': '', 'b': '' }
currentKey = lastKey.copy()

data = 0
token = 1
chord = 0

result = ""
result += "\n\t\\new Staff {\n\t\t"

if relativePitch:
result+="\\relative c {"
else :
result+="\\notes {"
result+="\n\t\t\n\n\t\t"
noOfTokens = findNoOfTokens(nwcData);
#print "00112233445566778899\n"
extra = ''
# the juice

while data!="":
token += 1
if token==noOfTokens:
result += "\n\t\t}\n\t}\n\t"
print "going next staff!"
break
data = nwcData.read(1)

# clef
if data=='\x00':
data = nwcData.read(6)
clefCount += 1

key = ord(data[2])
octave = ord(data[4])
# print binascii.hexlify(data) , "CLEF? "
lastClef = scale.index(clefs[key]) + octaves[octave]
#TODO check for octave shifts _8
result += '\clef ' + clefNames[key] + '\n\t\t'

# key signature
elif data=='\x01':
data = nwcData.read(12)
keysigCount = keysigCount + 1

#
flatBits = ord(data[2])
sharpBits = ord(data[4])

for note in lastKey.keys():
noteInd = ['a','b','c','d','e','f','g'].index(note)
if (flatBits >> noteInd & 1):
lastKey[note] = 'es'
elif (sharpBits >> noteInd & 1):
lastKey[note] = 'is'
else:
lastKey[note] = ''

currentKey = lastKey.copy()
result = result + getKey(data[1:5]) + "\n\t\t"

#print "data", binascii.hexlify(data)
#print "flat", binascii.hexlify(flatBits)
#print "sharp", binascii.hexlify(data[4])
#print getKey(data[1:5])

# barline
elif data=='\x02':
data = nwcData.read(4)
barlineCount += 1

currentKey = lastKey.copy()

result += "|\n\t\t"
if (barlineCount % barLinesComments == 0):
result += "\n\t\t% Bar " + str(barlineCount + 1) +
"\n\t\t"
print "Bar ",barlineCount, " completed, "

# timesig
elif data=='\x05':
data = nwcData.read(8)
timesigCount = timesigCount + 1
beats = ord(data[2])
beatValues = [ 1, 2, 4, 8 ,6, 32 ]
beatValue = beatValues[ord(data[4])]
timesig = str(beats) + "/" + str(beatValue)

result += "\\time " + timesig + " "
# Tempo
elif data=='\x06':
data = nwcData.read(8)
#tempoCount = tempoCount + 1

# note
elif data=='\x08':
data = nwcData.read(10)
noteCount = noteCount + 1
#print binascii.hexlify(data) , noteCount

pitch = getLocation(data)+lastClef

note = scale[pitch]

# get Accidentals
accidental = getAccidental(data[9])
#print 'accidental',accidental
if (accidental!='auto'):
currentKey[note[0]] = accidental
accidental = currentKey[note[0]]
if (relativePitch):
octave = ''
diff = pitch - lastPitch
if diff>3:
for i in range((diff-4 + 7)/7):
octave += "'"
if octave == '':
octave += "'"
elif diff<-3:
for i in range((-diff-4 + 7)/7):
octave += ","
if octave == '':
octave += ","
lastPitch = pitch
else:
octave = note[1:]
pitch = note[0] + accidental + octave

# get Relative Duration
duration = getDuration(data)
if (relativeDuration):
if (lastDuration==duration):
duration = ''
else:
lastDuration = duration

# check stems
stem = ord(data[4])
stem = (stem >> 4) & 3
if (stem!= lastStem) :
lastStem = stem
stem = stems[stem]
else :
stem = ''
# check beam
beams = [ '', '[', '',']' ]
beam = beams[ord(data[4]) & 3]

# triplets
triplet = triplets [ord(data[4])>>2 & 3 ]

# check slur
slur = slurs[ord(data[7]) & 3 ]

# check tie
tie = ''
if ord(data[6]) >> 4 & 1:
tie = '~'

if extra!='':
extra = '-"' + extra + '"'

result += triplet[0] + stem + pitch + duration + extra
result += slur + tie + beam + triplet[1] + " "
extra = ''

if chord>0:
chord -= 1
if chord==0:
result += '>> '
else :
result += '\\\\ '

# rest
elif data=='\x09':
data = nwcData.read(10)
restCount = restCount + 1

# get Relative Duration
duration = getDuration(data)
if (relativeDuration):
if (lastDuration==duration):
duration = ''
else:
lastDuration = duration

result = result + 'r' + duration + " "

if chord>0:
chord -= 1
if chord==0:
result += '>> '
else :
result += '\\\\ '
# text
elif data=='\x11':
textCount = textCount + 1
data = nwcData.read(2) #pad
textpos = nwcData.read(1)
data = nwcData.read(2) #pad
text = ''
data = nwcData.read(1)
while data!='\x00':
text = text + data
data = nwcData.read(1)

#if text.isdigit() : # check numbers
# text = "-\\markup -\\number "+ text
# #text = "-\\markup {\\number "+ text +"}"
#else :
# text = '-"' + text + '"'

extra += ' ' + text

# dynamics
elif data=='\x07':
dynamicCount = dynamicCount + 1
data = nwcData.read(9)
# chord
elif data=='\x0A':

data = nwcData.read(12)

print "WARNING Chord support is experimental"
if chord == 0:
result += '<< '
chord = ord(data[2])
# fermata
elif data=='\x0E':
print "Fermata"
data = nwcData.read(6)
extra += "\\fermata"

# todo
else :
print "WARNING: Unrecognised token
",binascii.hexlify(data), " at #", nwcData.tell(), " at
Token", token

# output converted file?
print "\nStats"
print keysigCount, " keysigCount found"
print noteCount, " notes found"
print staffCount, " staffCount found"
print clefCount, " clefCount found"
print barlineCount, " barlineCount found"
print timesigCount, " timesigCount found"
print textCount, " textCount found"
print dynamicCount, " dynamicCount found"
print restCount, " restCount found"

#print "\nLilypond format:\n"
#print result
resultFile += result

resultFile+= "\n\t>>"
resultFile+= "\n\t\layout {}"
resultFile+= "\n\t\midi {}"
resultFile+= "\n}"
nwcData.close()

if lyfile=='':
print 'Dumping output file to screen'
print resultFile
else :
write = open( lyfile ,'w')
write.write (resultFile)
write.close()

except IOError:
print 'file does not exist'

print "Please send all bugs and requests to
joshuakoo@myrealbox.com"

Date: 2007-May-11 11:58
Sender: whiteangel
Logged In: YES
user_id=11952
Browser: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201
Firefox/2.0.0.3 (Ubuntu-feisty)

If specifications of the file format would be available, the
import should be more or less pretty trivial to create (let
it be as a plugin or builtin feature).

If you find any, please post them here.

Date: 2007-Jan-17 21:33
Sender: bdumont
Logged In: YES
user_id=14181
Browser: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20060911
SUSE/1.5.0.8-0.2 Firefox/1.5.0.8

I *think* that nwc2ly is open source. Windows only program,
but might be helpful.

http://nwc2ly.sourceforge.net/

Date: 2007-Jan-17 21:07
Sender: suamor
Logged In: YES
user_id=428
Browser: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20060802
Mandriva/1.5.0.8-1mdv2007.1 (2007.1) Firefox/1.5.0.8

Is there any public information for that format available?
Signing an NDA is out of the question as this conflicts with
the Canorus license. Free tools (i.e. source code freely
available) for converting NWC to a different open (i.e.
known) format would help too.

Date: 2007-Jan-08 18:51
Sender: bdumont
Logged In: YES
user_id=14181
Browser: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.8) Gecko/20060911
SUSE/1.5.0.8-0.2 Firefox/1.5.0.8

Although I never use Noteworty Composer, many peers use it.

Also, a large number of sites have NWC or Midi files
available for download. There's sufficient slop in
midi to make nwc preferable.

Big task, I'm sure; but useful. Import would be more
important than export for me personally

Brian

Discussion

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks