nu_wav 0.3.4 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,331 @@
1
+ module NuWav
2
+ class Chunk
3
+ attr_accessor :id, :size, :raw
4
+
5
+ def self.parse(id, size, file)
6
+ raw = file.read(size)
7
+ chunk = self.new(id, size, raw)
8
+ chunk.parse
9
+ return chunk
10
+ end
11
+
12
+ def initialize(id=nil, size=nil, raw=nil)
13
+ @id, @size, @raw = id, size, raw
14
+ end
15
+
16
+ def parse
17
+ end
18
+
19
+ def read_dword(start)
20
+ @raw[start..(start+3)].unpack('V').first
21
+ end
22
+
23
+ def read_word(start)
24
+ @raw[start..(start+1)].unpack('v').first
25
+ end
26
+
27
+ def read_char(start, length=(@raw.length-start))
28
+ (@raw[start..(start+length-1)] || '').strip
29
+ end
30
+
31
+ def write_dword(val)
32
+ val ||= 0
33
+ [val].pack('V')
34
+ end
35
+
36
+ def write_word(val)
37
+ val ||= 0
38
+ [val].pack('v')
39
+ end
40
+
41
+ def write_char(val, length=nil)
42
+ val ||= ''
43
+ val = val.to_s
44
+ length ||= val.length
45
+ # NuWav::WaveFile.log "length:#{length} val.length:#{val.length} val:#{val}"
46
+ padding = "\0" * [(length - val.length), 0].max
47
+ out = val[0,length] + padding
48
+ # NuWav::WaveFile.log out
49
+ out
50
+ end
51
+
52
+ def to_binary
53
+ end
54
+ end
55
+
56
+
57
+ class RiffChunk
58
+ attr_accessor :id, :size, :riff_type
59
+
60
+ def initialize(riff_name, riff_length, riff_type)
61
+ @id, @size, @riff_type = riff_name, riff_length, riff_type
62
+ end
63
+
64
+ def to_s
65
+ "<chunk type:riff id:#{@id} size:#{@size} type:#{@riff_type} />"
66
+ end
67
+
68
+ end
69
+
70
+ class FmtChunk < Chunk
71
+
72
+ attr_accessor :compression_code, :number_of_channels, :sample_rate, :byte_rate, :block_align, :sample_bits, :extra_size, :extra,
73
+ :head_layer, :head_bit_rate, :head_mode, :head_mode_ext, :head_emphasis, :head_flags, :pts_low, :pts_high
74
+
75
+ def parse
76
+ NuWav::WaveFile.log "@raw.size = #{@raw.size}"
77
+ @compression_code = read_word(0)
78
+ @number_of_channels = read_word(2)
79
+ @sample_rate = read_dword(4)
80
+ @byte_rate = read_dword(8)
81
+ @block_align = read_word(12)
82
+ @sample_bits = read_word(14)
83
+ @extra_size = read_word(16)
84
+
85
+ if (@compression_code.to_i == MPEG_COMPRESSION)
86
+ @head_layer = read_word(18)
87
+ @head_bit_rate = read_dword(20)
88
+ @head_mode = read_word(24)
89
+ @head_mode_ext = read_word(26)
90
+ @head_emphasis = read_word(28)
91
+ @head_flags = read_word(30)
92
+ @pts_low = read_dword(32)
93
+ @pts_high = read_dword(36)
94
+ end
95
+ end
96
+
97
+ def to_binary
98
+ out = ''
99
+ out += write_word(@compression_code)
100
+ out += write_word(@number_of_channels)
101
+ out += write_dword(@sample_rate)
102
+ out += write_dword(@byte_rate)
103
+ out += write_word(@block_align)
104
+ out += write_word(@sample_bits)
105
+ out += write_word(@extra_size)
106
+
107
+ if (@compression_code.to_i == MPEG_COMPRESSION)
108
+ out += write_word(@head_layer)
109
+ out += write_dword(@head_bit_rate)
110
+ out += write_word(@head_mode)
111
+ out += write_word(@head_mode_ext)
112
+ out += write_word(@head_emphasis)
113
+ out += write_word(@head_flags)
114
+ out += write_dword(@pts_low)
115
+ out += write_dword(@pts_high)
116
+ end
117
+ "fmt " + write_dword(out.size) + out
118
+ end
119
+
120
+ def to_s
121
+ extra = if (@compression_code.to_i == MPEG_COMPRESSION)
122
+ ", head_layer:#{head_layer}, head_bit_rate:#{head_bit_rate}, head_mode:#{head_mode}, head_mode_ext:#{head_mode_ext}, head_emphasis:#{head_emphasis}, head_flags:#{head_flags}, pts_low:#{pts_low}, pts_high:#{pts_high}"
123
+ else
124
+ ""
125
+ end
126
+ "<chunk type:fmt compression_code:#{compression_code}, number_of_channels:#{number_of_channels}, sample_rate:#{sample_rate}, byte_rate:#{byte_rate}, block_align:#{block_align}, sample_bits:#{sample_bits}, extra_size:#{extra_size} #{extra} />"
127
+ end
128
+ end
129
+
130
+ class FactChunk < Chunk
131
+ attr_accessor :samples_number
132
+
133
+ def parse
134
+ @samples_number = read_dword(0)
135
+ end
136
+
137
+ def to_s
138
+ "<chunk type:fact samples_number:#{@samples_number} />"
139
+ end
140
+
141
+ def to_binary
142
+ "fact" + write_dword(4) + write_dword(@samples_number)
143
+ end
144
+
145
+ end
146
+
147
+ class MextChunk < Chunk
148
+ attr_accessor :sound_information, :frame_size, :ancillary_data_length, :ancillary_data_def, :reserved
149
+
150
+ def parse
151
+ @sound_information = read_word(0)
152
+ @frame_size = read_word(2)
153
+ @ancillary_data_length = read_word(4)
154
+ @ancillary_data_def = read_word(6)
155
+ @reserved = read_char(8,4)
156
+ end
157
+
158
+ def to_s
159
+ "<chunk type:mext sound_information:(#{sound_information}) #{(0..15).inject(''){|s,x| "#{s}#{sound_information[x]}"}}, frame_size:#{frame_size}, ancillary_data_length:#{ancillary_data_length}, ancillary_data_def:#{(0..15).inject(''){|s,x| "#{s}#{ancillary_data_def[x]}"}}, reserved:'#{reserved}' />"
160
+ end
161
+
162
+ def to_binary
163
+ out = "mext" + write_dword(12)
164
+ out += write_word(@sound_information)
165
+ out += write_word(@frame_size)
166
+ out += write_word(@ancillary_data_length)
167
+ out += write_word(@ancillary_data_def)
168
+ out += write_char(@reserved, 4)
169
+ out
170
+ end
171
+ end
172
+
173
+ class BextChunk < Chunk
174
+ attr_accessor :description, :originator, :originator_reference, :origination_date, :origination_time, :time_reference_low, :time_reference_high,
175
+ :version, :umid, :reserved, :coding_history
176
+
177
+ def parse
178
+ @description = read_char(0,256)
179
+ @originator = read_char(256,32)
180
+ @originator_reference = read_char(288,32)
181
+ @origination_date = read_char(320,10)
182
+ @origination_time = read_char(330,8)
183
+ @time_reference_low = read_dword(338)
184
+ @time_reference_high = read_dword(342)
185
+ @version = read_word(346)
186
+ @umid = read_char(348,64)
187
+ @reserved = read_char(412,190)
188
+ @coding_history = read_char(602)
189
+ end
190
+
191
+ def to_s
192
+ "<chunk type:bext description:'#{description}', originator:'#{originator}', originator_reference:'#{originator_reference}', origination_date:'#{origination_date}', origination_time:'#{origination_time}', time_reference_low:#{time_reference_low}, time_reference_high:#{time_reference_high}, version:#{version}, umid:#{umid}, reserved:'#{reserved}', coding_history:#{coding_history} />"
193
+ end
194
+
195
+ def to_binary
196
+ out = "bext" + write_dword(602 + @coding_history.length )
197
+ out += write_char(@description, 256)
198
+ out += write_char(@originator, 32)
199
+ out += write_char(@originator_reference, 32)
200
+ out += write_char(@origination_date, 10)
201
+ out += write_char(@origination_time, 8)
202
+ out += write_dword(@time_reference_low)
203
+ out += write_dword(@time_reference_high)
204
+ out += write_word(@version)
205
+ out += write_char(@umid, 64)
206
+ out += write_char(@reserved, 190)
207
+ out += write_char(@coding_history)
208
+ # make sure coding history ends in '\r\n'
209
+ out
210
+ end
211
+
212
+ end
213
+
214
+ class CartChunk < Chunk
215
+ attr_accessor :version, :title, :artist, :cut_id, :client_id, :category, :classification, :out_cue, :start_date, :start_time, :end_date, :end_time,
216
+ :producer_app_id, :producer_app_version, :user_def, :level_reference, :post_timer, :reserved, :url, :tag_text
217
+
218
+ def parse
219
+ @version = read_char(0,4)
220
+ @title = read_char(4,64)
221
+ @artist = read_char(68,64)
222
+ @cut_id = read_char(132,64)
223
+ @client_id = read_char(196,64)
224
+ @category = read_char(260,64)
225
+ @classification = read_char(324,64)
226
+ @out_cue = read_char(388,64)
227
+ @start_date = read_char(452,10)
228
+ @start_time = read_char(462,8)
229
+ @end_date = read_char(470,10)
230
+ @end_time = read_char(480,8)
231
+ @producer_app_id = read_char(488,64)
232
+ @producer_app_version = read_char(552,64)
233
+ @user_def = read_char(616,64)
234
+ @level_reference = read_dword(680)
235
+ @post_timer = read_char(684,64)
236
+ @reserved = read_char(748,276)
237
+ @url = read_char(1024,1024)
238
+ @tag_text = read_char(2048)
239
+ end
240
+
241
+ def to_s
242
+ "<chunk type:cart version:#{version}, title:#{title}, artist:#{artist}, cut_id:#{cut_id}, client_id:#{client_id}, category:#{category}, classification:#{classification}, out_cue:#{out_cue}, start_date:#{start_date}, start_time:#{start_time}, end_date:#{end_date}, end_time:#{end_time}, producer_app_id:#{producer_app_id}, producer_app_version:#{producer_app_version}, user_def:#{user_def}, level_reference:#{level_reference}, post_timer:#{post_timer}, reserved:#{reserved}, url:#{url}, tag_text:#{tag_text} />"
243
+ end
244
+
245
+ def to_binary
246
+ out = "cart" + write_dword(2048 + @tag_text.length )
247
+ out += write_char(@version,4)
248
+ out += write_char(@title,64)
249
+ out += write_char(@artist,64)
250
+ out += write_char(@cut_id,64)
251
+ out += write_char(@client_id,64)
252
+ out += write_char(@category,64)
253
+ out += write_char(@classification,64)
254
+ out += write_char(@out_cue,64)
255
+ out += write_char(@start_date,10)
256
+ out += write_char(@start_time,8)
257
+ out += write_char(@end_date,10)
258
+ out += write_char(@end_time,8)
259
+ out += write_char(@producer_app_id,64)
260
+ out += write_char(@producer_app_version,64)
261
+ out += write_char(@user_def,64)
262
+ out += write_dword(@level_reference)
263
+ out += write_char(@post_timer,64)
264
+ out += write_char(@reserved,276)
265
+ out += write_char(@url,1024)
266
+ out += write_char(@tag_text)
267
+ out
268
+ end
269
+
270
+ end
271
+
272
+ class DataChunk < Chunk
273
+ attr_accessor :tmp_data_file
274
+
275
+ def self.parse(id, size, file)
276
+
277
+ # tmp_data = File.open('./data_chunk.mp2', 'wb')
278
+ tmp_data = Tempfile.open('data_chunk')
279
+ tmp_data.binmode
280
+
281
+ remaining = size
282
+ while (remaining > 0 && !file.eof?)
283
+ read_bytes = [128, remaining].min
284
+ tmp_data << file.read(read_bytes)
285
+ remaining -= read_bytes
286
+ end
287
+ tmp_data.rewind
288
+ chunk = self.new(id, size, tmp_data)
289
+
290
+ return chunk
291
+ end
292
+
293
+ def self.new_from_file(file)
294
+ tmp_data = Tempfile.open('data_chunk')
295
+ tmp_data.binmode
296
+ FileUtils.cp(file.path, tmp_data.path)
297
+ tmp_data.rewind
298
+ self.new('data', File.size(tmp_data.path).to_s, tmp_data)
299
+ end
300
+
301
+ def initialize(id=nil, size=nil, tmp_data_file=nil)
302
+ @id, @size, @tmp_data_file = id, size, tmp_data_file
303
+ end
304
+
305
+ def data
306
+ f = ''
307
+ if self.tmp_data_file
308
+ NuWav::WaveFile.log "we have a tmp_data_file!"
309
+ self.tmp_data_file.rewind
310
+ f = self.tmp_data_file.read
311
+ self.tmp_data_file.rewind
312
+ else
313
+ NuWav::WaveFile.log "we have NO tmp_data_file!"
314
+ end
315
+ f
316
+ end
317
+
318
+ def to_s
319
+ "<chunk type:data (size:#{data.size})/>"
320
+ end
321
+
322
+ def to_binary
323
+ NuWav::WaveFile.log "data chunk to_binary"
324
+ d = self.data
325
+ NuWav::WaveFile.log "got data size = #{d.size} #{d[0,10]}"
326
+ out = "data" + write_dword(d.size) + d
327
+ out
328
+ end
329
+
330
+ end
331
+ end
@@ -0,0 +1,3 @@
1
+ module NuWav
2
+ VERSION = "0.4.2"
3
+ end
@@ -0,0 +1,286 @@
1
+ module NuWav
2
+
3
+ class WaveFile
4
+
5
+ attr_accessor :header, :chunks
6
+
7
+ def self.parse(wave_file)
8
+ NuWav::WaveFile.new.parse(wave_file)
9
+ end
10
+
11
+ def initialize
12
+ self.chunks = {}
13
+ end
14
+
15
+ def parse(wave_file)
16
+ NuWav::WaveFile.log "Processing wave file #{wave_file.inspect}...."
17
+ wave_file_size = File.size(wave_file)
18
+
19
+ File.open(wave_file, File::RDWR) do |f|
20
+
21
+ #only for windows, make sure we are operating in binary mode
22
+ f.binmode
23
+ #start at the very beginning, a very good place to start
24
+ f.seek(0)
25
+
26
+ riff, riff_length = read_chunk_header(f)
27
+ NuWav::WaveFile.log "riff: #{riff}"
28
+ NuWav::WaveFile.log "riff_length: #{riff_length}"
29
+ NuWav::WaveFile.log "wave_file_size: #{wave_file_size}"
30
+
31
+ raise NotRIFFFormat unless riff == 'RIFF'
32
+ riff_end = [f.tell + riff_length, wave_file_size].min
33
+
34
+ riff_type = f.read(4)
35
+ raise NotWAVEFormat unless riff_type == 'WAVE'
36
+
37
+ @header = RiffChunk.new(riff, riff_length, riff_type)
38
+
39
+ while (f.tell + 8) <= riff_end
40
+ NuWav::WaveFile.log "while #{f.tell} < #{riff_end}"
41
+ chunk_name, chunk_length = read_chunk_header(f)
42
+ fpos = f.tell
43
+
44
+ NuWav::WaveFile.log "found chunk: '#{chunk_name}', size #{chunk_length}"
45
+
46
+ if chunk_name && chunk_length
47
+
48
+ self.chunks[chunk_name.to_sym] = chunk_class(chunk_name).parse(chunk_name, chunk_length, f)
49
+ parsed_chunk_size = self.chunks[chunk_name.to_sym].size
50
+
51
+ NuWav::WaveFile.log "about to do a seek..."
52
+ NuWav::WaveFile.log "f.seek #{fpos} + #{parsed_chunk_size}"
53
+ f.seek(fpos + parsed_chunk_size)
54
+ NuWav::WaveFile.log "seek done"
55
+
56
+ if parsed_chunk_size.odd?
57
+ pad = f.read(1)
58
+ f.seek(fpos + parsed_chunk_size) if (pad.nil? || pad.ord != 0)
59
+ end
60
+
61
+ else
62
+ NuWav::WaveFile.log "chunk or length was off - remainder of file does not parse properly: #{riff_end} - #{fpos} = #{riff_end - fpos}"
63
+ f.seek(riff_end)
64
+ end
65
+ end
66
+ end
67
+ @chunks.each{|k,v| NuWav::WaveFile.log "#{k}: #{v}\n\n" unless k.to_s == 'data'}
68
+ NuWav::WaveFile.log "parse done"
69
+ self
70
+ end
71
+
72
+ def duration
73
+ fmt = @chunks[:fmt]
74
+
75
+ if (PCM_COMPRESSION.include?(fmt.compression_code.to_i))
76
+ data = @chunks[:data]
77
+ data.size / (fmt.sample_rate * fmt.number_of_channels * (fmt.sample_bits / 8))
78
+ elsif (fmt.compression_code.to_i == MPEG_COMPRESSION)
79
+ # <chunk type:fact samples_number:78695424 />
80
+ fact = @chunks[:fact]
81
+ fact.samples_number / fmt.sample_rate
82
+ else
83
+ raise "Duration implemented for PCM and MEPG files only."
84
+ end
85
+ end
86
+
87
+ def is_mpeg?
88
+ (@chunks[:fmt] && (@chunks[:fmt].compression_code.to_i == MPEG_COMPRESSION))
89
+ end
90
+
91
+ def is_pcm?
92
+ (@chunks[:fmt] && (PCM_COMPRESSION.include?(@chunks[:fmt].compression_code.to_i)))
93
+ end
94
+
95
+ def to_s
96
+ out = "NuWav:#{@header}\n"
97
+ out = [:fmt, :fact, :mext, :bext, :cart, :data ].inject(out) do |s, chunk|
98
+ s += "#{self.chunks[chunk]}\n" if self.chunks[chunk]
99
+ s
100
+ end
101
+ end
102
+
103
+ def to_file(file_name, add_extension=false)
104
+ if add_extension && !(file_name =~ /\.wav/)
105
+ file_name += ".wav"
106
+ end
107
+ NuWav::WaveFile.log "NuWav::WaveFile.to_file: file_name = #{file_name}"
108
+
109
+ #get all the chunks together to get final length
110
+ chunks_out = [:fmt, :fact, :mext, :bext, :cart, :data].inject([]) do |list, chunk|
111
+ if self.chunks[chunk]
112
+ out = self.chunks[chunk].to_binary
113
+ NuWav::WaveFile.log out.length
114
+ list << out
115
+ end
116
+ list
117
+ end
118
+
119
+ # TODO: handle other chunks not in the above list, but that might have been in a parsed wav
120
+
121
+ riff_length = chunks_out.inject(0){|sum, chunk| sum += chunk.size}
122
+ NuWav::WaveFile.log "NuWav::WaveFile.to_file: riff_length = #{riff_length}"
123
+
124
+ #open file for writing
125
+ open(file_name, "wb") do |o|
126
+ #write the header
127
+ o << "RIFF"
128
+ o << [(riff_length + 4)].pack('V')
129
+ o << "WAVE"
130
+ #write the chunks
131
+ chunks_out.each{|c| o << c}
132
+ end
133
+
134
+ end
135
+
136
+ def write_data_file(file_name)
137
+ open(file_name, "wb") do |o|
138
+ o << chunks[:data].data
139
+ end
140
+ end
141
+
142
+
143
+ # method to create a wave file using the
144
+ def self.from_mpeg(file_name)
145
+ # read and display infos & tags
146
+ NuWav::WaveFile.log "NuWav::from_mpeg::file_name:#{file_name}"
147
+ mp3info = Mp3Info.open(file_name)
148
+ NuWav::WaveFile.log mp3info
149
+ file = File.open(file_name)
150
+ wave = WaveFile.new
151
+
152
+ # data chunk
153
+ data = DataChunk.new_from_file(file)
154
+ wave.chunks[:data] = data
155
+
156
+ # fmt chunk
157
+ fmt = FmtChunk.new
158
+ fmt.compression_code = MPEG_COMPRESSION
159
+ fmt.number_of_channels = (mp3info.channel_mode == "Single Channel") ? 1 : 2
160
+ fmt.sample_rate = mp3info.samplerate
161
+ fmt.byte_rate = mp3info.bitrate / 8 * 1000
162
+ fmt.block_align = calculate_mpeg_frame_size(mp3info)
163
+ fmt.sample_bits = 65535
164
+ fmt.extra_size = 22
165
+ fmt.head_layer = ACM_LAYERS[mp3info.layer.to_i-1]
166
+ fmt.head_bit_rate = mp3info.bitrate * 1000
167
+ fmt.head_mode = CHANNEL_MODES[mp3info.channel_mode]
168
+ # fmt.head_mode_ext = (mp3info.channel_mode == "JStereo") ? 2**mp3info.mode_extension : 0
169
+ fmt.head_mode_ext = (mp3info.channel_mode == "JStereo") ? 2**mp3info.header[:mode_extension] : 0
170
+ # fmt.head_emphasis = mp3info.emphasis + 1
171
+ fmt.head_emphasis = mp3info.header[:emphasis] + 1
172
+ fmt.head_flags = calculate_mpeg_head_flags(mp3info)
173
+ fmt.pts_low = 0
174
+ fmt.pts_high = 0
175
+ wave.chunks[:fmt] = fmt
176
+ # NuWav::WaveFile.log "fmt: #{fmt}"
177
+
178
+ # fact chunk
179
+ fact = FactChunk.new
180
+ fact.samples_number = calculate_mpeg_samples_number(file, mp3info)
181
+ wave.chunks[:fact] = fact
182
+ # NuWav::WaveFile.log "fact: #{fact}"
183
+
184
+ #mext chunk
185
+ mext = MextChunk.new
186
+ mext.sound_information = 5
187
+ mext.sound_information += 2 if mp3info.header[:padding]
188
+ mext.frame_size = calculate_mpeg_frame_size(mp3info)
189
+ mext.ancillary_data_length = 0
190
+ mext.ancillary_data_def = 0
191
+ wave.chunks[:mext] = mext
192
+ # NuWav::WaveFile.log "mext: #{mext}"
193
+
194
+
195
+ #bext chunk
196
+ bext = BextChunk.new
197
+ bext.time_reference_high = 0
198
+ bext.time_reference_low = 0
199
+ bext.version = 1
200
+ bext.coding_history = "A=MPEG1L#{mp3info.layer},F=#{mp3info.samplerate},B=#{mp3info.bitrate},M=#{CODING_HISTORY_MODE[mp3info.channel_mode]},T=PRX\r\n\0\0"
201
+ wave.chunks[:bext] = bext
202
+ # NuWav::WaveFile.log "bext: #{bext}"
203
+
204
+ #cart chunk
205
+ cart = CartChunk.new
206
+ now = Time.now
207
+ today = Date.today
208
+ later = today << 12
209
+ cart.version = '0101'
210
+ cart.title = File.basename(file_name) # this is just a default
211
+ cart.start_date = today.strftime("%Y-%m-%d")
212
+ cart.start_time = now.strftime("%H:%M:%S")
213
+ cart.end_date = later.strftime("%Y-%m-%d")
214
+ cart.end_time = now.strftime("%H:%M:%S")
215
+ cart.producer_app_id = 'NuWav'
216
+ cart.producer_app_version = '1.0'
217
+ cart.level_reference = 0
218
+ cart.tag_text = "\r\n"
219
+ wave.chunks[:cart] = cart
220
+ # NuWav::WaveFile.log "cart: #{cart}"
221
+ wave
222
+ end
223
+
224
+ def self.calculate_mpeg_samples_number(file, info)
225
+ (File.size(file.path) / calculate_mpeg_frame_size(info)) * Mp3Info::SAMPLES_PER_FRAME[info.layer][info.mpeg_version]
226
+ end
227
+
228
+ def self.calculate_mpeg_head_flags(info)
229
+ flags = 0
230
+ flags += 1 if (info.header[:private_bit])
231
+ flags += 2 if (info.header[:copyright])
232
+ flags += 4 if (info.header[:original])
233
+ flags += 8 if (info.header[:error_protection])
234
+ flags += 16 if (info.mpeg_version > 0)
235
+ flags
236
+ end
237
+
238
+ def self.calculate_mpeg_frame_size(info)
239
+ samples_per_frame = Mp3Info::SAMPLES_PER_FRAME[info.layer][info.mpeg_version]
240
+ ((samples_per_frame / 8) * (info.bitrate * 1000))/info.samplerate
241
+ end
242
+
243
+ protected
244
+
245
+ def read_chunk_header(file)
246
+ hdr = file.read(8)
247
+ chunkName, chunkLen = hdr.unpack("A4V") rescue [nil, nil]
248
+ # NuWav::WaveFile.log "chunkName: '#{chunkName}', chunkLen: '#{chunkLen}'"
249
+ [chunkName, chunkLen]
250
+ end
251
+
252
+ def chunk_class(name)
253
+ begin
254
+ constantize("NuWav::#{camelize("#{name}_chunk")}")
255
+ rescue NameError
256
+ NuWav::Chunk
257
+ end
258
+
259
+ end
260
+
261
+ # File vendor/rails/activesupport/lib/active_support/inflector.rb, line 147
262
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
263
+ if first_letter_in_uppercase
264
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
265
+ else
266
+ lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
267
+ end
268
+ end
269
+
270
+ # File vendor/rails/activesupport/lib/active_support/inflector.rb, line 252
271
+ def constantize(camel_cased_word)
272
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
273
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
274
+ end
275
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
276
+ end
277
+
278
+ def self.log(m)
279
+ if NuWav::DEBUG
280
+ puts "#{Time.now}: NuWav: #{m}"
281
+ end
282
+ end
283
+
284
+ end
285
+
286
+ end