I am developing a media player application, and wish to implement Audio CD handling and playback. I have a method that will theoretically work, however I would like to ensure that this method is the most efficient, since I can’t find particularly clear information on the subject.
Are there any better ways (for example, a system notification of Audio CD insertion)? Also, any other suggestions related to working with Audio General/CDs is welcomed (even to the extent of CDDB/General/FreeDB functionality).
CDDB access costs money unless you’re releasing a freeware app; General/FreeDB unfortunately has shut down.
If your app is for 10.4 and up, you can use the General/DiskArbitration framework. To do this, you need to create a General/DASession that you will use as your context for all calls to the framework.
To do this, and to register for mount and unmount notifications:
// Only request mount/unmount information for audio General/CDs General/NSDictionary *match = General/[NSDictionary dictionaryWithObjects:General/[NSArray arrayWithObjects:@”General/IOCDMedia”, General/[NSNumber numberWithBool:YES], nil] forKeys:General/[NSArray arrayWithObjects:(General/NSString *)kDADiskDescriptionMediaKindKey, kDADiskDescriptionMediaWholeKey, nil]];
_session = General/DASessionCreate(kCFAllocatorDefault); General/DASessionScheduleWithRunLoop(_session, General/CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); General/DARegisterDiskAppearedCallback(_session, (General/CFDictionaryRef)match, diskAppearedCallback, NULL); General/DARegisterDiskDisappearedCallback(_session, (General/CFDictionaryRef)match, diskDisappearedCallback, NULL);
Then, when a disc is mounted (or your app is first launched), diskApperaredCallback will be called.
In my code I have a singleton object that handles the mount/unmount notifications, so I don’t use the context above. It would be normal practice to pass self. When your callback is invoked, you can use General/DADiskGetBSDName() to get the device name, and then use the DKIOCD* ioctls for accessing the disc’s TOC and audio data (and subchannel data if desired). For example, the following code is the kind of thing you could do to open a CD and read the TOC. I omitted any error checking for brevity.
const char *bsdName = General/DADiskGetBSDName(disk); // disk comes from the diskAppearedCallback int fd = opendev(bsdName, O_RDONLY | O_NONBLOCK, 0, NULL);
dk_cd_read_toc_t cd_read_toc; uint8_t buffer ;
bzero(&cd_read_toc, sizeof(cd_read_toc)); bzero(buffer, sizeof(buffer));
cd_read_toc.format = kCDTOCFormatTOC; cd_read_toc.buffer = buffer; cd_read_toc.bufferLength = sizeof(buffer);
int result = ioctl(fd, DKIOCCDREADTOC, &cd_read_toc);
// Read the descriptors close(fd);
Finally, when you’re finished, clean up:
General/DAUnregisterCallback(_session, diskAppearedCallback, NULL); General/DAUnregisterCallback(_session, diskDisappearedCallback, NULL); General/DASessionUnscheduleFromRunLoop(_session, General/CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); General/CFRelease(_session);