📄 wmaudiostream.c
字号:
/*-----------------------------------------------------------------------------
* Copyright (c) Wolfson Microelectronics plc. All rights reserved.
*
* This software as well as any related documentation is furnished under
* license and may only be used or copied in accordance with the terms of the
* license. The information in this file is furnished for informational use
* only, is subject to change without notice, and should not be construed as
* a commitment by Wolfson Microelectronics plc. Wolfson Microelectronics plc
* assumes no responsibility or liability for any errors or inaccuracies that
* may appear in this document or any software that may be provided in
* association with this document.
*
* Except as permitted by such license, no part of this document may be
* reproduced, stored in a retrieval system, or transmitted in any form or by
* any means without the express written consent of Wolfson Microelectronics plc.
*
* $Id: WMAudioStream.c 2823 2006-04-04 12:31:45Z fb $
*
* This file contains platform-independent routines for controlling the audio
* operation on Wolfson codecs.
*
* Warning:
* This driver is specifically written for Wolfson Codecs. It is not a
* general CODEC device driver.
*
* -----------------------------------------------------------------------------*/
/*
* Include files
*/
#include <string.h>
#include "WMCommon.h"
#include "WMDevice.h"
#include "WMGlobals.h"
#include "WMAudio.h"
#include "WMAudioInternal.h"
#include "WMDMA.h"
#include "WMWaveGen.h"
#include "WMVoice.h"
#include "Test/WMTestCommon.h"
#include "Test/WMInternalTests.h"
/*
* Only build this if we're doing Audio support.
*/
#if WM_AUDIO_STREAM
/*
* Global definitions
*/
typedef void (*CopyBufferFn)( void *pDest, void *pSrc, unsigned int nSamples );
/*
* Structure for holding details about an audio stream.
*/
typedef struct tagStreamCtx
{
WM_STREAM_ID stream;
WMAUDIO_CHANNEL APIChannel;
WMDMA_CHAN_HANDLE DMAChan;
int sampleRate;
int samplesPerBuffer;
int nSamples;
int bytesPerUserSample;
int bytesPerDMASample;
CopyBufferFn copyFunc;
unsigned int flags;
} StreamCtx;
/*
* Stream flags.
*/
#define STREAM_ACTIVE 0x00000001
#define STREAM_STEREO_USER 0x00000010
#define STREAM_STEREO_DMA 0x00000020
#define STREAM_MONO_DMA 0x00000040
#define STREAM_MONO_DMA_WIDE 0x00000080
#define STREAM_INPUT 0x00000100
#define STREAM_OUTPUT 0x00000200
#define STREAM_DEFAULT 0x00001000
/*
* The number of stream contexts we support.
* Defined channels + 2 to allow for the default channels.
*/
#define WM_STREAM_CONTEXTS (WMAUDIO_DEFINED_CHANNELS + 2)
/*
* The WM_STREAM_ID in StreamCtx structures which haven't yet been assigned
* to a stream.
*/
#define STREAM_UNASSIGNED 0
/*
* Convert between 8-bit (0-255) and 16-bit data (-32768-32767).
*/
#define CONVERT_8BIT_TO_16BIT( _sample ) \
((unsigned short) ((int)((unsigned char)(_sample) - 128) << 8 ))
#define CONVERT_16BIT_TO_8BIT( _sample ) \
((unsigned char)(((_sample) >> 8) + 128 ))
/*
* Convert between stream handles and stream contexts.
* The basic level of obfuscation gives us a good chance of catching bogus
* handles.
*/
#define STREAM_HANDLE_BASE 0x574D4C00
#define STREAM_HANDLE_BASE_MASK 0xFFFFFF00
#if WM_STREAM_CONTEXTS > 256
# error If you really want this many streams, you will need to change the way handles work.
#endif /* WM_STREAM_CONTEXTS > 256 */
#define STREAM_INDEX_FROM_CONTEXT( _pCtx ) ((_pCtx) - s_streamContexts)
#define STREAM_CONTEXT_FROM_INDEX( _index ) (&s_streamContexts[_index])
#define STREAM_HANDLE_FROM_INDEX( _index ) \
((WM_STREAM_HANDLE)(((unsigned int)(_index)) | STREAM_HANDLE_BASE))
#define STREAM_INDEX_FROM_HANDLE( _handle ) \
((((unsigned int)(_handle)) & ~STREAM_HANDLE_BASE_MASK))
#define IS_VALID_STREAM_HANDLE( _handle ) \
((STREAM_HANDLE_BASE == \
(((unsigned int)(_handle)) & STREAM_HANDLE_BASE_MASK) \
) && (STREAM_INDEX_FROM_HANDLE( _handle ) < WM_STREAM_CONTEXTS ))
#define STREAM_HANDLE_FROM_CONTEXT( _pCtx ) \
( STREAM_HANDLE_FROM_INDEX( STREAM_INDEX_FROM_CONTEXT( _pCtx ) ) )
#define STREAM_CONTEXT_FROM_HANDLE( _handle ) \
( IS_VALID_STREAM_HANDLE( _handle ) ? \
STREAM_CONTEXT_FROM_INDEX( STREAM_INDEX_FROM_HANDLE(_handle) ) : \
NULL )
/*
* Private data
*/
/*
* The stream data.
*/
static StreamCtx s_streamContexts[ WM_STREAM_CONTEXTS ] = {0};
/*
* Function prototypes
*/
static void private_ClearRestOfBuffer( WM_DEVICE_HANDLE hDevice,
StreamCtx *pStreamCtx
);
/* Stream context management */
static StreamCtx *private_GetStreamContext( WM_DEVICE_HANDLE hDevice,
WM_STREAM_ID stream
);
static WMSTATUS private_AllocateStreamContext( WM_DEVICE_HANDLE hDevice,
WM_STREAM_ID stream,
StreamCtx **ppStreamCtx
);
static void private_InitialiseStreamContext( WM_DEVICE_HANDLE hDevice,
WM_STREAM_ID stream,
StreamCtx *pStreamCtx
);
static void private_FreeStreamContext( WM_DEVICE_HANDLE hDevice,
StreamCtx *pStreamCtx
);
/* Copy functions - playback */
static void private_Fill8BitMonoTo16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill8BitMonoTo32BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill8BitMonoToStereo( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill16BitMonoTo16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill16BitMonoTo32BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill16BitMonoToStereo( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill8BitStereoTo16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill8BitStereoTo32BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill8BitStereoToStereo( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill16BitStereoTo16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill16BitStereoTo32BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Fill16BitStereoToStereo( void *pDest, void *pSrc, unsigned int nSamples );
/* Copy functions - record */
static void private_Get8BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Get8BitMonoFrom16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Get16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Get16BitMonoFrom16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Get8BitStereo( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Get8BitStereoFrom16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Get16BitStereo( void *pDest, void *pSrc, unsigned int nSamples );
static void private_Get16BitStereoFrom16BitMono( void *pDest, void *pSrc, unsigned int nSamples );
/*-----------------------------------------------------------------------------
* Function: WMAudioOpenStream
*
* Prepares an audio stream for communicating and streaming.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* stream the stream to prepare
* pStreamHandle variable to receive the stream handle
* sampleRate the sample rate for the stream (in Hz, e.g. 44100)
* bitsPerSample the number of bits per sample (8 or 16)
* isStereo the number of channels (FALSE = mono TRUE = stereo)
*
* Returns: WMSTATUS
* See WMStatus.h.
*---------------------------------------------------------------------------*/
WMSTATUS WMAudioOpenStream( WM_DEVICE_HANDLE hDevice,
WM_STREAM_ID stream,
WM_STREAM_HANDLE *pStreamHandle,
int sampleRate,
int bitsPerSample,
WM_BOOL isStereo
)
{
WMSTATUS status;
StreamCtx *pStreamCtx;
unsigned int bufSize;
unsigned int bytesPerDMASample;
unsigned int samplesPerDMABuffer;
WM_BOOL isInput;
WM_BOOL isStereoDMA;
WM_BOOL isWideDMA;
WMAUDIO_CHANNEL APIChannel;
/*
* Parameter validation.
*/
switch ( sampleRate )
{
case 8000:
case 11025:
case 16000:
case 22050:
case 32000:
case 44100:
case 48000:
break;
default:
status = WMS_UNSUPPORTED;
goto error0;
}
if ( bitsPerSample != 8 && bitsPerSample != 16 )
{
status = WMS_UNSUPPORTED;
goto error0;
}
/*
* Look up the API channel for this stream.
*/
APIChannel = _WMAudioStreamToChannel( hDevice, stream );
if ( WMAUDIO_INVALID_CHANNEL == APIChannel )
{
status = WMS_UNSUPPORTED;
WM_TRACE( hDevice, (
"WMAudioOpenChannel - stream %d not supported on this codec",
stream
));
goto error0;
}
/*
* Check no-one else is already using this stream.
*/
pStreamCtx = private_GetStreamContext( hDevice, stream );
if ( pStreamCtx && ( pStreamCtx->flags & STREAM_ACTIVE ) )
{
status = WMS_DEVICE_BUSY;
goto error0;
}
/*
* Set up some defaults.
*/
bufSize = WMDMAGetMaxBufferSize( hDevice );
bytesPerDMASample = WMDMAGetBytesPerSample( hDevice, APIChannel );
isStereoDMA = WMDMAIsStereo( hDevice, APIChannel );
isInput = WMDMAIsInput( hDevice, APIChannel );
isWideDMA = WMDMAIsWide( hDevice, APIChannel );
WM_ASSERT( hDevice, WM_IS_INPUT_STREAM( stream ) == isInput );
/*
* This can only fail if we have passed in the wrong channel number.
* We checked the channel above, so if it failed, something's broken
* with one or more of our tables in the ChipDef and in the DMAContext.
*/
WM_ASSERT( hDevice, 0 != bytesPerDMASample );
samplesPerDMABuffer = bufSize/bytesPerDMASample;
/*
* Get a stream context structure.
*/
status = private_AllocateStreamContext( hDevice, stream, &pStreamCtx );
if ( WM_ERROR( status ) )
{
goto error0;
}
/*
* Support Low-latency Streaming
*
* Adjust the effective DMA buffer size, based on the requested sample rate.
* The idea is that we want our DMA capture buffers to represent a fixed amount
* of time, regardless of the sample rate.
*
* This means that if the application buffers are small (as is the case with
* VoIP or DirectSoundCapture) then we still can return the buffer very soon
* after we have enough captured data to fill it.
*
* If we don't do this and use fixed-size capture buffers instead, at low
* sample rates we could wait for hundreds of milliseconds, then turn around
* and discover that the application had only given us 80 milliseconds of wave
* headers, so we would be forced to drop samples on the floor.
*/
{
unsigned int maxSamplesPerBuf;
unsigned int maxBufferDuration;
if ( isInput )
maxBufferDuration = WMAUDIO_MAX_RECORD_BUFFER_DURATION;
else
maxBufferDuration = WMAUDIO_MAX_PLAYBACK_BUFFER_DURATION;
/*
* Calculate the maximum number of samples in a buffer, based on the time,
* and round up to the next 32-byte boundary.
*/
maxSamplesPerBuf = sampleRate * maxBufferDuration / 1000;
maxSamplesPerBuf = (maxSamplesPerBuf + 31) & ~31;
/*
* If the default buffer size is larger than our time based buffer size
* set samplesPerBuffer to our time based buffer size.
*/
if ( samplesPerDMABuffer > maxSamplesPerBuf )
samplesPerDMABuffer = maxSamplesPerBuf;
}
/*
* Now calculate the buffer size.
*/
bufSize = samplesPerDMABuffer * bytesPerDMASample;
/*
* Fill in the stream structure.
*/
pStreamCtx->APIChannel = APIChannel;
pStreamCtx->bytesPerUserSample = bitsPerSample/8;
if ( isInput )
{
pStreamCtx->flags |= STREAM_INPUT;
pStreamCtx->bytesPerDMASample = bitsPerSample/8;
}
else
{
pStreamCtx->flags |= STREAM_OUTPUT;
pStreamCtx->bytesPerDMASample = bytesPerDMASample;
}
pStreamCtx->samplesPerBuffer = samplesPerDMABuffer;
pStreamCtx->sampleRate = sampleRate;
pStreamCtx->nSamples = 0;
if ( isStereo )
{
pStreamCtx->flags |= STREAM_STEREO_USER;
pStreamCtx->bytesPerUserSample *= 2;
if ( isInput )
{
pStreamCtx->bytesPerDMASample *= 2;
}
}
if ( isStereoDMA )
{
pStreamCtx->flags |= STREAM_STEREO_DMA;
}
else if ( isWideDMA )
{
pStreamCtx->flags |= STREAM_MONO_DMA_WIDE;
}
else
{
pStreamCtx->flags |= STREAM_MONO_DMA;
}
if ( WM_IS_DEFAULT_STREAM( stream ) )
{
pStreamCtx->flags |= STREAM_DEFAULT;
}
/*
* Set up the copy function.
*/
if ( isInput )
{
if ( isStereoDMA )
{
if ( isStereo )
{
if ( 16 == bitsPerSample )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -