/* vim: set ft=objc ts=4 nowrap: */ /* * AudioCD.m * * Copyright (c) 2002 - 2003 * * Author: Andreas Heppel * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "AudioCD.h" static BOOL exitThread = NO; @interface AudioCD (Private) - (BOOL) allocDiscInfo; - (void) releaseDiscInfo; - (BOOL) checkDeviceForCD: (NSString *) testDevice; - (BOOL) checkAllDevicesForCD: (NSString *)customDevice; - (void) checkDrivesThread: (id)anObject; @end @implementation AudioCD - init { return [self initWithPath: nil andHandler: nil]; } - initWithPath: (NSString *)device { return [self initWithPath: device andHandler: nil]; } - initWithPath: (NSString *)device andHandler: (id)handler { self = [super init]; if (self != nil) { _fd = -1; discInfo = NULL; preferredDevice = [device copy]; [self setHandler: handler]; [self startCheck]; } return self; } - (void) reset { [self releaseDiscInfo]; if (_fd > 0) close(_fd); _fd = cd_init_device((char *)[foundDevice cString]); if(_fd < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; } [self allocDiscInfo]; } - (void) startCheck { exitThread = NO; [NSThread detachNewThreadSelector: @selector(checkDrivesThread:) toTarget: self withObject: preferredDevice]; } - (void) stopCheck { exitThread = YES; } - (void)dealloc { [self stopCheck]; [self releaseDiscInfo]; if (_fd > 0) close(_fd); [foundDevice release]; [preferredDevice release]; [super dealloc]; } - (NSString *)device { return [[foundDevice copy] autorelease]; } - (void)setHandler: (id)handler { _handler = handler; } - (NSMutableDictionary *)readTOC { int i; NSMutableDictionary *toc = [NSMutableDictionary dictionaryWithCapacity: 6]; NSMutableArray *tracks; if (!discInfo && ![self allocDiscInfo]) return nil; tracks = [NSMutableArray arrayWithCapacity: discInfo->disc_total_tracks]; for (i = 0; i < discInfo->disc_total_tracks; i++) { NSMutableDictionary *track = [NSMutableDictionary dictionaryWithCapacity: 5]; [track setObject: [NSString stringWithFormat: _(@"Track%d"), i+1] forKey: @"title"]; [track setObject: [NSString stringWithFormat: @"%d", cd_msf_to_frames(discInfo->disc_track[i].track_length)] forKey: @"length"]; [track setObject: [NSString stringWithFormat: @"%d", cd_msf_to_frames(discInfo->disc_track[i].track_pos)] forKey: @"offset"]; [track setObject: discInfo->disc_track[i].track_type==CDAUDIO_TRACK_AUDIO?@"audio":@"data" forKey: @"type"]; [tracks addObject: track]; } [toc setObject: foundDevice forKey: @"device"]; [toc setObject: _(@"Unknown") forKey: @"artist"]; [toc setObject: _(@"Unknown") forKey: @"title"]; [toc setObject: [NSString stringWithFormat: @"%08X", cddb_direct_discid(*discInfo)] forKey: @"cddbid"]; [toc setObject: [NSString stringWithFormat: @"%d", cd_msf_to_frames(discInfo->disc_length)] forKey: @"discLength"]; [toc setObject: [NSString stringWithFormat: @"%d", discInfo->disc_total_tracks] forKey: @"numberOfTracks"]; [toc setObject: tracks forKey: @"tracks"]; return toc; } - (BOOL) checkForCDWithId: (NSString *)cddbId { NSString *temp; if (!discInfo && ![self allocDiscInfo]) return NO; temp = [NSString stringWithFormat: @"%08X", cddb_direct_discid(*discInfo)]; if ([cddbId isEqual: temp]) { return YES; } return NO; } - (void) playStart: (int)start End: (int)end { if (_fd < 0) return; if (!discInfo && ![self allocDiscInfo]) return; /* * Skip leading/trailing data tracks */ while (discInfo->disc_track[start-1].track_type != CDAUDIO_TRACK_AUDIO) start++; while (discInfo->disc_track[end-1].track_type != CDAUDIO_TRACK_AUDIO) end--; if(cd_play_track(_fd, start, end) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; } } - (void) pause { if (_fd < 0) return; if(cd_pause(_fd) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; } } - (void) resume { if (_fd < 0) return; if(cd_resume(_fd) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; } } - (void) stop { if (_fd < 0) return; if(cd_stop(_fd) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; } } - (void) eject { if (_fd < 0) return; if(cd_eject(_fd) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; } } - (void) close { if (_fd < 0) return; if(cd_close(_fd) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; } } - (BOOL) cdPresent { /* * We must query the drive here and must not use the static * disc inormation. */ struct disc_info info; if (_fd < 0) return NO; if(cd_stat(_fd, &info) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; return NO; } return info.disc_present; } - (int) currentState { struct disc_info info; if (_fd < 0) return -1; if(cd_stat(_fd, &info) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; return -1; } return info.disc_mode; } - (int) currentTrack { struct disc_info info; if (_fd < 0) return -1; if(cd_stat(_fd, &info) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; return -1; } return info.disc_current_track; } - (int) currentMin { struct disc_info info; if (_fd < 0) return 0; if(cd_stat(_fd, &info) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; return -1; } return info.disc_track_time.minutes; } - (int) currentSec { struct disc_info info; if (_fd < 0) return 0; if(cd_stat(_fd, &info) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; return -1; } return info.disc_track_time.seconds; } - (int) firstTrack { if (!discInfo && ![self allocDiscInfo]) return -1; return discInfo->disc_first_track; } - (int) totalTrack { if (!discInfo && ![self allocDiscInfo]) return -1; return discInfo->disc_total_tracks; } - (int) trackLength: (int)track { if (!discInfo && ![self allocDiscInfo]) return -1; return (discInfo->disc_track[track].track_length.minutes * 60) + discInfo->disc_track[track].track_length.seconds; } @end // // private methods // @implementation AudioCD (Private) - (BOOL) allocDiscInfo { if (_fd < 0) return NO; discInfo = (struct disc_info*)malloc(sizeof(struct disc_info)); if(cd_stat(_fd, discInfo) < 0) { [_handler audioCD: self error: errno message: [NSString stringWithCString: strerror(errno)]]; [self releaseDiscInfo]; return NO; } return YES; } - (void) releaseDiscInfo { if (discInfo) { free(discInfo); discInfo = NULL; } } - (BOOL) checkAllDevicesForCD: (NSString *)customDevice { int i, count; char *pos; const char *dev; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSDictionary *domain = [defaults persistentDomainForName: @"AudioCD"]; NSArray *devices = [domain objectForKey: @"Devices"]; // iterate over all known, potential cdrom devices count = [devices count]; for (i = -1; i < count; i++) { /* * Let's check whether the user defined a certain device * to be read from. This will precede the predefined ones. * Nevertheless, we check the predefined ones, if the user * user defined device has no audio cd in it. */ if (i == -1) { if (!customDevice || ![customDevice length]) { continue; } dev = [customDevice cString]; } else { dev = [[devices objectAtIndex: i] cString]; } if(dev && (pos = strchr(dev, '?'))) { char j; /* try first eight of each device */ for(j = 0; j < 4; j++) { BOOL found = NO; char *temp = strdup(dev); /* number, then letter */ temp[pos - dev] = j + 48; found = [self checkDeviceForCD: [NSString stringWithCString: temp]]; if (!found) { temp[pos - dev] = j + 97; found = [self checkDeviceForCD: [NSString stringWithCString: temp]]; } free(temp); if (found) { return YES; } } } else { /* Name. Go for it. */ if ([self checkDeviceForCD: [NSString stringWithCString: dev]]) { return YES; } } } return NO; } - (BOOL) checkDeviceForCD: (NSString *) testDevice { _fd = cd_init_device((char *)[testDevice cString]); // could we open the device? if (_fd < 0) return NO; // stop here, we found a CD foundDevice = [testDevice copy]; return YES; } - (void) checkDrivesThread: (id)anObject { id pool; NSString *checkDevice; BOOL present = NO; pool = [NSAutoreleasePool new]; checkDevice = [[(NSString *)anObject copy] autorelease]; do { if(_fd < 0) { [self checkAllDevicesForCD: checkDevice]; } // is a disc present in the drive? if([self cdPresent] != present) { present = ((present == YES) ? NO : YES); if(!present) { close(_fd); _fd = -1; } // release the old disc information [self releaseDiscInfo]; // send a message to owner [_handler audioCDChanged: self]; } // wait a second for the next check [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.]]; } while (!exitThread); RELEASE(pool); } @end