Skip to content
Browse files

removing a bunch of difficult C code

  • Loading branch information...
1 parent c900520 commit 73bf05a77a4eb58db9e10e867b60d3bce172b391 @tenderlove committed Sep 27, 2008
View
174 ext/icanhasaudio/audio_mpeg_decoder.c
@@ -6,7 +6,7 @@
*
* Released under the GPL
*/
-#include <native.h>
+#include <audio_mpeg_decoder.h>
static void reader_mark(lame_global_flags * lgf) {}
static void reader_free(lame_global_flags * gfp) {
@@ -44,70 +44,30 @@ static VALUE method_lame_version(VALUE klass) {
return rb_str_new(version, strlen(version));
}
-static int lame_decode_initfile(VALUE file, mp3data_struct * mp3data) {
- unsigned char * buf;
- VALUE str;
- int len = 4;
+static VALUE decode_headers_for(VALUE self, VALUE rb_buffer)
+{
int enc_delay;
int enc_padding;
- int ret;
- short int pcm_l[1152], pcm_r[1152];
-
- str = rb_funcall(file, rb_intern("read"), 1, INT2NUM(len));
- buf = (unsigned char *)StringValuePtr(str);
- if(buf[0] == 'I' && buf[1] == 'D' && buf[2] == '3') {
- len = 6;
- str = rb_funcall(file, rb_intern("read"), 1, INT2NUM(len));
- buf = (unsigned char *)StringValuePtr(str);
-
- buf[2] &= 127; buf[3] &= 127; buf[4] &= 127; buf[5] &= 127;
- len = (((((buf[2] << 7) + buf[3]) << 7) + buf[4]) << 7) + buf[5];
-
- rb_funcall( file,
- rb_intern("read"),
- 1,
- INT2NUM(len));
-
- len = 4;
- str = rb_funcall(file, rb_intern("read"), 1, INT2NUM(len));
- buf = (unsigned char *)StringValuePtr(str);
- }
+ mp3data_struct * mp3data;
- /* Check for Album ID */
- if(0 == rb_str_cmp(str, rb_str_new2("AiD\1"))) {
- /* FIXME */
- rb_raise(rb_eRuntimeError, "Found Album ID.\n");
- }
+ unsigned char * buf = (unsigned char *)StringValuePtr(rb_buffer);
+ int len = NUM2INT(rb_funcall(rb_buffer, rb_intern("length"), 0));
+ short int pcm_l[1152], pcm_r[1152];
- while (!is_syncword_mp123(buf)) {
- int i;
- for(i = 0; i < len - 1; i++) {
- buf[i] = buf[i + 1];
- }
- buf[len - 1] = NUM2INT(rb_funcall(file, rb_intern("getc"), 0));
- }
+ VALUE rb_mp3data = rb_funcall(self, rb_intern("mp3data"), 0);
+ Data_Get_Struct(rb_mp3data, mp3data_struct, mp3data);
- if ((buf[2] & 0xF0) == 0) {
- printf("Input file is freeformat\n");
- }
+ int ret = lame_decode1_headersB(buf,
+ len,
+ pcm_l,
+ pcm_r,
+ mp3data,
+ &enc_delay,
+ &enc_padding
+ );
- ret = lame_decode1_headersB(buf, len, pcm_l, pcm_r, mp3data, &enc_delay, &enc_padding);
if(ret == -1)
rb_raise(rb_eRuntimeError, "Decode headers failed.\n");
-
- while(!mp3data->header_parsed) {
- str = rb_funcall(file, rb_intern("read"), 1, INT2NUM(100));
- buf = (unsigned char *)StringValuePtr(str);
- ret = lame_decode1_headersB(buf, 100, pcm_l, pcm_r, mp3data,&enc_delay,&enc_padding);
- if(ret == -1)
- rb_raise(rb_eRuntimeError, "Decode headers failed.\n");
- }
-
- if(mp3data->totalframes > 0) {
- } else {
- mp3data->nsamp = MAX_U_32_NUM;
- }
- return 0;
}
/*
@@ -116,24 +76,97 @@ static int lame_decode_initfile(VALUE file, mp3data_struct * mp3data) {
*
* Decode the input IO and write it to the output IO.
*/
-static VALUE native_decode(VALUE self, VALUE file, VALUE outf) {
- mp3data_struct mp3data;
+static VALUE native_decode(VALUE self, VALUE infile, VALUE outf) {
lame_global_flags * gfp;
+ short int Buffer[2][1152];
+ int iread;
+ double wavsize;
+ int i;
+ int tmp_num_channels;
+ int skip;
+ char headbuf[44];
+ VALUE raw;
+ mp3data_struct * mp3data;
+
+ VALUE rb_mp3data = rb_funcall(self, rb_intern("mp3data"), 0);
+ Data_Get_Struct(rb_mp3data, mp3data_struct, mp3data);
+
+ raw = rb_iv_get(self, "@raw");
+ if(raw == Qnil) {
+ raw = Qfalse;
+ rb_iv_set(self, "@raw", Qfalse);
+ }
- memset(&mp3data, 0, sizeof(mp3data_struct));
- lame_decode_initfile(file, &mp3data);
-
- Data_Get_Struct (self, lame_global_flags, gfp);
+ Data_Get_Struct(self, lame_global_flags, gfp);
+ tmp_num_channels = lame_get_num_channels( gfp );
+ lame_set_num_samples(gfp, MAX_U_32_NUM);
+
+ skip = lame_get_encoder_delay(gfp)+528+1;
+
+ if(!raw) {
+ rb_iv_set(self, "@bits", INT2NUM(16));
+ prelim_header( self,
+ headbuf,
+ 0x7FFFFFFF,
+ 0,
+ tmp_num_channels,
+ lame_get_in_samplerate( gfp )
+ );
+ rb_funcall(outf, rb_intern("write"), 1, rb_str_new(headbuf, 44));
+ }
- rb_iv_set(self, "@stereo", INT2NUM(mp3data.stereo));
- rb_iv_set(self, "@samplerate", INT2NUM(mp3data.samplerate));
- rb_iv_set(self, "@bitrate", INT2NUM(mp3data.bitrate));
- rb_iv_set(self, "@mode", INT2NUM(mp3data.mode));
- rb_iv_set(self, "@mode_ext", INT2NUM(mp3data.mode_ext));
- rb_iv_set(self, "@framesize", INT2NUM(mp3data.framesize));
+ wavsize = -skip;
+ if(lame_get_num_samples(gfp) == MAX_U_32_NUM) {
+ VALUE samples = rb_funcall(self, rb_intern("determine_samples_for"), 1, infile);
+ }
+ mp3data->totalframes = mp3data->nsamp / mp3data->framesize;
+
+ assert(tmp_num_channels >= 1 && tmp_num_channels <= 2);
+
+ do {
+ char BitBuffer16[1152 * 4];
+ int bit_16_i = 0;
+ int total = 0;
+ iread = get_audio16(self, infile, Buffer, mp3data);
+ mp3data->framenum += iread / mp3data->framesize;
+ wavsize += iread;
+
+ memset(&BitBuffer16, 0, 1152 * 4);
+
+ skip -= (i = skip < iread ? skip : iread); /* 'i' samples are to skip in this frame */
+
+ for (; i < iread; i++) {
+ /* Write the 0 channel */
+ BitBuffer16[bit_16_i] = Buffer[0][i] & 0xFF;
+ BitBuffer16[bit_16_i + 1] = ((Buffer[0][i] >> 8) & 0xFF);
+
+ if (tmp_num_channels == 2) {
+ BitBuffer16[bit_16_i + 2] = Buffer[1][i] & 0xFF;
+ BitBuffer16[bit_16_i + 3] = ((Buffer[1][i] >> 8) & 0xFF);
+ bit_16_i += 4;
+ } else {
+ bit_16_i += 2;
+ }
+ }
+ rb_funcall(outf, rb_intern("write"), 1, rb_str_new(BitBuffer16, bit_16_i));
+ } while (iread);
- lame_decoder(self, file, outf, &mp3data);
+ i = (16 / 8) * tmp_num_channels;
+ assert(i > 0);
+ if (wavsize <= 0) {
+ wavsize = 0;
+ }
+ else if (wavsize > 0xFFFFFFD0 / i) {
+ wavsize = 0xFFFFFFD0;
+ }
+ else {
+ wavsize *= i;
+ }
+ if(!raw && rb_funcall(self, rb_intern("attempt_rewind"), 1, outf)) {
+ rewrite_header(headbuf, (int)wavsize);
+ rb_funcall(outf, rb_intern("write"), 1, rb_str_new(headbuf, 44));
+ }
return Qnil;
}
@@ -180,4 +213,5 @@ void init_audio_mpeg_decoder() {
rb_define_method(rb_cDecoder, "num_samples=", set_num_samples, 1);
rb_define_method(rb_cDecoder, "in_samplerate", get_in_samplerate, 0);
rb_define_private_method(rb_cDecoder, "native_decode", native_decode, 2);
+ rb_define_private_method(rb_cDecoder, "decode_headers_for", decode_headers_for, 1);
}
View
66 ext/icanhasaudio/audio_mpeg_decoder_mp3data.c
@@ -0,0 +1,66 @@
+#include <audio_mpeg_decoder_mp3data.h>
+
+static VALUE header_parsed_eh(VALUE self)
+{
+ mp3data_struct * mp3data;
+ Data_Get_Struct(self, mp3data_struct, mp3data);
+
+ if(mp3data->header_parsed == 1) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE bitrate(VALUE self)
+{
+ mp3data_struct * mp3data;
+ Data_Get_Struct(self, mp3data_struct, mp3data);
+ return INT2NUM(mp3data->bitrate);
+}
+
+static VALUE total_frames(VALUE self)
+{
+ mp3data_struct * mp3data;
+ Data_Get_Struct(self, mp3data_struct, mp3data);
+ return INT2NUM(mp3data->totalframes);
+}
+
+static VALUE get_nsamp(VALUE self)
+{
+ mp3data_struct * mp3data;
+ Data_Get_Struct(self, mp3data_struct, mp3data);
+ return LONG2NUM(mp3data->nsamp);
+}
+
+static VALUE set_nsamp(VALUE self, VALUE num)
+{
+ mp3data_struct * mp3data;
+ Data_Get_Struct(self, mp3data_struct, mp3data);
+ mp3data->nsamp = NUM2LONG(num);
+ return num;
+}
+
+static VALUE deallocate(mp3data_struct * mp3data)
+{
+ free(mp3data);
+}
+
+static VALUE allocate(VALUE klass)
+{
+ mp3data_struct * mp3data = calloc(1, sizeof(mp3data_struct));
+ return Data_Wrap_Struct(klass, 0, deallocate, mp3data);
+}
+
+void init_audio_mpeg_decoder_mp3data()
+{
+ VALUE rb_mAudio = rb_define_module("Audio");
+ VALUE rb_mMpeg = rb_define_module_under(rb_mAudio, "MPEG");
+ VALUE rb_cDecoder = rb_define_class_under(rb_mMpeg, "Decoder", rb_cObject);
+ VALUE klass = rb_define_class_under(rb_cDecoder, "MP3Data", rb_cObject);
+
+ rb_const_set(klass, rb_intern("MAX_U_32_NUM"), INT2NUM(MAX_U_32_NUM));
+ rb_define_method(klass, "header_parsed?", header_parsed_eh, 0);
+ rb_define_method(klass, "bitrate", bitrate, 0);
+ rb_define_method(klass, "total_frames", total_frames, 0);
+ rb_define_method(klass, "nsamp", get_nsamp, 0);
+ rb_define_method(klass, "nsamp=", set_nsamp, 1);
+ rb_define_alloc_func(klass, allocate);
+}
View
8 ext/icanhasaudio/audio_mpeg_decoder_mp3data.h
@@ -0,0 +1,8 @@
+#ifndef AUDIO_MPEG_DECODER_MP3DATA
+#define AUDIO_MPEG_DECODER_MP3DATA
+
+#include <native.h>
+
+void init_audio_mpeg_decoder_mp3data();
+
+#endif
View
94 ext/icanhasaudio/decoder.c
@@ -1,94 +0,0 @@
-#include <native.h>
-
-int
-lame_decoder(VALUE self, VALUE infile, VALUE outf, mp3data_struct * mp3data)
-{
- lame_global_flags * gfp;
- short int Buffer[2][1152];
- int iread;
- double wavsize;
- int i;
- int tmp_num_channels;
- int skip;
- char headbuf[44];
- VALUE raw;
-
- raw = rb_iv_get(self, "@raw");
- if(raw == Qnil) {
- raw = Qfalse;
- rb_iv_set(self, "@raw", Qfalse);
- }
-
- Data_Get_Struct(self, lame_global_flags, gfp);
- tmp_num_channels = lame_get_num_channels( gfp );
- lame_set_num_samples(gfp, MAX_U_32_NUM);
-
- skip = lame_get_encoder_delay(gfp)+528+1;
-
- if(!raw) {
- rb_iv_set(self, "@bits", INT2NUM(16));
- prelim_header( self,
- headbuf,
- 0x7FFFFFFF,
- 0,
- tmp_num_channels,
- lame_get_in_samplerate( gfp )
- );
- rb_funcall(outf, rb_intern("write"), 1, rb_str_new(headbuf, 44));
- }
-
- wavsize = -skip;
- if(lame_get_num_samples(gfp) == MAX_U_32_NUM) {
- VALUE samples = rb_funcall(self, rb_intern("determine_samples_for"), 1, infile);
- }
- mp3data->totalframes = mp3data->nsamp / mp3data->framesize;
-
- assert(tmp_num_channels >= 1 && tmp_num_channels <= 2);
-
- do {
- char BitBuffer16[1152 * 4];
- int bit_16_i = 0;
- int total = 0;
- iread = get_audio16(self, infile, Buffer, mp3data);
- mp3data->framenum += iread / mp3data->framesize;
- wavsize += iread;
-
- memset(&BitBuffer16, 0, 1152 * 4);
-
- skip -= (i = skip < iread ? skip : iread); /* 'i' samples are to skip in this frame */
-
- for (; i < iread; i++) {
- /* Write the 0 channel */
- BitBuffer16[bit_16_i] = Buffer[0][i] & 0xFF;
- BitBuffer16[bit_16_i + 1] = ((Buffer[0][i] >> 8) & 0xFF);
-
- if (tmp_num_channels == 2) {
- BitBuffer16[bit_16_i + 2] = Buffer[1][i] & 0xFF;
- BitBuffer16[bit_16_i + 3] = ((Buffer[1][i] >> 8) & 0xFF);
- bit_16_i += 4;
- } else {
- bit_16_i += 2;
- }
- }
- rb_funcall(outf, rb_intern("write"), 1, rb_str_new(BitBuffer16, bit_16_i));
- } while (iread);
-
- i = (16 / 8) * tmp_num_channels;
- assert(i > 0);
- if (wavsize <= 0) {
- wavsize = 0;
- }
- else if (wavsize > 0xFFFFFFD0 / i) {
- wavsize = 0xFFFFFFD0;
- }
- else {
- wavsize *= i;
- }
-
- if(!raw && rb_funcall(self, rb_intern("attempt_rewind"), 1, outf)) {
- rewrite_header(headbuf, (int)wavsize);
- rb_funcall(outf, rb_intern("write"), 1, rb_str_new(headbuf, 44));
- }
- return 0;
-}
-
View
9 ext/icanhasaudio/decoder.h
@@ -1,9 +0,0 @@
-#ifndef ICANHASAUDIO_LAME_DECODER_H
-#define ICANHASAUDIO_LAME_DECODER_H
-
-#include <native.h>
-
-int
-lame_decoder(VALUE self, VALUE infile, VALUE outf, mp3data_struct * mp3data);
-
-#endif
View
1 ext/icanhasaudio/native.c
@@ -3,6 +3,7 @@
void Init_native()
{
init_audio_mpeg_decoder();
+ init_audio_mpeg_decoder_mp3data();
init_audio_mpeg_encoder();
init_audio_ogg_decoder();
}
View
2 ext/icanhasaudio/native.h
@@ -10,10 +10,10 @@
#include <vorbis/vorbisfile.h>
#include <syncword.h>
-#include <decoder.h>
#include <get_audio.h>
#include <rb_wav.h>
#include <audio_mpeg_decoder.h>
+#include <audio_mpeg_decoder_mp3data.h>
#include <audio_mpeg_encoder.h>
#include <audio_ogg_decoder.h>
View
60 lib/icanhasaudio/mpeg/decoder.rb
@@ -1,10 +1,9 @@
module Audio
module MPEG
class Decoder
- attr_reader :stereo, :samplerate, :bitrate, :mode, :mode_ext, :framesize
-
# Number of bits, 8 or 16
attr_accessor :bits
+ attr_reader :mp3data
def initialize
@stereo = nil
@@ -15,15 +14,68 @@ def initialize
@framesize = nil
@bits = 16
@raw = nil
+ @mp3data = MP3Data.new
yield self if block_given?
end
def decode input, output
+ buf = skip_id3_header(input)
+
+ decode_headers_for(buf)
+ while !mp3data.header_parsed?
+ decode_headers_for(input.read(100))
+ end
+ mp3data.nsamp = MP3Data::MAX_U_32_NUM unless mp3data.total_frames > 0
native_decode(input, output)
end
private
- def attempt_rewind(outf)
+ ID3 = [73, 68, 51, 3].pack('C*')
+ AID = [65, 105, 68, 1].pack('C*')
+
+ def skip_id3_header input
+ header = input.read(4)
+ if header == ID3
+ puts "asdfadsf"
+ id3_len = input.read(6).unpack('C*')[2..-1].map { |chr|
+ chr & 127
+ }.inject(0) { |total,chr|
+ (total + chr) << 7
+ } >> 7
+ input.read(id3_len) # skip the ID3 tag
+ header = input.read(4)
+ end
+ raise "Found AiD header" if header == AID
+
+ while !syncword_mp123?(header)
+ header = header.slice(1..-1) + input.getc
+ end
+ header
+ end
+
+ def syncword_mp123? header
+ alb2 = [0, 7, 7, 7, 0, 7, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8]
+ p_h = header.unpack('C*')
+ return false if p_h[0] & 0xFF != 0xFF # first 8 bits must be '1'
+ return false if p_h[1] & 0xE0 != 0xE0 # next 3 bits are also
+ return false if p_h[1] & 0x18 == 0x08 # no MPEG-1, -2, or -2.5
+ return false if p_h[1] & 0x06 == 0x00 # no Layer I, II and III
+ return false if p_h[1] & 0x06 == 0x03 * 2 # layer1 not supported
+ return false if p_h[1] & 0x06 == 0x02 * 2 # layer1 not supported
+ return false unless p_h[1] & 0x06 == 0x01 * 2 # incompatible layer
+ return false if p_h[2] & 0xF0 == 0xF0 # bad bitrate
+ return false if p_h[2] & 0x0C == 0x0C # no sample frequency
+
+ if( (p_h[1] & 0x18 == 0x18) && (p_h[1] & 0x06 == 0x04) )
+ if(abl2[p_h[2] >> 4] & (1 << (p_h[3] >> 6)) != 0)
+ return false
+ end
+ end
+ return false if p_h[3] & 3 == 2
+ true
+ end
+
+ def attempt_rewind outf
begin
outf.seek(0, IO::SEEK_SET)
true
@@ -34,7 +86,7 @@ def attempt_rewind(outf)
def determine_samples_for infile
length = File.stat(infile.path).size
- total_seconds = length * 8.0 / (1000.0 * @bitrate)
+ total_seconds = length * 8.0 / (1000.0 * mp3data.bitrate)
self.num_samples = (total_seconds * in_samplerate).to_i
end
end
View
6 test/test_mpeg_encoder.rb
@@ -1,8 +1,6 @@
-require 'test/unit'
-require 'icanhasaudio'
-require 'tempfile'
+require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
-class MPEGEncoderTest < Test::Unit::TestCase
+class MPEGEncoderTest < ICANHASAUDIO::TestCase
include Audio::MPEG
WAV_FILE = File.dirname(__FILE__) + "/assets/testcase.wav"

0 comments on commit 73bf05a

Please sign in to comment.
Something went wrong with that request. Please try again.