2 CDLow - Low level CDROM interface
3 Written to replace CDAudio, CDDB and PyGAME.CD and
4 add functionality (skipping)
5 Originally from the Gryphon CD player (http://gryphon.sourceforge.net/)
7 Copyright 2001 (c) Christian Storgaard.
8 Changes made by Ron Kuslak <rds@rdsarts.com>, 2003-2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 from fcntl
import ioctl
41 tocentry_fmt
= "BBBix"
42 addr_fmt
= "BBB"+"x"*(struct
.calcsize('i')-3)
44 subchnl_fmt
= "BBxBBiBBB"
52 data
= 0 # Display Data-tracks
54 """ #### Internal functions ####
55 #### -- start -- ####"""
62 cd
.cd
= os
.open(cd
.path
, os
.O_RDONLY|os
.O_NONBLOCK
)
81 atexit
.register(cd_close
)
83 """ #### Internal functions ####
84 #### -- end -- ####"""
88 """ We don't care if there's a CD in or not, we're just stopping ;)"""
89 if get_status() == PAUSED
:
91 ioctl(cd
.cd
, CDROMRESUME
)
96 ioctl(cd
.cd
, CDROMSTOP
)
102 status
= get_status()
104 if status
== PLAYING
:
105 ioctl(cd
.cd
, CDROMPAUSE
)
106 elif status
== PAUSED
:
107 ioctl(cd
.cd
, CDROMRESUME
)
110 def forward(val
=None):
111 if val
!=None: skip(0,val
,1)
114 def rewind(val
=None):
115 if val
!=None: skip(1,val
,1)
118 def goto(min, sec
, frm
=0):
119 """See if we have the last track to play in cache, else set to last on disc"""
120 if cd
.last
== (0,0,0):
121 if cd
.end
== None: get_header()
122 cd
.last
= get_track_time(cd
.end
)
123 try: play_msf(min, sec
, frm
, cd
.last
[0], cd
.last
[1], cd
.last
[2])
126 def skip(direction
, amount
=None, frames
=0):
127 if amount
== None: amount
= cd
.skip
128 if not frames
: amount
= amount
*CD_FRAMES
129 curmin
, cursec
, curfrm
= get_current_timing()['abs']
130 start
= msf2frm(curmin
,cursec
,curfrm
)
131 """Apply according to which direction we are skipping"""
132 if direction
== 1:start
=start
-amount
133 else:start
=start
+amount
134 startmin
,startsec
,startfrm
=frm2msf(start
)
135 """See if we have the last track to play in cache, else set to last on disc"""
136 if cd
.last
== (0,0,0):
137 if cd
.end
== None: get_header()
138 cd
.last
= get_track_time(cd
.end
)
139 try: play_msf(startmin
, startsec
, startfrm
, cd
.last
[0], cd
.last
[1], cd
.last
[2])
142 def play(first
,last
=None):
143 if last
== None:last
=first
144 i
=get_track_time(first
)
145 cd
.last
= get_track_time(last
+1)
146 play_msf(i
[0],i
[1],i
[2],cd
.last
[0],cd
.last
[1],cd
.last
[2])
148 def play_msf(startmin
, startsec
, startfrm
, endmin
, endsec
, endfrm
):
150 cd
.last
=(endmin
,endsec
,endfrm
)
151 region
= struct
.pack(region_fmt
,startmin
,startsec
,startfrm
,endmin
,endsec
,endfrm
)
152 ioctl(cd
.cd
, CDROMPLAYMSF
, region
)
158 ioctl(cd
.cd
, CDROMSTOP
)
159 # Always unlock door before eject, in case something else locked it
160 ioctl(cd
.cd
, CDROM_LOCKDOOR
, 0)
162 # ioctl(cd.cd, CDROMRESET)
163 ioctl(cd
.cd
, CDROMSTOP
)
164 # Always unlock door before eject, in case something else locked it
165 ioctl(cd
.cd
, CDROM_LOCKDOOR
, 0)
167 ioctl(cd
.cd
, CDROMEJECT_SW
, 0)
168 ioctl(cd
.cd
, CDROMEJECT
)
170 ioctl(cd
.cd
, CDROMEJECT_SW
, 0)
171 ioctl(cd
.cd
, CDROMEJECT
)
177 ioctl(cd
.cd
, CDROMCLOSETRAY
)
183 if debug
: print "Activated:\tCDLow.do_eject"
187 if debug
: print "\tStatus:",trayopen
188 ioctl(cd
.cd
, CDROMEJECT_SW
, 0)
192 if debug
: print "\tStopped"
196 if debug
: print "\tEjected Tray"
202 if debug
: print "\tClosed Tray"
205 if debug
: print "\tClosed CD"
207 if debug
: print "\tOpened CD"
213 return struct
.unpack("BBBB",ioctl(cd
.cd
, CDROMVOLREAD
, struct
.pack("BBBB",0,0,0,0)))
219 def set_volume(frontleft
,frontright
=None,backleft
=0,backright
=0):
221 if frontright
== None: frontright
= frontleft
223 return struct
.unpack("BBBB",ioctl(cd
.cd
, CDROMVOLCTRL
, struct
.pack("BBBB",frontleft
,frontright
,backleft
,backright
)))
229 def get_status(time
=0, all
=0):
233 while result
== 0 and i
< 5:
234 try: # If this fails then it's because it's happening too fast, just retry
235 info
= ioctl(cd
.cd
, CDROMSUBCHNL
, struct
.pack(subchnl_fmt
, CDROM_MSF
,0,0,0,0,0,0,0))
236 drvstatus
= get_drive_status()
241 format
, status
, track
, something
, absaddr
, relmin
, relsec
, relfrm
= struct
.unpack(subchnl_fmt
, info
)
242 absmin
, abssec
, absfrm
= struct
.unpack(addr_fmt
, struct
.pack("i", absaddr
))
243 zero
= (1, 0,0,0, 0,0,0)
244 if status
== CDROM_AUDIO_PLAY
: cd
.status
= PLAYING
245 elif status
== CDROM_AUDIO_PAUSED
: cd
.status
= PAUSED
246 elif status
== CDROM_AUDIO_COMPLETED
:
248 track
,relmin
,relsec
,relfrm
,absmin
,abssec
,absfrm
= zero
249 elif status
== CDROM_AUDIO_ERROR
:
251 track
,relmin
,relsec
,relfrm
,absmin
,abssec
,absfrm
= zero
252 elif status
== CDROM_AUDIO_INVALID
:
254 track
,relmin
,relsec
,relfrm
,absmin
,abssec
,absfrm
= zero
255 elif status
== CDROM_AUDIO_NO_STATUS
:
257 track
,relmin
,relsec
,relfrm
,absmin
,abssec
,absfrm
= zero
258 if drvstatus
== CDS_NO_DISC
:
260 track
,relmin
,relsec
,relfrm
,absmin
,abssec
,absfrm
= zero
261 if all
: return {'status': drvstatus
, 'format': format
, 'status': status
, 'track': track
, 'something': something
, 'absaddr': absaddr
, 'relmin': relmin
, 'relsec': relsec
, 'relfrm': relfrm
}
262 elif time
: return {'cur_t': track
,'rel':(relmin
,relsec
,relfrm
),'abs':(absmin
,abssec
,absfrm
)}
263 else: return cd
.status
267 ret
= ioctl(cd
.cd
, CDROM_DRIVE_STATUS
)
271 def get_drive_status():
274 ret
= ioctl(cd
.cd
, CDROM_DRIVE_STATUS
)
280 tochdr
= struct
.pack(tochdr_fmt
, 0, 0)
281 tochdr
= ioctl(cd
.cd
, CDROMREADTOCHDR
, tochdr
)
282 cd
.start
, cd
.end
= struct
.unpack(tochdr_fmt
, tochdr
)
285 def get_track_time(trk
):
289 elif trk
> cd
.end
: trk
= CDROM_LEADOUT
291 toc
= struct
.pack(tocentry_fmt
, trk
, 0, CDROM_MSF
, 0)
292 toc
= ioctl(cd
.cd
, CDROMREADTOCENTRY
, toc
)
294 track
, adrctrl
, format
, addr
= struct
.unpack(tocentry_fmt
, toc
)
295 m
, s
, f
= struct
.unpack(addr_fmt
, struct
.pack("i", addr
))
298 ctrl
= (adrctrl
& 0xf0) >> 4
300 start
= (m
* CD_SECS
+ s
) * CD_FRAMES
+ f
# 60 and 75
302 if ctrl
& CDROM_DATA_TRACK
:
305 return m
,s
,f
,start
,data
307 def get_track_length(first
,last
=None):
308 if last
==None: last
=first
+1
309 off1min
, off1sec
, off1frm
, off1
, data1
= get_track_time(first
)
310 off2min
, off2sec
, off2frm
, off2
, data2
= get_track_time(last
)
312 lenmin
,lensec
,lenfrm
=frm2msf(off2
-off1
)
314 return off1min
,off1sec
,off1frm
,lenmin
,lensec
,lenfrm
,off1
,data1
321 tracklist
= range(cd
.start
,cd
.end
+1)
322 tracklist
.append(CDROM_LEADOUT
)
324 m
,s
,f
,lenm
,lens
,lenf
,start
,tdata
= get_track_length(i
)
325 offset
.append((m
, s
, f
))
327 if i
<= cd
.end
: # We don't want length of CDROM_LEADOUT ;)
328 length
.append((lenm
, lens
, lenf
))#0)) # 0 is CDAudio's way
329 tracks
= {'start_t':cd
.start
, 'end_t':cd
.end
, 'offset': offset
, 'length': length
, 'id': disc_id(offset
, cd
.start
, cd
.end
), 'data': data
}
330 tracks
['cddb'] = get_cddb_id(tracks
['id'][2:-1], tracks
['id'][-1])
333 def get_current_timing(): # Wrapper for get_status(1)
336 def disc_id(offset
, first
, last
):
339 for i
in range(first
-1, last
):
340 (min, sec
, frame
) = offset
[i
]
341 checksum
= checksum
+ id_sum(min*CD_SECS
+ sec
)
342 track_frames
.append(min*CD_SECS
*CD_FRAMES
+ sec
*CD_FRAMES
+ frame
)
343 (min, sec
, frame
) = offset
[last
]
344 track_frames
.append(min*CD_SECS
*CD_FRAMES
+ sec
*CD_FRAMES
+ frame
)
346 total_time
= (track_frames
[-1] / CD_FRAMES
) - (track_frames
[0] / CD_FRAMES
)
348 discid
= (int((checksum
% 0xff) << 24) | total_time
<< 8 | last
)
350 return [discid
,last
]+track_frames
[:-1]+[track_frames
[-1]/CD_FRAMES
]
360 min = (frm
/ CD_FRAMES
) / CD_SECS
361 sec
= (frm
/ CD_FRAMES
) - (min * CD_SECS
)
362 frm
= frm
- ((min * CD_SECS
) * CD_FRAMES
+ sec
* CD_FRAMES
)
365 def msf2frm(min,sec
,frm
):
366 return min*CD_SECS
*CD_FRAMES
+sec
*CD_FRAMES
+frm
368 def get_cddb_id(tracks
, disclen
):
372 mfs
= (i
/ CD_FRAMES
)
374 sum = sum + (mfs
% 10)
376 checksum
= checksum
+ sum
377 tot_time
= disclen
- tracks
[0] / CD_FRAMES
378 return "%08x" % ((checksum
% 0xff) << 24 | tot_time
<< 8 |
len(tracks
))
381 """ #### Startup code ####