sf2_to_js 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. data/lib/sf2_to_js.rb +141 -0
  2. metadata +93 -0
data/lib/sf2_to_js.rb ADDED
@@ -0,0 +1,141 @@
1
+ class Sf2ToJs
2
+
3
+ require 'base64'
4
+ require 'midilib'
5
+ require 'fileutils'
6
+ require 'colorize'
7
+ include FileUtils
8
+
9
+ MIDI_LOWEST_NOTE_INDEX = 21
10
+ MIDI_HIGHTEST_NOTE_INDEX = 108
11
+ NOTES = MIDI_LOWEST_NOTE_INDEX..MIDI_HIGHTEST_NOTE_INDEX
12
+ JS_NOTE_DURATION = 2500 # ms
13
+ JS_NOTE_VELOCITY = 85
14
+
15
+ attr_accessor :instrument_ids, :sf2_path, :output_dir
16
+
17
+ def initialize sf2_path=nil, instrument_ids = [], output_dir="./soundfonts/"
18
+ check_deps
19
+ @sf2_path = File.expand_path(sf2_path)
20
+ @instrument_ids = instrument_ids
21
+ @output_dir = File.expand_path(output_dir)
22
+ mkdir_p @output_dir
23
+ end
24
+
25
+ def check_deps
26
+ raise 'missing fluidsynth (brew install fluidsynth)' unless `which fluidsynth`
27
+ raise 'missing oggenc (brew install fluidsynth)' unless `which oggenc`
28
+ end
29
+
30
+ def to_js
31
+ raise 'no sf2_path!' unless File.exist? @sf2_path
32
+
33
+ font_root_name = File.basename @sf2_path, '.*'
34
+ path = "window.Midi.Soundfont.#{font_root_name}"
35
+
36
+ instruments.each do |inst|
37
+ inst_root_name = inst[:name].gsub(/\s*/, "")
38
+ @js_file = File.open("#{@output_dir}/#{font_root_name}.#{inst_root_name}.sf2.js", "w")
39
+ @js_file.puts define_js(path)
40
+
41
+ @js_file.puts "#{path}[\"#{inst[:name]}\"] = ["
42
+ NOTES.each do |note_index|
43
+ ogg_64b = ogg_64b_note(inst[:bank], inst[:id], note_index)
44
+ @js_file.print " \"#{ogg_64b}\""
45
+ @js_file.puts ", " unless (note_index == MIDI_HIGHTEST_NOTE_INDEX)
46
+ end
47
+ @js_file.puts
48
+ @js_file.print "];"
49
+
50
+ @js_file.close
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def one_note_midi bank_id, instrument_id, note_index
57
+ seq = MIDI::Sequence.new()
58
+ track = MIDI::Track.new(seq)
59
+
60
+ seq.tracks << track
61
+ track.events << MIDI::ProgramChange.new(0, (bank_id*128) + instrument_id)
62
+ track.events << MIDI::NoteOn.new(0, note_index, JS_NOTE_VELOCITY, 0) # channel, note, velocity, delta
63
+ track.events << MIDI::NoteOff.new(0, note_index, JS_NOTE_VELOCITY, JS_NOTE_DURATION)
64
+
65
+ f = Tempfile.new 'midifile'
66
+ begin
67
+ seq.write f
68
+ f.close
69
+ yield f.path
70
+ ensure
71
+ f.unlink
72
+ end
73
+ end
74
+
75
+ def midi_to_ogg midi_path
76
+ wav_file = Tempfile.new 'wavfile'
77
+ ogg_file = Tempfile.new 'oggfile'
78
+ begin
79
+ sh "fluidsynth -C 1 -R 1 -g 0.5 -F #{wav_file.path} #{@sf2_path} #{midi_path}"
80
+ sh "oggenc -m 32 -M 64 #{wav_file.path} -o #{ogg_file.path}"
81
+ yield ogg_file.read
82
+ ensure
83
+ wav_file.unlink
84
+ ogg_file.unlink
85
+ end
86
+ end
87
+
88
+ def define_js object_path
89
+ path = ""
90
+ js = ""
91
+ object_path.split(".").each do |section|
92
+ path = path.length > 0 ? path + ".#{section}" : section
93
+ js += "if (typeof(#{path}) == 'undefined') #{path} = {};\n"
94
+ end
95
+ js
96
+ end
97
+
98
+ def instruments
99
+ @instruments = cmdfile 'inst 1' do |cmd_path|
100
+ raw = sh("fluidsynth -i -f #{cmd_path} #{@sf2_path}")
101
+ raw.scan(/^(\d\d\d)-(\d\d\d) *(.*)$/).map do |inst|
102
+ {bank: inst[0].to_i, id: inst[1].to_i, name: inst[2]}
103
+ end
104
+ end
105
+
106
+ @instruments.select! { |inst| @instrument_ids.member? inst[:id] } unless @instrument_ids.empty?
107
+ @instruments
108
+ end
109
+
110
+ def ogg_64b_note bank_id, instrument_id, note_index
111
+ encoded = nil
112
+ one_note_midi bank_id, instrument_id, note_index do |midi_path|
113
+ midi_to_ogg midi_path do |ogg|
114
+ encoded = Base64.strict_encode64(ogg)
115
+ end
116
+ end
117
+ "data:audio/ogg;base64,#{encoded}"
118
+ end
119
+
120
+ def cmdfile cmds
121
+ f = Tempfile.new 'cmdfile'
122
+ begin
123
+ f.write cmds
124
+ f.close
125
+ yield f.path
126
+ ensure
127
+ f.unlink
128
+ end
129
+ end
130
+
131
+ def sh cmd
132
+ puts cmd.green
133
+ result = `#{cmd}`
134
+ unless $?.success?
135
+ puts 'FAIL'.red
136
+ exit 1
137
+ end
138
+ result
139
+ end
140
+
141
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sf2_to_js
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Esteban Salazar
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: midilib
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: colorize
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: thor
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Output js files compatible with https://github.com/eagsalazar/midi.js
63
+ email:
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - lib/sf2_to_js.rb
69
+ homepage: https://github.com/eagsalazar/sf2_to_js
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.25
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Convert sf2 sound fonts to js
93
+ test_files: []