#!/usr/bin/env ruby # $Id: cdio.rb,v 1.21 2008/05/02 13:05:40 karl Exp $ # # Copyright (C) 2006, 2007, 2008, 2009 Rocky Bernstein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Author:: Rocky Bernstein (mailto:rocky@gnu.org) # # = cdio # Module for CD Input and Control library. # == Version # :include:VERSION # # == SYNOPSIS # # The CD Input and Control library (pycdio) encapsulates CD-ROM # reading and control. Applications wishing to be oblivious of the OS- # and device-dependent properties of a CD-ROM can use this library. # # require "cdio' # # cd_drives = Cdio::devices(Rubycdio::DRIVER_DEVICE) # for drive in cd_drives # puts "Drive %s" % drive # end # drivers.each_pair { |driver_name, driver_id| # if Cdio::driver?(driver_id): # puts "Driver %s is installed." % driver_name # end # # # == DESCRIPTION # # # This is an Ruby interface to the GNU CD Input and Control library, # libcdio, written in C. The library encapsulates CD-ROM reading # and control. Ruby programs wishing to be oblivious of the OS- and # device-dependent properties of a CD-ROM can use this library. # # # The encapsulation is done in two parts. The lower-level Ruby # module is called rubycdio and is generated by SWIG. # # This module uses rubycdio. Actually, there are no objects in # module, but there are in classes Device and Track. # # Although rubycdio is perfectly usable on its own, it is expected # that cdio is what most people will use. As rubycdio more closely # models the C interface libcdio, it is conceivable (if unlikely) # that die-hard libcdio C users who are very familiar with that # interface could prefer that. # # require 'rubygems'; require 'ruby-debug'; Debugger.start require "rubycdio" module Cdio # Raise a Driver Error exception on error as determined by drc def possibly_raise_exception__(drc, msg=nil) if drc==Rubycdio::DRIVER_OP_SUCCESS return end if drc==Rubycdio::DRIVER_OP_ERROR raise DriverError end if drc==Rubycdio::DRIVER_OP_UNINIT raise DriverUninitError end if drc==Rubycdio::DRIVER_OP_UNSUPPORTED raise DriverUnsupportedError end if drc==Rubycdio::DRIVER_OP_NOT_PERMITTED raise DriverUnsupportedError end if drc==Rubycdio::DRIVER_OP_BAD_PARAMETER raise DriverBadParameterError end if drc==Rubycdio::DRIVER_OP_BAD_POINTER raise DriverBadPointerError end if drc==Rubycdio::DRIVER_OP_NO_DRIVER raise NoDriverError end raise DeviceException('unknown exception %d' % drc) end # = class DeviceException # General device or driver exceptions class DeviceException < Exception end class DriverError < DeviceException; end class DriverUnsupportedError < DeviceException; end class DriverUninitError < DeviceException; end class DriverNotPermittedError < DeviceException; end class DriverBadParameterError < DeviceException; end class DriverBadPointerError < DeviceException; end class NoDriverError < DeviceException; end class TrackError < DeviceException; end # Note: the keys below match those the names returned by # cdio_get_driver_name(). def drivers() return { :Unknown => Rubycdio::DRIVER_UNKNOWN, :AIX => Rubycdio::DRIVER_AIX, :BSDI => Rubycdio::DRIVER_BSDI, :FreeBSD => Rubycdio::DRIVER_FREEBSD, :"GNU/Linux" => Rubycdio::DRIVER_LINUX, :Solaris => Rubycdio::DRIVER_SOLARIS, :"OS X" => Rubycdio::DRIVER_OSX, :WIN32 => Rubycdio::DRIVER_WIN32, :CDRDAO => Rubycdio::DRIVER_CDRDAO, :"BIN/CUE" => Rubycdio::DRIVER_BINCUE, :NRG => Rubycdio::DRIVER_NRG, :device => Rubycdio::DRIVER_DEVICE } end def read_mode2blocksize() return { Rubycdio::READ_MODE_AUDIO => Rubycdio::CD_FRAMESIZE_RAW, Rubycdio::READ_MODE_M1F1 => Rubycdio::M2RAW_SECTOR_SIZE, Rubycdio::READ_MODE_M1F2 => Rubycdio::CD_FRAMESIZE, Rubycdio::READ_MODE_M2F1 => Rubycdio::M2RAW_SECTOR_SIZE, Rubycdio::READ_MODE_M2F2 => Rubycdio::CD_FRAMESIZE } end # close media tray in CD drive if there is a routine to do so. # The +driver id +is returned. A DeviceException is thrown on error. def close_tray(drive=nil, driver_id=Rubycdio::DRIVER_UNKNOWN) drc, found_driver_id = Rubycdio::close_tray(drive, driver_id) possibly_raise_exception__(drc) return found_driver_id end # Returns: [device, driver] # # Return a string containing the default CD device if none is # specified. if +driver_id+ is DRIVER_UNKNOWN or DRIVER_DEVICE # then one set the default device for that. # # nil is returned as the device if we couldn't get a default # device. def default_device_driver(driver_id=Rubycdio::DRIVER_DEVICE) return Rubycdio::get_default_device_driver(driver_id) end # Returns: [device1, device2, ...] # # Get an list of device names. def devices(driver_id=Rubycdio::DRIVER_UNKNOWN) return Rubycdio::get_devices(driver_id) end # Returns: [device1, device2, ... driver_id] # # Like get_devices, but return the driver_id which may be different # from the passed-in driver_id if it was Rubycdio::DRIVER_DEVICE or # Rubycdio::DRIVER_UNKNOWN. The return +driver_id+ may be useful because # often one wants to get a drive name and then *open* it # afterwards. Giving the driver back facilitates this, and speeds things # up for libcdio as well. def devices_ret(driver_id=Rubycdio::DRIVER_UNKNOWN) devices = Rubycdio::get_devices_ret(driver_id) end # Get an array of device names in search_devices that have at least # the capabilities listed by the capabities parameter. # # If any is false then every capability listed in the # extended portion of capabilities (i.e. not the basic filesystem) # must be satisified. If any is true, then if any of the # capabilities matches, we call that a success. # # To find a CD-drive of any type, use the mask Rubycdio::CDIO_FS_MATCH_ALL. # # The array of device names is returned or nil if we couldn't get a # default device. It is also possible to return a non nil but after # dereferencing the the value is nil. This also means nothing was # found. def devices_with_cap(capabilities, any=false) return Rubycdio::get_devices_with_cap(capabilities, any) end # Returns: [device1, device2..., driver_id] # # Like cdio_get_devices_with_cap but we return the driver we found # as well. This is because often one wants to search for kind of drive # and then *open* it afterwards. Giving the driver back facilitates this, # and speeds things up for libcdio as well. def devices_with_cap_ret(capabilities, any=false) return Rubycdio::get_devices_with_cap_ret(capabilities, any) end # return bool # # Return true if we have driver driver_id. def driver?(driver_id) if driver_id.class == Fixnum return Rubycdio::have_driver(driver_id) == 1 elsif driver_id.class == Symbol and drivers.member?(driver_id) ret = Rubycdio::have_driver(drivers[driver_id]) if ret == 0 then return false end if ret == 1 then return true end raise ArgumentError else raise ArgumentError end end #-- # FIXME ? is not quite right # binfile?(binfile_name)->cue_name #++ # # Determine if +binfile_name+ is the BIN file part of a CDRWIN CD # disk image. # # Return the corresponding CUE file if bin_name is a BIN file or # nil if not a BIN file. def binfile?(binfile_name) return Rubycdio::is_binfile(binfile_name) end #-- # FIXME ? is not quite right #++ # return bin_name for a corresponding CUE file # # Determine if cuefile_name is the CUE file part of a CDRWIN CD # disk image. # # Return the corresponding BIN file if bin_name is a CUE file or # nil if not a CUE file. def cuefile?(cuefile_name) return Rubycdio::is_cuefile(cuefile_name) end # Returns: bool # # Return true if source refers to a real hardware CD-ROM. def device?(source, driver_id=Rubycdio::DRIVER_UNKNOWN) if not driver_id then driver_id=Rubycdio::DRIVER_UNKNOWN end return Rubycdio::device?(source, driver_id) end # Returns: bool # # Determine if nrgfile_name is a Nero CD disc image def nrg?(nrgfile_name) return Rubycdio::nrg?(nrgfile_name) end # tocfile?(tocfile_name)->bool # # Determine if +tocfile_name+ is a cdrdao CD disc image def tocfile?(tocfile_name) return Rubycdio::tocfile?(tocfile_name) end # Convert +bitmask+ for miscellaneous drive properties # into a dictionary of drive capabilities def convert_drive_cap_misc(bitmask) result={} if bitmask & Rubycdio::DRIVE_CAP_ERROR result[:DRIVE_CAP_ERROR] = true end if bitmask & Rubycdio::DRIVE_CAP_UNKNOWN result[:DRIVE_CAP_UNKNOWN] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_CLOSE_TRAY result[:DRIVE_CAP_MISC_CLOSE_TRAY] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_EJECT result[:DRIVE_CAP_MISC_EJECT] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_LOCK result['DRIVE_CAP_MISC_LOCK'] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_SELECT_SPEED result[:DRIVE_CAP_MISC_SELECT_SPEED] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_SELECT_DISC result[:DRIVE_CAP_MISC_SELECT_DISC] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_MULTI_SESSION result[:DRIVE_CAP_MISC_MULTI_SESSION] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_MEDIA_CHANGED result[:DRIVE_CAP_MISC_MEDIA_CHANGED] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_RESET result[:DRIVE_CAP_MISC_RESET] = true end if bitmask & Rubycdio::DRIVE_CAP_MISC_FILE result[:DRIVE_CAP_MISC_FILE] = true end return result end # Convert bit mask for drive read properties # into a dictionary of drive capabilities def convert_drive_cap_read(bitmask) result={} if bitmask & Rubycdio::DRIVE_CAP_READ_AUDIO result[:DRIVE_CAP_READ_AUDIO] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_CD_DA result[:DRIVE_CAP_READ_CD_DA] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_CD_G result[:DRIVE_CAP_READ_CD_G] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_CD_R result[:DRIVE_CAP_READ_CD_R] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_CD_RW result[:DRIVE_CAP_READ_CD_RW] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_DVD_R result[:DRIVE_CAP_READ_DVD_R] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_DVD_PR result[:DRIVE_CAP_READ_DVD_PR] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_DVD_RAM result[:DRIVE_CAP_READ_DVD_RAM] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_DVD_ROM result[:DRIVE_CAP_READ_DVD_ROM] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_DVD_RW result[:DRIVE_CAP_READ_DVD_RW] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_DVD_RPW result[:DRIVE_CAP_READ_DVD_RPW] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_C2_ERRS result[:DRIVE_CAP_READ_C2_ERRS] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_MODE2_FORM1 result[:DRIVE_CAP_READ_MODE2_FORM1] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_MODE2_FORM2 result[:DRIVE_CAP_READ_MODE2_FORM2] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_MCN result[:DRIVE_CAP_READ_MCN] = true end if bitmask & Rubycdio::DRIVE_CAP_READ_ISRC result[:DRIVE_CAP_READ_ISRC] = true end return result end # Convert +bitmask+ for drive write properties # into a dictionary of drive capabilities def convert_drive_cap_write(bitmask) result={} if bitmask & Rubycdio::DRIVE_CAP_WRITE_CD_R result[:DRIVE_CAP_WRITE_CD_R] = true end if bitmask & Rubycdio::DRIVE_CAP_WRITE_CD_RW result[:DRIVE_CAP_WRITE_CD_RW] = true end if bitmask & Rubycdio::DRIVE_CAP_WRITE_DVD_R result[:DRIVE_CAP_WRITE_DVD_R] = true end if bitmask & Rubycdio::DRIVE_CAP_WRITE_DVD_PR result[:DRIVE_CAP_WRITE_DVD_PR] = true end if bitmask & Rubycdio::DRIVE_CAP_WRITE_DVD_RAM result[:DRIVE_CAP_WRITE_DVD_RAM] = true end if bitmask & Rubycdio::DRIVE_CAP_WRITE_DVD_RW result[:DRIVE_CAP_WRITE_DVD_RW] = true end if bitmask & Rubycdio::DRIVE_CAP_WRITE_DVD_RPW result[:DRIVE_CAP_WRITE_DVD_RPW] = true end if bitmask & Rubycdio::DRIVE_CAP_WRITE_MT_RAINIER result[:DRIVE_CAP_WRITE_MT_RAINIER] = true end if bitmask & Rubycdio::DRIVE_CAP_WRITE_BURN_PROOF result[:DRIVE_CAP_WRITE_BURN_PROOF] = true end return result end # = class Device # # CD Input and control class for discs/devices # # == SYNOPSIS # # require "cdio" # d = Cdio::Device.new("", Rubycdio::DRIVER_UNKNOWN) # drive_name = d.device() # hw = d.hwinfo() # if hw then # puts "drive: %s, vendor: %s, model: %s, revision: %s" % # [drive_name, hw["vendor"], hw["model"], hw["revision"]] # end # # drivers.each_pair { |driver_name, driver_id| # begin # if driver?(driver_id): # puts "Driver %s is installed." % driver_name # end # rescue ValueError # end class Device def initialize(source=nil, driver_id=nil, access_mode=nil) @cd = nil if source or driver_id open(source, driver_id, access_mode) end end # Returns: bool # # return true if CD-ROM understand ATAPI commands. def ATAPI?() return Rubycdio::ATAPI?(@cd) end # Returns: status # # Pause playing CD through analog output. # A DeviceError exception may be raised. def audio_pause() drc=Rubycdio::audio_pause(@cd) possibly_raise_exception__(drc) end # Returns: status # # Playing CD through analog output from +start_lsn+ to +ending_lsn+ # A DeviceError exception may be raised. def audio_play_lsn(start_lsn, end_lsn) drc=Rubycdio::audio_play_lsn(@cd, start_lsn, end_lsn) possibly_raise_exception__(drc) end # Returns: status # # Resume playing an audio CD through the analog interface. # A DeviceError exception may be raised. def audio_resume() drc=Rubycdio::audio_resume(@cd) possibly_raise_exception__(drc) end # Returns: status # # Stop playing an audio CD through the analog interface. # A DeviceError exception may be raised. def audio_stop() drc=Rubycdio::audio_stop(@cd) possibly_raise_exception__(drc) end # Free (C memory) resources associated with the object. Call this when # done using using CD reading/control operations for the current # device. def close() if @cd Rubycdio::close(@cd) else puts "***No object to close" end @cd=nil end # Eject media in CD drive if there is a routine to do so. # A DeviceError exception may be raised. def eject_media() drc=Rubycdio::eject_media(@cd) @cd = nil possibly_raise_exception__(drc) end # Eject media in CD drive if there is a routine to do so. # An exception is thrown on error. def eject_media_drive(drive=nil) ### FIXME: combine into above by testing if drive is the string ### nil versus drive = Rubycdio::DRIVER_UNKNOWN Rubycdio::eject_media_drive(drive) end # Returns: String # Get the value associatied with key. def arg(key) return Rubycdio::get_arg(@cd, key) end # Returns: String # # Get the default CD device. # If we haven't initialized a specific device driver), # then find a suitable one and return the default device for that. # In some situations of drivers or OS's we can't find a CD device if # there is no media in it and it is possible for this routine to return # nil even though there may be a hardware CD-ROM. def device() if @cd return Rubycdio::get_arg(@cd, "source") end return Rubycdio::get_device(@cd) end # Returns: Fixnum # # Get the LSN of the end of the CD # # DriverError and IOError may raised on error. def disc_last_lsn() lsn = Rubycdio::get_disc_last_lsn(@cd) if lsn == Rubycdio::INVALID_LSN raise DriverError end return lsn end # Returns: String # # Get disc mode - the kind of CD (CD-DA, CD-ROM mode 1, CD-MIXED, ...) # that we've got. The notion of 'CD' is extended a little to include # DVD's. def disc_mode() if not @cd then return "Uninitialized Device" end return Rubycdio::get_disc_mode(@cd) end # Get drive capabilities of device. # # In some situations of drivers or OS's we can't find a CD # device if there is no media in it. In this situation # capabilities will show up as empty even though there is a # hardware CD-ROM. get_drive_cap_dev()->(read_cap, write_cap, # misc_cap) # # Get drive capabilities of device. # # In some situations of drivers or OS's we can't find a CD # device if there is no media in it. In this situation # capabilities will show up as empty even though there is a # hardware CD-ROM. def drive_cap() b_read_cap, b_write_cap, b_misc_cap = Rubycdio::get_drive_cap(@cd) return [convert_drive_cap_read(b_read_cap), convert_drive_cap_write(b_write_cap), convert_drive_cap_misc(b_misc_cap)] end def drive_cap_dev(device=nil) #--- ### FIXME: combine into above by testing on the type of device. #+++ b_read_cap, b_write_cap, b_misc_cap = Rubycdio::get_drive_cap_dev(device); return [convert_drive_cap_read(b_read_cap), convert_drive_cap_write(b_write_cap), convert_drive_cap_misc(b_misc_cap)] end # Returns: String # # return a string containing the name of the driver in use. # # An IOError exception is raised on error. def driver_name() if not @cd then raise DriverUninitError end return Rubycdio::get_driver_name(@cd) end # Returns: Fixnum # # Return the driver id of the driver in use. # if object has not been initialized or is nil, # return Rubycdio::DRIVER_UNKNOWN. def driver_id() return Rubycdio::get_driver_id(@cd) end # Returns: Track # # return a Track object of the first track. nil is returned # if there was a problem. def first_track() track = Rubycdio::get_first_track_num(@cd) if track == Rubycdio::INVALID_TRACK return nil end return Track.new(@cd, track) end # Returns: {"vendor"=>??, "model"=>??, "release"=>??} # # Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. def hwinfo() return Rubycdio::get_hwinfo(@cd) end # Returns: FixNum # Return the Joliet level recognized for cdio. This only makes # sense for something that has an ISO-9660 filesystem. def joliet_level() return Rubycdio::get_joliet_level(@cd) end # Returns: Fixnum # Get the LSN of the first track of the last session of on the CD. # An exception is thrown on error. def last_session() drc, session = Rubycdio::get_last_session(@cd) possibly_raise_exception__(drc) return session end # Returns: Track # # return a Track object of the first track. nil is returned # if there was a problem. def last_track() track = Rubycdio::get_last_track_num(@cd) if track == Rubycdio::INVALID_TRACK return nil end return Track.new(@cd, track) end # Returns: String # # Get the media catalog number (MCN) from the CD. def mcn() return Rubycdio::get_mcn(@cd) end # Find out if media has changed since the last call. # Return true if media has changed since last call. An exception # Error is given on error. def media_changed?() drc = Rubycdio::get_media_changed(@cd) if drc == 0 then return false end if drc == 1 then return true end possibly_raise_exception__(drc) raise DeviceException end # Returns: Fixnum # # Return the number of tracks on the CD. # A TrackError or IOError exception may be raised on error. def num_tracks() track = Rubycdio::get_num_tracks(@cd) if track == Rubycdio::INVALID_TRACK raise TrackError end return track end # Returns: track # # Return a track object for the given track number. def track(track_num) return Track.new(@cd, track_num) end # Returns: track # # Find the track which contains +lsn+. # nil is returned if the lsn outside of the CD or # if there was some error. # # If +lsn+ is before the pregap of the first track, # A track object with a 0 track is returned. # Otherwise we return the track that spans the LSN. def track_for_lsn(lsn) track = Rubycdio::get_last_track_num(@cd) if track == Rubycdio::INVALID_TRACK return nil end return Track.new(@cd, track) end # Returns: Fixnum # Reposition read offset # Similar to (if not the same as) libc's fseek() # # +offset+ is amount to seek and # +whence+ is like corresponding parameter in libc's lseek, e.g. # it should be SEEK_SET or SEEK_END. # # the offset is returned or -1 on error. def lseek(offset, whence) return Rubycdio::lseek(@cd, offset, whence) end # Sets up to read from place specified by +source+, +driver_id+ and # +access_mode+. This should be called before using any other routine # except those that act on a CD-ROM drive by name. # # If nil is the value of +source+, we'll use the default driver device. # If nil is the value of +driver_id+, we'll find a suitable device driver. # # If device object was, previously opened it is closed first. # # Device is opened so that subsequent operations can be performed. def open(source=nil, driver_id=Rubycdio::DRIVER_UNKNOWN, access_mode=nil) if not driver_id driver_id=Rubycdio::DRIVER_UNKNOWN end if not source source = '' end if not access_mode access_mode = '' end if @cd close() end @cd = Rubycdio::open_cd(source, driver_id, access_mode) end # Returns: [size, data] # # Reads the next +size+ bytes. # Similar to (if not the same as) libc's read() # # The number of bytes read and the data is returned. # A DeviceError exception may be raised. def read(size) size, data = Rubycdio::read_cd(@cd, size) possibly_raise_exception__(size) return [size, data] end # return [size, data] # # Reads a number of data sectors (AKA blocks). # # +lsn+ is the sector to read; +bytes+ is the number of bytes to read. # A DeviceError exception may be raised. def read_data_blocks(lsn, blocks=1) size = Rubycdio::ISO_BLOCKSIZE*blocks triple = Rubycdio::read_data_bytes(@cd, lsn, Rubycdio::ISO_BLOCKSIZE, size) if not triple return [-1, nil] end data, size, drc = triple possibly_raise_exception__(drc) return [size, data] end # return [blocks, data] # # Reads a number of sectors (AKA blocks). # # +lsn+ is the sector to read, +bytes+ is the number of bytes to read. # # If +read_mode+ is Rubycdio::MODE_AUDIO, the return buffer size will be # truncated to multiple of Rubycdio::CDIO_FRAMESIZE_RAW i_blocks bytes. # # If +read_mode+ is Rubycdio::MODE_DATA, buffer will be truncated to a # multiple of Rubycdio::ISO_BLOCKSIZE, Rubycdio::M1RAW_SECTOR_SIZE or # Rubycdio::M2F2_SECTOR_SIZE bytes depending on what mode the data is in. # # If +read_mode+ is Rubycdio::MODE_M2F1, buffer will be truncated to a # multiple of Rubycdio::M2RAW_SECTOR_SIZE bytes. # # If +read_mode+ is Rubycdio::MODE_M2F2, the return buffer size will be # truncated to a multiple of Rubycdio::CD_FRAMESIZE bytes. # # The number of bytes read and the data is returned. # A DeviceError exception may be raised. def read_sectors(lsn, read_mode, blocks=1) begin blocksize = read_mode2blocksize()[read_mode] size = blocks * blocksize rescue return [-1, nil] end triple = Rubycdio::read_sectors(@cd, lsn, read_mode, size) if not triple return [-1, nil] end data, size, drc = triple possibly_raise_exception__(drc) blocks = size / blocksize return [blocks, data] end # Set the blocksize for subsequent reads. # An exception is thrown on error. def blocksize=(blocksize) drc = Rubycdio::set_blocksize(@cd, blocksize) possibly_raise_exception__(drc) end # Set the drive speed. An exception is thrown on error. def speed=(speed) drc = Rubycdio::set_speed(@cd, speed) possibly_raise_exception__(drc) end end # Device # = CD Input and control track class # # == SYNOPSIS # # require "cdio" # d = Device.new("/dev/cdrom") # t = d.first_track() # first_track = t.track # num_tracks = d.num_tracks() # last_track = first_track+num_tracks-1 # for i in first_track .. last_track # t = d.track(i) # puts "%3d: %06u %-6s %s" % [t.track, t.lsn(), t.msf(), t.format()] # end # puts "%3X: %06u leadout" % [Rubycdio::CDROM_LEADOUT_TRACK, d.disc_last_lsn()] # class Track attr_accessor :track def initialize(device, track_num) if track_num.class != Fixnum raise TrackError('track number parameter is not an integer') end @track = track_num # See if the device parameter is a string or # a device object. if device.class == String @device = Device.new(device) else @device = device end end # Returns: Fixnum # # Return number of channels in track: 2 or 4. # Not meaningful if track is not an audio track. # An exception can be raised on error. def audio_channels() channels = Rubycdio::get_track_channels(@device, @track) if -2 == channels raise DriverUnsupportedError elsif -1 == channels raise TrackError else return channels end end def cdtext return CDText.new(Rubycdio::get_cdtext(@device, @track)) end # Returns: bool # # Return copy protection status on a track. Is this meaningful # not an audio track? def copy_permit?() return Rubycdio::track_copy_permit?(@device, @track) end # Returns: format (String) # # Get the format (e.g. 'audio', 'mode2', 'mode1') of track. def format() return Rubycdio::get_track_format(@device, @track) end # Returns: Fixnum (lsn) # # Return the ending LSN for a track. # A TrackError or IOError exception may be raised on error. def last_lsn() lsn = Rubycdio::get_track_last_lsn(@device, @track) if lsn == Rubycdio::INVALID_LSN raise TrackError('Invalid LSN returned') end return lsn end # Returns: Fixnum (lba) # # Return the starting LBA for a track # A TrackError exception is raised on error. def lba() lba = Rubycdio::get_track_lba(@device, @track) if lba == Rubycdio::INVALID_LBA raise TrackError('Invalid LBA returned') end return lba end # Returns: Fixnum (lsn) # # Return the starting LSN for a track # A TrackError exception is raised on error. def lsn() lsn = Rubycdio::get_track_lsn(@device, @track) if lsn == Rubycdio::INVALID_LSN raise TrackError('Invalid LSN returned') end return lsn end # Returns: String # # Return the starting MSF (minutes/secs/frames) for track number track. # Track numbers usually start at something greater than 0, usually 1. # # Returns string of the form mm:ss:ff if all good, or string nil on # error. def msf() return Rubycdio::get_track_msf(@device, @track) end # Returns: String # # Get linear preemphasis status on an audio track. # This is not meaningful if not an audio track? # A TrackError exception is raised on error. def preemphasis() rc = Rubycdio::get_track_preemphasis(@device, @track) if rc == Rubycdio::TRACK_FLAG_FALSE return 'none' elsif rc == Rubycdio::TRACK_FLAG_TRUE return 'preemphasis' elsif rc == Rubycdio::TRACK_FLAG_UNKNOWN return 'unknown' else raise TrackError('Invalid return value %d' % d) end end # Returns: Fixnum # # Get the number of sectors between this track an the next. This # includes any pregap sectors before the start of the next track. # Track numbers usually start at something # greater than 0, usually 1. # # A TrackError exception is raised on error. def sec_count() sec_count = Rubycdio::get_track_sec_count(@device, @track) if sec_count == 0 raise TrackError end return sec_count end # Return true if we have XA data (green, mode2 form1) or # XA data (green, mode2 form2). That is track begins: # sync - header - subheader # 12 4 - 8 def green?() return Rubycdio::track_green?(@device, @track) end end # class Track class CDText def initialize(opaque) @_cdtext = opaque end # # get(self, key)->string # # Get the value associatied with key. # def get(key) return Rubycdio::cdtext_get(key, @_cdtext) end def is_keyword(key) return Rubycdio::cdtext_is_keyword(key, @_cdtext) end # # set(self, key, string)->None # # Set the value associatied with key. # def set( key, string) return Rubycdio::cdtext_set(key, string, @_cdtext) end end end # module Cdio include Cdio # # Local variables: # mode: Ruby # End: