Not quite working but I can work out a bug or two it _should_ play back the audio much better for computers that support multitple channels (I assume that they all can!) and it should play the same for those computers that don't support it.
Right now I'm getting an error of -223 from the bufferCmd but I haven't figured out what the hell that actually is. If you want a working sound driver for OS X, just use the previous version of these files.
This commit is contained in:
@@ -19,184 +19,410 @@ 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 initialQTSoundChannels = 8;
|
||||||
|
const unsigned SndCommand_qLength = 2;
|
||||||
|
|
||||||
|
static ComponentInstance soundClock;
|
||||||
|
static bool useMultipleChannels;
|
||||||
|
|
||||||
/* 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;
|
volatile bool stopping;
|
||||||
|
volatile int last_pos;
|
||||||
|
volatile int flush_pos;
|
||||||
|
volatile Uint64 start_time;
|
||||||
|
volatile Uint32 fill_me;
|
||||||
|
SndChannelPtr channel;
|
||||||
|
CmpSoundHeader header;
|
||||||
|
Uint8 *buffer[2]; /* Double Buffer */
|
||||||
|
channelInfo() { snd=NULL; channel=NULL; }
|
||||||
|
void reset() { stopping=false; flush_pos=0; last_pos=0; start_time=0; fill_me=0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
static deque<channelInfo *>free_channels;
|
||||||
|
static vector<channelInfo *>playing_channels;
|
||||||
|
|
||||||
RageSound_QT::RageSound_QT() {
|
RageSound_QT::RageSound_QT() {
|
||||||
SndCallBackUPP callback;
|
useMultipleChannels = canPlayMultiChannels();
|
||||||
callback = NewSndCallBackUPP(GetData);
|
LOG->Trace("Can we use multiple channels? %s", (useMultipleChannels ? "yes" : "no"));
|
||||||
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];
|
|
||||||
buffer[1] = new Uint8[buffersize];
|
|
||||||
memset(buffer[0], 0, buffersize);
|
|
||||||
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);
|
||||||
|
|
||||||
TimeRecord record;
|
TimeRecord record;
|
||||||
|
ComponentInstance soundOutput;
|
||||||
SoundComponentGetInfo(soundOutput, NULL, siOutputLatency, &record);
|
SoundComponentGetInfo(soundOutput, NULL, siOutputLatency, &record);
|
||||||
latency = record.value.lo / record.scale;
|
latency = record.value.lo / record.scale;
|
||||||
|
if (!useMultipleChannels)
|
||||||
latency += samples / freq; /* double buffer */
|
latency += samples / freq; /* double buffer */
|
||||||
|
|
||||||
OSErr err = SndNewChannel(&channel, sampledSynth, initStereo, callback);
|
SndCallBackUPP callback = NewSndCallBackUPP(GetData);
|
||||||
|
|
||||||
if (err != noErr) {
|
|
||||||
delete channel;
|
|
||||||
channel = NULL;
|
|
||||||
RageException::ThrowNonfatal("Unable to create audio channel");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
soundClock = NULL;
|
||||||
|
SndChannelPtr chan = NULL;
|
||||||
SndCommand cmd;
|
SndCommand cmd;
|
||||||
|
OSErr err;
|
||||||
|
Uint8 *zero = NULL;
|
||||||
|
Uint8 *one = NULL;
|
||||||
|
|
||||||
|
for (unsigned i=0; i<initialQTSoundChannels; ++i) {
|
||||||
|
channelInfo *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;
|
||||||
|
|
||||||
|
if (useMultipleChannels || zero == NULL) {
|
||||||
|
zero = new Uint8[buffersize];
|
||||||
|
one = new Uint8[buffersize];
|
||||||
|
memset(zero, 0, buffersize);
|
||||||
|
memset(one, 0, buffersize);
|
||||||
|
}
|
||||||
|
info->buffer[0] = zero;
|
||||||
|
info->buffer[1] = one;
|
||||||
|
|
||||||
|
if (useMultipleChannels || chan == NULL) {
|
||||||
|
chan = new SndChannel;
|
||||||
|
chan->userInfo = reinterpret_cast<long>(info);
|
||||||
|
chan->qLength = SndCommand_qLength;
|
||||||
|
err = SndNewChannel(&chan, sampledSynth, initStereo, callback);
|
||||||
|
if (err != noErr) {
|
||||||
|
SAFE_DELETE(info->channel);
|
||||||
|
RageException::ThrowNonfatal("Unable to create audio channel. THIS IS A BUG!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info->channel = chan;
|
||||||
|
|
||||||
|
if (!soundClock) {
|
||||||
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!");
|
||||||
|
free_channels.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(free_channels.size() == initialQTSoundChannels);
|
||||||
|
playing_channels.reserve(initialQTSoundChannels);
|
||||||
|
if (!useMultipleChannels) { /* start the callbacks */
|
||||||
|
cmd.cmd = callBackCmd;
|
||||||
|
cmd.param1 = 0;
|
||||||
|
cmd.param2 = 0;
|
||||||
|
err |= SndDoCommand(chan, &cmd, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RageSound_QT::~RageSound_QT() {
|
RageSound_QT::~RageSound_QT() {
|
||||||
if (channel)
|
LockMutex L(SOUNDMAN->lock);
|
||||||
SndDisposeChannel(channel, true);
|
int size = playing_channels.size();
|
||||||
SAFE_DELETE_ARRAY(buffer[0]);
|
|
||||||
SAFE_DELETE_ARRAY(buffer[1]);
|
for (int i=0; i<size; ++i) {
|
||||||
|
channelInfo *info = playing_channels.back();
|
||||||
|
|
||||||
|
playing_channels.pop_back();
|
||||||
|
if (info->channel)
|
||||||
|
SndDisposeChannel(info->channel, true);
|
||||||
|
SAFE_DELETE_ARRAY(info->buffer[0]);
|
||||||
|
SAFE_DELETE_ARRAY(info->buffer[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
size = free_channels.size();
|
||||||
|
for (int i=0; i<size; ++i) {
|
||||||
|
channelInfo *info = free_channels.back();
|
||||||
|
|
||||||
|
free_channels.pop_back();
|
||||||
|
if (info->channel)
|
||||||
|
SndDisposeChannel(info->channel, true);
|
||||||
|
SAFE_DELETE_ARRAY(info->buffer[0]);
|
||||||
|
SAFE_DELETE_ARRAY(info->buffer[1]);
|
||||||
|
if (!useMultipleChannels)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
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 SoundMixBuffer mix;
|
register OSErr err = noErr;
|
||||||
RageSound_QT *P = reinterpret_cast<RageSound_QT *>(chan->userInfo);
|
static bool gettingData=false;
|
||||||
|
static int recursiveCalls = 0;
|
||||||
fill_me = cmd_passed->param2;
|
if (gettingData) {
|
||||||
UInt32 play_me = !fill_me;
|
LOG->Warn( "GetData() called recursively %D times.", ++recursiveCalls);
|
||||||
|
if (recursiveCalls >=10)
|
||||||
if (!P->last_pos){
|
RageException::Throw("GetData() called recursively too many times.");
|
||||||
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
|
} else
|
||||||
P->last_pos += samples;
|
recursiveCalls = 0;
|
||||||
|
gettingData=true;
|
||||||
/* Swap buffers */
|
channelInfo *info = reinterpret_cast<channelInfo *>(chan->userInfo);
|
||||||
header.samplePtr = reinterpret_cast<Ptr>(buffer[play_me]);
|
ASSERT(info);
|
||||||
|
LOG->Trace("GetData() sound 0x%X", reinterpret_cast<long>(info->snd));
|
||||||
SndCommand cmd;
|
SndCommand cmd;
|
||||||
|
register UInt32 play_me;
|
||||||
|
|
||||||
|
if (cmd_passed != NULL) {
|
||||||
|
info->fill_me = cmd_passed->param2;
|
||||||
|
play_me = !info->fill_me;
|
||||||
|
info->header.samplePtr = reinterpret_cast<Ptr>(info->buffer[play_me]);
|
||||||
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);
|
LOG->Trace("Sending bufferCmd sound 0x%X", reinterpret_cast<long>(info->snd));
|
||||||
|
err = SndDoCommand(chan, &cmd, false);
|
||||||
|
if (err != noErr)
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->stopping){
|
||||||
|
LOG->Trace("Stopping");
|
||||||
|
return; /* We're done */
|
||||||
|
}
|
||||||
|
|
||||||
/* Clear the fill buffer */
|
/* Clear the fill buffer */
|
||||||
memset(buffer[fill_me], 0, buffersize);
|
memset(info->buffer[info->fill_me], 0, buffersize);
|
||||||
|
|
||||||
for (unsigned i=0; i<P->sounds.size(); ++i) {
|
if (useMultipleChannels) {
|
||||||
if (P->sounds[i]->stopping)
|
if (!info->last_pos){
|
||||||
continue;
|
TimeRecord tr;
|
||||||
|
ClockGetTime(soundClock, &tr);
|
||||||
unsigned got = P->sounds[i]->snd->GetPCM(reinterpret_cast<char *>(buffer[fill_me]), buffersize, P->last_pos);
|
info->start_time = tr.value.hi;
|
||||||
mix.write(reinterpret_cast<SInt16 *>(buffer[fill_me]), got / 2);
|
info->start_time <<= 32;
|
||||||
if (got < buffersize) {
|
info->start_time |= tr.value.lo;
|
||||||
P->sounds[i]->stopping = true;
|
|
||||||
P->sounds[i]->flush_pos = P->last_pos + (got / samplesize / 8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mix.read(reinterpret_cast<SInt16 *>(buffer[fill_me]));
|
LOG->Trace("About to GetPCM() sound 0x%X", reinterpret_cast<long>(info->snd));
|
||||||
|
register int got = info->snd->GetPCM(reinterpret_cast<char *>(info->buffer[info->fill_me]), buffersize, info->last_pos);
|
||||||
|
LOG->Trace("Got %D bytes", got);
|
||||||
|
if (static_cast<unsigned>(got) < buffersize) {
|
||||||
|
info->stopping = true;
|
||||||
|
info->flush_pos = info->last_pos + (got * 8 / samplesize); /* (got / (samplesize / 8)) */
|
||||||
|
}
|
||||||
|
info->last_pos += samples;
|
||||||
|
} else {
|
||||||
|
static SoundMixBuffer mix;
|
||||||
|
register int size = playing_channels.size();
|
||||||
|
for (int i=0; i<size; ++i) {
|
||||||
|
channelInfo *info2 = playing_channels[i];
|
||||||
|
if (!info2->last_pos){
|
||||||
|
TimeRecord tr;
|
||||||
|
ClockGetTime(soundClock, &tr);
|
||||||
|
info2->start_time = tr.value.hi;
|
||||||
|
info2->start_time <<= 32;
|
||||||
|
info2->start_time |= tr.value.lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG->Trace("About to GetPCM() sound 0x%X", reinterpret_cast<long>(info2->snd));
|
||||||
|
register int got = info2->snd->GetPCM(reinterpret_cast<char *>(info->buffer[info->fill_me]), buffersize, info2->last_pos);
|
||||||
|
LOG->Trace("Got %D bytes", got);
|
||||||
|
if (static_cast<unsigned>(got) < buffersize) {
|
||||||
|
info2->stopping = true;
|
||||||
|
info2->flush_pos = info2->last_pos + (got * 8 / samplesize);
|
||||||
|
}
|
||||||
|
mix.write(reinterpret_cast<Sint16 *>(info->buffer[info->fill_me]), got / 2);
|
||||||
|
info2->last_pos += samples;
|
||||||
|
}
|
||||||
|
mix.read(reinterpret_cast<Sint16 *>(info->buffer[info->fill_me]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd_passed != NULL) {
|
||||||
cmd.cmd = callBackCmd;
|
cmd.cmd = callBackCmd;
|
||||||
cmd.param2 = play_me;
|
cmd.param2 = play_me;
|
||||||
SndDoCommand(chan, &cmd, 0);
|
LOG->Trace("Sending callBackCmd sound 0x%X", reinterpret_cast<long>(info->snd));
|
||||||
|
err = SndDoCommand(chan, &cmd, false);
|
||||||
|
if (err != noErr)
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
gettingData=false;
|
||||||
|
return;
|
||||||
|
|
||||||
|
bail:
|
||||||
|
RageException::Throw("SndDoCommand failed with error %d", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RageSound_QT::StartMixing(RageSound *snd) {
|
|
||||||
sound *s = new sound;
|
|
||||||
s->snd = snd;
|
|
||||||
|
|
||||||
|
void RageSound_QT::StartMixing(RageSound *snd) {
|
||||||
LockMutex L(SOUNDMAN->lock);
|
LockMutex L(SOUNDMAN->lock);
|
||||||
sounds.push_back(s);
|
LOG->Trace("StartMixing(). Mixing sound 0x%X", reinterpret_cast<long>(snd));
|
||||||
|
SndCommand cmd;
|
||||||
|
channelInfo *info;
|
||||||
|
|
||||||
|
if (free_channels.size()) {
|
||||||
|
info = free_channels.front();
|
||||||
|
free_channels.pop_front();
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
if (useMultipleChannels) {
|
||||||
|
info->channel = new SndChannel;
|
||||||
|
info->channel->userInfo = reinterpret_cast<long>(info);
|
||||||
|
info->channel->qLength = SndCommand_qLength;
|
||||||
|
register 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);
|
||||||
|
if (useMultipleChannels) {
|
||||||
|
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. */
|
/* Find the sound. */
|
||||||
unsigned i;
|
int size = playing_channels.size();
|
||||||
for(i = 0; i < sounds.size(); ++i)
|
int i;
|
||||||
if(sounds[i]->snd == snd) break;
|
channelInfo *info;
|
||||||
if(i == sounds.size())
|
|
||||||
{
|
for (i=0; i<=size; ++i)
|
||||||
LOG->Trace("not stopping a sound because it's not playing");
|
if ((info=playing_channels[i])->snd == snd)
|
||||||
|
break;
|
||||||
|
if (i==size) {
|
||||||
|
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);
|
if (useMultipleChannels) {
|
||||||
|
SndCommand cmd;
|
||||||
|
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_back(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);
|
register int size = playing_channels.size();
|
||||||
|
register 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 {
|
float RageSound_QT::GetPlayLatency() const {
|
||||||
return latency;
|
return latency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pascal code from SoundManager docs.
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
* Copyright (c) 2003 Steve Checkoway. All rights reserved.
|
* Copyright (c) 2003 Steve Checkoway. All rights reserved.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#include "global.h"
|
||||||
/* ugh, what a hack! QT includes Carbon/Carbon.h which
|
/* ugh, what a hack! QT includes Carbon/Carbon.h which
|
||||||
* has defs for a few things defined in RageUtil.h which
|
* has defs for a few things defined in RageUtil.h which
|
||||||
* is included in arch.cpp along w/ (eventually) this.
|
* is included in arch.cpp along w/ (eventually) this.
|
||||||
@@ -18,23 +18,13 @@
|
|||||||
namespace QT {
|
namespace QT {
|
||||||
#include <QuickTime/QuickTime.h>
|
#include <QuickTime/QuickTime.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "RageSound.h"
|
#include "RageSound.h"
|
||||||
#include "RageSoundDriver.h"
|
#include "RageSoundDriver.h"
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -50,5 +40,7 @@ public:
|
|||||||
static void GetData(QT::SndChannel *chan, QT::SndCommand *cmd_passed);
|
static void GetData(QT::SndChannel *chan, QT::SndCommand *cmd_passed);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool canPlayMultiChannels();
|
||||||
|
|
||||||
#endif /* RAGE_SOUND_QT */
|
#endif /* RAGE_SOUND_QT */
|
||||||
|
|
||||||
Reference in New Issue
Block a user