This commit is contained in:
Steve Checkoway
2004-07-16 09:25:43 +00:00
parent f4802875f6
commit f33e9db593
4 changed files with 40 additions and 297 deletions
-63
View File
@@ -1,63 +0,0 @@
#include "global.h"
#include "RageSoundDriver_CA.h"
#include "CAHelpers.h"
AudioConverter::AudioConverter( RageSound_CA *driver, const Desc &procFormat )
: mBuffer(NULL), mBufferSize(0)
{
const Desc SMFormat(44100.0, kAudioFormatLinearPCM, kBytesPerPacket,
kFramesPerPacket, kBytesPerFrame, kChannelsPerFrame,
kBitsPerChannel, kFormatFlags);
SMFormat.Print();
procFormat.Print();
if( this->Initialize(SMFormat, procFormat) )
RageException::ThrowNonfatal( "Couldn't create the converter." );
mDriver = driver;
}
OSStatus AudioConverter::FormatConverterInputProc(UInt32& ioNumberDataPackets,
AudioBufferList& ioData,
AudioStreamPacketDescription **outDesc)
{
AudioBuffer& buf = ioData.mBuffers[0];
if( mBufferSize < buf.mDataByteSize )
{
delete [] mBuffer;
mBuffer = new UInt8[buf.mDataByteSize];
mBufferSize = buf.mDataByteSize;
}
buf.mData = mBuffer;
const int frames = buf.mDataByteSize / kBytesPerPacket;
mDriver->FillConverter( buf.mData, frames );
return noErr;
}
/*
* (c) 2004 Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
-58
View File
@@ -1,58 +0,0 @@
#ifndef CA_HELPERS_H
#define CA_HELPERS_H
#include "FormatConverterClient.h"
#include "CAStreamBasicDescription.h"
static const UInt32 kFramesPerPacket = 1;
static const UInt32 kChannelsPerFrame = 2;
static const UInt32 kBitsPerChannel = 16;
static const UInt32 kBytesPerPacket = kChannelsPerFrame * kBitsPerChannel / 8;
static const UInt32 kBytesPerFrame = kBytesPerPacket;
static const UInt32 kFormatFlags = kAudioFormatFlagsNativeEndian |
kAudioFormatFlagIsSignedInteger;
typedef CAStreamBasicDescription Desc;
class AudioConverter : public FormatConverterClient
{
public:
AudioConverter( RageSound_CA *driver, const Desc &procFormat );
~AudioConverter() { delete [] mBuffer; }
protected:
OSStatus FormatConverterInputProc(UInt32& ioNumberDataPackets,
AudioBufferList& ioData,
AudioStreamPacketDescription **outDesc);
private:
RageSound_CA *mDriver;
UInt8 *mBuffer;
UInt32 mBufferSize;
};
#endif
/*
* (c) 2004 Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+37 -165
View File
@@ -1,10 +1,10 @@
#include "global.h"
#include "RageUtil.h"
#include "RageSoundDriver_CA.h"
#include "CAHelpers.h"
#include "RageLog.h"
#include "RageTimer.h"
#include <AudioToolbox/AudioConverter.h>
#include "CAAudioHardwareSystem.h"
#include "CAAudioHardwareDevice.h"
#include "CAAudioHardwareStream.h"
@@ -15,7 +15,16 @@
#include <mach/mach_init.h>
#include <mach/mach_error.h>
static AudioConverter *gConverter = NULL;
static const UInt32 kFramesPerPacket = 1;
static const UInt32 kChannelsPerFrame = 2;
static const UInt32 kBitsPerChannel = 16;
static const UInt32 kBytesPerPacket = kChannelsPerFrame * kBitsPerChannel / 8;
static const UInt32 kBytesPerFrame = kBytesPerPacket;
static const UInt32 kFormatFlags = kAudioFormatFlagsNativeEndian |
kAudioFormatFlagIsSignedInteger;
typedef CAStreamBasicDescription Desc;
/* temporary hack: */
static float g_fLastIOProcTime = 0;
@@ -40,79 +49,8 @@ static CString FormatToString( int fmt )
return ssprintf( "%c%c%c%c", c[0], c[1], c[2], c[3] );
}
static Desc FindClosestFormat(const vector<Desc>& formats,
RageSound_CA::format& type)
{
vector<Desc> v;
vector<Desc>::const_iterator i;
for (i = formats.begin(); i != formats.end(); ++i)
{
const Desc& format = *i;
if( !format.IsPCM() )
continue;
if( format.mSampleRate != 0. && format.mSampleRate != 44100.0 )
continue;
if (format.SampleWordSize() == 2 &&
(format.mFormatFlags & kFormatFlags) == kFormatFlags)
{ // exact match
type = RageSound_CA::EXACT;
return format;
}
v.push_back(format);
}
const Desc CanonicalFormat(44100.0, kAudioFormatLinearPCM, 8, 1, 8, 2, 32,
kAudioFormatFlagsNativeFloatPacked);
for (i = v.begin(); i != v.end(); ++i)
{
const Desc& format = *i;
if (format == CanonicalFormat)
{
type = RageSound_CA::CANONICAL;
return format;
}
}
if (v.empty())
RageException::ThrowNonfatal("Couldn't find a close format.");
type = RageSound_CA::OTHER;
return v[0]; // something is better than nothing.
}
static void GetIOProcFormats( CAAudioHardwareStream stream,
vector<Desc> &procFormats )
{
UInt32 numFormats = stream.GetNumberAvailableIOProcFormats();
for( UInt32 i=0; i<numFormats; ++i )
{
Desc desc;
stream.GetAvailableIOProcFormatByIndex( i, desc );
procFormats.push_back( desc );
}
}
static void GetPhysicalFormats( CAAudioHardwareStream stream,
vector<Desc> &physicalFormats )
{
UInt32 numFormats = stream.GetNumberAvailablePhysicalFormats();
for( UInt32 i=0; i<numFormats; ++i )
{
Desc desc;
stream.GetAvailablePhysicalFormatByIndex( i, desc );
physicalFormats.push_back( desc );
}
}
RageSound_CA::RageSound_CA()
{
{
try
{
AudioDeviceID dID;
@@ -150,44 +88,20 @@ RageSound_CA::RageSound_CA()
LOG->Warn("Could not install the overload listener.");
}
vector<Desc> physicalFormats;
GetPhysicalFormats( sID, physicalFormats );
unsigned i;
LOG->Info("Available physical formats:");
for (i = 0; i < physicalFormats.size(); ++i)
const Desc CanonicalFormat(44100.0, kAudioFormatLinearPCM, 8, 1, 8, 2, 32,
kAudioFormatFlagsNativeFloatPacked);
const Desc SMFormat(44100.0, kAudioFormatLinearPCM, kBytesPerPacket,
kFramesPerPacket, kBytesPerFrame, kChannelsPerFrame,
kBitsPerChannel, kFormatFlags);
stream.SetCurrentIOProcFormat(CanonicalFormat);
if (AudioConverterNew(&SMFormat, &CanonicalFormat, &mConverter))
{
const Desc& f = physicalFormats[i];
LOG->Info("Format %u: Rate: %i ID: %s Flags 0x%lx bpp %lu fpp %lu"
" bpf %lu channels %lu bits %lu", i, int(f.mSampleRate),
FormatToString(f.mFormatID).c_str(), f.mFormatFlags,
f.mBytesPerPacket, f.mFramesPerPacket, f.mBytesPerFrame,
f.mChannelsPerFrame, f.mBitsPerChannel);
delete mOutputDevice;
RageException::ThrowNonfatal("Couldn't create the audio converter");
}
const Desc& physicalFormat = FindClosestFormat( physicalFormats, mFormat );
stream.SetCurrentPhysicalFormat( physicalFormat );
vector<Desc> procFormats;
GetIOProcFormats( sID, procFormats );
LOG->Info("Available I/O procedure formats:");
for (i = 0; i < procFormats.size(); ++i)
{
const Desc& f = procFormats[i];
LOG->Info("Format %u: Rate: %i ID: %s Flags 0x%lx bpp %lu fpp %lu"
" bpf %lu channels %lu bits %lu", i, int(f.mSampleRate),
FormatToString(f.mFormatID).c_str(), f.mFormatFlags,
f.mBytesPerPacket, f.mFramesPerPacket, f.mBytesPerFrame,
f.mChannelsPerFrame, f.mBitsPerChannel);
}
const Desc& procFormat = FindClosestFormat( procFormats, mFormat );
stream.SetCurrentIOProcFormat( procFormat );
LOG->Info("Proc format is %s.",
mFormat == EXACT ? "exact" : (mFormat == CANONICAL ?
"canonical" : "other"));
try
{
UInt32 bufferSize = mOutputDevice->GetIOBufferSize();
@@ -218,13 +132,11 @@ RageSound_CA::RageSound_CA()
catch (const CAException& e)
{
delete mOutputDevice;
AudioConverterDispose(mConverter);
RageException::ThrowNonfatal("Couldn't get Latency.");
}
StartDecodeThread();
if (mFormat == OTHER)
gConverter = new AudioConverter( this, procFormat );
try
{
@@ -233,8 +145,8 @@ RageSound_CA::RageSound_CA()
}
catch(const CAException& e)
{
delete gConverter;
delete mOutputDevice;
AudioConverterDispose(mConverter);
RageException::Throw("Couldn't start the IOProc.");
}
}
@@ -243,7 +155,7 @@ RageSound_CA::~RageSound_CA()
{
mOutputDevice->StopIOProc(GetData);
delete mOutputDevice;
delete gConverter;
AudioConverterDispose(mConverter);
}
int64_t RageSound_CA::GetPosition(const RageSoundBase *sound) const
@@ -254,17 +166,6 @@ int64_t RageSound_CA::GetPosition(const RageSoundBase *sound) const
return int64_t(time.mSampleTime);
}
void RageSound_CA::FillConverter( void *data, UInt32 frames )
{
RageTimer tm;
Mix( (int16_t *)data, frames, mDecodePos, mNow );
g_fLastMixTimes[g_fLastMixTimePos] = tm.GetDeltaTime();
++g_fLastMixTimePos;
wrap( g_fLastMixTimePos, NUM_MIX_TIMES );
}
OSStatus RageSound_CA::GetData(AudioDeviceID inDevice,
const AudioTimeStamp *inNow,
const AudioBufferList *inInputData,
@@ -276,50 +177,21 @@ OSStatus RageSound_CA::GetData(AudioDeviceID inDevice,
RageTimer tm;
RageSound_CA *This = (RageSound_CA *)inClientData;
AudioBuffer& buf = outOutputData->mBuffers[0];
UInt32 dataPackets = buf.mDataByteSize;
UInt32 dataPackets = buf.mDataByteSize >> 3; // 8 byes per packet
int64_t decodePos = int64_t(inOutputTime->mSampleTime);
int64_t now = int64_t(inNow->mSampleTime);
if (This->mFormat == OTHER)
{
dataPackets /= gConverter->GetOutputFormat().mBytesPerPacket;
This->mDecodePos = decodePos;
This->mNow = now;
gConverter->FillComplexBuffer(dataPackets, *outOutputData, NULL);
}
else
{
RageTimer mixTM;
RageTimer tm2;
int16_t buffer[dataPackets * (kBytesPerPacket >> 1)];
if (This->mFormat == EXACT)
{
dataPackets /= kBytesPerPacket;
This->Mix((int16_t *)buf.mData, dataPackets, decodePos, now);
}
else // This->mFormat == CANONICAL
{
dataPackets /= 8; // 8 bytes per packet (1 frame per packet)
int16_t buffer[dataPackets * kBytesPerPacket];
int16_t *ip = buffer;
float *fp = (float *)buf.mData;
This->Mix(buffer, dataPackets, decodePos, now);
// Convert from signed 16 bit int to signed 32 bit float
for (unsigned i = 0; i < buf.mDataByteSize; i += 4)
{
int16_t val = *(ip++);
*(fp++) = val / (val < 0 ? 32768.0f : 32767.0f);
}
}
This->Mix(buffer, dataPackets, decodePos, now);
g_fLastMixTimes[g_fLastMixTimePos] = tm2.GetDeltaTime();
++g_fLastMixTimePos;
wrap(g_fLastMixTimePos, NUM_MIX_TIMES);
AudioConverterConvertBuffer(This->mConverter, dataPackets * kBytesPerPacket,
buffer, &buf.mDataByteSize, buf.mData);
g_fLastMixTimes[g_fLastMixTimePos] = tm.GetDeltaTime();
++g_fLastMixTimePos;
wrap(g_fLastMixTimePos, NUM_MIX_TIMES);
}
g_fLastIOProcTime = tm.GetDeltaTime();
++g_iNumIOProcCalls;
+3 -11
View File
@@ -5,6 +5,8 @@
struct AudioTimeStamp;
struct AudioBufferList;
struct OpaqueAudioConverter;
typedef struct OpaqueAudioConverter *AudioConverterRef;
typedef unsigned long UInt32;
typedef UInt32 AudioDeviceID;
typedef UInt32 AudioDevicePropertyID;
@@ -15,20 +17,11 @@ class RageSoundBase;
class RageSound_CA : public RageSound_Generic_Software
{
public:
enum format
{
EXACT,
CANONICAL,
OTHER
};
private:
int64_t mDecodePos;
float mLatency;
CAAudioHardwareDevice *mOutputDevice;
int64_t mNow;
format mFormat;
AudioConverterRef mConverter;
static OSStatus GetData(AudioDeviceID inDevice,
const AudioTimeStamp *inNow,
@@ -45,7 +38,6 @@ private:
void *inData);
public:
void FillConverter( void *data, UInt32 frames );
RageSound_CA();
~RageSound_CA();
float GetPlayLatency() const { return mLatency; }