Heavily Edited Intro I initially criticised the problem description as confusing. My problems stem from way the concept of a 'file' is used in the question. The question implies the Disc, Programs and Tracks are all stored in one 'file'. I thought the questioner was building his own filesystem, which would make this 'everything in one file' structure weird, but I have now decided he's probably not doing that, in which case it's less weird. So I'll go ahead and offer a real answer, based on the assumption he's using an existing (probably standard) file system, and his entire data structure is stored in one file within that file system. If I am assuming wrong no doubt I'll be corrected.
I will first offer one piece of general advice for situations like this; Look at things from the perspective of the API user first. Then design your API so that the code he will write flows easily with no need to deal with the details that are properly in your domain.
One way to work on your API design is to write some user code first and define the API so that that code is easy to write. As a bonus, after you actually implement the API you'll then have some test code to try it out with.
Moving on to more specific advice;
Here is a catalog of the three data types in the system. We can treat them as abstract data types or 'Objects' if you like and define a typdef'ed struct (DISC, PROGRAM, TRACK say) to represent each one.
disc = a collection of programs stored in a file
+-----------+
|file |
+-----------+
|program |
+-----------+
|program |
+-----------+
|... |
+-----------+
|program |
+-----------+
program = a collection of tracks
+-----------+
|ptr->disc |
+-----------+
|name |
+-----------+
|file offset|
+-----------+
|track |
+-----------+
|track |
+-----------+
|... |
+-----------+
|track |
+-----------+
track = a collection of audio samples
+------------------+
|ptr->program |
+------------------+
|name |
+------------------+
|file offset+length|
+------------------+
|file offset+length|
+------------------+
|... |
+------------------+
|file offset+length|
+------------------+
I would propose that you do not make your users pick out data from the structures. You can't really hide the internals of the structures in C (without jumping through hoops with casting etc.), but you can provde a family of functions that let's your users do what they need to do without accessing the contents of the abstract types themselves. For example our family of functions might look like this;
// DISC functions
DISC *dopen( const char *disc_name );
void dstats( int *ptr_nbr_programs, FILE **ptr_file );
void dclose( DISC *disc );
// PROGRAM functions
PROGRAM *popen_name( DISC *disc, const char *program_name );
PROGRAM *popen_idx ( DISC *disc, int program_idx );
void pstats( int *ptr_nbr_tracks );
void pclose( PROGRAM *program );
// TRACK functions
TRACK *topen_name( PROGRAM *program, const char *track_name );
TRACK *topen_idx ( PROGRAM *program, int track_idx );
int tread( unsigned char *buf, int nbytes_to_read );
void tseek( unsigned long offset );
void tclose( TRACK *track );
This should all be reasonably self explanantory - it is modelled on the existing standard C FILE paradigm.
First your user obtains a ptr to DISC with dopen(). Assuming this works (it will return NULL if it doesn't), he can obtain any global DISC information with dstats(). More to the point he can obtain a ptr to a PROGAM within the DISC with one of the popen() family of functions.
With a ptr to PROGRAM he can drill down further and obtain a single TRACK with one of the topen() family of functions.
A very important point is that you don't make your user iterate through the audio fragments himself to get data from the TRACK. The user provides a buffer to read the samples into, and you iterate as necessary through the fragments to fill that buffer. The tseek() function is provided to give him random access.
I haven't tried to work out the details of every parameter and how errors are handled etc., I am just presenting a concept to be refined.
Note that the 'sandwich' paradigm is used throughout. An introductory 'open' and a concluding 'close' sandwiches operations on every type.