icanhasaudio 0.1.1 → 0.1.2
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 +6 -0
- data/Manifest.txt +24 -14
- data/README.txt +2 -0
- data/Rakefile +15 -8
- data/ext/icanhasaudio/audio_mpeg_decoder.c +220 -0
- data/ext/icanhasaudio/audio_mpeg_decoder.h +8 -0
- data/ext/icanhasaudio/audio_mpeg_decoder_mp3data.c +96 -0
- data/ext/icanhasaudio/audio_mpeg_decoder_mp3data.h +8 -0
- data/ext/{mpeg_encoder.c → icanhasaudio/audio_mpeg_encoder.c} +94 -102
- data/ext/icanhasaudio/audio_mpeg_encoder.h +8 -0
- data/ext/{rb_ogg.c → icanhasaudio/audio_ogg_decoder.c} +18 -9
- data/ext/icanhasaudio/audio_ogg_decoder.h +8 -0
- data/ext/{extconf.rb → icanhasaudio/extconf.rb} +2 -2
- data/ext/{get_audio.c → icanhasaudio/get_audio.c} +1 -6
- data/ext/{get_audio.h → icanhasaudio/get_audio.h} +2 -0
- data/ext/icanhasaudio/native.c +9 -0
- data/ext/icanhasaudio/native.h +21 -0
- data/ext/{rb_wav.c → icanhasaudio/rb_wav.c} +1 -2
- data/ext/{rb_wav.h → icanhasaudio/rb_wav.h} +1 -2
- data/lib/icanhasaudio.rb +4 -0
- data/lib/icanhasaudio/mpeg.rb +1 -24
- data/lib/icanhasaudio/mpeg/decoder.rb +95 -0
- data/lib/icanhasaudio/ogg.rb +1 -21
- data/lib/icanhasaudio/ogg/decoder.rb +31 -0
- data/lib/icanhasaudio/version.rb +8 -0
- data/lib/icanhasaudio/wav.rb +1 -0
- data/lib/icanhasaudio/wav/file.rb +62 -0
- data/test/assets/icha.mp3 +0 -0
- data/test/helper.rb +15 -0
- data/test/mpeg/test_decoder.rb +20 -0
- data/test/test_mpeg_encoder.rb +2 -4
- metadata +31 -18
- data/ext/decoder.c +0 -95
- data/ext/decoder.h +0 -3
- data/ext/icanhasaudio.c +0 -183
- data/ext/icanhasaudio.h +0 -8
- data/ext/mpeg_encoder.h +0 -10
- data/ext/syncword.c +0 -45
- data/ext/syncword.h +0 -3
@@ -1,9 +1,10 @@
|
|
1
|
-
#include <
|
2
|
-
#include <ogg/ogg.h>
|
3
|
-
#include <vorbis/vorbisfile.h>
|
4
|
-
#include <rb_wav.h>
|
1
|
+
#include <native.h>
|
5
2
|
|
6
|
-
size_t rb_ogg_read(void *ptr,
|
3
|
+
static size_t rb_ogg_read( void *ptr,
|
4
|
+
size_t size,
|
5
|
+
size_t nmemb,
|
6
|
+
void *datasource)
|
7
|
+
{
|
7
8
|
VALUE file = (VALUE)datasource;
|
8
9
|
VALUE str;
|
9
10
|
size_t length;
|
@@ -29,24 +30,24 @@ int rb_ogg_seek(void *datasource, ogg_int64_t offset, int whence) {
|
|
29
30
|
return -1;
|
30
31
|
}
|
31
32
|
|
32
|
-
int rb_ogg_close(void *datasource) {
|
33
|
+
static int rb_ogg_close(void *datasource) {
|
33
34
|
VALUE file = (VALUE)datasource;
|
34
35
|
rb_funcall(file, rb_intern("close"), 0);
|
35
36
|
return 0;
|
36
37
|
}
|
37
38
|
|
38
|
-
long rb_ogg_tell(void *datasource) {
|
39
|
+
static long rb_ogg_tell(void *datasource) {
|
39
40
|
VALUE file = (VALUE)datasource;
|
40
41
|
return NUM2LONG(rb_funcall(file, rb_intern("tell"), 0));
|
41
42
|
}
|
42
43
|
|
43
44
|
/*
|
44
45
|
* call-seq:
|
45
|
-
*
|
46
|
+
* native_decode(input_io, output_io)
|
46
47
|
*
|
47
48
|
* Decode the input IO and write it to the output IO.
|
48
49
|
*/
|
49
|
-
VALUE
|
50
|
+
VALUE native_decode(VALUE self, VALUE infile, VALUE outf) {
|
50
51
|
OggVorbis_File vf;
|
51
52
|
ov_callbacks callbacks;
|
52
53
|
int bs = 0;
|
@@ -137,3 +138,11 @@ VALUE method_ogg_decode(VALUE self, VALUE infile, VALUE outf) {
|
|
137
138
|
|
138
139
|
return Qnil;
|
139
140
|
}
|
141
|
+
void init_audio_ogg_decoder()
|
142
|
+
{
|
143
|
+
VALUE rb_mAudio = rb_define_module("Audio");
|
144
|
+
VALUE rb_mOgg = rb_define_module_under(rb_mAudio, "OGG");
|
145
|
+
VALUE klass = rb_define_class_under(rb_mOgg, "Decoder", rb_cObject);
|
146
|
+
|
147
|
+
rb_define_private_method(klass, "native_decode", native_decode, 2);
|
148
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#ifndef ICANHASAUDIO_H
|
2
|
+
#define ICANHASAUDIO_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <rubyio.h>
|
6
|
+
#include <lame/lame.h>
|
7
|
+
#include <dlfcn.h>
|
8
|
+
#include <assert.h>
|
9
|
+
#include <ogg/ogg.h>
|
10
|
+
#include <vorbis/vorbisfile.h>
|
11
|
+
|
12
|
+
#include <get_audio.h>
|
13
|
+
#include <rb_wav.h>
|
14
|
+
#include <audio_mpeg_decoder.h>
|
15
|
+
#include <audio_mpeg_decoder_mp3data.h>
|
16
|
+
#include <audio_mpeg_encoder.h>
|
17
|
+
#include <audio_ogg_decoder.h>
|
18
|
+
|
19
|
+
void Init_native();
|
20
|
+
|
21
|
+
#endif
|
data/lib/icanhasaudio.rb
ADDED
data/lib/icanhasaudio/mpeg.rb
CHANGED
@@ -1,25 +1,2 @@
|
|
1
1
|
require 'icanhasaudio/mpeg/encoder'
|
2
|
-
|
3
|
-
class Audio::MPEG::Decoder
|
4
|
-
attr_reader :stereo, :samplerate, :bitrate, :mode, :mode_ext, :framesize
|
5
|
-
|
6
|
-
# Number of bits, 8 or 16
|
7
|
-
attr_accessor :bits
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@bits = 16
|
11
|
-
@raw = nil
|
12
|
-
yield self if block_given?
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
def attempt_rewind(outf)
|
17
|
-
begin
|
18
|
-
outf.seek(0, IO::SEEK_SET)
|
19
|
-
true
|
20
|
-
rescue
|
21
|
-
false
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
2
|
+
require 'icanhasaudio/mpeg/decoder'
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Audio
|
2
|
+
module MPEG
|
3
|
+
class Decoder
|
4
|
+
# Number of bits, 8 or 16
|
5
|
+
attr_accessor :bits
|
6
|
+
attr_reader :mp3data
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@bits = 16
|
10
|
+
@raw = false
|
11
|
+
@mp3data = MP3Data.new
|
12
|
+
yield self if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def decode input, output
|
16
|
+
buf = skip_id3_header(input)
|
17
|
+
|
18
|
+
decode_headers_for(buf)
|
19
|
+
while !mp3data.header_parsed?
|
20
|
+
decode_headers_for(input.read(100))
|
21
|
+
end
|
22
|
+
mp3data.nsamp = MP3Data::MAX_U_32_NUM unless mp3data.total_frames > 0
|
23
|
+
wav = WAV::File.new(output)
|
24
|
+
wav.write_header(0x7FFFFFFF, 0, num_channels, in_samplerate) if !@raw
|
25
|
+
native_decode(input, wav)
|
26
|
+
if !@raw && attempt_rewind(wav)
|
27
|
+
wav.write_header(@wavsize + 44, 0, num_channels, in_samplerate)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
ID3 = [73, 68, 51, 3].pack('C*')
|
33
|
+
AID = [65, 105, 68, 1].pack('C*')
|
34
|
+
|
35
|
+
def skip_id3_header input
|
36
|
+
header = input.read(4)
|
37
|
+
if header == ID3
|
38
|
+
puts "asdfadsf"
|
39
|
+
id3_len = input.read(6).unpack('C*')[2..-1].map { |chr|
|
40
|
+
chr & 127
|
41
|
+
}.inject(0) { |total,chr|
|
42
|
+
(total + chr) << 7
|
43
|
+
} >> 7
|
44
|
+
input.read(id3_len) # skip the ID3 tag
|
45
|
+
header = input.read(4)
|
46
|
+
end
|
47
|
+
raise "Found AiD header" if header == AID
|
48
|
+
|
49
|
+
while !syncword_mp123?(header)
|
50
|
+
header = header.slice(1..-1) + input.getc
|
51
|
+
end
|
52
|
+
header
|
53
|
+
end
|
54
|
+
|
55
|
+
def syncword_mp123? header
|
56
|
+
alb2 = [0, 7, 7, 7, 0, 7, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8]
|
57
|
+
p_h = header.unpack('C*')
|
58
|
+
return false if p_h[0] & 0xFF != 0xFF # first 8 bits must be '1'
|
59
|
+
return false if p_h[1] & 0xE0 != 0xE0 # next 3 bits are also
|
60
|
+
return false if p_h[1] & 0x18 == 0x08 # no MPEG-1, -2, or -2.5
|
61
|
+
return false if p_h[1] & 0x06 == 0x00 # no Layer I, II and III
|
62
|
+
return false if p_h[1] & 0x06 == 0x03 * 2 # layer1 not supported
|
63
|
+
return false if p_h[1] & 0x06 == 0x02 * 2 # layer1 not supported
|
64
|
+
return false unless p_h[1] & 0x06 == 0x01 * 2 # incompatible layer
|
65
|
+
return false if p_h[2] & 0xF0 == 0xF0 # bad bitrate
|
66
|
+
return false if p_h[2] & 0x0C == 0x0C # no sample frequency
|
67
|
+
|
68
|
+
if( (p_h[1] & 0x18 == 0x18) && (p_h[1] & 0x06 == 0x04) )
|
69
|
+
if(abl2[p_h[2] >> 4] & (1 << (p_h[3] >> 6)) != 0)
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return false if p_h[3] & 3 == 2
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def attempt_rewind outf
|
78
|
+
begin
|
79
|
+
outf.seek(0, IO::SEEK_SET)
|
80
|
+
true
|
81
|
+
rescue
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def determine_samples_for infile
|
87
|
+
length = File.stat(infile.path).size
|
88
|
+
total_seconds = length * 8.0 / (1000.0 * mp3data.bitrate)
|
89
|
+
self.num_samples = (total_seconds * in_samplerate).to_i
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
data/lib/icanhasaudio/ogg.rb
CHANGED
@@ -1,21 +1 @@
|
|
1
|
-
|
2
|
-
# Set to true for no WAV header
|
3
|
-
attr_accessor :raw
|
4
|
-
|
5
|
-
# Number of bits, 8 or 16
|
6
|
-
attr_accessor :bits
|
7
|
-
|
8
|
-
# Endianness
|
9
|
-
attr_accessor :endian
|
10
|
-
|
11
|
-
# Signedness
|
12
|
-
attr_accessor :sign
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
@raw = false
|
16
|
-
@bits = 16
|
17
|
-
@endian = 0
|
18
|
-
@sign = 1
|
19
|
-
yield self if block_given?
|
20
|
-
end
|
21
|
-
end
|
1
|
+
require 'icanhasaudio/ogg/decoder'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Audio
|
2
|
+
module OGG
|
3
|
+
class Decoder
|
4
|
+
# Set to true for no WAV header
|
5
|
+
attr_accessor :raw
|
6
|
+
|
7
|
+
# Number of bits, 8 or 16
|
8
|
+
attr_accessor :bits
|
9
|
+
|
10
|
+
# Endianness
|
11
|
+
attr_accessor :endian
|
12
|
+
|
13
|
+
# Signedness
|
14
|
+
attr_accessor :sign
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@raw = false
|
18
|
+
@bits = 16
|
19
|
+
@endian = 0
|
20
|
+
@sign = 1
|
21
|
+
yield self if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
####
|
25
|
+
# Decode +input+ and write to +output+
|
26
|
+
def decode input, output
|
27
|
+
native_decode(input, output)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'icanhasaudio/wav/file'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Audio
|
2
|
+
module WAV
|
3
|
+
class File
|
4
|
+
attr_accessor :bits
|
5
|
+
def initialize io_or_path, mode = 'wb'
|
6
|
+
@io = io_or_path.is_a?(IO) ? io_or_path : File.open(io_or_path, mode)
|
7
|
+
@bits = 16
|
8
|
+
if block_given?
|
9
|
+
yield self
|
10
|
+
close
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def write_header size, known_length, channels, samplerate
|
15
|
+
if known_length != 0 && known_length * bits / 8 * channels < size
|
16
|
+
size = known_length * bits / 8 * channels + 44
|
17
|
+
end
|
18
|
+
|
19
|
+
bytespersec = channels * samplerate * bits / 8
|
20
|
+
align = channels * bits / 8
|
21
|
+
|
22
|
+
header = [
|
23
|
+
'RIFF',
|
24
|
+
u32(size - 8),
|
25
|
+
'WAVE',
|
26
|
+
'fmt ',
|
27
|
+
u32(16),
|
28
|
+
u16(1),
|
29
|
+
u16(channels),
|
30
|
+
u32(samplerate),
|
31
|
+
u32(bytespersec),
|
32
|
+
u16(align),
|
33
|
+
u16(bits),
|
34
|
+
'data',
|
35
|
+
u32(size - 44),
|
36
|
+
].join
|
37
|
+
@io.write header
|
38
|
+
end
|
39
|
+
|
40
|
+
def write *args
|
41
|
+
@io.write(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def close *args
|
45
|
+
@io.close(*args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def seek *args
|
49
|
+
@io.seek(*args)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def u32 num
|
54
|
+
[0, 8, 16, 24].map { |x| (num >> x) & 0xFF }.pack('C4')
|
55
|
+
end
|
56
|
+
|
57
|
+
def u16 num
|
58
|
+
[0, 8].map { |x| (num >> x) & 0xFF }.pack('C2')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
Binary file
|
data/test/helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'md5'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'icanhasaudio'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
%w(../lib ../ext).each do |path|
|
7
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), path)))
|
8
|
+
end
|
9
|
+
|
10
|
+
module ICANHASAUDIO
|
11
|
+
class TestCase < Test::Unit::TestCase
|
12
|
+
MP3_FILE = File.dirname(__FILE__) + "/assets/icha.mp3"
|
13
|
+
undef :default_test
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
|
2
|
+
|
3
|
+
module Audio
|
4
|
+
module MPEG
|
5
|
+
class TestDecoder < ICANHASAUDIO::TestCase
|
6
|
+
def setup
|
7
|
+
@decoder = MPEG::Decoder.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_decode
|
11
|
+
out = "#{Dir::tmpdir}/out.wav"
|
12
|
+
File.open(out, 'wb+') { |outfile|
|
13
|
+
@decoder.decode(File.open(MP3_FILE, 'rb'), outfile)
|
14
|
+
}
|
15
|
+
digest = Digest::MD5.hexdigest(File.read(out))
|
16
|
+
#assert_equal '9a55bcdda77ec7c20f32031632927403', digest
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|