Re: Callback problems
Re: Callback problems
- Subject: Re: Callback problems
- From: Philippe Wicker <email@hidden>
- Date: Mon, 23 Feb 2004 08:48:10 +0100
On Feb 22, 2004, at 9:57 PM, Frances Buck wrote:
Hey Phillipe
Thanx, Thanx, Thanx. It works!.
Thank you.
fread() can do his stuff in MyRenderer()... .
But you suggest to do a own Thread?
Yes.
The golden rule is to never block the IO thread (the one in which the
render takes place). Look in the list archive for a recent thread with
subject "multithreaded mixer", it's explained here why it's a bad thing
So you must never call an API that may block: e.g. allocating memory,
reading data from disk, taking a lock to synchronize your IO thread
with another thread, ...
fread may work ... and it may not. It will work OK if data you're
accessing are already resident in the physical memory (e.g. in the disk
cache). It will not work if the data must be fetched from the disk
itself. The result will be as much worse as the CPU is heavily used. It
will be also worse if you need to access several files concurrently
(e.g. in a sample player).
To access your file data you have two solutions (at least):
1- map your file to memory (mmap api I think, type "man mmap" in a
terminal). Then anticipate the use of a given part of the file data and
touch the pages containing these soon to use data. "Touching" simply
means reading a word somewhere in the page to force the system to load
the data in physical memory. A page is a 4K byte block aligned to a 4K
byte boundary.
2- anticipate the use of the file data and read ahead a bunch (e.g. 64K
bytes). Use two 64K bytes bunch buffers. While data from the disk is
loaded in one of the two bunches, the other is used in the render
callback. Anticipating here means that when you detect - in the render
callback - that you will need data from another bunch at next call to
render, then you have to issue the disk read command and prepare to
switch the role of the two bunches buffer (aka ping-pong technique).
In both cases, you must issue the touching code or the command to read
data from disk in a thread different than the IO thread because both
methods may result in an access to the disk. To do this, use a queue to
store commands (touch or read disk) and use a non blocking mechanism to
signal the thread that will dequeue and execute the command. The
PlayAudioFile sample in the CoreAudio SDK uses a synch technique based
on the use of POSIX mutexes and condition variables
(pthread_mutex_lock, pthread_cond_signal et al) encapsulated in the
CAGuard utility class (CoreAudio/PublicUtility).
Which method is better? I personnally prefer the 2nd one. The first
method is well adapted to randomly read one or several files. The
second one better fits the need when the file(s) is (are) read
sequentially and it gives a better control over the amount of physical
memory actually used. A 3rd (more sophisticated) method could be built
upon a caching mechanism. It gives you a fine control on the physical
memory use and allows to tune the code to optimize accesses which are
not only sequential. (e.g. when you have to loop between two locators:
it is sequential between the 2 locators and more or less random when
you have to jump back to the left locator).
Frances.
From: Philippe Wicker <email@hidden>
To: "Frances Buck" <email@hidden>
Subject: Re: Callback problems
Date: Sun, 22 Feb 2004 21:33:59 +0100
On Feb 22, 2004, at 8:38 PM, Frances Buck wrote:
Hello Philippe,
Thank you for your answer.
Okay, fread() is to slow. I started a new Thread to read the buffer
into a Queue.
So, when this Thread is done the Callback will start.
I even get the crackle... .
In MyRenderer you return in mDataByteSize the number of bytes
actually read from the disk. It should work the other way. The
size - expressed as frames number - of the slice to render is
passed to your callback in inNumberFrames parameters and you
**must** either render that much of audio or return that much
silence - no more no less - in the buffers pointed to by
ioData->mBuffers[x].mData . ioData->mBuffers[x].mDataByteSize
fields contain the size in bytes corresponding to inNumberFrames
frames. You don't need to modify these fields.
-> I didn't get you. Sorry. I hope that you can step into more
details.
Let me start. If I have my first buffer[8192] ( the buffer is
already in memory before MyRenderer starts).
Code
memcpy(ioData->mBuffers[0].mData, bufferAlreadyinMemory, 8192);
ioData->mBuffers[0].mDataByteSize = 8192; //Did I get you - here is
the problem?
Yes.
What I meant is that it is the **caller** that tells you the number
of frames which must be rendered, and **not** the callee (ie your
render code) that tells how many frames has been rendered. The caller
tells you how many frames have to be rendered by passing this value
in the IN parameter inNumberFrames. The caller tells you how many
Bytes you have to provide in each ioData buffers. This information is
passed to you in ioData->mBuffers[0].mDataByteSize. So both data
(inNumberFrames and mDataByteSize ) are somehow redundant.
In your case, the stream is interleaved (only one buffer). So what
you could write is the following:
UInt32 the_size_to_render =
ioData->mBuffers[0].mDataByteSize;
memcpy(ioData->mBuffers[0].mData, bufferAlreadyInMemory,
the_size_to_render);
And you should not modify mDataByteSize.
The reason why is that the size of the buffer to render is determined
outside your application. The number of frames is set in AMS
(AudioMidiSetup) where you can choose the device frame buffer size.
Depending on the size of bufferAlreadyInMemory, don't forget to
increment this pointer by "the_size_to_render" for the next call.
Thank you,
Frances.
Philippe Wicker
email@hidden
_________________________________________________________________
Use MSN Messenger to send music and pics to your friends
http://www.msn.co.uk/messenger
Philippe Wicker
email@hidden
_______________________________________________
coreaudio-api mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
Do not post admin requests to the list. They will be ignored.