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.
- data/lib/sf2_to_js.rb +141 -0
- 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: []
|