failing an assertion but getting closer

This commit is contained in:
Steve Checkoway
2003-07-09 08:27:27 +00:00
parent a98fffe856
commit 2156e76b0b
2 changed files with 273 additions and 147 deletions
+260 -125
View File
@@ -13,42 +13,50 @@
#include "RageUtil.h" #include "RageUtil.h"
#include "RageLog.h" #include "RageLog.h"
#include <QuickTime/QuickTime.h> #include <QuickTime/QuickTime.h>
#include <stdlib.h> #include <queue>
#include <vector>
/* Ugh */ /* Ugh */
using namespace QT; using namespace QT;
const unsigned channels = 2; const unsigned channels = 2;
const unsigned samplesize = channels*16; const unsigned samplesize = channels*16;
const unsigned samples = 512; const unsigned samples = 4096;
const unsigned freq = 44100; const unsigned freq = 44100;
const unsigned buffersize = samples*samplesize/8; const unsigned buffersize = samples*samplesize/8;
const unsigned SndCommand_qLength = 2;
const unsigned initialQTSoundChannels = 8;
/* Oh boy! dealing with memory at inturupt time. What fun! */
#pragma options align=power #pragma options align=power
static volatile Uint32 fill_me = 0; struct channelInfo {
static UInt8 *buffer[2]; RageSound *snd;
static CmpSoundHeader header; bool stopping;
int flush_pos;
int last_pos;
int got;
Uint64 start_time;
volatile Uint32 fill_me;
SndChannelPtr channel;
CmpSoundHeader header;
Uint8 *buffer[2];
channelInfo() { snd=NULL; channel=NULL; }
void reset() { stopping=false; flush_pos=0; last_pos=0; start_time=0; fill_me=0; }
};
static queue<channelInfo *>free_channels;
static vector<channelInfo *>playing_channels;
static ComponentInstance soundClock;
RageSound_QT::RageSound_QT() { RageSound_QT::RageSound_QT() {
#if 1
RageException::ThrowNonfatal("Class not finished!"); RageException::ThrowNonfatal("Class not finished!");
#endif
/*SndCallBackUPP callback; if (!CanPlayMultiChannels())
callback = NewSndCallBackUPP(GetData); RageException::ThrowNonfatal("Multiple Sound Manager channels not supported. Fallback");
memset(&header, 0, sizeof(header));
header.numChannels = channels;
header.sampleSize = samplesize / channels;
header.sampleRate = rate44khz; /* really 44.1kHz * /
header.numFrames = samples;
header.encode = cmpSH;
buffer[0] = new Uint8[buffersize]; channelInfo *info;
buffer[1] = new Uint8[buffersize]; SndCallBackUPP callback = NewSndCallBackUPP(GetData);
memset(buffer[0], 0, buffersize); OSErr err;
memset(buffer[1], 0, buffersize);
channel = new SndChannel;
channel->userInfo = reinterpret_cast<long>(this);
channel->qLength = 2;
soundOutput = OpenDefaultComponent(kSoundOutputDeviceType, NULL); soundOutput = OpenDefaultComponent(kSoundOutputDeviceType, NULL);
ASSERT(soundOutput != NULL); ASSERT(soundOutput != NULL);
@@ -56,160 +64,287 @@ RageSound_QT::RageSound_QT() {
TimeRecord record; TimeRecord record;
SoundComponentGetInfo(soundOutput, NULL, siOutputLatency, &record); SoundComponentGetInfo(soundOutput, NULL, siOutputLatency, &record);
latency = record.value.lo / record.scale; latency = record.value.lo / record.scale;
latency += samples / freq; /* double buffer * /
OSErr err = SndNewChannel(&channel, sampledSynth, initStereo, callback); playing_channels.reserve(initialQTSoundChannels);
for (unsigned i=0; i<initialQTSoundChannels; ++i) {
info = new channelInfo;
info->buffer[0] = new Uint8[buffersize];
info->buffer[1] = new Uint8[buffersize];
info->channel = new SndChannel;
info->channel->userInfo = reinterpret_cast<long>(this);
info->channel->qLength = SndCommand_qLength;
info->header.numChannels = channels;
info->header.sampleSize = samplesize / channels;
info->header.sampleRate = rate44khz;
info->header.encode = cmpSH;
err = SndNewChannel(&info->channel, sampledSynth, initStereo, callback);
if (err != noErr) { if (err != noErr) {
delete channel; SAFE_DELETE(info->channel);
channel = NULL; RageException::ThrowNonfatal("Unable to create audio channel. THIS IS A BUG!");
RageException::ThrowNonfatal("Unable to create audio channel"); }
free_channels.push(info);
} }
SndCommand cmd; SndCommand cmd;
cmd.cmd = clockComponentCmd; cmd.cmd = clockComponentCmd;
cmd.param1 = true; cmd.param1 = true;
cmd.param2 = 0; cmd.param2 = 0;
err |= SndDoImmediate(channel, &cmd); err = SndDoImmediate(info->channel, &cmd);
cmd.cmd = getClockComponentCmd; cmd.cmd = getClockComponentCmd;
cmd.param1 = 0; cmd.param1 = 0;
cmd.param2 = reinterpret_cast<long>(&clock); cmd.param2 = reinterpret_cast<long>(&soundClock);
err |= SndDoImmediate(channel, &cmd); err |= SndDoImmediate(info->channel, &cmd);
last_pos = 0;
cmd.cmd = callBackCmd;
cmd.param2 = 0;
err |= SndDoCommand(channel, &cmd, false);
if (err != noErr) if (err != noErr)
RageException::ThrowNonfatal("Unable to create audio channel");*/ RageException::ThrowNonfatal("Unable to create audio channel. THIS IS A BUG!");
} }
RageSound_QT::~RageSound_QT() { RageSound_QT::~RageSound_QT() {
/*if (channel) unsigned size = playing_channels.size();
SndDisposeChannel(channel, true);
SAFE_DELETE_ARRAY(buffer[0]); for (unsigned i=0; i<size; ++i) {
SAFE_DELETE_ARRAY(buffer[1]); channelInfo *info = playing_channels.back();
if (info->channel)
SndDisposeChannel(info->channel, true);
SAFE_DELETE_ARRAY(info->buffer[0]);
SAFE_DELETE_ARRAY(info->buffer[1]);
playing_channels.pop_back();
}
while (!free_channels.empty()) {
channelInfo *info = free_channels.front();
if (info->channel)
SndDisposeChannel(info->channel, true);
SAFE_DELETE_ARRAY(info->buffer[0]);
SAFE_DELETE_ARRAY(info->buffer[1]);
free_channels.pop();
}
if (soundOutput) if (soundOutput)
CloseComponent(soundOutput);*/ CloseComponent(soundOutput);
if (soundClock)
CloseComponent(soundClock);
} }
void RageSound_QT::GetData(SndChannel *chan, SndCommand *cmd_passed) { void RageSound_QT::GetData(SndChannelPtr chan, SndCommand *cmd_passed) {
/*while (!SOUNDMAN)
SDL_Delay(10);
LockMutex L(SOUNDMAN->lock); LockMutex L(SOUNDMAN->lock);
static bool gettingData=false;
static int recursiveCalls = 0;
if (gettingData) {
LOG->Warn( "GetData() called recursively %D times.", ++recursiveCalls);
if (recursiveCalls >=10)
RageException::Throw("GetData() called recursively too many times.");
} else
recursiveCalls = 0;
gettingData = true;
static SoundMixBuffer mix;
RageSound_QT *P = reinterpret_cast<RageSound_QT *>(chan->userInfo);
fill_me = cmd_passed->param2; channelInfo *info = reinterpret_cast<channelInfo *>(chan->userInfo);
UInt32 play_me = !fill_me; ASSERT(info);
ASSERT(chan == info->channel);
if (!P->last_pos){
TimeRecord tr;
ClockGetTime(P->clock, &tr);
UInt64 temp = tr.value.hi;
temp <<= 32;
temp |= tr.value.lo;
double d = static_cast<double>(temp)/tr.scale*freq;
temp = static_cast<UInt64>(d);
P->last_pos = static_cast<UInt32>(temp % 0x00000000FFFFFFFFLL);
} else
P->last_pos += samples;
/* Swap buffers * /
header.samplePtr = reinterpret_cast<Ptr>(buffer[play_me]);
SndCommand cmd; SndCommand cmd;
Uint32 play_me;
OSErr err;
int got;
if (cmd_passed) {
info->fill_me = cmd_passed->param2;
play_me = !info->fill_me;
info->header.samplePtr = reinterpret_cast<Ptr>(info->buffer[play_me]);
info->header.numFrames = info->got;
cmd.cmd = bufferCmd; cmd.cmd = bufferCmd;
cmd.param1 = 0; cmd.param1 = 0;
cmd.param2 = reinterpret_cast<long>(&header); cmd.param2 = reinterpret_cast<long>(&info->header);
SndDoCommand(chan, &cmd, 0); err = SndDoCommand(chan, &cmd, false);
if (err != noErr)
/* Clear the fill buffer * / goto bail;
memset(buffer[fill_me], 0, buffersize);
for (unsigned i=0; i<P->sounds.size(); ++i) {
if (P->sounds[i]->stopping)
continue;
unsigned got = P->sounds[i]->snd->GetPCM(reinterpret_cast<char *>(buffer[fill_me]), buffersize, P->last_pos);
mix.write(reinterpret_cast<SInt16 *>(buffer[fill_me]), got / 2);
if (got < buffersize) {
P->sounds[i]->stopping = true;
P->sounds[i]->flush_pos = P->last_pos + (got * 8 / samplesize);
}
} }
mix.read(reinterpret_cast<SInt16 *>(buffer[fill_me])); if (info->stopping)
return;
memset(info->buffer[info->fill_me], 0, buffersize);
if (!info->last_pos) {
TimeRecord tr;
ClockGetTime(soundClock, &tr);
info->start_time = tr.value.hi;
info->start_time <<= 32;
info->start_time |= tr.value.lo;
}
got = info->snd->GetPCM(reinterpret_cast<char *>(info->buffer[info->fill_me]), buffersize, info->last_pos);
if (static_cast<unsigned>(got) < buffersize) {
info->stopping = true;
info->flush_pos = info->last_pos + (got * 8 / samplesize);
}
info->got = got * 8 / samplesize; /* got / (samplesize / 8) */
info->last_pos += samples;
if (cmd_passed) {
cmd.cmd = callBackCmd; cmd.cmd = callBackCmd;
cmd.param2 = play_me; cmd.param2 = play_me;
SndDoCommand(chan, &cmd, 0); err = SndDoCommand(chan, &cmd, false);
gettingData=false;*/ if (err != noErr)
goto bail;
}
return;
bail:
RageException::Throw("SndDoCommand failed with error %d", err);
} }
void RageSound_QT::StartMixing(RageSound *snd) { void RageSound_QT::StartMixing(RageSound *snd) {
/*sound *s = new sound;
s->snd = snd;
LockMutex L(SOUNDMAN->lock); LockMutex L(SOUNDMAN->lock);
sounds.push_back(s); SndCommand cmd;
LOG->Trace("There are %D sounds playing", sounds.size());*/ channelInfo *info;
if (free_channels.size()) {
info = free_channels.front();
free_channels.pop();
} else { /* No free audio channels, create another */
LOG->Warn("Out of audio channels, creating channel number %D", playing_channels.size()+1);
info = new channelInfo;
memset(&info->header, 0, sizeof(&info->header));
info->header.numChannels = channels;
info->header.sampleSize = samplesize / channels;
info->header.sampleRate = rate44khz; /* really 44.1kHz */
info->header.numFrames = samples;
info->header.encode = cmpSH;
info->buffer[0] = new Uint8[buffersize];
info->buffer[1] = new Uint8[buffersize];
memset(info->buffer[0], 0, buffersize);
memset(info->buffer[1], 0, buffersize);
info->channel = new SndChannel;
info->channel->userInfo = reinterpret_cast<long>(info);
info->channel->qLength = SndCommand_qLength;
OSErr err = SndNewChannel(&(info->channel), sampledSynth, initStereo, NewSndCallBackUPP(GetData));
if (err != noErr) {
SAFE_DELETE(info->channel);
RageException::Throw("Unable to create audio channel. THIS IS A BUG.");
}
}
info->reset();
info->snd = snd;
/* Fill the first buffer with data */
playing_channels.push_back(info);
GetData(info->channel, NULL);
cmd.cmd = callBackCmd;
cmd.param1 = 0;
cmd.param2 = !info->fill_me;
SndDoCommand(info->channel, &cmd, 0); /* command queue is empty */
} }
void RageSound_QT::StopMixing(RageSound *snd) { void RageSound_QT::StopMixing(RageSound *snd) {
/*LockMutex L(SOUNDMAN->lock); LockMutex L(SOUNDMAN->lock);
LOG->Trace("StopMixing(). Stopping sound 0x%X", reinterpret_cast<long>(snd));
/* Find the sound. */
int size = playing_channels.size();
int i;
channelInfo *info;
/* Find the sound. * / for (i=0; i<=size; ++i)
unsigned i; if ((info=playing_channels[i])->snd == snd)
for(i = 0; i < sounds.size(); ++i) break;
if(sounds[i]->snd == snd) break; if (i==size) {
if(i == sounds.size()) LOG->Trace("Not stopping a sound because it's not playing.");
{
LOG->Trace("not stopping a sound because it's not playing");
return; return;
} }
delete sounds[i]; playing_channels.erase(playing_channels.begin()+i);
sounds.erase(sounds.begin()+i, sounds.begin()+i+1); SndCommand cmd;
LOG->Trace("There are %D sounds playing", sounds.size());*/ cmd.cmd = flushCmd;
cmd.param1 = 0;
cmd.param2 = 0;
LOG->Trace("Flushing channel with song %X", reinterpret_cast<long>(snd));
SndDoImmediate(info->channel, &cmd);
cmd.cmd = quietCmd;
SndDoImmediate(info->channel, &cmd);
free_channels.push(info);
} }
void RageSound_QT::Update(float delta) { void RageSound_QT::Update(float delta) {
#pragma unused (delta) #pragma unused (delta)
/*LockMutex L(SOUNDMAN->lock); LockMutex L(SOUNDMAN->lock);
vector<sound *>snds = sounds; vector<channelInfo *> playing = playing_channels;
for (unsigned i = 0; i < snds.size(); ++i) { int size = playing.size();
if (!sounds[i]->stopping) for (int i=0; i<size; ++i) {
channelInfo *info = playing[i];
if (info->stopping != 0)
continue; continue;
if (GetPosition(snds[i]->snd) < sounds[i]->flush_pos) if (GetPosition(info->snd) < info->flush_pos)
continue; continue;
snds[i]->snd->StopPlaying(); info->snd->StopPlaying();
}*/ }
} }
int RageSound_QT::GetPosition(const RageSound *snd) const { int RageSound_QT::GetPosition(const RageSound *snd) const {
#pragma unused (snd) LockMutex L(SOUNDMAN->lock);
/*TimeRecord tr; TimeRecord tr;
ClockGetTime(clock, &tr); ClockGetTime(soundClock, &tr);
UInt64 temp = tr.value.hi; UInt64 time = tr.value.hi;
temp <<= 32; time <<= 32;
temp |= tr.value.lo; time |= tr.value.lo;
double d = static_cast<double>(temp)/tr.scale*freq;
temp = static_cast<UInt64>(d); /* Find the info */
return static_cast<UInt32>(temp % 0x00000000FFFFFFFFLL);*/ int size = playing_channels.size();
int i;
channelInfo *info;
for (i=0; i<size; ++i)
if ((info = playing_channels[i])->snd == snd)
break;
if (i == size)
RageException::Throw("Can't get the position of a sound that isn't playing");
LOG->Trace("GetPosition() sound 0x%X", reinterpret_cast<long>(snd));
time -= info->start_time; /* Now time contains the total time the sound was played. */
time = time * freq / tr.scale; /* Now time contains the number of samples played. */
return static_cast<int>(time % 0x00000000FFFFFFFFLL);
} }
float RageSound_QT::GetPlayLatency() const { /* Pascal code from SoundManager docs.
//return latency;
FUNCTION MyCanPlayMultiChannels: Boolean;
VAR
myResponse: LongInt;
myResult: Boolean;
myErr: OSErr;
myVersion: NumVersion;
BEGIN
myResult := FALSE;
myVersion := SndSoundManagerVersion;
myErr := Gestalt(gestaltSoundAttr, myResponse);
IF myVersion.majorRev >= 3 THEN
IF (myErr = noErr) AND (BTst(myResponse, gestaltMultiChannels)) THEN
myResult := TRUE
ELSE
BEGIN
myErr := Gestalt(gestaltHardwareAttr, myResponse);
IF (myErr = noErr) AND (BTst(myResponse, gestaltHasASC)) THEN
myResult := TRUE
END;
MyCanPlayMultiChannels := myResult;
END;
*/
bool RageSound_QT::CanPlayMultiChannels(){
register long response;
register OSErr err;
NumVersion version;
register bool result;
result = false;
version = SndSoundManagerVersion();
err = Gestalt(gestaltSoundAttr, &response);
if (version.majorRev >= 3){
if((err == noErr) && (response & gestaltMultiChannels == gestaltMultiChannels))
result = true;
} else {
err = Gestalt(gestaltHardwareAttr, &response);
if ((err == noErr) && (response & gestaltHasASC == gestaltHasASC))
result = true;
}
return result;
} }
+3 -12
View File
@@ -25,26 +25,17 @@ namespace QT {
class RageSound_QT: public RageSoundDriver { class RageSound_QT: public RageSoundDriver {
private: private:
struct sound {
RageSound *snd;
bool stopping;
int flush_pos;
sound() { snd=NULL; stopping=false; flush_pos=0; }
};
vector<sound *> sounds;
QT::ComponentInstance clock;
QT::ComponentInstance soundOutput; QT::ComponentInstance soundOutput;
QT::SndChannelPtr channel;
int last_pos;
float latency; float latency;
bool CanPlayMultiChannels();
protected: protected:
virtual void StartMixing(RageSound *snd); virtual void StartMixing(RageSound *snd);
virtual void StopMixing(RageSound *snd); virtual void StopMixing(RageSound *snd);
virtual int GetPosition(const RageSound *snd) const; virtual int GetPosition(const RageSound *snd) const;
virtual void Update (float delta); virtual void Update (float delta);
virtual float GetPlayLatency() const; virtual float GetPlayLatency() const { return latency; }
public: public:
RageSound_QT(); RageSound_QT();