Click here to Skip to main content
Click here to Skip to main content

Tutorial on reading Audio-CDs

By , 9 Oct 2006
 

Introduction

I really got mad trying to find tutorials about audio-CDs. I found a few with some documentation about the structure of an audio-CD, but nothing that I could use for my "Save-Down-Audio-Tracks-To-File"-class. So, as you see, at last I got it... On my way up to my code I basically relied on two articles. One of them, written by Idael Cardoso and published here at CodeProject, is unfortunately written in C# and the technique of audio-CD-ripping isn't explained at all, as well as the code is not commented that well... The other article, in fact a series of articles by Larry Osterman, didn't make it that easy to rebuild code. So I wrote my class with the basic functionality (and some of it stolen from Idael and Larry). Regarding the fact that I did not find any reliable C++ source about ripping audio-CDs for hours of searching, I decided to publish my class with some explaining around.

Audio Formats

In the whole article I talk about uncompressed wave-audio in the PCM-Format. So don't ask me anything about MP3s or the like. Wave-data has some attributes. The attributes which decide about a wave-data's appearance are:

  • Bits per sample:The bits per sample determine the accuracy and bandwidth of frequencies which a sound contains. Usual values are 8 bit and 16 bit. As you may suppose, a single 16 bit-frequency needs a 16-bit-word-variable to be contained.
  • Count of Channels: Specifies the count of channels the wave-data uses. Usual values are 1 (mono) and 2 (stereo). Wave-data in stereo needs twice the size as mono-data. The frequencies in stereo-waves are stored in blocks like [Frequ Left Channel] [Frequ Right Channel] [Frequ Left Channel] ...
  • Sampling-Rate: A single block contains the frequency-data of all channels for one single moment. So a block of a wave-sample using stereo 16 bit, would be 4 bytes of size (ChannelCount*(BitsPerSample/8)). The sampling-rate determines how many blocks are used to display 1 second. Usual values within waves are 44100Hz, 22050Hz, 11025Hz and 8000Hz.

With these three attributes given (bits/second, channels, sampling-rate) you are able to compute the needed size for wave-audio. As an example, the audio-CD format always is 44100Hz, 16 bit, stereo. For one second of audio 44100*2*2 bytes are required. If you need 176400 byte for one second, you'll need 846720000 bytes (807MB) for 80 minutes, which is the maximum size of data fitting on an audio-CD.

So it's no wonder the MP3-format became such popular! An audio-sample with CD-quality which is 4 minutes long needs about 40 MB on your hard disk. The MP3-file takes about 4 MB with almost equivalent quality!
BTW: The expression "frequeny" is not the real meaning of the audio-data. It represents something close to it, but I think it's OK to imagine it in that way.

About audio-CDs

The data stored on CDs is determined in sectors. A "normal" CD-sector takes 2048 bytes (2KB) of size. Something special about audio-CDs is, that their audio-data is stored in sectors of 2352 bytes of size. That is because one sector should store 1/75 of one second of audio-data. One second needs 176400 bytes, so 1/75 needs 2352 bytes.

Each audio-CD contains a table of contents (TOC). It holds information about the track-count and the address of every track on CD. Usually Windows loads the TOC when you insert the CD and is updated on CD-change. You will retrieve the TOC by a single call to the CD-ROM drive. Because Windows holds the TOC, the CD-drive is not spinned up to get the data, you'll get it directly from your OS. Here you see it's structure:

typedef struct _TRACK_DATA
{
    UCHAR Reserved;
    UCHAR Control : 4;
    UCHAR Adr : 4;
    UCHAR TrackNumber;
    UCHAR Reserved1;
    UCHAR Address[4];
} TRACK_DATA;

typedef struct _CDROM_TOC
{
    UCHAR Length[2];
    UCHAR FirstTrack;
    UCHAR LastTrack;
    TRACK_DATA TrackData[100];
} CDROM_TOC;

The CDROM_TOC-structure contains the FirstTrack (1) and the LastTrack (max. track nr). CDROM_TOC::TrackData[0] contains info of the first track on the CD.
Each track has an address. It represents the track's play-time using individual members for the hour, minute, second and frame. The "frame"-value (Address[3]) is given in 1/75-parts of a second -> Remember: 75 frames form one second and one frame occupies one sector.
To specify the size in sectors of the wave-track, use the following function:

ULONG AddressToSectors( UCHAR Addr[4] )
{
    ULONG Sectors = Addr[1]*75*60 + Addr[2]*75 + Addr[3];
    return Sectors - 150;
}

As you may have noticed, the hours-value of the address is not used. I can't see any sense in it, but if a CD-track exceeds 60 minutes, the hours-value stays unused and the minutes exceed the 60-mark. A value of 150 is subtracted because, as I said, the first accessible address is 2 seconds (150 frames) behind the CD-start.

To read out the track-data we need to have the address and the length of a track, both in sectors.

For my class I chose quite a tiny structure to hold the track-info.

struct CDTRACK
{
    ULONG Address;
    ULONG Length;
};

To calculate the address, just pass the TRACK_DATA::Address-value to AddressToSectors. To calculate the length, subtract the sector's address from the next track's sector-address.

CDROM_TOC Toc;
CDTRACK SmallData;

SmallData.Address = AddressToSectors( Toc.TrackData[x].Address );
SmallData.Length = AddressToSectors( Toc.TrackData[x+1].Address )
                   - SmallData.Address;

Accessing the disc-drive

Once you know, the access to the disc-drive is really simple. You create a handle using CreateFile, communicate with the CD-drive using DeviceIoControl and close that handle via CloseHandle.

Using "CreateFile"

Many of you will know the usage of CreateFile, so here's just a short line of code on how to create the handle.

char Fn[8] = { '\\', '\\', '.', '\\', Drive, ':', '\0' };
HANDLE hCD = CreateFile( Fn, GENERIC_READ, FILE_SHARE_READ,
                         NULL, OPEN_EXISTING, 0, NULL );

Note the path-parameter for CreateFile. It must have the form \\.\F: (in case F is your CD-drive).

Using "DeviceIoControl"

Be aware: DeviceIoControl works well with Win2000/XP. To use it with Win95/98/Me click here.

BOOL DeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped);

hDevice takes our handle to the CD. dwIoControlCode gets one of the IOCTL_... messages and the next parameters specify the input, output and their size. Additionally, there's a dummy-parameter (lpBytesReturned) to which we will always pass some ULONG. We won't use the lpOverlapped-param, so set it to NULL.
There are several IOCTL-messages we are interested in:

  • IOCTL_CDROM_READ_TOC: Reads out the TOC as described above. Set both input-parameters to 0, output-parameters to CDROM_TOC* and sizeof(CDROM_TOC)
  • IOCTL_CDROM_RAW_READ: Reads raw data from the CD-drive. You have to pass a RAW_READ_INFO* and sizeof(RAW_READ_INFO) as input and a valid buffer-pointer with the bytes it can contain as output.
    At this point my code failed for a long time. In RAW_READ_INFO you specify from which sectors and how many sectors you want to read. Just specifying the song's sector-data (e.g. address=4492, length=16110 sectors) in RAW_READ_INFO, the call to DeviceIoControl will fail with GetLastError set to 87, ERROR_INVALID_PARAMETER.
    There is a maximum of sectors to be read at once! I did not find the maximum-number and perhaps it's drive-dependant. But be sure a value <= 1000 should work and a value around 20 is really safe.

Here an example, how to use IOCTL_CDROM_RAW_READ. Regarding the fact that we are not allowed to read a wave-track at once, we need to read it out piece for piece. It's an excerpt from the code:

CDTRACK Track; // Filled with valid info
char* pBuf = new char [Track.Length*2352];

RAW_READ_INFO ReadInfo;
ReadInfo.TrackMode = CDDA; // Always use CDDA (numerical: 2)
ReadInfo.SectorCount = 20; // We'll read 20 sectors with each operation.

// Read the track-data in a loop. Read 20*2352 bytes per pass.
for ( ULONG i=0; i<Track.Length/20; i++ )
{
    // Calculate the new offset from where to read.
    ReadInfo.DiskOffset.QuadPart = (Track.Address + i*20) * 2048;

    // Call DeviceIoControl and read the audio-data to out buffer.
    ULONG Dummy;
    if ( 0 == DeviceIoControl( hCD, IOCTL_CDROM_RAW_READ,
                               &ReadInfo, sizeof(ReadInfo),
                               pBuf+i*20*2352,
                               20*2352,
                               &Dummy, NULL ) )
    {
        delete [] pBuf;
        return FALSE;
    }
}

// Read the remaining sectors.
ReadInfo.SectorCount = Track.Length % 20;
ReadInfo.DiskOffset.QuadPart = (Track.Address + i*20) * 2048;
ULONG Dummy;
if ( 0 == DeviceIoControl( hCD, IOCTL_CDROM_RAW_READ,
                           &ReadInfo, sizeof(ReadInfo),
                           pBuf+i*20*2352,
                           ReadInfo.SectorCount*2352,
                           &Dummy, NULL ) )
{
    delete [] pBuf;
    return FALSE;
}

delete [] pBuf;

Looks quite simple, huh? The only thing I tumble over is the number 2048. This is the only thing about the whole CD-ripping that I really do not understand! It would make sense if you'd replace the number 2048 with 2352! It's pretty weird... But that's the only way it works and was a huge hurdle (if you do not have great tutorials).

The rest of the code should be self-explanatory. At first, the data is read in a loop, 20 sectors (20*2352 bytes) per pass. Then the remaining sectors are read. During the read-process, the correct cd-offset and buf-offset are calculated and the audio-data is stored to that computed buffer-offset.

Additional, there are some more IOCTL_...-messages of interest:

  • IOCTL_STORAGE_CHECK_VERIFY: Checks whether your CD-drive is accessible
  • IOCTL_STORAGE_LOAD_MEDIA: Injects the CD-drive if opened
  • IOCTL_CDROM_EJECT_MEDIA: Ejects the CD-drive
  • IOCTL_CDROM_GET_CONFIGURATION: Retrieves the type of disk (CD-ROM/CD-R/CD-RW/DVD-ROM/DVD-R/...)
  • IOCTL_CDROM_PLAY_AUDIO_MSF, IOCTL_CDROM_PAUSE_AUDIO, IOCTL_CDROM_RESUME_AUDIO, IOCTL_CDROM_STOP_AUDIO: Plays audio data. Pretty simple to control!

Using the code

The class CAudioCD was written to extract audio-tracks from a CD onto your hard-disc. That's the reason why the class is not able to do much more than that. It is able to:

  • Get some info about the count of tracks and each track's play length
  • Read tracks into memory
  • Read tracks from CD directly to a wave-file
  • Do the reading in an extra-thread
  • Inform you about the current progress (callback)
  • Inject & eject the CD, my favourite :D

Using the code should be really simple. The main class is CAudioCD. Here's an example on how to use the class:

#include "CAudioCD.h"
#include <stdio.h>

#define MY_CDROM_DRIVE 'F'

void OnAudioCDProgress( ULONG Track, ULONG Percentage, VOID* Param )
{
    printf( "Ripping track nr. %i\n", Track );
    printf( ": Progress at %i%%\n", Percentage );
}

int main( ... )
{
    CAudioCD AudioCD;
    if ( ! AudioCD.Open( MY_CDROM_DRIVE ) )
    {
        printf( "Cannot open cd-drive!\n" );
        return 0;
    }

    ULONG TrackCount = AudioCD.GetTrackCount();
    printf( "Track-Count: %i\n", TrackCount );

    for ( ULONG i=0; i<TrackCount; i++ )
    {
        ULONG Time = AudioCD.GetTrackTime( i );
        printf( "Track %i: %i:%.2i;  %i bytes of size\n", i+1,
                Time/60, Time%60, AudioCD.GetTrackSize(i) );
    }

    // Prepare param for reading...
    AUDIOCD_READTRACK ReadInfo;
    ReadInfo.Track = 7;
    ReadInfo.SaveToFile = "C:\\Song.wav" );
    ReadInfo.ProgressCb = OnAudioCDProgress;

    if ( ! AudioCD.ReadTrack( &ReadInfo ) )
        printf( "Cannot start reading track: %i\n", GetLastError() );

    return 0;
}

I won't explain anything about this code, read it and you will understand.

Finally...

I hope the article was interesting for you and I hope you'll forgive my bad English, I'm a native German and did write my last English essay in school 2 years ago. So, contact me for any grammatical or spelling mistakes or if I wrote something totally wrong about some audio-stuff.

Greetings, Michel

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Michel Helms
Germany Germany
Member
MichelHelms@Web.de

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow to fast reverse the song or any media in c#.net.memberAkash khurmi2 May '12 - 0:55 
FastReverse() method is not working at all. Is there any other option for this?
GeneralPossible cause of maximum read sizememberPascalos27 Mar '08 - 5:41 
Great article!
 
Just yesterday I wanted to copy an audio CD, again, and started wonderering why I couldn't make a simple image file. I started googling and was pretty amazed that it is just impossible (for causes like "jitter"/positioning, gaps, limited API etc). I think you cannot read a TOC or gap the same way as an audio track, so I guess you cannot read the whole disc in one time (DAO).
 
I have two comments/suggestions on the article, which are in fact assumptions (so please be aware Smile | :) )
 
1. I think the reason that there are many different CD readers and none of them is *perfect* is that you cannot read a track at once, and most of the CD-ROM drives cannot position the head where the last read was. So some bytes will be missing or double. This routine will work if the driver itself does the so called jitter-correction (I know it is not really Jitter, but it's just called that way). If the driver doesn't, the sound will be poor (though I still have to try this code out).
 
2. One article I read said something about the maximum number of data you can read at once. (http://xiph.org/paranoia/faq.html#analog[^]) So maybe it is because of DMA that the number is limited.
 
Thanks for the article, I'll try it out soon!!
 
Chau chau,
 
Pascalos

GeneralSome questionsmemberchocm18 Jan '08 - 0:09 
Thank you for the article!
I have some questions after reading it and the source code.
First:
In the console example, there is the call to ReadTrack() as below,
if ( ! AudioCD.ReadTrack( &ReadInfo ) )
but the member function of the CAudio is declared as
BOOL CAudioCD::ReadTrack( ULONG TrackNr, CBuf<char>* pBuf )
 
parameters are not the same.
 
Second:
The class CAudioCD can do the reading in an extra-thread. but I find no extra thread created in the CAudioCD.
 
Third:
About AUDIOCD_READTRACK, where is the definition for this structure?
 
Thanks for help!
GeneralCD graphicsmemberSreeniTheGinie11 Nov '07 - 17:40 
Hi how about reading the cd graphics?
GeneralGreat stuffmemberPaul Sanders (AlpineSoft)26 Oct '07 - 10:44 
Nice article - useful information and easy to follow. Making a CD ripper is not as hard as I thought Smile | :)

 

Generalcompiling the codememberminostro16 Sep '07 - 20:04 
Hi,
 
first of all, what a great help is this tutorial, congratulations. I want to compile the code using cygwin. I'm using your "example file" but it does not compile. I included in the command line all the cpp that are in the package, but it gives some troubles:
 

$ g++ SimpleAudioCD.cpp CAudioCD.cpp AudioCD_Helpers.cpp
CAudioCD.cpp: In member function `BOOL CAudioCD::LockCD()':
CAudioCD.cpp:212: error: `PREVENT_MEDIA_REMOVAL' undeclared (first use this function)
CAudioCD.cpp:212: error: (Each undeclared identifier is reported only once for each function it appears in.)
CAudioCD.cpp:212: error: expected `;' before "pmr"
CAudioCD.cpp:213: error: `IOCTL_STORAGE_MEDIA_REMOVAL' undeclared (first use this function)
CAudioCD.cpp:213: error: `pmr' undeclared (first use this function)
CAudioCD.cpp: In member function `BOOL CAudioCD::UnlockCD()':
CAudioCD.cpp:222: error: `PREVENT_MEDIA_REMOVAL' undeclared (first use this function)
CAudioCD.cpp:222: error: expected `;' before "pmr"
CAudioCD.cpp:223: error: `IOCTL_STORAGE_MEDIA_REMOVAL' undeclared (first use this function)
CAudioCD.cpp:223: error: `pmr' undeclared (first use this function)
CAudioCD.cpp: In member function `BOOL CAudioCD::InjectCD()':
CAudioCD.cpp:235: error: `IOCTL_STORAGE_LOAD_MEDIA' undeclared (first use this function)
CAudioCD.cpp: In member function `BOOL CAudioCD::IsCDReady(char)':
CAudioCD.cpp:258: error: `IOCTL_STORAGE_CHECK_VERIFY2' undeclared (first use this function)
CAudioCD.cpp: In member function `BOOL CAudioCD::EjectCD()':
CAudioCD.cpp:272: error: `IOCTL_STORAGE_EJECT_MEDIA' undeclared (first use this function)
 
is any library that I'm missing?. Sorry if the question is too basic, but I'm new in this.
 
Thank you!!
 
minostro
GeneralRe: compiling the codememberMichel Helms17 Sep '07 - 7:23 
Oh, these are declared in the Windows Driver Development Kit (WinDDK). But take a look a few lines below to the last answer to "CD-Text" (I think this link gets it).
You'll have to include those lines as a header-file, then it should work Smile | :)
GeneralRe: compiling the codememberminostro18 Sep '07 - 3:57 
But in the code that you're referring there is no definition for the undeclared values. Anyway I put the definitions as a header file, but as I was expecting it didn't work. Any other idea?.
 
thank you in advance Big Grin | :-D
GeneralRe: compiling the codememberMichel Helms21 Sep '07 - 12:30 
Oh, I just had a look at the code, you won't need those definitions, they are already put into "AudioCD_Helpers.h" header file.
And I'm sorry but I can't help you without having a bit more information about your errors! What Compiler do you have? What compile-options do you use?
 
After trying out with MSVC++ 2005 Express I suppose, if you do use this one too, that you'll have to set char-encoding to from default unicode to multi-byte (in project's preferences->general) and in project preferences->C++->Language->for-Loop to "no". Or alternatively, set compiler-options '/D "_MBCS"' and '/Zc:forScope-'.
GeneralRe: compiling the codememberDNeptune17 Dec '07 - 10:52 
I have the same problem compiling the code, because some of the constants like "PREVENT_MEDIA_REMOVAL" are not defined anywhere (even in the AudioCD_Helper file. Sigh | :sigh:
GeneralRe: compiling the codememberMichel Helms19 Dec '07 - 1:42 
If you don't have the Windows Platform-SDK (PSDK), you should download that one at first: http://www.microsoft.com/downloads/details.aspx?familyid=E15438AC-60BE-41BD-AA14-7F1E0F19CA0D&displaylang=en[^]
If it's already installed or after you did it, include the header-file "Winioctl.h"
GeneralRe: compiling the codememberDNeptune21 Dec '07 - 10:42 
Thanks so much! Now I have it compiling perfectly. Great code. Now I'm trying to retreive the 'Artist" and 'Song' information. Big Grin | :-D
Questioncan u guide me i am new for mixer controlmemberrajneshmalik30 Aug '07 - 2:16 
hi
i want to play two sound file same time and want to listen the sound of these file
on separtly on left and right speaker,means sound of one file should play on left
speaker and second file should play on right speaker
 
thank u in advance
malik
 
malik
AnswerRe: can u guide me i am new for mixer controlmemberMichel Helms31 Aug '07 - 13:15 
uh, that's a hard one.. I can give it a try but know I'm not a good one in that sound-mixing-stuff Wink | ;)
 
So let's start:
Let's suppose we have an audio sample (wav) of cd quality, means 44 kHz 16 bit stereo. The data is stored from left to right, alternating two bytes (=word) for the left speaker and two bytes for the right one.
You'll need them to be mono, means you have to mix those which is a quite hard thing to do. As I heard, there are several ways to do so:
- First, you could add the the left-speaker-word to the right-speaker-word and halfen the result. This gives you a sound-quality which is really not good but kind of easy to achive.
- Second, add both and cut the result at 0xFFFF. Not perfect but still better than the first one and definetly not harder to implement. If you have signed words you will have to clip your data between -32768 and +32767; I currently don't know if cd's samples are signed or unsigned so you'll have to give it a try Poke tongue | ;-P
- Third, look which of the channels is louder (gets higher frequency), add the channels and clip the result at that value.
- Fourth, ask google...
 
When you mixed down both wave-samples to mono, store the data alternating again to the destination-buffer. The whole thing shouldn't get too complex and will work on the fly (at least I hope so)!
 
An example of three samples with technique nr 2, adding and clipping the data:
 

Wave-Samples nr 1 (stereo, values are words, channelwise):
[left] [right]  |  [left] [right]  |  [left] [right] 
19872 3828  |  00000   1287  |  58723 13211
 
Wave Samples nr 1, mixed to mono:
     23700      |       1287       |       65536
 
Wave-Samples nr 2 (stereo, values are words, channelwise):
[left] [right]  |  [left] [right]  |  [left] [right]
 8282  12889  |  50123 3023   |    536  3287
 
Wave Samples nr 2, mixed to mono:
    21171       |       53146      |       3823
 

The resulting data, wave nr 1 on the left channel, wave nr 2 on the right one:
[left] [right]  |  [left] [right]  |  [left] [right] 
 23700   21171  |  1287 53146  |   65536  3823
 

I hope I did no mistakes..;)
QuestionWhere's the "good" audio data?memberdzeta200628 May '07 - 5:27 
A simple question, related to the 2048/2352 mismatch.
It's clear that the difference between 2352 and 2048 bytes per sector is related to the presence of error control codes and other data in the CD structure. But when you read a sector and get 2352 bytes ... how do you
separate the "good" samples (the music, 2048 bytes) from them? Are they at the beginning?
From your code I assume, seeing that you can write the data as a wave file, that ALL the 2352 bytes are written in the file. I guess that the mmio functions used to read the wave files (by the player programs) are able to separate the good bytes .... but how can we do that in memory?
What's the usage of the data that you read in memory with the ReadTrack function? Did you attempt any usaful
work on them (like editing, playing ...)
Thank you for your excellent article. Big Grin | :-D
dzeta2006
AnswerRe: Where's the "good" audio data?memberMichel Helms29 May '07 - 1:57 
It's quite weird with that 2352/2048-bytes-thing... We tell the DeviceIo-function to read out 2352 bytes, calculate the offset on cd with a multiplicator of 2048 bytes and store 2352 bytes into our memory-buffer. That means, we shall get the error codes and stuff also in our buffer, but don't ask me whether they influence the raw wave-data.
I really do not understand exactly, how and why it works this way, and unfortunately I simply do not have the time to get into it; also there's a lack of documentation about that thematics (or I'm too dumb to find it Big Grin | :-D ).
 
I'm pretty sure the error-correction is done automatically, so we do not have to care about this. In theory, we should get only the wave-data when reading the cd; and it seems to work, it sounds equally when listening to it on a cd-player Big Grin | :-D
 
In a comment below, someone asked how to extract extra-data like track-titles from cd. I did make a try, these informations are definetely hidden in these control-bits and it's kind of ease to read them out. The problem was that the track-titles seemed to be partly incorrect, e.g. "Red Hot Chilli Peppers" turned out to "Re70pot Chil_:i 121ppers" (just an example, I did it a few moth ago).
I wasn't able to find out, why these track-titles, artist-info and so on where screwed up, so I decided to leave it out of this article. If you are interested (or anybody else), I could have a look and try to find that sourcecode.
 

>>What's the usage of the data that you read in memory with the ReadTrack function?
Maybe you may find it too slow to write the wave-data to file and then playing it, or you might want to mix it on the fly or whatever. Maybe your program does not have the rights to write to disk or even there may be no disk to write to, also I meant to code an alternative to the flush-it-to-hdd Smile | :)
 
And, of course, thank you very much for the praise and your comment! Rose | [Rose]
AnswerRe: Where's the "good" audio data?memberPaul Sanders (AlpineSoft)26 Oct '07 - 11:30 
All 2352 bytes of audio data are 'good'. The mathematics are these:
 
75 sectors per second x 2352 bytes = 176400 bytes / sec
176400 / 4 = 44100 Hz audio (each 16 bit stereo sample is 4 bytes)
 
Data CD's use only 2048 bytes of each audio sector, the remaining 304 bytes being used for additional error correction. It is done this way because minor errors in an audio stream are usually inaudible whereas an error of any sort in a data stream is unacceptable.
 
But, as commented on below, what's *really* interesting is what is tucked away on the raw data on the CD, which is more than 2352 bytes per sector.
 

GeneralFinally Got Itmemberjstclairfindlay3 Apr '07 - 1:47 
Found this work on a different site a few days ago and been trying like mad to get it to work. Finally realized my problem was I was putting 2352 where the 2048 is supposed to go... a little more clearer on this site I guess. A lot less #defines to follow. Feeling pretty ignorant at the moment. Wish I would have figured it out earlier. Finally can get some sleep now. Way way way more helpful than anything I found on MSDN or anywhere else for that matter.
GeneralVery nicememberjerryf6512 Mar '07 - 14:11 
VERY VERY helpful. Could not find out information on the meaning of the Address vector on MSDN.
 
Couple comments:
1. The loop to read has a minor bug. If the total number of sectors to read is a multiple of 20, when you go to do the remaining sectors, it will give an error. I would suggest the following code:
 
for ( i=0; i
GeneralRe: Very nicememberMichel Helms12 Mar '07 - 23:17 
Hey Wow! Thank you that much for your constructive criticism, error finding and the solution you've already found Big Grin | :-D
Right now I'm quite busy with my study but next week I'll have a deeper look at it and possibly insert your suggestions Wink | ;)
 
That far, thank you again,
friendly regards,
Michel
GeneralCD-TextmemberGille Montreal Canada6 Oct '06 - 7:38 
This article is a good starting point. It would be nice to be able to add CD-Text information.
 
I have not yet seen an article on how to do it although many software do it. If any reader has some information to share, it could improve this tutorial.
 
Gilles
 
Gilles-Montreal-Canada

GeneralRe: CD-TextmemberMichel Helms7 Oct '06 - 2:04 
What do you mean with "CD-Text information"? Some kind of Tags like MP3-files have it? So artist/album/song-information is stored in the file?
If it's what you want to know, I'll have to disappoint you. There is no standard or support for file-tags in wave-files!
AnswerRe: CD-TextmemberGille Montreal Canada7 Oct '06 - 11:22 
Hi Michel
 
Here is what I know about CD-Text
 
CNET glossary has this definition:
An superset of the CD-DA audio CD format that allows up to 5,000 characters of text information such as the artist, album name, and song names to be stored along with the song files. A CD player that recognizes this information can read and display it for you.
 
Personnally, CD-Text is the information I write about a track (Artist-Title) when I make a CD (Easy CD Creator). It is a Sony standard. This information is displayed on my CD PLayer (JVC) when I play it back instead of the track number 01, 02 etc.
 
If I knew how to retreive this information, it would be easy to make a database file containing the tracks on my CD. Right now I do it using the text file from a freeware Audio CD Player HACP (http://www.freedownloadscenter.com/Best/cd-text-hacp.html) is an example of a software displaying this information.
 
Gilles
 
Gilles-Montreal-Canada

GeneralRe: CD-Text [modified]memberMichel Helms7 Oct '06 - 12:33 
You are right, I did not think of this! So thank you for that point, I'll try to integrate it!
 
Michel
GeneralRe: CD-Text [modified]memberMichel Helms7 Oct '06 - 14:07 
did a little research and it seems to be unpossible (without much deeper code). The data on a cd is internally stored in sectors which contain 3234 bytes, means 98 frames, each 33 bytes. These 33 bytes contain the control byte, 24 bytes of audio data and 8 byte of error-correction. Reading out the cd-data with the windows-functions causes the retrieve of only the 24 bytes of audio-data.
 
What we would need is the control byte. It contains 8 bit, named P, Q, R, S, T, U, V, W. We'd need the subchannels from R to W. Concatenating the bits of the subchannels (6 bits per frame), we would have 6*98=588 bits of subchannel-data in one sector. This data is subdivided in a leading synchronisation-ID (2 frames) and 4 packets (each 24 frames).
 
A packet consists of:
 
The kind of package
3-bit mode field
3-bit item field
6-bit directive-field
 
Error correction
6-bit Q0-field (nothing to do with Q-subchannel!!)
6-bit Q0-field ("")
 
Data
15*6 bits of data
 
Error correction again
6-bit P0-field (nothing to do with Q-subchannel!!)
6-bit P1-field ("")
6-bit P2-field ("")
 

 
I do not really know, how the cd-text is integrated, but it's very interesting. I'd like to test reading out the data, but as I said, it's not possible for me
 
Greets,
Michel
 

-- modified at 9:06 Sunday 8th October, 2006
 
Now I found out how you are able to read out the Q-subchannel. Quite easy, just use IOCTL_CDROM_READ_Q_CHANNEL.
But still nothing about the R-W subchannels Sniff | :^)
GeneralRe: CD-TextmemberEdmundJTP9 Oct '06 - 22:01 
Perhaps the use of IOCTL_CDROM_READ_TOC_EX with CDROM_READ_TOC_EX_FORMAT_CDTEXT could help.
 
One documentation is still available on
http://www.osronline.com/ddkx/storage/k306_2q42.htm
 
The definition is in two NT-DDK files "ntddcdrm.h" and "ntddstor.h"
 
So looks that you have to install the DDK to use this "feature" Frown | :(
 
Greets,
Edmund
GeneralRe: CD-TextmemberMichel Helms10 Oct '06 - 7:55 
Thank you very much! And I thought it would be impossible^^
 
OK, but you won't need the DDK, you just need a few definitions:
 
#define MAXIMUM_NUMBER_TRACKS 100
#define IOCTL_CDROM_READ_TOC 0x24000
#define IOCTL_CDROM_RAW_READ 0x2403E
#define IOCTL_CDROM_READ_TOC_EX 0x24054
#define CDROM_READ_TOC_EX_FORMAT_CDTEXT 0x05
 
#define CDROM_CD_TEXT_PACK_ALBUM_NAME 0x80
#define CDROM_CD_TEXT_PACK_PERFORMER 0x81
#define CDROM_CD_TEXT_PACK_SONGWRITER 0x82
#define CDROM_CD_TEXT_PACK_COMPOSER 0x83
#define CDROM_CD_TEXT_PACK_ARRANGER 0x84
#define CDROM_CD_TEXT_PACK_MESSAGES 0x85
#define CDROM_CD_TEXT_PACK_DISC_ID 0x86
#define CDROM_CD_TEXT_PACK_GENRE 0x87
#define CDROM_CD_TEXT_PACK_TOC_INFO 0x88
#define CDROM_CD_TEXT_PACK_TOC_INFO2 0x89
#define CDROM_CD_TEXT_PACK_UPC_EAN 0x8e
#define CDROM_CD_TEXT_PACK_SIZE_INFO 0x8f
 

 
typedef struct _TRACK_DATA
{
UCHAR Reserved;
UCHAR Control : 4;
UCHAR Adr : 4;
UCHAR TrackNumber;
UCHAR Reserved1;
UCHAR Address[4];
} TRACK_DATA;
 

typedef struct _CDROM_TOC
{
UCHAR Length[2];
UCHAR FirstTrack;
UCHAR LastTrack;
TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];
} CDROM_TOC;
 

typedef struct _CDROM_READ_TOC_EX {
UCHAR Format : 4;
UCHAR Reserved1 : 3;
UCHAR Msf : 1;
UCHAR SessionTrack;
UCHAR Reserved2;
UCHAR Reserved3;
} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX;
 

typedef enum _TRACK_MODE_TYPE
{
YellowMode2,
XAForm2,
CDDA
} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE;
 

typedef struct __RAW_READ_INFO
{
LARGE_INTEGER DiskOffset;
ULONG SectorCount;
TRACK_MODE_TYPE TrackMode;
} RAW_READ_INFO, *PRAW_READ_INFO;
 

typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK {
UCHAR PackType;
UCHAR TrackNumber : 7;
UCHAR ExtensionFlag : 1;
UCHAR SequenceNumber;
UCHAR CharacterPosition : 4;
UCHAR BlockNumber : 3;
UCHAR Unicode : 1;
union {
UCHAR Text[12];
WCHAR WText[6];
};
UCHAR CRC[2];
} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK;
 

typedef struct _CDROM_TOC_CD_TEXT_DATA
{
UCHAR Length[2];
UCHAR Reserved1;
UCHAR Reserved2;
CDROM_TOC_CD_TEXT_DATA_BLOCK Descriptors[0];
} CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA;
GeneralRe: CD-Textmemberso3311 Nov '07 - 6:17 
Can you actualize the tutorial with the posibility of reading CD-text?
 
Here is the code that reads the cd-text information.
 
http://forum.ioprogrammo.it/thread.php?threadid=13044&boardid=27&sid=c3972a17de78306311228d97106d9d53
GeneralGood articlemembervik2027 Sep '06 - 19:38 
Nice article.
 
Vikram
http://www.vikramlakhotia.com/

GeneralMaybe, of interest ... explains where 2352 comes from.memberThomas George26 Sep '06 - 23:01 
Data structure
 
The smallest entity in the CD audio format is called a frame. A frame can accommodate six complete 16-bit stereo samples, i.e. 2×2×6 = 24 bytes. A frame comprises 33 bytes, of which 24 are audio bytes (six full stereo samples), eight CIRC-generated error correction bytes, and one subcode byte. The eight bits of a subcode byte are available for control and display. Under Eight-to-Fourteen Modulation (EFM) rules, each data/audio byte is translated into 14-bit EFM words, which alternates with 3-bit merging words. In total we have 33*(14+3) = 561 bits. A 27-bit unique synchronization word is added, so that the number of bits in a frame totals 588. The synchronization word cannot occur in the normal bit stream, and can thus be used to identify the beginning of a frame. Data in a CD-ROM are organized in both frames and sectors, where a CD-ROM sector contains 98 frames, and holds 98×24 = 2352 (user) bytes.
This is where I got the information Smile | :) [^]

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 9 Oct 2006
Article Copyright 2006 by Michel Helms
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid