Class: Audio::MPEG::Encoder

Inherits:
Object
  • Object
show all
Defined in:
lib/icanhasaudio/mpeg/encoder.rb,
ext/icanhasaudio/audio_mpeg_encoder.c

Constant Summary collapse

WAV_ID_RIFF =
0x52494646
WAV_ID_WAVE =

“WAVE”

0x57415645
WAV_ID_FMT =

“fmt ”

0x666d7420
WAV_ID_DATA =

“data”

0x64617461
IFF_ID_FORM =

“FORM”

0x464f524d
IFF_ID_AIFF =

“AIFF”

0x41494646
IFF_ID_AIFC =

“AIFC”

0x41494643
IFF_ID_COMM =

“COMM”

0x434f4d4d
IFF_ID_SSND =

“SSND”

0x53534e44
IFF_ID_NONE =

“NONE” AIFF-C data format

0x4e4f4e45
IFF_ID_2CBE =

“twos” AIFF-C data format

0x74776f73
IFF_ID_2CLE =

“sowt” AIFF-C data format

0x736f7774
VBR_OFF =
0
VBR_NORMAL =
2
VBR_FAST =
4
MODE_NAMES =
[
  [ 'stereo', 'j-stereo', 'dual-ch', 'single-ch' ],
  [ 'stereo', 'force-ms', 'dual-ch', 'single-ch' ],
]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEncoder

Returns a new instance of Encoder.



31
32
33
34
35
36
# File 'lib/icanhasaudio/mpeg/encoder.rb', line 31

def initialize
  @pcmbitwidth = 16
  @logger = nil
  self.vbr_quality = 2
  self.vbr_type    = VBR_NORMAL
end

Instance Attribute Details

#loggerObject

Returns the value of attribute logger.



30
31
32
# File 'lib/icanhasaudio/mpeg/encoder.rb', line 30

def logger
  @logger
end

#pcmbitwidthObject

Returns the value of attribute pcmbitwidth.



29
30
31
# File 'lib/icanhasaudio/mpeg/encoder.rb', line 29

def pcmbitwidth
  @pcmbitwidth
end

Class Method Details

.parse_aiff_header(file) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/icanhasaudio/mpeg/encoder.rb', line 115

def parse_aiff_header(file)
  chunk_size  = file.read(4).unpack('N').first
  type_id     = file.read(4).unpack('N').first

  raise unless [IFF_ID_AIFF, IFF_ID_AIFC].any? { |x| type_id == x }

  sub_size = 0
  sample_type = nil
  sample_size = nil
  num_channels = nil
  block_size = nil
  sample_rate = nil
  is_aiff = false
  while chunk_size > 0
    type = file.read(4).unpack('N').first
    chunk_size -= 4

    case type
    when IFF_ID_COMM
      sub_size = file.read(4).unpack('N').first
      chunk_size -= 4

      num_channels = file.read(2).unpack('n').first
      sub_size -= 2
      num_sample_frames = file.read(4).unpack('N').first
      sub_size -= 4
      sample_size = file.read(2).unpack('n').first
      sub_size -= 2
      sample_rate = unpack_ieee(file.read(10))
      sub_size -= 10

      if type_id == IFF_ID_AIFC
        data_type = file.read(4).unpack('N').first
        sub_size -=4

        raise unless [  IFF_ID_2CLE,
                        IFF_ID_2CBE,
                        IFF_ID_NONE,
        ].any? { |x| data_type == x }

        #if sample_size == 16
        # swap bytes....
      end

      file.read(sub_size)
    when IFF_ID_SSND
      sub_size = file.read(4).unpack('N').first
      chunk_size -= sub_size

      block_offset = file.read(4).unpack('N').first
      sub_size -= 4
      block_size = file.read(4).unpack('N').first
      sub_size -= 4

      sample_type = IFF_ID_SSND
      file.read(block_offset)
      is_aiff = true
      break
    else
      sub_size = file.read(4).unpack('N').first
      chunk_size -= sub_size
      file.read(sub_size)
    end
  end

  # Validate the header
  if is_aiff
    raise "Sound data is not PCM" unless sample_type == IFF_ID_SSND
    raise "Sound data is not 16 bits" unless sample_size == 16
    unless num_channels == 1 || num_channels == 2
      raise "Sound data is not mono or stereo" 
    end
    raise "Block size is not 0 bytes" unless block_size == 0
  end

  {
    :num_channels   => num_channels,
    :in_samplerate  => simple_rate.to_i,
    :num_samples    => num_sample_frames,
    :bit_width      => sample_size,
  }
end

.parse_wave_header(file) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/icanhasaudio/mpeg/encoder.rb', line 204

def parse_wave_header(file)
  format_tag        = nil
  channels          = nil
  samples_per_sec   = nil
  avg_bytes_per_sec = nil
  block_align       = nil
  bits_per_sample   = nil
  is_wav            = false
  data_length       = 0

  file_length = file.read(4).unpack('N').first
  raise "Corrupt wave" if file.read(4).unpack('N').first != WAV_ID_WAVE
  20.times {
    type = file.read(4).unpack('N').first
    case type
    when WAV_ID_FMT
      sub_size = file.read(4).unpack('V').first
      raise "Corrupt wave" if sub_size < 16

      ( format_tag,
        channels, 
        samples_per_sec,
        avg_bytes_per_sec,
        block_align, bits_per_sample) = *(file.read(16).unpack('vvVVvv'))
      sub_size -= 16

      file.read(sub_size) if sub_size > 0
    when WAV_ID_DATA
      sub_size = file.read(4).unpack('V').first
      data_length = sub_size
      is_wav = true
      break;
    else
      sub_size = file.read(4).unpack('V').first
      file.read(sub_size)
    end
  }
  raise "Unsupported format" unless format_tag == 1
  raise unless is_wav

  {
    :num_channels   => channels,
    :in_samplerate  => samples_per_sec,
    :bytes_in_seconds => data_length,
    :milliseconds   => (data_length/2)/(samples_per_sec/1000)/2,
    :num_samples    => data_length / (channels*((bits_per_sample+7)/8)),
    :bit_width      => bits_per_sample,
  }
end

.unpack_ieee(data) ⇒ Object



198
199
200
201
202
# File 'lib/icanhasaudio/mpeg/encoder.rb', line 198

def unpack_ieee(data)
  (expon, hi_mant, lo_mant) = data.unpack('nNN')
  expon -= 16383
  hi_mant * (2 ** (expon -= 31)) + lo_mant * (2 ** (expon -= 32))
end

Instance Method Details

#album=Object

Set the ID3 album.



239
240
241
242
243
244
245
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 239

static VALUE set_album(VALUE self, VALUE album) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  id3tag_set_album(gfp, StringValuePtr(album));
  return album;
}

#artist=Object

Set the ID3 artist.



211
212
213
214
215
216
217
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 211

static VALUE set_artist(VALUE self, VALUE artist) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  id3tag_set_artist(gfp, StringValuePtr(artist));
  return artist;
}

#bitrateObject

Get the bitrate.



130
131
132
133
134
135
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 130

static VALUE get_bitrate(VALUE self) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  return INT2NUM(lame_get_brate(gfp));
}

#bitrate=Object

Set the bitrate.



115
116
117
118
119
120
121
122
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 115

static VALUE set_bitrate(VALUE self, VALUE brate) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  lame_set_brate(gfp, NUM2INT(brate));
  lame_set_VBR_min_bitrate_kbps(gfp, lame_get_brate(gfp));
  return brate;
}

#encode(infile, outfile) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/icanhasaudio/mpeg/encoder.rb', line 38

def encode(infile, outfile)
  raise "Out file must be a FILE.  :-(" unless outfile.is_a?(File)

  num_samples = 0xFFFFFFFF
  parse_header(infile)
  init_params

  logger.debug(encoding_info) if logger

  num_samples_read = 0

  sw = case @pcmbitwidth
       when 8
         32 - 8
       when 16
         32 - 16
       when 24
         32 - 24
       end
  while !infile.eof?
    tmp_num_samples = num_samples()
    samples_to_read = framesize()
    remaining = tmp_num_samples - [tmp_num_samples, num_samples_read].min
    if remaining < framesize && 0 != tmp_num_samples
      samples_to_read = remaining
    end

    read_size = num_channels * samples_to_read * (pcmbitwidth / 8)

    samples = infile.read(read_size)
    samples_read = samples.length / num_channels

    buffers = [[], []]
    samples.unpack('v*').each_with_index do |b,i|
      (buffers[(i % 2)]) << (b << sw)
    end
    outfile.write(encode_buffer(buffers[0], buffers[1]))
  end
  outfile.write(flush())
  write_vbr_tags(outfile) if write_vbr_tag?
end

#genre=Object

Set the ID3 genre.



287
288
289
290
291
292
293
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 287

static VALUE set_genre(VALUE self, VALUE genre) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  id3tag_set_genre(gfp, StringValuePtr(genre));
  return genre;
}

Print the encoder configuration.



197
198
199
200
201
202
203
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 197

static VALUE print_config(VALUE self) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  lame_print_config(gfp);
  return Qnil;
}

#title=Object

Set the ID3 title.



225
226
227
228
229
230
231
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 225

static VALUE set_title(VALUE self, VALUE title) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  id3tag_set_title(gfp, StringValuePtr(title));
  return title;
}

#track=Object

Set the ID3 track.



268
269
270
271
272
273
274
275
276
277
278
279
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 268

static VALUE set_track(VALUE self, VALUE track) {
  lame_global_flags * gfp;

  int track_number = NUM2INT(track);
  if(track < 0 || track > 255)
    rb_raise(rb_eRuntimeError, "Track must be between 0 and 255.\n");

  Data_Get_Struct(self, lame_global_flags, gfp);
  VALUE track_string = rb_funcall(track, rb_intern("to_s"), 0);
  id3tag_set_track(gfp, StringValuePtr(track_string));
  return track;
}

#vbr_hard_min=Object

Strictly enforce the vbr min bitrate. Normally it will be violated for analog silence.



31
32
33
34
35
36
37
38
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 31

static VALUE set_vbr_hard_min(VALUE self, VALUE boolean)
{
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  lame_set_VBR_hard_min(gfp, boolean == Qtrue ? 1 : 0);
  return boolean;
}

#vbr_hard_min?Boolean

Get the hard minimum flag.

Returns:

  • (Boolean)


46
47
48
49
50
51
52
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 46

static VALUE get_vbr_hard_min(VALUE self)
{
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  return lame_get_VBR_hard_min(gfp) == 0 ? Qfalse : Qtrue;
}

#vbr_max_bitrateObject

Get the maximum vbr bitrate.



75
76
77
78
79
80
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 75

static VALUE get_vbr_max_bitrate(VALUE self) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  return INT2NUM(lame_get_VBR_max_bitrate_kbps(gfp));
}

#vbr_max_bitrate=Object

Set the maximum vbr bitrate.



60
61
62
63
64
65
66
67
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 60

static VALUE set_vbr_max_bitrate(VALUE self, VALUE brate)
{
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  lame_set_VBR_max_bitrate_kbps(gfp, NUM2INT(brate));
  return brate;
}

#vbr_min_bitrateObject

Get the minimum vbr bitrate.



102
103
104
105
106
107
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 102

static VALUE get_vbr_min_bitrate(VALUE self) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  return INT2NUM(lame_get_VBR_min_bitrate_kbps(gfp));
}

#vbr_min_bitrate=Object

Set the minimum vbr bitrate.



88
89
90
91
92
93
94
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 88

static VALUE set_vbr_min_bitrate(VALUE self, VALUE brate) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  lame_set_VBR_min_bitrate_kbps(gfp, NUM2INT(brate));
  return brate;
}

#qualityObject

Get the VBR quality. 0 = highest, 9 = lowest



157
158
159
160
161
162
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 157

static VALUE get_vbr_quality(VALUE self) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  return INT2NUM(lame_get_VBR_q(gfp));
}

#quality=Object

Set the VBR quality. 0 = highest, 9 = lowest



143
144
145
146
147
148
149
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 143

static VALUE set_vbr_quality(VALUE self, VALUE quality) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  lame_set_VBR_q(gfp, NUM2INT(quality));
  return quality;
}

#vbr_typeObject

Get the type of VBR.



184
185
186
187
188
189
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 184

static VALUE get_vbr_type(VALUE self) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  return INT2NUM(lame_get_VBR(gfp));
}

#vbr_type=Object

Set the type of VBR. Must be VBR_OFF, VBR_NORMAL, or VBR_FAST



170
171
172
173
174
175
176
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 170

static VALUE set_vbr_type(VALUE self, VALUE type) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  lame_set_VBR(gfp, NUM2INT(type));
  return type;
}

#year=Object

Set the ID3 year.



253
254
255
256
257
258
259
260
# File 'ext/icanhasaudio/audio_mpeg_encoder.c', line 253

static VALUE set_year(VALUE self, VALUE year) {
  lame_global_flags * gfp;

  Data_Get_Struct(self, lame_global_flags, gfp);
  VALUE year_string = rb_funcall(year, rb_intern("to_s"), 0);
  id3tag_set_year(gfp, StringValuePtr(year_string));
  return year;
}