AudioCDSample/AudioCDSample.c
/* |
File: AudioCDSample.c |
Abstract: Command-line tool demonstrating how to discover audio CDs and |
access the TOC information presented by the CD-DA filesystem. |
Version: 1.5 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2011 Apple Inc. All Rights Reserved. |
*/ |
#include <CoreFoundation/CoreFoundation.h> |
#include <Carbon/Carbon.h> |
#include <IOKit/storage/IOCDTypes.h> |
#include <string.h> |
// Constants |
#define kAudioCDFilesystemID (UInt16)(('J' << 8) | 'H' ) // 'JH'; this avoids compiler warning |
// XML property list keys. These constants are not defined in any public header file. |
#define kRawTOCDataString "Format 0x02 TOC Data" |
#define kSessionsString "Sessions" |
#define kSessionTypeString "Session Type" |
#define kTrackArrayString "Track Array" |
#define kFirstTrackInSessionString "First Track" |
#define kLastTrackInSessionString "Last Track" |
#define kLeadoutBlockString "Leadout Block" |
#define kDataKeyString "Data" |
#define kPointKeyString "Point" |
#define kSessionNumberKeyString "Session Number" |
#define kStartBlockKeyString "Start Block" |
// Disc/session types |
#define kDiscTypeCDDA_CDROM 0x00 |
#define kDiscTypeCD_I 0x10 |
#define kDiscTypeCDROM_XA 0x20 |
void PrintTOCData(const FSRef *theFSRef); |
void PrintTOCData(const FSRef *theFSRef) |
{ |
HFSUniStr255 dataForkName; |
OSErr result; |
FSIORefNum forkRefNum; |
SInt64 forkSize; |
UInt8 *forkData; |
ByteCount actualRead; |
CFDataRef dataRef = 0; |
CFPropertyListRef propertyListRef = 0; |
// Open the hidden TOC data plist and read the data from it. |
result = FSGetDataForkName(&dataForkName); |
if (result != noErr) { |
fprintf(stderr, "FSGetDataForkName() returned %d\n", result); |
return; |
} |
result = FSOpenFork(theFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum); |
if (result != noErr) { |
fprintf(stderr, "FSOpenFork() returned %d\n", result); |
return; |
} |
// How large is the plist data? |
result = FSGetForkSize(forkRefNum, &forkSize); |
if (result != noErr) { |
fprintf(stderr, "FSGetForkSize() returned %d\n", result); |
return; |
} |
// Allocate memory for the plist data. |
forkData = malloc(forkSize); |
if (forkData == NULL) { |
fprintf(stderr, "malloc() returned NULL while allocating memory for plist data\n"); |
return; |
} |
result = FSReadFork(forkRefNum, fsFromStart, 0, forkSize, forkData, &actualRead); |
if (result != noErr) { |
fprintf(stderr, "FSReadFork() returned %d\n", result); |
free(forkData); |
FSCloseFork(forkRefNum); |
return; |
} |
// Done with the plist file. |
FSCloseFork(forkRefNum); |
// Turn the raw plist data into a CFData object. |
dataRef = CFDataCreate(kCFAllocatorDefault, forkData, forkSize); |
if (dataRef != 0) { |
CFStringRef errorString; |
// Now turn the CFData into a CFPropertyList that we can parse using other CoreFoundation calls. |
propertyListRef = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, |
dataRef, |
kCFPropertyListImmutable, |
&errorString); |
// Ignore the error string if we get one back. |
if (errorString != 0) { |
CFRelease(errorString); |
} |
// Now done with the CFData. |
CFRelease(dataRef); |
} |
if (propertyListRef != 0) { |
// Now we have the property list in memory. Parse it. |
// First, make sure the root item is a CFDictionary. If not, release and bail. |
if (CFGetTypeID(propertyListRef) == CFDictionaryGetTypeID()) { |
CFDataRef theRawTOCDataRef = 0; |
CFArrayRef theSessionArrayRef = 0; |
CFIndex numSessions = 0; |
UInt32 index = 0; |
// This is how we can get the raw TOC data. You can use the routines in <IOKit/storage/IOCDTypes.h> |
// to access the contents of the raw TOC data. |
theRawTOCDataRef = (CFDataRef) CFDictionaryGetValue(propertyListRef, CFSTR(kRawTOCDataString)); |
UInt32 descriptorCount = CDTOCGetDescriptorCount((CDTOC *)CFDataGetBytePtr(theRawTOCDataRef)); |
printf("Descriptor count = %u\n\n", descriptorCount); |
// Get the session array info. |
theSessionArrayRef = (CFArrayRef) CFDictionaryGetValue(propertyListRef, CFSTR(kSessionsString)); |
// Find out how many sessions there are. |
numSessions = CFArrayGetCount(theSessionArrayRef); |
printf("Number of sessions = %u\n\n", (UInt32) numSessions); |
// Iterate across the sessions on the disc. |
for (index = 0; index < numSessions; index++) { |
CFDictionaryRef theSessionDict = 0; |
CFNumberRef firstTrackNumber = 0; |
CFNumberRef lastTrackNumber = 0; |
CFNumberRef leadoutBlock = 0; |
CFNumberRef sessionNumber = 0; |
CFNumberRef sessionType = 0; |
CFArrayRef trackArray = 0; |
CFIndex numTracks = 0; |
UInt32 trackIndex = 0; |
UInt32 value = 0; |
theSessionDict = (CFDictionaryRef) CFArrayGetValueAtIndex(theSessionArrayRef, index); |
firstTrackNumber = (CFNumberRef) CFDictionaryGetValue(theSessionDict, CFSTR(kFirstTrackInSessionString)); |
lastTrackNumber = (CFNumberRef) CFDictionaryGetValue(theSessionDict, CFSTR(kLastTrackInSessionString)); |
leadoutBlock = (CFNumberRef) CFDictionaryGetValue(theSessionDict, CFSTR(kLeadoutBlockString)); |
sessionType = (CFNumberRef) CFDictionaryGetValue(theSessionDict, CFSTR(kSessionTypeString)); |
sessionNumber = (CFNumberRef) CFDictionaryGetValue(theSessionDict, CFSTR(kSessionNumberKeyString)); |
if (CFNumberGetValue(sessionNumber, kCFNumberSInt32Type, &value)) { |
printf("-----------------------------------\n"); |
printf("Session Number %u\n", value); |
printf("-----------------------------------\n"); |
} |
if (CFNumberGetValue(firstTrackNumber, kCFNumberSInt32Type, &value)) { |
printf("First Track in session = %u\n", value); |
} |
if (CFNumberGetValue(lastTrackNumber, kCFNumberSInt32Type, &value)) { |
printf("Last Track in session = %u\n", value); |
} |
if (CFNumberGetValue(leadoutBlock, kCFNumberSInt32Type, &value)) { |
printf("Leadout block for session = %u\n", value); |
} |
if (CFNumberGetValue(sessionType, kCFNumberSInt32Type, &value)) { |
switch (value) { |
case kDiscTypeCDDA_CDROM: |
printf("Session Type is CD-DA or CD-ROM with first track in Mode 1\n"); |
break; |
case kDiscTypeCD_I: |
printf("Session Type is CD-I\n"); |
break; |
case kDiscTypeCDROM_XA: |
printf ("Session Type is CD-ROM XA with first track in Mode 2\n"); |
break; |
default: |
break; |
} |
} |
trackArray = (CFArrayRef) CFDictionaryGetValue(theSessionDict, CFSTR(kTrackArrayString)); |
numTracks = CFArrayGetCount(trackArray); |
printf ( "\n" ); |
// Now iterate across the tracks in this session. |
for (trackIndex = 0; trackIndex < numTracks; trackIndex++) { |
CFDictionaryRef theTrackDict = 0; |
CFNumberRef trackNumber = 0; |
CFNumberRef startBlock = 0; |
CFBooleanRef isDataTrack = kCFBooleanFalse; |
theTrackDict = (CFDictionaryRef) CFArrayGetValueAtIndex(trackArray, trackIndex); |
trackNumber = (CFNumberRef) CFDictionaryGetValue(theTrackDict, CFSTR(kPointKeyString)); |
sessionNumber = (CFNumberRef) CFDictionaryGetValue(theTrackDict, CFSTR(kSessionNumberKeyString)); |
startBlock = (CFNumberRef) CFDictionaryGetValue(theTrackDict, CFSTR(kStartBlockKeyString)); |
isDataTrack = (CFBooleanRef) CFDictionaryGetValue(theTrackDict, CFSTR(kDataKeyString)); |
if (CFNumberGetValue(trackNumber, kCFNumberSInt32Type, &value)) { |
printf("Track Number = %u\n", value); |
} |
if (CFNumberGetValue(sessionNumber, kCFNumberSInt32Type, &value)) { |
printf("It resides in session %u\n", value); |
} |
if (CFNumberGetValue(startBlock, kCFNumberSInt32Type, &value)) { |
printf("Track starts at block = %u\n", value); |
} |
if (isDataTrack == kCFBooleanTrue) { |
printf("This is a DATA track\n"); |
} |
else { |
printf("This is an AUDIO track\n"); |
} |
printf("\n"); |
} |
} |
} |
// Now done with the property list. |
CFRelease(propertyListRef); |
} |
} |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
// main |
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ |
int main(int argc, char* argv[]) |
{ |
OSErr result = noErr; |
ItemCount volumeIndex; |
// Enumerate all of the mounted volumes on the system looking for audio CDs. |
for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++) { |
FSVolumeRefNum actualVolume; |
HFSUniStr255 volumeName; |
FSVolumeInfo volumeInfo; |
FSRef rootDirectory; |
bzero((void *) &volumeInfo, sizeof(volumeInfo)); |
// Get the volume info, which includes the filesystem ID. |
result = FSGetVolumeInfo(kFSInvalidVolumeRefNum, |
volumeIndex, |
&actualVolume, |
kFSVolInfoFSInfo, |
&volumeInfo, |
&volumeName, |
&rootDirectory); |
if (result == noErr) { |
// Work around a bug in Mac OS X 10.0.x where the filesystem ID and signature bytes were |
// erroneously swapped. This was fixed in Mac OS X 10.1 (r. 2653443), broken again in Jaguar (r. 3015107), |
// and finally fixed in 10.2.3. |
// |
// This is the same workaround used by iTunes, so if iTunes thinks a disc is an audio CD, |
// so should this sample. |
if (volumeInfo.signature == kAudioCDFilesystemID || |
volumeInfo.filesystemID == kAudioCDFilesystemID) { |
// It's an audio CD. |
FSRef tocPlistFSRef; |
OSStatus status; |
pid_t dissenter; |
// The CD-DA (Digital Audio) filesystem makes every track appear as a separate AIFF audio file. |
// These files can be played using QuickTime or Core Audio. |
// The filesystem also makes the table of contents (TOC) appear as a hidden XML property list file |
// named ".TOC.plist". The plist file contains both the raw and parsed TOC data. |
// Create an FSRef referring to the TOC plist file. |
const char plistName[] = ".TOC.plist"; |
CFURLRef rootURL = CFURLCreateFromFSRef(kCFAllocatorDefault, &rootDirectory); |
CFURLRef plistURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(kCFAllocatorDefault, |
(const UInt8 *)plistName, |
strlen(plistName), |
false, |
rootURL); |
if (CFURLGetFSRef(plistURL, &tocPlistFSRef)) { |
PrintTOCData(&tocPlistFSRef); |
} |
else { |
printf("CFURLGetFSRef() returned false\n"); |
} |
CFRelease(plistURL); |
CFRelease(rootURL); |
// Eject the disc. |
status = FSEjectVolumeSync(actualVolume, kNilOptions, &dissenter); |
if (status != noErr) { |
printf("FSEjectVolumeSync returned %d\n", status); |
} |
} |
} |
} |
return 0; |
} |
Copyright © 2011 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2011-07-26