icanhasaudio 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Manifest.txt +7 -0
- data/README.txt +15 -2
- data/Rakefile +16 -1
- data/examples/encoder.rb +28 -0
- data/ext/extconf.rb +2 -0
- data/ext/get_audio.c +4 -0
- data/ext/get_audio.h +7 -0
- data/ext/icanhasaudio.c +6 -5
- data/ext/icanhasaudio.h +8 -0
- data/ext/mpeg_encoder.c +528 -0
- data/ext/mpeg_encoder.h +10 -0
- data/lib/icanhasaudio/mpeg.rb +2 -0
- data/lib/icanhasaudio/mpeg/encoder.rb +241 -0
- data/test/assets/testcase.wav +0 -0
- data/test/test_mpeg_encoder.rb +120 -0
- metadata +83 -72
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -4,6 +4,7 @@ Manifest.txt
|
|
4
4
|
README.txt
|
5
5
|
Rakefile
|
6
6
|
examples/decoder.rb
|
7
|
+
examples/encoder.rb
|
7
8
|
examples/kexp.rb
|
8
9
|
ext/decoder.c
|
9
10
|
ext/decoder.h
|
@@ -11,10 +12,16 @@ ext/extconf.rb
|
|
11
12
|
ext/get_audio.c
|
12
13
|
ext/get_audio.h
|
13
14
|
ext/icanhasaudio.c
|
15
|
+
ext/icanhasaudio.h
|
16
|
+
ext/mpeg_encoder.c
|
17
|
+
ext/mpeg_encoder.h
|
14
18
|
ext/rb_ogg.c
|
15
19
|
ext/rb_wav.c
|
16
20
|
ext/rb_wav.h
|
17
21
|
ext/syncword.c
|
18
22
|
ext/syncword.h
|
19
23
|
lib/icanhasaudio/mpeg.rb
|
24
|
+
lib/icanhasaudio/mpeg/encoder.rb
|
20
25
|
lib/icanhasaudio/ogg.rb
|
26
|
+
test/assets/testcase.wav
|
27
|
+
test/test_mpeg_encoder.rb
|
data/README.txt
CHANGED
@@ -5,7 +5,8 @@
|
|
5
5
|
== DESCRIPTION. LULZ
|
6
6
|
|
7
7
|
Hai! icanhasaudio? is an interface to lame for decoding ur MP3s. I iz in ur
|
8
|
-
computer. Decodin ur mp3s. Whatevs! I also decodin ur OGGz!
|
8
|
+
computer. Decodin ur mp3s. Whatevs! I also decodin ur OGGz! I kin also
|
9
|
+
encodin' ur WAV and AIFF to mp3z!
|
9
10
|
|
10
11
|
== SYNOPSYS ROFLOL
|
11
12
|
|
@@ -23,15 +24,27 @@ Or even smaller:
|
|
23
24
|
reader = Audio::OGG::Decoder.new
|
24
25
|
reader.decode(File.open(ARGV[0], 'rb'), File.open(ARGV[1], 'wb'))
|
25
26
|
|
27
|
+
Encoder!!!!!! LOL
|
28
|
+
|
29
|
+
writer = Audio::MPEG::Encoder.new
|
30
|
+
File.open(ARGV[0]), 'rb') { |wav_lol|
|
31
|
+
File.open(ARGV[1]), 'wb+') { |mp3_lol|
|
32
|
+
writer.encode(wav_lol, mp3_lol)
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
26
36
|
== PROBLEMS
|
27
37
|
|
28
|
-
Currently only decodes MP3/OGG data.
|
38
|
+
Currently only decodes MP3/OGG data. Also encodes WAV/AIFF to mp3. Plus many
|
39
|
+
other problems.... YMMV. LOL.
|
29
40
|
Not laugh plz!
|
30
41
|
|
31
42
|
== DEPENDENSEEZ
|
32
43
|
|
33
44
|
Make sure lame is installed on ur 'puter. Also ogg and vorbisfile!
|
34
45
|
|
46
|
+
# port install libvorbis vorbis-tools lame
|
47
|
+
|
35
48
|
== CREDITZ
|
36
49
|
|
37
50
|
Thanx Ryan for mah name! Also, most of this code was taken from the lame
|
data/Rakefile
CHANGED
@@ -2,7 +2,9 @@ require 'hoe'
|
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "lib")
|
4
4
|
|
5
|
-
|
5
|
+
kind = Config::CONFIG["DLEXT"]
|
6
|
+
|
7
|
+
Hoe.new('icanhasaudio', '0.1.0') do |p|
|
6
8
|
p.rubyforge_name = 'seattlerb'
|
7
9
|
p.author = 'Aaron Patterson'
|
8
10
|
p.email = 'aaronp@rubyforge.org'
|
@@ -11,5 +13,18 @@ Hoe.new('icanhasaudio', '0.0.3') do |p|
|
|
11
13
|
p.url = p.paragraphs_of('README.txt', 1).first.strip
|
12
14
|
p.changes = p.paragraphs_of('History.txt', 0..2).join("\n\n")
|
13
15
|
p.spec_extras = { :extensions => ['ext/extconf.rb'] }
|
16
|
+
p.clean_globs = ["ext/Makefile", "ext/*.{o,so,bundle,log}"]
|
17
|
+
end
|
18
|
+
|
19
|
+
Rake::Task[:test].prerequisites << :extension
|
20
|
+
|
21
|
+
desc "I can haz binary"
|
22
|
+
task :extension => ["ext/icanhasaudio.#{kind}"]
|
23
|
+
|
24
|
+
file "ext/Makefile" => "ext/extconf.rb" do
|
25
|
+
Dir.chdir("ext") { ruby "extconf.rb" }
|
14
26
|
end
|
15
27
|
|
28
|
+
file "ext/icanhasaudio.#{kind}" => FileList["ext/Makefile", "ext/*.{c,h}"] do
|
29
|
+
Dir.chdir("ext") { sh "make" }
|
30
|
+
end
|
data/examples/encoder.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'icanhasaudio'
|
2
|
+
|
3
|
+
include Audio::MPEG
|
4
|
+
|
5
|
+
writer = Encoder.new
|
6
|
+
|
7
|
+
### NOTE: All of these settings are optional ###
|
8
|
+
# The default setup is 128kbps minimum plus VBR.
|
9
|
+
|
10
|
+
# Set up the encoder to be 128kbps, NON-VBR
|
11
|
+
writer.bitrate = 128
|
12
|
+
writer.vbr_type = Encoder::VBR_OFF
|
13
|
+
|
14
|
+
# Set the ID3 tags
|
15
|
+
writer.title = 'tenderlovemaking.com'
|
16
|
+
writer.artist = 'Aaron Patterson'
|
17
|
+
writer.album = 'ICANHASAUDIO'
|
18
|
+
writer.year = 2008
|
19
|
+
writer.track = 1
|
20
|
+
writer.genre = 'Porn Groove'
|
21
|
+
|
22
|
+
# Make sure to open your outfile as read and write. Writing VBR tags requires
|
23
|
+
# seeking through the file.
|
24
|
+
File.open(ARGV[1], 'wb+') { |outfile|
|
25
|
+
File.open(ARGV[0], 'rb') { |infile|
|
26
|
+
writer.encode(infile, outfile)
|
27
|
+
}
|
28
|
+
}
|
data/ext/extconf.rb
CHANGED
data/ext/get_audio.c
CHANGED
@@ -10,6 +10,10 @@ get_audio_common(VALUE self, VALUE musicin,
|
|
10
10
|
int buffer[2][1152], short buffer16[2][1152],
|
11
11
|
mp3data_struct * mp3data);
|
12
12
|
|
13
|
+
int get_audio_pcm(VALUE self, VALUE musicin, int buffer[2][1152]) {
|
14
|
+
return 0;
|
15
|
+
}
|
16
|
+
|
13
17
|
static int
|
14
18
|
read_samples_mp3(VALUE self, VALUE musicin,
|
15
19
|
short int mpg123pcm[2][1152], int stereo, mp3data_struct * mp3data);
|
data/ext/get_audio.h
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
+
#ifndef GET_AUDIO_H
|
2
|
+
#define GET_AUDIO_H
|
3
|
+
|
1
4
|
#define MAX_U_32_NUM 0xFFFFFFFF
|
2
5
|
int
|
3
6
|
get_audio16(VALUE self, VALUE musicin, short buffer[2][1152],
|
4
7
|
mp3data_struct * mp3data);
|
8
|
+
|
9
|
+
int get_audio_pcm(VALUE self, VALUE musicin, int buffer[2][1152]);
|
10
|
+
|
11
|
+
#endif
|
data/ext/icanhasaudio.c
CHANGED
@@ -6,11 +6,10 @@
|
|
6
6
|
*
|
7
7
|
* Released under the GPL
|
8
8
|
*/
|
9
|
-
#include
|
10
|
-
#include <lame/lame.h>
|
11
|
-
#include <dlfcn.h>
|
9
|
+
#include "icanhasaudio.h"
|
12
10
|
#include "syncword.h"
|
13
11
|
#include "decoder.h"
|
12
|
+
#include "mpeg_encoder.h"
|
14
13
|
#include "get_audio.h"
|
15
14
|
|
16
15
|
static VALUE rb_mAudio;
|
@@ -164,8 +163,8 @@ void Init_icanhasaudio() {
|
|
164
163
|
rb_mOgg = rb_define_module_under(rb_mAudio, "OGG");
|
165
164
|
rb_cOggDecoder = rb_define_class_under(rb_mOgg, "Decoder", rb_cObject);
|
166
165
|
|
167
|
-
/* VERSION = '0.0
|
168
|
-
rb_define_const(rb_cDecoder, "VERSION", rb_str_new2("0.0
|
166
|
+
/* VERSION = '0.1.0' */
|
167
|
+
rb_define_const(rb_cDecoder, "VERSION", rb_str_new2("0.1.0"));
|
169
168
|
rb_define_singleton_method(
|
170
169
|
rb_cDecoder,
|
171
170
|
"lame_version",
|
@@ -176,6 +175,8 @@ void Init_icanhasaudio() {
|
|
176
175
|
rb_define_alloc_func(rb_cDecoder, reader_allocate);
|
177
176
|
rb_define_method(rb_cDecoder, "decode", method_lame_decode, 2);
|
178
177
|
rb_define_method(rb_cOggDecoder, "decode", method_ogg_decode, 2);
|
178
|
+
|
179
|
+
init_MpegEncoder(rb_mMpeg);
|
179
180
|
rb_require("icanhasaudio/mpeg");
|
180
181
|
rb_require("icanhasaudio/ogg");
|
181
182
|
}
|
data/ext/icanhasaudio.h
ADDED
data/ext/mpeg_encoder.c
ADDED
@@ -0,0 +1,528 @@
|
|
1
|
+
#include "mpeg_encoder.h"
|
2
|
+
#include <rubyio.h>
|
3
|
+
|
4
|
+
static VALUE cMpegEncoder;
|
5
|
+
|
6
|
+
static void encoder_free(lame_global_flags * gfp) {
|
7
|
+
lame_close(gfp);
|
8
|
+
}
|
9
|
+
|
10
|
+
/*
|
11
|
+
* call-seq:
|
12
|
+
* Audio::MPEG::Encoder.new
|
13
|
+
*
|
14
|
+
* Returns a new MPEG Encoder object.
|
15
|
+
*/
|
16
|
+
static VALUE
|
17
|
+
encoder_allocate(VALUE klass) {
|
18
|
+
lame_global_flags * gfp = lame_init();
|
19
|
+
id3tag_init(gfp);
|
20
|
+
|
21
|
+
return Data_Wrap_Struct(klass, NULL, encoder_free, gfp);
|
22
|
+
}
|
23
|
+
|
24
|
+
/* Public methods. */
|
25
|
+
|
26
|
+
/*
|
27
|
+
* call-seq:
|
28
|
+
* encoder.vbr_hard_min=
|
29
|
+
*
|
30
|
+
* Strictly enforce the vbr min bitrate. Normally it will be violated for
|
31
|
+
* analog silence.
|
32
|
+
*/
|
33
|
+
static VALUE MpegEncoder_set_vbr_hard_min(VALUE self, VALUE boolean) {
|
34
|
+
lame_global_flags * gfp;
|
35
|
+
|
36
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
37
|
+
lame_set_VBR_hard_min(gfp, boolean == Qtrue ? 1 : 0);
|
38
|
+
return boolean;
|
39
|
+
}
|
40
|
+
|
41
|
+
/*
|
42
|
+
* call-seq:
|
43
|
+
* encoder.vbr_hard_min?
|
44
|
+
*
|
45
|
+
* Get the hard minimum flag.
|
46
|
+
*/
|
47
|
+
static VALUE MpegEncoder_get_vbr_hard_min(VALUE self) {
|
48
|
+
lame_global_flags * gfp;
|
49
|
+
|
50
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
51
|
+
return lame_get_VBR_hard_min(gfp) == 0 ? Qfalse : Qtrue;
|
52
|
+
}
|
53
|
+
|
54
|
+
/*
|
55
|
+
* call-seq:
|
56
|
+
* encoder.vbr_max_bitrate=
|
57
|
+
*
|
58
|
+
* Set the maximum vbr bitrate.
|
59
|
+
*/
|
60
|
+
static VALUE MpegEncoder_set_vbr_max_bitrate(VALUE self, VALUE brate) {
|
61
|
+
lame_global_flags * gfp;
|
62
|
+
|
63
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
64
|
+
lame_set_VBR_max_bitrate_kbps(gfp, NUM2INT(brate));
|
65
|
+
return brate;
|
66
|
+
}
|
67
|
+
|
68
|
+
/*
|
69
|
+
* call-seq:
|
70
|
+
* encoder.vbr_max_bitrate
|
71
|
+
*
|
72
|
+
* Get the maximum vbr bitrate.
|
73
|
+
*/
|
74
|
+
static VALUE MpegEncoder_get_vbr_max_bitrate(VALUE self) {
|
75
|
+
lame_global_flags * gfp;
|
76
|
+
|
77
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
78
|
+
return INT2NUM(lame_get_VBR_max_bitrate_kbps(gfp));
|
79
|
+
}
|
80
|
+
|
81
|
+
/*
|
82
|
+
* call-seq:
|
83
|
+
* encoder.vbr_min_bitrate=
|
84
|
+
*
|
85
|
+
* Set the minimum vbr bitrate.
|
86
|
+
*/
|
87
|
+
static VALUE MpegEncoder_set_vbr_min_bitrate(VALUE self, VALUE brate) {
|
88
|
+
lame_global_flags * gfp;
|
89
|
+
|
90
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
91
|
+
lame_set_VBR_min_bitrate_kbps(gfp, NUM2INT(brate));
|
92
|
+
return brate;
|
93
|
+
}
|
94
|
+
|
95
|
+
/*
|
96
|
+
* call-seq:
|
97
|
+
* encoder.vbr_min_bitrate
|
98
|
+
*
|
99
|
+
* Get the minimum vbr bitrate.
|
100
|
+
*/
|
101
|
+
static VALUE MpegEncoder_get_vbr_min_bitrate(VALUE self) {
|
102
|
+
lame_global_flags * gfp;
|
103
|
+
|
104
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
105
|
+
return INT2NUM(lame_get_VBR_min_bitrate_kbps(gfp));
|
106
|
+
}
|
107
|
+
|
108
|
+
/*
|
109
|
+
* call-seq:
|
110
|
+
* encoder.bitrate=
|
111
|
+
*
|
112
|
+
* Set the bitrate.
|
113
|
+
*/
|
114
|
+
static VALUE MpegEncoder_set_bitrate(VALUE self, VALUE brate) {
|
115
|
+
lame_global_flags * gfp;
|
116
|
+
|
117
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
118
|
+
lame_set_brate(gfp, NUM2INT(brate));
|
119
|
+
lame_set_VBR_min_bitrate_kbps(gfp, lame_get_brate(gfp));
|
120
|
+
return brate;
|
121
|
+
}
|
122
|
+
|
123
|
+
/*
|
124
|
+
* call-seq:
|
125
|
+
* encoder.bitrate
|
126
|
+
*
|
127
|
+
* Get the bitrate.
|
128
|
+
*/
|
129
|
+
static VALUE MpegEncoder_get_bitrate(VALUE self) {
|
130
|
+
lame_global_flags * gfp;
|
131
|
+
|
132
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
133
|
+
return INT2NUM(lame_get_brate(gfp));
|
134
|
+
}
|
135
|
+
|
136
|
+
/*
|
137
|
+
* call-seq:
|
138
|
+
* encoder.quality=
|
139
|
+
*
|
140
|
+
* Set the VBR quality. 0 = highest, 9 = lowest
|
141
|
+
*/
|
142
|
+
static VALUE MpegEncoder_set_vbr_quality(VALUE self, VALUE quality) {
|
143
|
+
lame_global_flags * gfp;
|
144
|
+
|
145
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
146
|
+
lame_set_VBR_q(gfp, NUM2INT(quality));
|
147
|
+
return quality;
|
148
|
+
}
|
149
|
+
|
150
|
+
/*
|
151
|
+
* call-seq:
|
152
|
+
* encoder.quality
|
153
|
+
*
|
154
|
+
* Get the VBR quality. 0 = highest, 9 = lowest
|
155
|
+
*/
|
156
|
+
static VALUE MpegEncoder_get_vbr_quality(VALUE self) {
|
157
|
+
lame_global_flags * gfp;
|
158
|
+
|
159
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
160
|
+
return INT2NUM(lame_get_VBR_q(gfp));
|
161
|
+
}
|
162
|
+
|
163
|
+
/*
|
164
|
+
* call-seq:
|
165
|
+
* encoder.vbr_type=
|
166
|
+
*
|
167
|
+
* Set the type of VBR. Must be VBR_OFF, VBR_NORMAL, or VBR_FAST
|
168
|
+
*/
|
169
|
+
static VALUE MpegEncoder_set_vbr_type(VALUE self, VALUE type) {
|
170
|
+
lame_global_flags * gfp;
|
171
|
+
|
172
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
173
|
+
lame_set_VBR(gfp, NUM2INT(type));
|
174
|
+
return type;
|
175
|
+
}
|
176
|
+
|
177
|
+
/*
|
178
|
+
* call-seq:
|
179
|
+
* encoder.vbr_type
|
180
|
+
*
|
181
|
+
* Get the type of VBR.
|
182
|
+
*/
|
183
|
+
static VALUE MpegEncoder_get_vbr_type(VALUE self) {
|
184
|
+
lame_global_flags * gfp;
|
185
|
+
|
186
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
187
|
+
return INT2NUM(lame_get_VBR(gfp));
|
188
|
+
}
|
189
|
+
|
190
|
+
/*
|
191
|
+
* call-seq:
|
192
|
+
* encoder.print_config
|
193
|
+
*
|
194
|
+
* Print the encoder configuration.
|
195
|
+
*/
|
196
|
+
static VALUE MpegEncoder_print_config(VALUE self) {
|
197
|
+
lame_global_flags * gfp;
|
198
|
+
|
199
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
200
|
+
lame_print_config(gfp);
|
201
|
+
return Qnil;
|
202
|
+
}
|
203
|
+
|
204
|
+
/*
|
205
|
+
* call-seq:
|
206
|
+
* encoder.artist=
|
207
|
+
*
|
208
|
+
* Set the ID3 artist.
|
209
|
+
*/
|
210
|
+
static VALUE MpegEncoder_set_artist(VALUE self, VALUE artist) {
|
211
|
+
lame_global_flags * gfp;
|
212
|
+
|
213
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
214
|
+
id3tag_set_artist(gfp, StringValuePtr(artist));
|
215
|
+
return artist;
|
216
|
+
}
|
217
|
+
|
218
|
+
/*
|
219
|
+
* call-seq:
|
220
|
+
* encoder.title=
|
221
|
+
*
|
222
|
+
* Set the ID3 title.
|
223
|
+
*/
|
224
|
+
static VALUE MpegEncoder_set_title(VALUE self, VALUE title) {
|
225
|
+
lame_global_flags * gfp;
|
226
|
+
|
227
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
228
|
+
id3tag_set_title(gfp, StringValuePtr(title));
|
229
|
+
return title;
|
230
|
+
}
|
231
|
+
|
232
|
+
/*
|
233
|
+
* call-seq:
|
234
|
+
* encoder.album=
|
235
|
+
*
|
236
|
+
* Set the ID3 album.
|
237
|
+
*/
|
238
|
+
static VALUE MpegEncoder_set_album(VALUE self, VALUE album) {
|
239
|
+
lame_global_flags * gfp;
|
240
|
+
|
241
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
242
|
+
id3tag_set_album(gfp, StringValuePtr(album));
|
243
|
+
return album;
|
244
|
+
}
|
245
|
+
|
246
|
+
/*
|
247
|
+
* call-seq:
|
248
|
+
* encoder.year=
|
249
|
+
*
|
250
|
+
* Set the ID3 year.
|
251
|
+
*/
|
252
|
+
static VALUE MpegEncoder_set_year(VALUE self, VALUE year) {
|
253
|
+
lame_global_flags * gfp;
|
254
|
+
VALUE * year_string;
|
255
|
+
|
256
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
257
|
+
year_string = rb_funcall(year, rb_intern("to_s"), 0);
|
258
|
+
id3tag_set_year(gfp, StringValuePtr(year_string));
|
259
|
+
return year;
|
260
|
+
}
|
261
|
+
|
262
|
+
/*
|
263
|
+
* call-seq:
|
264
|
+
* encoder.track=
|
265
|
+
*
|
266
|
+
* Set the ID3 track.
|
267
|
+
*/
|
268
|
+
static VALUE MpegEncoder_set_track(VALUE self, VALUE track) {
|
269
|
+
lame_global_flags * gfp;
|
270
|
+
int track_number;
|
271
|
+
VALUE * track_string;
|
272
|
+
|
273
|
+
track_number = NUM2INT(track);
|
274
|
+
if(track < 0 || track > 255)
|
275
|
+
rb_raise(rb_eRuntimeError, "Track must be between 0 and 255.\n");
|
276
|
+
|
277
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
278
|
+
track_string = rb_funcall(track, rb_intern("to_s"), 0);
|
279
|
+
id3tag_set_track(gfp, StringValuePtr(track_string));
|
280
|
+
return track;
|
281
|
+
}
|
282
|
+
|
283
|
+
/*
|
284
|
+
* call-seq:
|
285
|
+
* encoder.genre=
|
286
|
+
*
|
287
|
+
* Set the ID3 genre.
|
288
|
+
*/
|
289
|
+
static VALUE MpegEncoder_set_genre(VALUE self, VALUE genre) {
|
290
|
+
lame_global_flags * gfp;
|
291
|
+
|
292
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
293
|
+
id3tag_set_genre(gfp, StringValuePtr(genre));
|
294
|
+
return genre;
|
295
|
+
}
|
296
|
+
|
297
|
+
/* Private methods. */
|
298
|
+
|
299
|
+
static VALUE MpegEncoder_write_vbr_tag(VALUE self) {
|
300
|
+
lame_global_flags * gfp;
|
301
|
+
|
302
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
303
|
+
if(lame_get_bWriteVbrTag(gfp))
|
304
|
+
return Qtrue;
|
305
|
+
return Qfalse;
|
306
|
+
}
|
307
|
+
|
308
|
+
static VALUE MpegEncoder_print_internals(VALUE self) {
|
309
|
+
lame_global_flags * gfp;
|
310
|
+
|
311
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
312
|
+
lame_print_internals(gfp);
|
313
|
+
return Qnil;
|
314
|
+
}
|
315
|
+
|
316
|
+
static VALUE MpegEncoder_get_mpeg_quality(VALUE self) {
|
317
|
+
lame_global_flags * gfp;
|
318
|
+
|
319
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
320
|
+
return INT2NUM(lame_get_quality(gfp));
|
321
|
+
}
|
322
|
+
|
323
|
+
static VALUE MpegEncoder_get_compression_ratio(VALUE self) {
|
324
|
+
lame_global_flags * gfp;
|
325
|
+
|
326
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
327
|
+
return rb_float_new(lame_get_compression_ratio(gfp));
|
328
|
+
}
|
329
|
+
|
330
|
+
static VALUE MpegEncoder_get_mpeg_version(VALUE self) {
|
331
|
+
lame_global_flags * gfp;
|
332
|
+
|
333
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
334
|
+
return INT2NUM(lame_get_version(gfp));
|
335
|
+
}
|
336
|
+
|
337
|
+
static VALUE MpegEncoder_get_mpeg_mode(VALUE self) {
|
338
|
+
lame_global_flags * gfp;
|
339
|
+
|
340
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
341
|
+
return INT2NUM(lame_get_mode(gfp));
|
342
|
+
}
|
343
|
+
|
344
|
+
static VALUE MpegEncoder_get_force_ms(VALUE self) {
|
345
|
+
lame_global_flags * gfp;
|
346
|
+
|
347
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
348
|
+
return INT2NUM(lame_get_force_ms(gfp));
|
349
|
+
}
|
350
|
+
|
351
|
+
static VALUE MpegEncoder_get_out_samplerate(VALUE self) {
|
352
|
+
lame_global_flags * gfp;
|
353
|
+
|
354
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
355
|
+
return INT2NUM(lame_get_out_samplerate(gfp));
|
356
|
+
}
|
357
|
+
|
358
|
+
static VALUE MpegEncoder_set_in_samplerate(VALUE self, VALUE samplerate) {
|
359
|
+
lame_global_flags * gfp;
|
360
|
+
|
361
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
362
|
+
lame_set_in_samplerate(gfp, NUM2INT(samplerate));
|
363
|
+
return samplerate;
|
364
|
+
}
|
365
|
+
|
366
|
+
static VALUE MpegEncoder_set_num_samples(VALUE self, VALUE num_samples) {
|
367
|
+
lame_global_flags * gfp;
|
368
|
+
|
369
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
370
|
+
lame_set_num_samples(gfp, NUM2ULONG(num_samples));
|
371
|
+
return num_samples;
|
372
|
+
}
|
373
|
+
|
374
|
+
static VALUE MpegEncoder_get_num_samples(VALUE self) {
|
375
|
+
lame_global_flags * gfp;
|
376
|
+
|
377
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
378
|
+
return ULONG2NUM(lame_get_num_samples(gfp));
|
379
|
+
}
|
380
|
+
|
381
|
+
static VALUE MpegEncoder_set_num_channels(VALUE self, VALUE num_channels) {
|
382
|
+
lame_global_flags * gfp;
|
383
|
+
|
384
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
385
|
+
lame_set_num_channels(gfp, NUM2INT(num_channels));
|
386
|
+
return num_channels;
|
387
|
+
}
|
388
|
+
|
389
|
+
static VALUE MpegEncoder_get_num_channels(VALUE self) {
|
390
|
+
lame_global_flags * gfp;
|
391
|
+
|
392
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
393
|
+
return INT2NUM(lame_get_num_channels(gfp));
|
394
|
+
}
|
395
|
+
|
396
|
+
static VALUE MpegEncoder_get_framesize(VALUE self) {
|
397
|
+
lame_global_flags * gfp;
|
398
|
+
|
399
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
400
|
+
return INT2NUM(lame_get_framesize(gfp));
|
401
|
+
}
|
402
|
+
|
403
|
+
static VALUE MpegEncoder_encoder_buffer(VALUE self, VALUE left, VALUE right) {
|
404
|
+
unsigned char mp3buffer[LAME_MAXMP3BUFFER];
|
405
|
+
int * buffer_left;
|
406
|
+
int * buffer_right;
|
407
|
+
int length, i;
|
408
|
+
int imp3;
|
409
|
+
lame_global_flags * gfp;
|
410
|
+
|
411
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
412
|
+
|
413
|
+
length = NUM2INT(rb_funcall(left, rb_intern("length"), 0));
|
414
|
+
buffer_left = calloc(length, sizeof(int));
|
415
|
+
buffer_right = calloc(length, sizeof(int));
|
416
|
+
|
417
|
+
for(i = 0; i < length; i++) {
|
418
|
+
buffer_left[i] = NUM2UINT(rb_funcall(left, rb_intern("[]"), 1, INT2NUM(i)));
|
419
|
+
buffer_right[i] = NUM2UINT(rb_funcall(right, rb_intern("[]"), 1,INT2NUM(i)));
|
420
|
+
}
|
421
|
+
|
422
|
+
imp3 = lame_encode_buffer_int(gfp, buffer_left, buffer_right, length,
|
423
|
+
mp3buffer, sizeof(mp3buffer));
|
424
|
+
|
425
|
+
free(buffer_left);
|
426
|
+
free(buffer_right);
|
427
|
+
|
428
|
+
if(imp3 < 0) {
|
429
|
+
if(imp3 == -1)
|
430
|
+
rb_raise(rb_eRuntimeError, "Mp3 buffer is not big enough.\n");
|
431
|
+
else
|
432
|
+
rb_raise(rb_eRuntimeError, "internal error.\n");
|
433
|
+
}
|
434
|
+
return rb_str_new(mp3buffer, imp3);
|
435
|
+
}
|
436
|
+
|
437
|
+
static VALUE MpegEncoder_flush(VALUE self) {
|
438
|
+
unsigned char mp3buffer[LAME_MAXMP3BUFFER];
|
439
|
+
int imp3;
|
440
|
+
lame_global_flags * gfp;
|
441
|
+
|
442
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
443
|
+
imp3 = lame_encode_flush(gfp, mp3buffer, sizeof(mp3buffer));
|
444
|
+
if(imp3 < 0) {
|
445
|
+
if(imp3 == -1)
|
446
|
+
rb_raise(rb_eRuntimeError, "Mp3 buffer is not big enough.\n");
|
447
|
+
else
|
448
|
+
rb_raise(rb_eRuntimeError, "internal error.\n");
|
449
|
+
}
|
450
|
+
return rb_str_new(mp3buffer, imp3);
|
451
|
+
}
|
452
|
+
|
453
|
+
static VALUE MpegEncoder_write_vbr_tags(VALUE self, VALUE outfile) {
|
454
|
+
OpenFile *fp;
|
455
|
+
lame_global_flags * gfp;
|
456
|
+
|
457
|
+
GetOpenFile(outfile, fp);
|
458
|
+
|
459
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
460
|
+
lame_mp3_tags_fid(gfp, fp->f);
|
461
|
+
return Qnil;
|
462
|
+
}
|
463
|
+
|
464
|
+
static VALUE MpegEncoder_init_params(VALUE self) {
|
465
|
+
lame_global_flags * gfp;
|
466
|
+
|
467
|
+
Data_Get_Struct(self, lame_global_flags, gfp);
|
468
|
+
|
469
|
+
if(lame_init_params(gfp) < 0) {
|
470
|
+
rb_raise(rb_eRuntimeError, "Fatal error during initialization.\n");
|
471
|
+
}
|
472
|
+
|
473
|
+
return Qnil;
|
474
|
+
}
|
475
|
+
|
476
|
+
void init_MpegEncoder(VALUE rb_mMpeg) {
|
477
|
+
/*
|
478
|
+
rb_mAudio = rb_define_module("Audio");
|
479
|
+
rb_mMpeg = rb_define_module_under(rb_mAudio, "MPEG");
|
480
|
+
*/
|
481
|
+
/*
|
482
|
+
* Encode mp3s
|
483
|
+
*/
|
484
|
+
cMpegEncoder = rb_define_class_under(rb_mMpeg, "Encoder", rb_cObject);
|
485
|
+
rb_define_alloc_func(cMpegEncoder, encoder_allocate);
|
486
|
+
|
487
|
+
/* Public Methods */
|
488
|
+
|
489
|
+
rb_define_method(cMpegEncoder, "vbr_quality=",MpegEncoder_set_vbr_quality, 1);
|
490
|
+
rb_define_method(cMpegEncoder, "vbr_quality", MpegEncoder_get_vbr_quality, 0);
|
491
|
+
rb_define_method(cMpegEncoder, "vbr_type=", MpegEncoder_set_vbr_type, 1);
|
492
|
+
rb_define_method(cMpegEncoder, "vbr_type", MpegEncoder_get_vbr_type, 0);
|
493
|
+
rb_define_method(cMpegEncoder, "print_config", MpegEncoder_print_config, 0);
|
494
|
+
rb_define_method(cMpegEncoder, "title=", MpegEncoder_set_title, 1);
|
495
|
+
rb_define_method(cMpegEncoder, "artist=", MpegEncoder_set_artist, 1);
|
496
|
+
rb_define_method(cMpegEncoder, "album=", MpegEncoder_set_album, 1);
|
497
|
+
rb_define_method(cMpegEncoder, "year=", MpegEncoder_set_year, 1);
|
498
|
+
rb_define_method(cMpegEncoder, "track=", MpegEncoder_set_track, 1);
|
499
|
+
rb_define_method(cMpegEncoder, "genre=", MpegEncoder_set_genre, 1);
|
500
|
+
rb_define_method(cMpegEncoder, "bitrate=", MpegEncoder_set_bitrate, 1);
|
501
|
+
rb_define_method(cMpegEncoder, "bitrate", MpegEncoder_get_bitrate, 0);
|
502
|
+
rb_define_method(cMpegEncoder, "vbr_min_bitrate=", MpegEncoder_set_vbr_min_bitrate, 1);
|
503
|
+
rb_define_method(cMpegEncoder, "vbr_min_bitrate", MpegEncoder_get_vbr_min_bitrate, 0);
|
504
|
+
rb_define_method(cMpegEncoder, "vbr_max_bitrate=", MpegEncoder_set_vbr_max_bitrate, 1);
|
505
|
+
rb_define_method(cMpegEncoder, "vbr_max_bitrate", MpegEncoder_get_vbr_max_bitrate, 0);
|
506
|
+
rb_define_method(cMpegEncoder, "vbr_hard_min=", MpegEncoder_set_vbr_hard_min, 1);
|
507
|
+
rb_define_method(cMpegEncoder, "vbr_hard_min?", MpegEncoder_get_vbr_hard_min, 0);
|
508
|
+
|
509
|
+
|
510
|
+
rb_define_private_method(cMpegEncoder, "init_params", MpegEncoder_init_params, 0);
|
511
|
+
rb_define_private_method(cMpegEncoder, "num_channels=", MpegEncoder_set_num_channels, 1);
|
512
|
+
rb_define_private_method(cMpegEncoder, "num_channels", MpegEncoder_get_num_channels, 0);
|
513
|
+
rb_define_private_method(cMpegEncoder, "in_samplerate=", MpegEncoder_set_in_samplerate, 1);
|
514
|
+
rb_define_private_method(cMpegEncoder, "num_samples=", MpegEncoder_set_num_samples, 1);
|
515
|
+
rb_define_private_method(cMpegEncoder, "num_samples", MpegEncoder_get_num_samples, 0);
|
516
|
+
rb_define_private_method(cMpegEncoder, "out_samplerate", MpegEncoder_get_out_samplerate, 0);
|
517
|
+
rb_define_private_method(cMpegEncoder, "framesize", MpegEncoder_get_framesize, 0);
|
518
|
+
rb_define_private_method(cMpegEncoder, "encode_buffer", MpegEncoder_encoder_buffer, 2);
|
519
|
+
rb_define_private_method(cMpegEncoder, "flush", MpegEncoder_flush, 0);
|
520
|
+
rb_define_private_method(cMpegEncoder, "write_vbr_tags", MpegEncoder_write_vbr_tags, 1);
|
521
|
+
rb_define_private_method(cMpegEncoder, "force_ms", MpegEncoder_get_force_ms, 0);
|
522
|
+
rb_define_private_method(cMpegEncoder, "mpeg_mode", MpegEncoder_get_mpeg_mode, 0);
|
523
|
+
rb_define_private_method(cMpegEncoder, "mpeg_version", MpegEncoder_get_mpeg_version, 0);
|
524
|
+
rb_define_private_method(cMpegEncoder, "compression_ratio", MpegEncoder_get_compression_ratio, 0);
|
525
|
+
rb_define_private_method(cMpegEncoder, "mpeg_quality", MpegEncoder_get_mpeg_quality, 0);
|
526
|
+
rb_define_private_method(cMpegEncoder, "print_internals", MpegEncoder_print_internals, 0);
|
527
|
+
rb_define_private_method(cMpegEncoder, "write_vbr_tag?", MpegEncoder_write_vbr_tag, 0);
|
528
|
+
}
|
data/ext/mpeg_encoder.h
ADDED
data/lib/icanhasaudio/mpeg.rb
CHANGED
@@ -0,0 +1,241 @@
|
|
1
|
+
module Audio
|
2
|
+
module MPEG
|
3
|
+
class Encoder
|
4
|
+
WAV_ID_RIFF = 0x52494646
|
5
|
+
WAV_ID_WAVE = 0x57415645 # "WAVE"
|
6
|
+
WAV_ID_FMT = 0x666d7420 # "fmt "
|
7
|
+
WAV_ID_DATA = 0x64617461 # "data"
|
8
|
+
|
9
|
+
IFF_ID_FORM = 0x464f524d # "FORM"
|
10
|
+
IFF_ID_AIFF = 0x41494646 # "AIFF"
|
11
|
+
IFF_ID_AIFC = 0x41494643 # "AIFC"
|
12
|
+
IFF_ID_COMM = 0x434f4d4d # "COMM"
|
13
|
+
IFF_ID_SSND = 0x53534e44 # "SSND"
|
14
|
+
|
15
|
+
IFF_ID_NONE = 0x4e4f4e45 # "NONE" AIFF-C data format
|
16
|
+
IFF_ID_2CBE = 0x74776f73 # "twos" AIFF-C data format
|
17
|
+
IFF_ID_2CLE = 0x736f7774 # "sowt" AIFF-C data format
|
18
|
+
|
19
|
+
|
20
|
+
VBR_OFF = 0
|
21
|
+
VBR_NORMAL = 2
|
22
|
+
VBR_FAST = 4
|
23
|
+
|
24
|
+
MODE_NAMES = [
|
25
|
+
[ 'stereo', 'j-stereo', 'dual-ch', 'single-ch' ],
|
26
|
+
[ 'stereo', 'force-ms', 'dual-ch', 'single-ch' ],
|
27
|
+
]
|
28
|
+
|
29
|
+
attr_accessor :pcmbitwidth
|
30
|
+
attr_accessor :logger
|
31
|
+
def initialize
|
32
|
+
@pcmbitwidth = 16
|
33
|
+
@logger = nil
|
34
|
+
self.vbr_quality = 2
|
35
|
+
self.vbr_type = VBR_NORMAL
|
36
|
+
end
|
37
|
+
|
38
|
+
def encode(infile, outfile)
|
39
|
+
raise "Out file must be a FILE. :-(" unless outfile.is_a?(File)
|
40
|
+
|
41
|
+
num_samples = 0xFFFFFFFF
|
42
|
+
parse_header(infile)
|
43
|
+
init_params
|
44
|
+
|
45
|
+
logger.debug(encoding_info) if logger
|
46
|
+
|
47
|
+
num_samples_read = 0
|
48
|
+
|
49
|
+
sw = case @pcmbitwidth
|
50
|
+
when 8
|
51
|
+
32 - 8
|
52
|
+
when 16
|
53
|
+
32 - 16
|
54
|
+
when 24
|
55
|
+
32 - 24
|
56
|
+
end
|
57
|
+
while !infile.eof?
|
58
|
+
tmp_num_samples = num_samples()
|
59
|
+
samples_to_read = framesize()
|
60
|
+
remaining = tmp_num_samples - [tmp_num_samples, num_samples_read].min
|
61
|
+
if remaining < framesize && 0 != tmp_num_samples
|
62
|
+
samples_to_read = remaining
|
63
|
+
end
|
64
|
+
|
65
|
+
read_size = num_channels * samples_to_read * (pcmbitwidth / 8)
|
66
|
+
|
67
|
+
samples = infile.read(read_size)
|
68
|
+
samples_read = samples.length / num_channels
|
69
|
+
|
70
|
+
buffers = [[], []]
|
71
|
+
samples.unpack('v*').each_with_index do |b,i|
|
72
|
+
(buffers[(i % 2)]) << (b << sw)
|
73
|
+
end
|
74
|
+
outfile.write(encode_buffer(buffers[0], buffers[1]))
|
75
|
+
end
|
76
|
+
outfile.write(flush())
|
77
|
+
write_vbr_tags(outfile) if write_vbr_tag?
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def encoding_info
|
82
|
+
sprintf("Encoding as %g kHz ", 1e-3 * out_samplerate) +
|
83
|
+
sprintf("VBR(q=%i)", vbr_quality) +
|
84
|
+
sprintf(" %s MPEG-%u%s Layer III (%s%gx) qval=%i\n",
|
85
|
+
MODE_NAMES[force_ms][mpeg_mode],
|
86
|
+
2 - mpeg_version,
|
87
|
+
out_samplerate < 16000 ? ".5" : '',
|
88
|
+
'',
|
89
|
+
0.1 * (10.0 * compression_ratio + 0.5).to_i,
|
90
|
+
mpeg_quality)
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_header(file)
|
94
|
+
header = file.read(4).unpack('N').first
|
95
|
+
case header
|
96
|
+
when WAV_ID_RIFF # Wave file
|
97
|
+
parse_wave_header(file)
|
98
|
+
when IFF_ID_FORM # AIFF file
|
99
|
+
parse_aiff_header(file)
|
100
|
+
else
|
101
|
+
$stderr.puts "Assuming RAW PCM"
|
102
|
+
file.rewind
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_aiff_header(file)
|
107
|
+
chunk_size = file.read(4).unpack('N').first
|
108
|
+
type_id = file.read(4).unpack('N').first
|
109
|
+
|
110
|
+
raise unless [IFF_ID_AIFF, IFF_ID_AIFC].any? { |x| type_id == x }
|
111
|
+
|
112
|
+
sub_size = 0
|
113
|
+
sample_type = nil
|
114
|
+
sample_size = nil
|
115
|
+
num_channels = nil
|
116
|
+
block_size = nil
|
117
|
+
sample_rate = nil
|
118
|
+
is_aiff = false
|
119
|
+
while chunk_size > 0
|
120
|
+
type = file.read(4).unpack('N').first
|
121
|
+
chunk_size -= 4
|
122
|
+
|
123
|
+
case type
|
124
|
+
when IFF_ID_COMM
|
125
|
+
sub_size = file.read(4).unpack('N').first
|
126
|
+
chunk_size -= 4
|
127
|
+
|
128
|
+
num_channels = file.read(2).unpack('n').first
|
129
|
+
sub_size -= 2
|
130
|
+
num_sample_frames = file.read(4).unpack('N').first
|
131
|
+
sub_size -= 4
|
132
|
+
sample_size = file.read(2).unpack('n').first
|
133
|
+
sub_size -= 2
|
134
|
+
sample_rate = unpack_ieee(file.read(10))
|
135
|
+
sub_size -= 10
|
136
|
+
|
137
|
+
if type_id == IFF_ID_AIFC
|
138
|
+
data_type = file.read(4).unpack('N').first
|
139
|
+
sub_size -=4
|
140
|
+
|
141
|
+
raise unless [ IFF_ID_2CLE,
|
142
|
+
IFF_ID_2CBE,
|
143
|
+
IFF_ID_NONE,
|
144
|
+
].any? { |x| data_type == x }
|
145
|
+
|
146
|
+
#if sample_size == 16
|
147
|
+
# swap bytes....
|
148
|
+
end
|
149
|
+
|
150
|
+
file.read(sub_size)
|
151
|
+
when IFF_ID_SSND
|
152
|
+
sub_size = file.read(4).unpack('N').first
|
153
|
+
chunk_size -= sub_size
|
154
|
+
|
155
|
+
block_offset = file.read(4).unpack('N').first
|
156
|
+
sub_size -= 4
|
157
|
+
block_size = file.read(4).unpack('N').first
|
158
|
+
sub_size -= 4
|
159
|
+
|
160
|
+
sample_type = IFF_ID_SSND
|
161
|
+
file.read(block_offset)
|
162
|
+
is_aiff = true
|
163
|
+
break
|
164
|
+
else
|
165
|
+
sub_size = file.read(4).unpack('N').first
|
166
|
+
chunk_size -= sub_size
|
167
|
+
file.read(sub_size)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Validate the header
|
172
|
+
if is_aiff
|
173
|
+
raise "Sound data is not PCM" unless sample_type == IFF_ID_SSND
|
174
|
+
raise "Sound data is not 16 bits" unless sample_size == 16
|
175
|
+
unless num_channels == 1 || num_channels == 2
|
176
|
+
raise "Sound data is not mono or stereo"
|
177
|
+
end
|
178
|
+
raise "Block size is not 0 bytes" unless block_size == 0
|
179
|
+
end
|
180
|
+
|
181
|
+
@pcmbitwidth = sample_size
|
182
|
+
self.num_channels = num_channels
|
183
|
+
self.in_samplerate = sample_rate.to_i
|
184
|
+
self.num_samples = num_sample_frames
|
185
|
+
end
|
186
|
+
|
187
|
+
def unpack_ieee(data)
|
188
|
+
(expon, hi_mant, lo_mant) = data.unpack('nNN')
|
189
|
+
expon -= 16383
|
190
|
+
hi_mant * (2 ** (expon -= 31)) + lo_mant * (2 ** (expon -= 32))
|
191
|
+
end
|
192
|
+
|
193
|
+
def parse_wave_header(file)
|
194
|
+
format_tag = nil
|
195
|
+
channels = nil
|
196
|
+
samples_per_sec = nil
|
197
|
+
avg_bytes_per_sec = nil
|
198
|
+
block_align = nil
|
199
|
+
bits_per_sample = nil
|
200
|
+
is_wav = false
|
201
|
+
data_length = 0
|
202
|
+
|
203
|
+
file_length = file.read(4).unpack('N').first
|
204
|
+
raise "Corrupt wave" if file.read(4).unpack('N').first != WAV_ID_WAVE
|
205
|
+
20.times {
|
206
|
+
type = file.read(4).unpack('N').first
|
207
|
+
case type
|
208
|
+
when WAV_ID_FMT
|
209
|
+
sub_size = file.read(4).unpack('V').first
|
210
|
+
raise "Corrupt wave" if sub_size < 16
|
211
|
+
|
212
|
+
( format_tag,
|
213
|
+
channels,
|
214
|
+
samples_per_sec,
|
215
|
+
avg_bytes_per_sec,
|
216
|
+
block_align, bits_per_sample) = *(file.read(16).unpack('vvVVvv'))
|
217
|
+
sub_size -= 16
|
218
|
+
|
219
|
+
file.read(sub_size) if sub_size > 0
|
220
|
+
when WAV_ID_DATA
|
221
|
+
sub_size = file.read(4).unpack('V').first
|
222
|
+
data_length = sub_size
|
223
|
+
is_wav = true
|
224
|
+
break;
|
225
|
+
else
|
226
|
+
sub_size = file.read(4).unpack('V').first
|
227
|
+
file.read(sub_size)
|
228
|
+
end
|
229
|
+
}
|
230
|
+
raise "Unsupported format" unless format_tag == 1
|
231
|
+
raise unless is_wav
|
232
|
+
|
233
|
+
self.num_channels = channels
|
234
|
+
self.in_samplerate = samples_per_sec
|
235
|
+
self.num_samples = data_length / (channels * ((bits_per_sample+7)/8))
|
236
|
+
@pcmbitwidth = bits_per_sample
|
237
|
+
is_wav
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
Binary file
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'icanhasaudio'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
class MPEGEncoderTest < Test::Unit::TestCase
|
6
|
+
include Audio::MPEG
|
7
|
+
|
8
|
+
WAV_FILE = File.dirname(__FILE__) + "/assets/testcase.wav"
|
9
|
+
#AIFF_FILE = File.dirname(__FILE__) + "/assets/cow.aiff"
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@encoder = Audio::MPEG::Encoder.new
|
13
|
+
assert File.exists?(WAV_FILE)
|
14
|
+
#assert File.exists?(AIFF_FILE)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_encoder_initialize
|
18
|
+
assert Audio::MPEG::Encoder.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_encode
|
22
|
+
File.open("#{Dir::tmpdir}/out.mp3", 'wb+') { |outfile|
|
23
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_set_bitrate
|
28
|
+
@encoder.bitrate = 128
|
29
|
+
assert_equal(128, @encoder.bitrate)
|
30
|
+
assert_equal(128, @encoder.vbr_min_bitrate)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_vbr_type
|
34
|
+
@encoder.vbr_type = Encoder::VBR_OFF
|
35
|
+
assert_equal(Encoder::VBR_OFF, @encoder.vbr_type)
|
36
|
+
File.open("#{Dir::tmpdir}/no_vbr.mp3", 'wb+') { |outfile|
|
37
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_set_min_vbr_bitrate
|
42
|
+
@encoder.vbr_min_bitrate = 128
|
43
|
+
assert_equal(128, @encoder.vbr_min_bitrate)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_set_vbr_max_bitrate
|
47
|
+
@encoder.vbr_max_bitrate = 128
|
48
|
+
assert_equal(128, @encoder.vbr_max_bitrate)
|
49
|
+
@encoder.vbr_max_bitrate = 256
|
50
|
+
assert_equal(256, @encoder.vbr_max_bitrate)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_set_vbr_hard_min
|
54
|
+
assert_equal(false, @encoder.vbr_hard_min?)
|
55
|
+
@encoder.vbr_hard_min = true
|
56
|
+
assert_equal(true, @encoder.vbr_hard_min?)
|
57
|
+
@encoder.vbr_hard_min = false
|
58
|
+
assert_equal(false, @encoder.vbr_hard_min?)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_set_title
|
62
|
+
@encoder.title = 'tenderlovemaking.com'
|
63
|
+
|
64
|
+
File.open("#{Dir::tmpdir}/title.mp3", 'wb+') { |outfile|
|
65
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_set_artist
|
70
|
+
@encoder.artist = 'Aaron Patterson'
|
71
|
+
|
72
|
+
File.open("#{Dir::tmpdir}/artist.mp3", 'wb+') { |outfile|
|
73
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_set_album
|
78
|
+
@encoder.album = 'Some Album'
|
79
|
+
|
80
|
+
File.open("#{Dir::tmpdir}/album.mp3", 'wb+') { |outfile|
|
81
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_set_year
|
86
|
+
@encoder.year = 1999
|
87
|
+
|
88
|
+
File.open("#{Dir::tmpdir}/year.mp3", 'wb+') { |outfile|
|
89
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_set_track
|
94
|
+
@encoder.track = 1
|
95
|
+
|
96
|
+
File.open("#{Dir::tmpdir}/track.mp3", 'wb+') { |outfile|
|
97
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_set_genre
|
102
|
+
@encoder.genre = 'Porn Groove'
|
103
|
+
|
104
|
+
File.open("#{Dir::tmpdir}/genre.mp3", 'wb+') { |outfile|
|
105
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_set_all
|
110
|
+
@encoder.title = 'tenderlovemaking.com'
|
111
|
+
@encoder.artist = 'Aaron Patterson'
|
112
|
+
@encoder.album = 'ICANHASAUDIO'
|
113
|
+
@encoder.year = 2008
|
114
|
+
@encoder.track = 1
|
115
|
+
@encoder.genre = 'Rock'
|
116
|
+
File.open("#{Dir::tmpdir}/all.mp3", 'wb+') { |outfile|
|
117
|
+
@encoder.encode(File.open(WAV_FILE, 'rb'), outfile)
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
metadata
CHANGED
@@ -1,81 +1,92 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.0
|
3
|
-
specification_version: 1
|
4
2
|
name: icanhasaudio
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
- lib
|
11
|
-
- ext
|
12
|
-
email: aaronp@rubyforge.org
|
13
|
-
homepage: http://seattlerb.rubyforge.org/
|
14
|
-
rubyforge_project: seattlerb
|
15
|
-
description: "Hai! icanhasaudio? is an interface to lame for decoding ur MP3s. I iz in ur
|
16
|
-
computer. Decodin ur mp3s. Whatevs! I also decodin ur OGGz! == SYNOPSYS
|
17
|
-
ROFLOL require 'icanhasaudio' reader = Audio::MPEG::Decoder.new
|
18
|
-
File.open(ARGV[0], 'rb') { |input_lol| File.open(ARGV[1], 'wb') { |output_lol|
|
19
|
-
reader.decode(input_lol, output_lol) } }"
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aaron Patterson
|
20
8
|
autorequire:
|
21
|
-
default_executable:
|
22
9
|
bindir: bin
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-02-15 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
28
21
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
cert_chain:
|
34
|
-
post_install_message:
|
35
|
-
authors:
|
36
|
-
- Aaron Patterson
|
37
|
-
files:
|
38
|
-
- History.txt
|
39
|
-
- LICENSE.txt
|
40
|
-
- Manifest.txt
|
41
|
-
- README.txt
|
42
|
-
- Rakefile
|
43
|
-
- examples/decoder.rb
|
44
|
-
- examples/kexp.rb
|
45
|
-
- ext/decoder.c
|
46
|
-
- ext/decoder.h
|
47
|
-
- ext/extconf.rb
|
48
|
-
- ext/get_audio.c
|
49
|
-
- ext/get_audio.h
|
50
|
-
- ext/icanhasaudio.c
|
51
|
-
- ext/rb_ogg.c
|
52
|
-
- ext/rb_wav.c
|
53
|
-
- ext/rb_wav.h
|
54
|
-
- ext/syncword.c
|
55
|
-
- ext/syncword.h
|
56
|
-
- lib/icanhasaudio/mpeg.rb
|
57
|
-
- lib/icanhasaudio/ogg.rb
|
58
|
-
test_files: []
|
59
|
-
rdoc_options:
|
60
|
-
- "--main"
|
61
|
-
- README.txt
|
62
|
-
extra_rdoc_files:
|
63
|
-
- History.txt
|
64
|
-
- LICENSE.txt
|
65
|
-
- Manifest.txt
|
66
|
-
- README.txt
|
22
|
+
version: 1.5.0
|
23
|
+
version:
|
24
|
+
description: Hai! icanhasaudio? is an interface to lame for decoding ur MP3s. I iz in ur computer. Decodin ur mp3s. Whatevs! I also decodin ur OGGz! I kin also encodin' ur WAV and AIFF to mp3z! == SYNOPSYS ROFLOL require 'icanhasaudio' reader = Audio::MPEG::Decoder.new File.open(ARGV[0], 'rb') { |input_lol| File.open(ARGV[1], 'wb') { |output_lol| reader.decode(input_lol, output_lol) } }
|
25
|
+
email: aaronp@rubyforge.org
|
67
26
|
executables: []
|
27
|
+
|
68
28
|
extensions:
|
69
|
-
|
29
|
+
- ext/extconf.rb
|
30
|
+
extra_rdoc_files:
|
31
|
+
- History.txt
|
32
|
+
- LICENSE.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
files:
|
36
|
+
- History.txt
|
37
|
+
- LICENSE.txt
|
38
|
+
- Manifest.txt
|
39
|
+
- README.txt
|
40
|
+
- Rakefile
|
41
|
+
- examples/decoder.rb
|
42
|
+
- examples/encoder.rb
|
43
|
+
- examples/kexp.rb
|
44
|
+
- ext/decoder.c
|
45
|
+
- ext/decoder.h
|
46
|
+
- ext/extconf.rb
|
47
|
+
- ext/get_audio.c
|
48
|
+
- ext/get_audio.h
|
49
|
+
- ext/icanhasaudio.c
|
50
|
+
- ext/icanhasaudio.h
|
51
|
+
- ext/mpeg_encoder.c
|
52
|
+
- ext/mpeg_encoder.h
|
53
|
+
- ext/rb_ogg.c
|
54
|
+
- ext/rb_wav.c
|
55
|
+
- ext/rb_wav.h
|
56
|
+
- ext/syncword.c
|
57
|
+
- ext/syncword.h
|
58
|
+
- lib/icanhasaudio/mpeg.rb
|
59
|
+
- lib/icanhasaudio/mpeg/encoder.rb
|
60
|
+
- lib/icanhasaudio/ogg.rb
|
61
|
+
- test/assets/testcase.wav
|
62
|
+
- test/test_mpeg_encoder.rb
|
63
|
+
has_rdoc: true
|
64
|
+
homepage: http://seattlerb.rubyforge.org/
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options:
|
67
|
+
- --main
|
68
|
+
- README.txt
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
- ext
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
version:
|
70
84
|
requirements: []
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
- !ruby/object:Gem::Version
|
80
|
-
version: 1.2.2
|
81
|
-
version:
|
85
|
+
|
86
|
+
rubyforge_project: seattlerb
|
87
|
+
rubygems_version: 1.0.1
|
88
|
+
signing_key:
|
89
|
+
specification_version: 2
|
90
|
+
summary: icanhasaudio is a lame/vorbis wrapper for decoding ur mp3s and ur oggs.
|
91
|
+
test_files:
|
92
|
+
- test/test_mpeg_encoder.rb
|