Skip to content

Instantly share code, notes, and snippets.

@kmckelvin
Created November 11, 2012 17:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kmckelvin/4055586 to your computer and use it in GitHub Desktop.
Save kmckelvin/4055586 to your computer and use it in GitHub Desktop.
Converts OpenSong song files to OnSong format

Usage

Pretty easy to use - just make sure that Ruby is installed, then run this:

ruby opensong_to_onsong.rb ./path_to_opensong_file.xml > ./export_filename.txt

It just outputs straight to STDIO.

Disclaimer

I just put this together pretty quickly, and by no means intended to cover the entire OpenSong spec. In my trials it seems to work pretty accurately. If there are issues - drop me a message and I'll look into them :)

require 'nokogiri'
file = ARGV[0]
xml = Nokogiri::XML(File.read(file))
title = xml.css('title').text
content = xml.css('lyrics').text
def to_lines(content)
file_lines = content.split("\n")
lines = []
pair = []
file_lines.each do |line|
if pair.any?
pair << line[1..-1]
pair[1] ||= ""
lines << pair
pair = []
elsif line[0] == '.' && pair.empty?
pair = [line[1..-1]]
elsif line[0] == ' ' && pair.empty?
pair = []
lines << ["", line]
elsif line =~ /\[V(\d*)\]/
pair = []
lines << ["", "Verse #{$1}:"]
elsif line =~ /\[C(\d*)\]/
pair = []
number = $1.length != 0 ? " #{$1}" : ""
lines << ["", "Chorus#{number}:"]
elsif line =~ /\[PC(\d*)\]/
pair = []
number = $1.length != 0 ? " #{$1}" : ""
lines << ["", "Prechorus#{number}:"]
elsif line =~ /\[B(\d*)\]/
pair = []
number = $1.length != 0 ? " #{$1}" : ""
lines << ["", "Bridge#{number}:"]
elsif line.strip == ""
pair = []
lines << ["", ""]
else
pair = []
end
end
lines
end
def get_chord_positions(chord_line)
positions = []
chord = ""
start_pos = 0
(0..chord_line.length - 1).each do |i|
if chord_line[i] == ' ' && chord != ""
positions << [chord, start_pos]
chord = ""
start_pos = 0
end
if chord_line[i] != ' '
start_pos = i if chord == ""
chord << chord_line[i]
end
end
if chord != ""
positions << [chord, start_pos]
end
positions
end
def interpolate_line(chord_positions, lyrics)
output = lyrics.dup
offset = 0
chord_positions.each do |chord, position|
insert_string = "[#{chord}]"
if output.length < (position + offset)
output << (" " * (position + offset - output.length))
end
output.insert(position + offset, insert_string)
offset += insert_string.length
end
output.strip
end
lines = to_lines(content)
onsong = lines.map { |chords, lyrics| [get_chord_positions(chords), lyrics] }.map{ |chord_positions, lyrics| interpolate_line(chord_positions, lyrics) }
key = get_chord_positions(lines.find { |chords, lyrics| chords.length != 0 }[0])[0][0] rescue nil
puts title
puts "Key: [#{key}]" if key
onsong.each do |line|
puts line
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment