Playback operation

Describes the operation of the Sound Driver for sound playback.

Normal operation

The client calls RSoundSc::PlayData() to issue an audio request.

The LDD breaks the audio request into manageable fragments and transfers them to the PDD by calling DSoundScPdd::TransferData().

When the PDD has transmitted the fragment it calls DSoundScLdd::PlayCallback() to signal to the LDD that it has finished transfer of a fragment. When the client request has been completed, the LDD calls DSoundScPdd::StopTransfer() to stop the PDD transmitting data and to release any transfer specific resources.

To ensure uninterrupted playback, a client must have multiple play requests pending on the driver. As soon as one request completes, the client issues a further request until the end of the track. Typically, a client issues a series of calls to RSoundSc::PlayData() to play an audio track.

Each RSoundSc::PlayData() request is handled in the context of the driver's Deferred Function Call (DFC) thread. Assuming that the driver is not already in the process of playing data when the first request is received, the LDD checks whether the client has specified or supplied a shared chunk to the Driver channel and also whether the audio configuration and volume have both been set. If the chunk configuration has not been specified, then the driver cannot proceed and returns KErrNotReady. However, if the audio configuration or volume has not been specified, then the LDD simply applies default settings to the audio hardware device. These values are returned by the PDD functions DSoundScPdd::SetConfig() and DSoundScPdd::SetVolume() when called by the LDD.

The LDD may need to break the play request down into fragments to support the maximum amount of data that the PDD can handle in a single transfer. The function DSoundScPdd::MaxTransferLen() returns this maximum value. The LDD queues as many transfer fragments on the PDD as it can accept with a call to DSoundScPdd::TransferData() for each fragment.

To support uninterrupted transfer of audio data, the PDD must be able to accept at least two transfer fragments simultaneously: the first being actively transferred by the audio hardware device, the other being queued for transfer on the same device. Therefore, as long as the LDD has transfer fragments still to queue, it continues to call DSoundScPdd::TransferData() on the PDD until this signals that it has temporarily reached its capacity, by returning KErrNotReady.

If the PDD accepts all the fragments for this initial play request then the LDD moves on to process the subsequent requests from the client. These are also fragmented until either the PDD reaches its capacity or all pending play requests are queued.

Each time the PDD completes the transfer of a fragment from a play buffer, it must signal this event back to the LDD by calling the function DSoundScLdd::PlayCallback(). This must always be called in the context of the driver DFC thread so there are no synchronisation problems. For example, where transfer completion interrupts the processing of new play requests from the client.

In executing DSoundScLdd::PlayCallback(), the LDD checks whether the entire transfer for the current request is now complete. If so, it signals completion back to the client. Also within the DSoundScLdd::PlayCallback() function, the LDD will attempt to queue further fragments on the PDD, by calling DSoundScPdd::TransferData(), as the PDD should now have the capability to accept more transfers. The PDD must be written to handle calls to DSoundScPdd::TransferData() within its callback to DSoundScLdd::PlayCallBack().

If, on completing a request from the client, the LDD discovers that there are no further play requests pending from the client, then this is treated as an underflow situation and KErrUnderflow is returned to the client. If however, the client specified KSndFlagLastSample as the aFlag argument of the RSoundSc::PlayData() function then an underflow is expected after this request completes.

When the audio request has been completed the LDD calls DSoundScPdd::StopTransfer() on the PDD. This allows the PDD to release any resources necessary for transfer.

Pausing and resuming audio playback

The client may temporarily halt the progress of audio playback at any time by issuing DSoundScLdd::Pause(). In order to configure the audio hardware device to halt playback, the LDD calls DSoundScPdd::PauseTransfer() on the PDD. Ideally, playback should be suspended in such a way that it can be resumed later, starting from next sample following the one last played.

The client requests resumption of playback by calling DSoundScLdd::Resume() on the LDD. The LDD, in turn tells the PDD to re-commence playback by calling DSoundScPdd::ResumeTransfer()

Since access to the hardware is required in both cases, pause and resume are handled in the context of the driver DFC thread.

Error handling during playback

If the PDD reports an error when setting up the audio device for playback then the LDD immediately completes the first play request back to the client returning the error value as the result. For example, if the PDD returns a value other than KErrNone from a call to the function DSoundScPdd::StartTransfer(). The LDD attempts to re-start playback data transfer when it processes any further play requests from the client.

If the PDD reports an error when commencing transfer or as the result of the transfer of a playback fragment, then the LDD ceases transfer of the associated request and instead immediately completes the request back to the client returning the error value as the result.

Unexpected errors from the PDD are returned to the LDD via the functions DSoundScPdd::TransferData() and DSoundScLdd::PlayCallback(). The DSoundScPdd::TransferData() function signals that an error has occurred when setting up the transfer of a playback fragment by returning an error value other than KErrNone or KErrNotReady. The DSoundScLdd::PlayCallback() function called by the PDD at the end of a transfer, signals an error to the LDD if the value passed is not equal to KErrNone.

The LDD does not cancel the transfer of other fragments for the same request which are already queued on the PDD, but it ignores their outcome. However, the LDD does try to carry on with the transfer of subsequent play requests queued by the client.

In any of the above situations, the client may choose to terminate playback operation by cancelling all outstanding play requests when it detects a playback error.