diff --git a/stepmania/src/arch/Sound/RageSoundDriver_QT1.cpp b/stepmania/src/arch/Sound/RageSoundDriver_QT1.cpp new file mode 100644 index 0000000000..4cd6c420a5 --- /dev/null +++ b/stepmania/src/arch/Sound/RageSoundDriver_QT1.cpp @@ -0,0 +1,216 @@ +/* + * 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 "RageSoundManager.h" +#include "RageSoundDriver_QT1.h" +#include "RageLog.h" +#include "RageUtil.h" +#include "RageLog.h" +#include +#include +/* 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(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 += static_cast(samples) / freq; /* double buffer */ + LOG->Trace("The output latency is %f", latency); + + 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(&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(channel); + 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 SoundMixBuffer mix; + RageSound_QT1 *P = reinterpret_cast(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(temp)/tr.scale*freq; + temp = static_cast(d); + P->last_pos = static_cast(temp & 0x00000000FFFFFFFFLL); + P->last_pos += samples * 3 / 2; + } else + P->last_pos += samples; + + /* Swap buffers */ + header.samplePtr = reinterpret_cast(buffer[play_me]); + SndCommand cmd; + cmd.cmd = bufferCmd; + cmd.param1 = 0; + cmd.param2 = reinterpret_cast(&header); + OSErr err = SndDoCommand(chan, &cmd, 0); + if (err != noErr) + goto bail; + + /* Clear the fill buffer */ + memset(buffer[fill_me], 0, buffersize); + + for (unsigned i=0; isounds.size(); ++i) { + if (P->sounds[i]->stopping) + continue; + + unsigned got = P->sounds[i]->snd->GetPCM(reinterpret_cast(buffer[fill_me]), buffersize, P->last_pos); + mix.write(reinterpret_cast(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(buffer[fill_me])); + + cmd.cmd = callBackCmd; + cmd.param2 = play_me; + err = SndDoCommand(chan, &cmd, 0); + if (err != noErr) + goto bail; + return; + +bail: + RageException::Throw("SndDoCommand failed with error %d", err); + +} + +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); + + vectorsnds = 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(temp)/tr.scale*freq; + temp = static_cast(d); + return static_cast(temp & 0x00000000FFFFFFFFLL); +} + +float RageSound_QT1::GetPlayLatency() const { + return latency; +} diff --git a/stepmania/src/arch/Sound/RageSoundDriver_QT1.h b/stepmania/src/arch/Sound/RageSoundDriver_QT1.h new file mode 100644 index 0000000000..a237fd0ccb --- /dev/null +++ b/stepmania/src/arch/Sound/RageSoundDriver_QT1.h @@ -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 +} +#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 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 */