Seperate the QuickTime sound drivers into two classes.
One (working atm) class for a single Sound Manager channel. One (not working atm) class for multiple Sound Manager channels.
This commit is contained in:
@@ -19,148 +19,86 @@ using namespace QT;
|
|||||||
|
|
||||||
const unsigned channels = 2;
|
const unsigned channels = 2;
|
||||||
const unsigned samplesize = channels*16;
|
const unsigned samplesize = channels*16;
|
||||||
const unsigned samples = 4096;
|
const unsigned samples = 512;
|
||||||
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
|
||||||
struct channelInfo {
|
static volatile Uint32 fill_me = 0;
|
||||||
RageSound *snd;
|
static UInt8 *buffer[2];
|
||||||
volatile bool stopping;
|
static CmpSoundHeader header;
|
||||||
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() {
|
||||||
useMultipleChannels = canPlayMultiChannels();
|
RageException::ThrowNonfatal("Class not finished!");
|
||||||
LOG->Trace("Can we use multiple channels? %s", (useMultipleChannels ? "yes" : "no"));
|
|
||||||
|
/*SndCallBackUPP callback;
|
||||||
|
callback = NewSndCallBackUPP(GetData);
|
||||||
|
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 * /
|
||||||
|
|
||||||
SndCallBackUPP callback = NewSndCallBackUPP(GetData);
|
OSErr err = SndNewChannel(&channel, sampledSynth, initStereo, callback);
|
||||||
|
|
||||||
soundClock = NULL;
|
|
||||||
SndChannelPtr chan = NULL;
|
|
||||||
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) {
|
if (err != noErr) {
|
||||||
SAFE_DELETE(info->channel);
|
delete channel;
|
||||||
RageException::ThrowNonfatal("Unable to create audio channel. THIS IS A BUG!");
|
channel = NULL;
|
||||||
|
RageException::ThrowNonfatal("Unable to create audio channel");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
info->channel = chan;
|
|
||||||
|
|
||||||
if (!soundClock) {
|
SndCommand cmd;
|
||||||
cmd.cmd = clockComponentCmd;
|
cmd.cmd = clockComponentCmd;
|
||||||
cmd.param1 = true;
|
cmd.param1 = true;
|
||||||
cmd.param2 = 0;
|
cmd.param2 = 0;
|
||||||
err |= SndDoImmediate(info->channel, &cmd);
|
err |= SndDoImmediate(channel, &cmd);
|
||||||
|
|
||||||
cmd.cmd = getClockComponentCmd;
|
cmd.cmd = getClockComponentCmd;
|
||||||
cmd.param1 = 0;
|
cmd.param1 = 0;
|
||||||
cmd.param2 = reinterpret_cast<long>(&soundClock);
|
cmd.param2 = reinterpret_cast<long>(&clock);
|
||||||
err |= SndDoImmediate(info->channel, &cmd);
|
err |= SndDoImmediate(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. THIS IS A BUG!");
|
RageException::ThrowNonfatal("Unable to create audio channel");*/
|
||||||
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() {
|
||||||
LockMutex L(SOUNDMAN->lock);
|
/*if (channel)
|
||||||
int size = playing_channels.size();
|
SndDisposeChannel(channel, true);
|
||||||
|
SAFE_DELETE_ARRAY(buffer[0]);
|
||||||
for (int i=0; i<size; ++i) {
|
SAFE_DELETE_ARRAY(buffer[1]);
|
||||||
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(SndChannelPtr chan, SndCommand *cmd_passed) {
|
void RageSound_QT::GetData(SndChannel *chan, SndCommand *cmd_passed) {
|
||||||
|
/*while (!SOUNDMAN)
|
||||||
|
SDL_Delay(10);
|
||||||
LockMutex L(SOUNDMAN->lock);
|
LockMutex L(SOUNDMAN->lock);
|
||||||
register OSErr err = noErr;
|
|
||||||
static bool gettingData=false;
|
static bool gettingData=false;
|
||||||
static int recursiveCalls = 0;
|
static int recursiveCalls = 0;
|
||||||
if (gettingData) {
|
if (gettingData) {
|
||||||
@@ -170,259 +108,108 @@ void RageSound_QT::GetData(SndChannelPtr chan, SndCommand *cmd_passed) {
|
|||||||
} else
|
} else
|
||||||
recursiveCalls = 0;
|
recursiveCalls = 0;
|
||||||
gettingData = true;
|
gettingData = true;
|
||||||
channelInfo *info = reinterpret_cast<channelInfo *>(chan->userInfo);
|
static SoundMixBuffer mix;
|
||||||
ASSERT(info);
|
RageSound_QT *P = reinterpret_cast<RageSound_QT *>(chan->userInfo);
|
||||||
LOG->Trace("GetData() sound 0x%X", reinterpret_cast<long>(info->snd));
|
|
||||||
SndCommand cmd;
|
|
||||||
register UInt32 play_me;
|
|
||||||
|
|
||||||
if (cmd_passed != NULL) {
|
fill_me = cmd_passed->param2;
|
||||||
info->fill_me = cmd_passed->param2;
|
UInt32 play_me = !fill_me;
|
||||||
play_me = !info->fill_me;
|
|
||||||
info->header.samplePtr = reinterpret_cast<Ptr>(info->buffer[play_me]);
|
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;
|
||||||
cmd.cmd = bufferCmd;
|
cmd.cmd = bufferCmd;
|
||||||
cmd.param1 = 0;
|
cmd.param1 = 0;
|
||||||
cmd.param2 = reinterpret_cast<long>(&info->header);
|
cmd.param2 = reinterpret_cast<long>(&header);
|
||||||
LOG->Trace("Sending bufferCmd sound 0x%X", reinterpret_cast<long>(info->snd));
|
SndDoCommand(chan, &cmd, 0);
|
||||||
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(info->buffer[info->fill_me], 0, buffersize);
|
memset(buffer[fill_me], 0, buffersize);
|
||||||
|
|
||||||
if (useMultipleChannels) {
|
for (unsigned i=0; i<P->sounds.size(); ++i) {
|
||||||
if (!info->last_pos){
|
if (P->sounds[i]->stopping)
|
||||||
TimeRecord tr;
|
continue;
|
||||||
ClockGetTime(soundClock, &tr);
|
|
||||||
info->start_time = tr.value.hi;
|
unsigned got = P->sounds[i]->snd->GetPCM(reinterpret_cast<char *>(buffer[fill_me]), buffersize, P->last_pos);
|
||||||
info->start_time <<= 32;
|
mix.write(reinterpret_cast<SInt16 *>(buffer[fill_me]), got / 2);
|
||||||
info->start_time |= tr.value.lo;
|
if (got < buffersize) {
|
||||||
|
P->sounds[i]->stopping = true;
|
||||||
|
P->sounds[i]->flush_pos = P->last_pos + (got * 8 / samplesize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG->Trace("About to GetPCM() sound 0x%X", reinterpret_cast<long>(info->snd));
|
mix.read(reinterpret_cast<SInt16 *>(buffer[fill_me]));
|
||||||
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;
|
||||||
LOG->Trace("Sending callBackCmd sound 0x%X", reinterpret_cast<long>(info->snd));
|
SndDoCommand(chan, &cmd, 0);
|
||||||
err = SndDoCommand(chan, &cmd, false);
|
gettingData=false;*/
|
||||||
if (err != noErr)
|
|
||||||
goto bail;
|
|
||||||
}
|
}
|
||||||
gettingData=false;
|
|
||||||
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);
|
||||||
LOG->Trace("StartMixing(). Mixing sound 0x%X", reinterpret_cast<long>(snd));
|
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_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. */
|
|
||||||
int size = playing_channels.size();
|
|
||||||
int i;
|
|
||||||
channelInfo *info;
|
|
||||||
|
|
||||||
for (i=0; i<=size; ++i)
|
/* Find the sound. * /
|
||||||
if ((info=playing_channels[i])->snd == snd)
|
unsigned i;
|
||||||
break;
|
for(i = 0; i < sounds.size(); ++i)
|
||||||
if (i==size) {
|
if(sounds[i]->snd == snd) break;
|
||||||
LOG->Trace("Not stopping a sound because it's not playing.");
|
if(i == sounds.size())
|
||||||
|
{
|
||||||
|
LOG->Trace("not stopping a sound because it's not playing");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
playing_channels.erase(playing_channels.begin()+i);
|
delete sounds[i];
|
||||||
if (useMultipleChannels) {
|
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_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<channelInfo *> playing = playing_channels;
|
vector<sound *>snds = sounds;
|
||||||
int size = playing.size();
|
for (unsigned i = 0; i < snds.size(); ++i) {
|
||||||
for (int i=0; i<size; ++i) {
|
if (!sounds[i]->stopping)
|
||||||
channelInfo *info = playing[i];
|
|
||||||
if (info->stopping != 0)
|
|
||||||
continue;
|
continue;
|
||||||
if (GetPosition(info->snd) < info->flush_pos)
|
if (GetPosition(snds[i]->snd) < sounds[i]->flush_pos)
|
||||||
continue;
|
continue;
|
||||||
info->snd->StopPlaying();
|
snds[i]->snd->StopPlaying();
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
int RageSound_QT::GetPosition(const RageSound *snd) const {
|
int RageSound_QT::GetPosition(const RageSound *snd) const {
|
||||||
LockMutex L(SOUNDMAN->lock);
|
#pragma unused (snd)
|
||||||
TimeRecord tr;
|
/*TimeRecord tr;
|
||||||
ClockGetTime(soundClock, &tr);
|
ClockGetTime(clock, &tr);
|
||||||
UInt64 time = tr.value.hi;
|
UInt64 temp = tr.value.hi;
|
||||||
time <<= 32;
|
temp <<= 32;
|
||||||
time |= tr.value.lo;
|
temp |= tr.value.lo;
|
||||||
|
double d = static_cast<double>(temp)/tr.scale*freq;
|
||||||
/* Find the info */
|
temp = static_cast<UInt64>(d);
|
||||||
register int size = playing_channels.size();
|
return static_cast<UInt32>(temp % 0x00000000FFFFFFFFLL);*/
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
* RageSoundDriver_QT.h
|
* RageSoundDriver_QT.h
|
||||||
* stepmania
|
* stepmania
|
||||||
*
|
*
|
||||||
|
* Use multiple Sound Manager channels to output sound.
|
||||||
|
*
|
||||||
* Created by Steve Checkoway on Mon Jun 23 2003.
|
* Created by Steve Checkoway on Mon Jun 23 2003.
|
||||||
* 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,13 +20,23 @@
|
|||||||
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:
|
||||||
@@ -40,7 +52,4 @@ 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 */
|
||||||
|
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* RageSoundDriver_QT.cpp
|
||||||
|
* stepmania
|
||||||
|
*
|
||||||
|
* Created by Steve Checkoway on Mon Jun 23 2003.
|
||||||
|
* Copyright (c) 2003 Steve Checkoway. All rights reserved.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "RageSoundDriver_QT1.h"
|
||||||
|
#include "RageLog.h"
|
||||||
|
#include "RageUtil.h"
|
||||||
|
#include "RageLog.h"
|
||||||
|
#include <QuickTime/QuickTime.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
/* Ugh */
|
||||||
|
using namespace QT;
|
||||||
|
|
||||||
|
const unsigned channels = 2;
|
||||||
|
const unsigned samplesize = channels*16;
|
||||||
|
const unsigned samples = 512;
|
||||||
|
const unsigned freq = 44100;
|
||||||
|
const unsigned buffersize = samples*samplesize/8;
|
||||||
|
|
||||||
|
/* Oh boy! dealing with memory at inturupt time. What fun! */
|
||||||
|
#pragma options align=power
|
||||||
|
static volatile Uint32 fill_me = 0;
|
||||||
|
static UInt8 *buffer[2];
|
||||||
|
static CmpSoundHeader header;
|
||||||
|
|
||||||
|
RageSound_QT1::RageSound_QT1() {
|
||||||
|
SndCallBackUPP callback;
|
||||||
|
callback = NewSndCallBackUPP(GetData);
|
||||||
|
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);
|
||||||
|
ASSERT(soundOutput != NULL);
|
||||||
|
|
||||||
|
TimeRecord record;
|
||||||
|
SoundComponentGetInfo(soundOutput, NULL, siOutputLatency, &record);
|
||||||
|
latency = record.value.lo / record.scale;
|
||||||
|
latency += samples / freq; /* double buffer */
|
||||||
|
|
||||||
|
OSErr err = SndNewChannel(&channel, sampledSynth, initStereo, callback);
|
||||||
|
|
||||||
|
if (err != noErr) {
|
||||||
|
delete channel;
|
||||||
|
channel = NULL;
|
||||||
|
RageException::ThrowNonfatal("Unable to create audio channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
SndCommand cmd;
|
||||||
|
cmd.cmd = clockComponentCmd;
|
||||||
|
cmd.param1 = true;
|
||||||
|
cmd.param2 = 0;
|
||||||
|
err |= SndDoImmediate(channel, &cmd);
|
||||||
|
|
||||||
|
cmd.cmd = getClockComponentCmd;
|
||||||
|
cmd.param1 = 0;
|
||||||
|
cmd.param2 = reinterpret_cast<long>(&clock);
|
||||||
|
err |= SndDoImmediate(channel, &cmd);
|
||||||
|
last_pos = 0;
|
||||||
|
|
||||||
|
cmd.cmd = callBackCmd;
|
||||||
|
cmd.param2 = 0;
|
||||||
|
err |= SndDoCommand(channel, &cmd, false);
|
||||||
|
|
||||||
|
if (err != noErr)
|
||||||
|
RageException::ThrowNonfatal("Unable to create audio channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
RageSound_QT1::~RageSound_QT1() {
|
||||||
|
if (channel)
|
||||||
|
SndDisposeChannel(channel, true);
|
||||||
|
SAFE_DELETE_ARRAY(buffer[0]);
|
||||||
|
SAFE_DELETE_ARRAY(buffer[1]);
|
||||||
|
if (soundOutput)
|
||||||
|
CloseComponent(soundOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RageSound_QT1::GetData(SndChannel *chan, SndCommand *cmd_passed) {
|
||||||
|
while (!SOUNDMAN)
|
||||||
|
SDL_Delay(10);
|
||||||
|
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_QT1 *P = reinterpret_cast<RageSound_QT1 *>(chan->userInfo);
|
||||||
|
|
||||||
|
fill_me = cmd_passed->param2;
|
||||||
|
UInt32 play_me = !fill_me;
|
||||||
|
|
||||||
|
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;
|
||||||
|
cmd.cmd = bufferCmd;
|
||||||
|
cmd.param1 = 0;
|
||||||
|
cmd.param2 = reinterpret_cast<long>(&header);
|
||||||
|
SndDoCommand(chan, &cmd, 0);
|
||||||
|
|
||||||
|
/* Clear the fill buffer */
|
||||||
|
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]));
|
||||||
|
|
||||||
|
cmd.cmd = callBackCmd;
|
||||||
|
cmd.param2 = play_me;
|
||||||
|
SndDoCommand(chan, &cmd, 0);
|
||||||
|
gettingData=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RageSound_QT1::StartMixing(RageSound *snd) {
|
||||||
|
sound *s = new sound;
|
||||||
|
s->snd = snd;
|
||||||
|
|
||||||
|
LockMutex L(SOUNDMAN->lock);
|
||||||
|
sounds.push_back(s);
|
||||||
|
LOG->Trace("There are %D sounds playing", sounds.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RageSound_QT1::StopMixing(RageSound *snd) {
|
||||||
|
LockMutex L(SOUNDMAN->lock);
|
||||||
|
|
||||||
|
/* Find the sound. */
|
||||||
|
unsigned i;
|
||||||
|
for(i = 0; i < sounds.size(); ++i)
|
||||||
|
if(sounds[i]->snd == snd) break;
|
||||||
|
if(i == sounds.size())
|
||||||
|
{
|
||||||
|
LOG->Trace("not stopping a sound because it's not playing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete sounds[i];
|
||||||
|
sounds.erase(sounds.begin()+i, sounds.begin()+i+1);
|
||||||
|
LOG->Trace("There are %D sounds playing", sounds.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RageSound_QT1::Update(float delta) {
|
||||||
|
#pragma unused (delta)
|
||||||
|
LockMutex L(SOUNDMAN->lock);
|
||||||
|
|
||||||
|
vector<sound *>snds = sounds;
|
||||||
|
for (unsigned i = 0; i < snds.size(); ++i) {
|
||||||
|
if (!sounds[i]->stopping)
|
||||||
|
continue;
|
||||||
|
if (GetPosition(snds[i]->snd) < sounds[i]->flush_pos)
|
||||||
|
continue;
|
||||||
|
snds[i]->snd->StopPlaying();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int RageSound_QT1::GetPosition(const RageSound *snd) const {
|
||||||
|
#pragma unused (snd)
|
||||||
|
TimeRecord tr;
|
||||||
|
ClockGetTime(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);
|
||||||
|
return static_cast<UInt32>(temp % 0x00000000FFFFFFFFLL);
|
||||||
|
}
|
||||||
|
|
||||||
|
float RageSound_QT1::GetPlayLatency() const {
|
||||||
|
return latency;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef RAGE_SOUND_QT1
|
||||||
|
#define RAGE_SOUND_QT1
|
||||||
|
/*
|
||||||
|
* RageSoundDriver_QT1.h
|
||||||
|
* stepmania
|
||||||
|
*
|
||||||
|
* Use only a single Sound Manager channel to output sound.
|
||||||
|
*
|
||||||
|
* Created by Steve Checkoway on Mon Jun 23 2003.
|
||||||
|
* Copyright (c) 2003 Steve Checkoway. All rights reserved.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ugh, what a hack! QT includes Carbon/Carbon.h which
|
||||||
|
* has defs for a few things defined in RageUtil.h which
|
||||||
|
* is included in arch.cpp along w/ (eventually) this.
|
||||||
|
* How do I do this without using phony namespaces?
|
||||||
|
* --Steve
|
||||||
|
*/
|
||||||
|
namespace QT {
|
||||||
|
#include <QuickTime/QuickTime.h>
|
||||||
|
}
|
||||||
|
#include "RageSound.h"
|
||||||
|
#include "RageSoundDriver.h"
|
||||||
|
|
||||||
|
class RageSound_QT1: public RageSoundDriver {
|
||||||
|
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::SndChannelPtr channel;
|
||||||
|
int last_pos;
|
||||||
|
float latency;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void StartMixing(RageSound *snd);
|
||||||
|
virtual void StopMixing(RageSound *snd);
|
||||||
|
virtual int GetPosition(const RageSound *snd) const;
|
||||||
|
virtual void Update (float delta);
|
||||||
|
virtual float GetPlayLatency() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RageSound_QT1();
|
||||||
|
virtual ~RageSound_QT1();
|
||||||
|
static void GetData(QT::SndChannel *chan, QT::SndCommand *cmd_passed);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* RAGE_SOUND_QT1 */
|
||||||
@@ -76,14 +76,13 @@ RageSoundDriver *MakeRageSoundDriver(CString drivers)
|
|||||||
#ifdef RAGE_SOUND_QT
|
#ifdef RAGE_SOUND_QT
|
||||||
if(!DriversToTry[i].CompareNoCase("QT")) ret = new RageSound_QT;
|
if(!DriversToTry[i].CompareNoCase("QT")) ret = new RageSound_QT;
|
||||||
#endif
|
#endif
|
||||||
/*#ifdef RAGE_SOUND_SDL
|
#ifdef RAGE_SOUND_QT1
|
||||||
if(!DriversToTry[i].CompareNoCase("SDL")) ret = new RageSound_SDL;
|
if(!DriversToTry[i].CompareNoCase("QT1")) ret = new RageSound_QT1;
|
||||||
#endif*/
|
#endif
|
||||||
if(!DriversToTry[i].CompareNoCase("Null")) ret = new RageSound_Null;
|
if(!DriversToTry[i].CompareNoCase("Null")) ret = new RageSound_Null;
|
||||||
if( !ret )
|
if( !ret )
|
||||||
LOG->Warn("Unknown sound driver name: %s", DriversToTry[i].c_str());
|
LOG->Warn("Unknown sound driver name: %s", DriversToTry[i].c_str());
|
||||||
}
|
} catch(const RageException &e) {
|
||||||
catch(const RageException &e) {
|
|
||||||
LOG->Info("Couldn't load driver %s: %s", DriversToTry[i].c_str(), e.what());
|
LOG->Info("Couldn't load driver %s: %s", DriversToTry[i].c_str(), e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//#include "Sound/RageSoundDriver_QT_old.h"
|
#include "Sound/RageSoundDriver_QT1.h"
|
||||||
#include "Sound/RageSoundDriver_QT.h"
|
#include "Sound/RageSoundDriver_QT.h"
|
||||||
#include "LoadingWindow/LoadingWindow_Cocoa.h"
|
#include "LoadingWindow/LoadingWindow_Cocoa.h"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user