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.
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