icanhasaudio 0.0.3 → 0.1.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/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
|