Class: MusicBrainz::DiscID

Inherits:
Object
  • Object
show all
Defined in:
lib/mb-discid.rb,
ext/mb_discid.c

Overview

Class to calculate a MusicBrainz DiscID from an audio CD in the drive.

This is basically an interface to the libdiscid C library (musicbrainz.org/doc/libdiscid).

Usage

Basic Usage:

require 'mb-discid'

# Create a new DiscID object.
disc = MusicBrainz::DiscID.new

# Read the TOC from the default device.
# An audio CD must be inserted in the drive. An exception will be thrown
# if the CD can't be read.
begin
  disc.read
rescue Exception => e
  puts e
  exit(1)
end

# Print information about the disc:
print "DiscID      : \#{disc.id}\nSubmit via  : \#{disc.submission_url}\nFreeDB ID   : \#{disc.freedb_id}\nFirst track : \#{disc.first_track_num}\nLast track  : \#{disc.last_track_num}\nTotal length: \#{disc.seconds} seconds\nSectors     : \#{disc.sectors}\n"

# Print information about individual tracks:
disc.track_details do |track_info|
  puts "Track ##{track_info.number}"
  puts "  Length: %02d:%02d (%i sectors)" %
     [track_info.seconds / 60, track_info.seconds % 60, track_info.sectors]
  puts "  Start : %02d:%02d (sector %i)" % 
     [track_info.start_time / 60, track_info.start_time % 60, track_info.start_sector]
  puts "  End   : %02d:%02d (sector %i)" %
     [track_info.end_time / 60, track_info.end_time % 60, track_info.end_sector]
end

# Print a submission URL that can be used to submit
# the disc ID to MusicBrainz.org.
puts "\nSubmit via #{disc.submission_url}"

Specifying the device to read from:

# Create a new DiscID object and read the disc in /dev/dvd:
disc = MusicBrainz::DiscID.new
disc.read('/dev/dvd')

# Create a new DiscID object and directly read the disc in /dev/dvd:
disc = MusicBrainz::DiscID.new('/dev/dvd')

# Create a new DiscID object and directly read the disc in the platform's
# default device:
disc = MusicBrainz::DiscID.new(MusicBrainz::DiscID.default_device)

Calculating the DiscID for a given TOC

disc = MusicBrainz::DiscID.new

first_track   = 1
sectors       = 224556
track_offsets = [150, 9078, 13528, 34182, 53768, 70987, 96424,
                 118425, 136793, 159514, 179777, 198006]

disc.put(first_track, sectors, track_offsets)
puts disc.id # Should print "T_prJXQSrqbnH8OE.dgOKsHm5Uw-"

Defined Under Namespace

Classes: TrackInfo

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.MusicBrainz::DiscID.default_device(device = nil) ⇒ String

Returns a device string for the default device for this platform.

Returns:

  • (String)


334
335
336
337
# File 'ext/mb_discid.c', line 334

VALUE mb_discid_default_device(VALUE class)
{
  return rb_str_new2(discid_get_default_device());
}

.MusicBrainz::DiscID.new(device = nil) ⇒ Object

Construct a new DiscID object.

As an optional argument the name of the device to read the ID from may be given. If you don’t specify a device here you can later read the ID with the read method.

Raises

ArgumentError, TypeError, Exception

Returns:

  • (Object)


311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'ext/mb_discid.c', line 311

VALUE mb_discid_new(int argc, VALUE *argv, VALUE class)
{
  DiscId *disc = discid_new();
  VALUE tdata = Data_Wrap_Struct(class, 0, discid_free, disc);
  VALUE device = Qnil;
  rb_obj_call_init(tdata, 0, 0);
  rb_iv_set(tdata, "@read", Qfalse);
  
  /* Check the number of arguments */
  rb_scan_args(argc, argv, "01", &device);
  
  if (device != Qnil)
    rb_funcall(tdata, rb_intern("read"), 1, device);
  
  return tdata;
}

.sectors_to_seconds(sectors) ⇒ Object

Converts sectors to seconds.

According to the red book standard 75 sectors are one second.



223
224
225
# File 'lib/mb-discid.rb', line 223

def self.sectors_to_seconds(sectors)
  return (sectors.to_f / 75).round
end

Instance Method Details

#first_track_numInteger?

Return the number of the first track on this disc (usually 1).

Returns nil if no ID was yet read.

Returns:

  • (Integer, nil)


97
98
99
100
101
102
103
104
105
106
107
108
# File 'ext/mb_discid.c', line 97

static VALUE mb_discid_first_track_num(VALUE self)
{
  if (rb_iv_get(self, "@read") == Qfalse)
    return Qnil;
  else
  {
    DiscId *disc;
    Data_Get_Struct(self, DiscId, disc);
    
    return INT2FIX(discid_get_first_track_num(disc));
  }
}

#freedb_idString?

Returns a FreeDB DiscID as a string.

Returns nil if no ID was yet read.

Returns:

  • (String, nil)


76
77
78
79
80
81
82
83
84
85
86
87
# File 'ext/mb_discid.c', line 76

static VALUE mb_discid_freedb_id(VALUE self)
{
  if (rb_iv_get(self, "@read") == Qfalse)
    return Qnil;
  else
  {
    DiscId *disc;
    Data_Get_Struct(self, DiscId, disc);
    
    return rb_str_new2(discid_get_freedb_id(disc));
  }
}

#idString?

Returns the DiscID as a string.

Returns nil if no ID was yet read.

Returns:

  • (String, nil)


34
35
36
37
38
39
40
41
42
43
44
45
# File 'ext/mb_discid.c', line 34

static VALUE mb_discid_id(VALUE self)
{
  if (rb_iv_get(self, "@read") == Qfalse)
    return Qnil;
  else
  {
    DiscId *disc;
    Data_Get_Struct(self, DiscId, disc);
    
    return rb_str_new2(discid_get_id(disc));
  }
}

#last_track_numInteger?

Return the number of the last track on this disc.

Returns nil if no ID was yet read.

Returns:

  • (Integer, nil)


118
119
120
121
122
123
124
125
126
127
128
129
# File 'ext/mb_discid.c', line 118

static VALUE mb_discid_last_track_num(VALUE self)
{
  if (rb_iv_get(self, "@read") == Qfalse)
    return Qnil;
  else
  {
    DiscId *disc;
    Data_Get_Struct(self, DiscId, disc);
    
    return INT2FIX(discid_get_last_track_num(disc));
  }
}

#put(first_track, sectors, offsets) ⇒ Object

Set the TOC information directly instead of reading it from a device.

Use this instead of read if the TOC information was already read elsewhere and you want to recalculate the ID. Throws an Exception if the CD’s TOC can not be read.

Parameters:

first_track

The number of the first track on the disc (usually 1).

sectors

The total number of sectors on the disc.

offsets

Array of all track offsets. The number of tracks must not exceed 99.

Raises

Exception



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'ext/mb_discid.c', line 266

static VALUE mb_discid_put(VALUE self, VALUE first_track, VALUE sectors,
                           VALUE offsets)
{
  DiscId *disc;                       /* Pointer to the disc struct */
  long length = RARRAY_LEN(offsets);  /* length of the offsets array */
  int cfirst  = NUM2INT(first_track); /* number of the first track */
  int clast   = length + 1 - cfirst;  /* number of the last track */
  int coffsets[100];                  /* C array to hold the offsets */
  int i = 1;                          /* Counter for iterating over coffsets*/
  
  Data_Get_Struct(self, DiscId, disc);
  
  /* Convert the Ruby array to an C array of integers. discid_puts expects
     always an offsets array with exactly 100 elements. */
  coffsets[0] = NUM2INT(sectors); /* 0 is always the leadout track */
  while (i <= length && i < 100)
  {
    coffsets[i] = NUM2INT(rb_ary_entry(offsets, i - 1));
    i++;
  }
  
  /* Mark the disc id as unread in case something goes wrong. */
  rb_iv_set(self, "@read", Qfalse);
  
  /* Read the discid */
  if (discid_put(disc, cfirst, clast, coffsets) == 0)
    rb_raise(rb_eException, discid_get_error_msg(disc));
  else /* Remember that we already read the ID. */
    rb_iv_set(self, "@read", Qtrue);
  
  return Qnil;
}

#read(device = nil) ⇒ Object

Read the disc ID from the given device.

If no device is given the default device of the platform will be used. Throws an Exception if the CD’s TOC can not be read.

Raises

ArgumentError, TypeError, Exception



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
# File 'ext/mb_discid.c', line 215

static VALUE mb_discid_read(int argc, VALUE *argv, VALUE self)
{
  DiscId *disc;        /* Pointer to the disc struct */
  VALUE device = Qnil; /* The device string as a Ruby string */
  char* cdevice;       /* The device string as a C string */
  
  Data_Get_Struct(self, DiscId, disc);
  
  /* Check the number and types of arguments */
  rb_scan_args(argc, argv, "1", &device);
  if(rb_respond_to(device, rb_intern("to_s")))
    device = rb_funcall(device, rb_intern("to_s"), 0, 0);
  else
    rb_raise(rb_eTypeError, "wrong argument type (expected String)");
  
  /* Use the default device if none was given. */
  if (device == Qnil)
    cdevice = discid_get_default_device();
  else
    cdevice = StringValuePtr(device);
  
  /* Mark the disc id as unread in case something goes wrong. */
  rb_iv_set(self, "@read", Qfalse);
  
  /* Read the discid */
  if (discid_read(disc, cdevice) == 0)
    rb_raise(rb_eException, discid_get_error_msg(disc));
  else /* Remember that we already read the ID. */
    rb_iv_set(self, "@read", Qtrue);
  
  return Qnil;
  
}

#secondsObject

Return the length of the disc in sectors.

Returns nil if no ID was yet read.



188
189
190
# File 'lib/mb-discid.rb', line 188

def seconds
  DiscID.sectors_to_seconds(sectors) unless @read == false
end

#sectorsInteger?

Return the length of the disc in sectors.

Returns nil if no ID was yet read.

Returns:

  • (Integer, nil)


139
140
141
142
143
144
145
146
147
148
149
150
# File 'ext/mb_discid.c', line 139

static VALUE mb_discid_sectors(VALUE self)
{
  if (rb_iv_get(self, "@read") == Qfalse)
    return Qnil;
  else
  {
    DiscId *disc;
    Data_Get_Struct(self, DiscId, disc);
    
    return INT2FIX(discid_get_sectors(disc));
  }
}

#submission_urlString?

Returns a submission URL for the DiscID as a string.

Returns nil if no ID was yet read.

Returns:

  • (String, nil)


55
56
57
58
59
60
61
62
63
64
65
66
# File 'ext/mb_discid.c', line 55

static VALUE mb_discid_submission_url(VALUE self)
{
  if (rb_iv_get(self, "@read") == Qfalse)
    return Qnil;
  else
  {
    DiscId *disc;
    Data_Get_Struct(self, DiscId, disc);
    
    return rb_str_new2(discid_get_submission_url(disc));
  }
}

#to_sObject

DiscID to String conversion. Same as calling the method id but guaranteed to return a string.



181
182
183
# File 'lib/mb-discid.rb', line 181

def to_s
  id.to_s
end

#track_detailsObject

Returns an array of TrackInfo objects. Each TrackInfo object contains detailed information about the track.

If a block is given this method returns nil and instead iterates over the block calling the block with one argument |track_info|.

Returns always nil if no ID was yet read. The block won’t be called in this case.



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/mb-discid.rb', line 200

def track_details
  unless @read == false
    track_number = self.first_track_num - 1
    tracks = []
    
    self.tracks do |offset, length|
      track_number += 1
      track_info = TrackInfo.new(track_number, offset, length)
      
      if block_given?
        yield track_info
      else
        tracks << track_info
      end
    end
    
    return tracks unless block_given?
  end
end

#tracksArray #tracks {|offset, length| ... } ⇒ Object

Returns an array of [offset, length] tuples for each track.

Offset and length are both integer values representing sectors. If a block is given this method returns nil and instead iterates over the block calling the block with two arguments |offset, length|.

Returns always nil if no ID was yet read. The block won’t be called in this case.

You may want to use the method track_details instead of this method to retrieve more detailed information about the tracks.

Overloads:

  • #tracksArray

    Returns:

    • (Array)
  • #tracks {|offset, length| ... } ⇒ Object

    Yields:

    • (offset, length)


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
197
198
199
200
201
202
# File 'ext/mb_discid.c', line 169

static VALUE mb_discid_tracks(VALUE self)
{
  if (rb_iv_get(self, "@read") == Qfalse)
    return Qnil;
  else
  {
    DiscId *disc; /* Pointer to the disc struct */
    VALUE result = rb_ary_new(); /* Array of all [offset, length] tuples */
    VALUE tuple; /* Array to store one [offset, length] tuple. */
    int track;   /* Counter for the track number to process. */
    
    Data_Get_Struct(self, DiscId, disc);
    
    track = discid_get_first_track_num(disc); /* First track number */
    while (track <= discid_get_last_track_num(disc))
    {
      tuple = rb_ary_new3(2,
        INT2FIX(discid_get_track_offset(disc, track)),
        INT2FIX(discid_get_track_length(disc, track)) );
      
        if (rb_block_given_p())
        rb_yield(tuple);
      else
        rb_ary_push(result, tuple);
        
      track++;
    }
    
    if (rb_block_given_p())
      return Qnil;
    else
      return result;
  }
}