icanhasaudio 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/History.txt +6 -0
  2. data/Manifest.txt +24 -14
  3. data/README.txt +2 -0
  4. data/Rakefile +15 -8
  5. data/ext/icanhasaudio/audio_mpeg_decoder.c +220 -0
  6. data/ext/icanhasaudio/audio_mpeg_decoder.h +8 -0
  7. data/ext/icanhasaudio/audio_mpeg_decoder_mp3data.c +96 -0
  8. data/ext/icanhasaudio/audio_mpeg_decoder_mp3data.h +8 -0
  9. data/ext/{mpeg_encoder.c → icanhasaudio/audio_mpeg_encoder.c} +94 -102
  10. data/ext/icanhasaudio/audio_mpeg_encoder.h +8 -0
  11. data/ext/{rb_ogg.c → icanhasaudio/audio_ogg_decoder.c} +18 -9
  12. data/ext/icanhasaudio/audio_ogg_decoder.h +8 -0
  13. data/ext/{extconf.rb → icanhasaudio/extconf.rb} +2 -2
  14. data/ext/{get_audio.c → icanhasaudio/get_audio.c} +1 -6
  15. data/ext/{get_audio.h → icanhasaudio/get_audio.h} +2 -0
  16. data/ext/icanhasaudio/native.c +9 -0
  17. data/ext/icanhasaudio/native.h +21 -0
  18. data/ext/{rb_wav.c → icanhasaudio/rb_wav.c} +1 -2
  19. data/ext/{rb_wav.h → icanhasaudio/rb_wav.h} +1 -2
  20. data/lib/icanhasaudio.rb +4 -0
  21. data/lib/icanhasaudio/mpeg.rb +1 -24
  22. data/lib/icanhasaudio/mpeg/decoder.rb +95 -0
  23. data/lib/icanhasaudio/ogg.rb +1 -21
  24. data/lib/icanhasaudio/ogg/decoder.rb +31 -0
  25. data/lib/icanhasaudio/version.rb +8 -0
  26. data/lib/icanhasaudio/wav.rb +1 -0
  27. data/lib/icanhasaudio/wav/file.rb +62 -0
  28. data/test/assets/icha.mp3 +0 -0
  29. data/test/helper.rb +15 -0
  30. data/test/mpeg/test_decoder.rb +20 -0
  31. data/test/test_mpeg_encoder.rb +2 -4
  32. metadata +31 -18
  33. data/ext/decoder.c +0 -95
  34. data/ext/decoder.h +0 -3
  35. data/ext/icanhasaudio.c +0 -183
  36. data/ext/icanhasaudio.h +0 -8
  37. data/ext/mpeg_encoder.h +0 -10
  38. data/ext/syncword.c +0 -45
  39. data/ext/syncword.h +0 -3
@@ -0,0 +1,8 @@
1
+ #ifndef MPEG_ENCODER_H
2
+ #define MPEG_ENCODER_H
3
+
4
+ #include <native.h>
5
+
6
+ void init_audio_mpeg_encoder();
7
+
8
+ #endif
@@ -1,9 +1,10 @@
1
- #include <ruby.h>
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, size_t size, size_t nmemb, void *datasource) {
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
- * decoder.decode(input_io, output_io)
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 method_ogg_decode(VALUE self, VALUE infile, VALUE outf) {
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,8 @@
1
+ #ifndef AUDIO_OGG_DECODER
2
+ #define AUDIO_OGG_DECODER
3
+
4
+ #include <native.h>
5
+
6
+ void init_audio_ogg_decoder();
7
+
8
+ #endif
@@ -50,5 +50,5 @@ end
50
50
  end
51
51
  end
52
52
 
53
- dir_config('icanhasaudio')
54
- create_makefile('icanhasaudio')
53
+ dir_config('icanhasaudio/native')
54
+ create_makefile('icanhasaudio/native')
@@ -1,9 +1,4 @@
1
- #include <ruby.h>
2
- #include <assert.h>
3
- #include <lame/lame.h>
4
- #include "get_audio.h"
5
-
6
- #define MAX_U_32_NUM 0xFFFFFFFF
1
+ #include <native.h>
7
2
 
8
3
  static int
9
4
  get_audio_common(VALUE self, VALUE musicin,
@@ -1,6 +1,8 @@
1
1
  #ifndef GET_AUDIO_H
2
2
  #define GET_AUDIO_H
3
3
 
4
+ #include <native.h>
5
+
4
6
  #define MAX_U_32_NUM 0xFFFFFFFF
5
7
  int
6
8
  get_audio16(VALUE self, VALUE musicin, short buffer[2][1152],
@@ -0,0 +1,9 @@
1
+ #include <native.h>
2
+
3
+ void Init_native()
4
+ {
5
+ init_audio_mpeg_decoder();
6
+ init_audio_mpeg_decoder_mp3data();
7
+ init_audio_mpeg_encoder();
8
+ init_audio_ogg_decoder();
9
+ }
@@ -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
@@ -1,5 +1,4 @@
1
- #include <ruby.h>
2
- #include <rb_wav.h>
1
+ #include <native.h>
3
2
 
4
3
  #define WRITE_U32(buf, x) *(buf) = (unsigned char)((x)&0xff);\
5
4
  *((buf)+1) = (unsigned char)(((x)>>8)&0xff);\
@@ -1,5 +1,4 @@
1
- #include <ogg/ogg.h>
2
- #include <vorbis/vorbisfile.h>
1
+ #include <native.h>
3
2
 
4
3
  void prelim_header( VALUE self,
5
4
  char *headbuf,
@@ -0,0 +1,4 @@
1
+ require 'icanhasaudio/wav'
2
+ require 'icanhasaudio/mpeg'
3
+ require 'icanhasaudio/ogg'
4
+ require 'icanhasaudio/native'
@@ -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
+
@@ -1,21 +1 @@
1
- class Audio::OGG::Decoder
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,8 @@
1
+ module Audio
2
+ module MPEG
3
+ class Decoder
4
+ VERSION = '0.1.2'
5
+ end
6
+ end
7
+ end
8
+
@@ -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