diff --git a/stepmania/src/arch/Sound/RageSoundDriver_QT.cpp b/stepmania/src/arch/Sound/RageSoundDriver_QT.cpp index 54861e5c0f..566ff47af0 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_QT.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_QT.cpp @@ -9,346 +9,194 @@ #include "global.h" #include "RageSoundDriver_QT.h" +#include "RageSoundManager.h" +#include "RageSound.h" #include "RageLog.h" -#include "RageUtil.h" -#include "RageLog.h" +#include "RageThreads.h" #include #include #include -/* Ugh */ -using namespace QT; +#include +#include -const unsigned channels = 2; -const unsigned samplesize = channels*16; +const unsigned samplesize = 4; const unsigned samples = 4096; const unsigned freq = 44100; -const unsigned buffersize = samples*samplesize/8; -const unsigned SndCommand_qLength = 2; -const unsigned initialQTSoundChannels = 1; +const unsigned buffersize = samples * samplesize; -#pragma options align=power -struct channelInfo { - RageSound *snd; - 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 queuefree_channels; -static vectorplaying_channels; -static ComponentInstance soundClock; +typedef struct soundInfo{ + RageSound *snd; + Movie movie[2]; + long fillme; + long stopPos; + long lastPos; + long finishedBuffers; +} *soundInfoPtr, **soundInfoHdl; +static void CallBack(QTCallBack cb, long refCon); +static inline void GetData(soundInfoPtr s); + +static vector sounds; +static MovieImportComponent miComponent; RageSound_QT::RageSound_QT() { #if 1 - RageException::ThrowNonfatal("Class not finished!"); + RageException::ThrowNonfatal("Class not finished!"); #endif - - if (!CanPlayMultiChannels()) - RageException::ThrowNonfatal("Multiple Sound Manager channels not supported. Fallback"); - - channelInfo *info; - SndCallBackUPP callback = NewSndCallBackUPP(GetData); - OSErr err; - - soundOutput = OpenDefaultComponent(kSoundOutputDeviceType, NULL); - ASSERT(soundOutput != NULL); - - TimeRecord record; - SoundComponentGetInfo(soundOutput, NULL, siOutputLatency, &record); - latency = record.value.lo / record.scale; - - playing_channels.reserve(initialQTSoundChannels); - - for (unsigned i=0; ibuffer[0] = new Uint8[buffersize]; - info->buffer[1] = new Uint8[buffersize]; - info->channel = new SndChannel; - info->channel->userInfo = reinterpret_cast(info); - 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) { - SAFE_DELETE(info->channel); - RageException::ThrowNonfatal("Unable to create audio channel. THIS IS A BUG!"); - } - free_channels.push(info); - } - - SndCommand cmd; - cmd.cmd = clockComponentCmd; - cmd.param1 = true; - cmd.param2 = 0; - err = SndDoImmediate(info->channel, &cmd); - - cmd.cmd = getClockComponentCmd; - cmd.param1 = 0; - cmd.param2 = reinterpret_cast(&soundClock); - err |= SndDoImmediate(info->channel, &cmd); - - if (err != noErr) - RageException::ThrowNonfatal("Unable to create audio channel. THIS IS A BUG!"); + if (EnterMovies() != noErr) + RageException::ThrowNonfatal("Could not start movies. Falling back."); + miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeWave); } RageSound_QT::~RageSound_QT() { - unsigned size = playing_channels.size(); - - for (unsigned i=0; ichannel) - 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) - CloseComponent(soundOutput); - if (soundClock) - CloseComponent(soundClock); -} - -void RageSound_QT::GetData(SndChannelPtr chan, SndCommand *cmd_passed) { - LockMutex L(SOUNDMAN->lock); - - channelInfo *info = reinterpret_cast(chan->userInfo); - ASSERT(info); - ASSERT(chan == info->channel); - 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(info->buffer[play_me]); - info->header.numFrames = info->got; - cmd.cmd = bufferCmd; - cmd.param1 = 0; - cmd.param2 = reinterpret_cast(&info->header); - LOG->Trace("bufferCmd"); - err = SndDoCommand(chan, &cmd, false); - if (err != noErr) - goto bail; - } - - 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(info->buffer[info->fill_me]), buffersize, info->last_pos); - if (static_cast(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.param2 = play_me; - LOG->Trace("callBackCmd"); - err = SndDoCommand(chan, &cmd, false); - if (err != noErr) - goto bail; - } - return; - -bail: - RageException::Throw("SndDoCommand failed with error %d", err); + LockMutex L(SOUNDMAN->lock); + while (!sounds.empty()) { + soundInfoHdl sHdl = sounds.back(); + soundInfoPtr s = *sHdl; + if (s->movie[0]) { + StopMovie(s->movie[0]); + DisposeMovie(s->movie[0]); + } + if (s->movie[1]) { + StopMovie(s->movie[1]); + DisposeMovie(s->movie[1]); + } + DisposeHandle(Handle(sHdl)); + sounds.pop_back(); + } + if (miComponent) + CloseComponent(miComponent); } void RageSound_QT::StartMixing(RageSound *snd) { - LockMutex L(SOUNDMAN->lock); - SndCommand cmd; - channelInfo *info; + LockMutex L(SOUNDMAN->lock); + soundInfoHdl sHdl = (soundInfoHdl)NewHandle(sizeof(soundInfo)); + HLockHi(Handle(sHdl)); /* Move to the top of the heap */ + soundInfoPtr s = *sHdl; - 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(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; - OSErr err = SndDoCommand(info->channel, &cmd, 0); /* command queue is empty */ - if (err != noErr) - RageException::Throw("Couldn't start the callback"); + s->fillme = 0; + s->movie[0] = NULL; + s->movie[1] = NULL; + s->stopPos = 0; + s->lastPos = 0; + s->snd = snd; + s->finishedBuffers = -1; + sounds.push_back(sHdl); + CallBack(NULL, long(s)); } void RageSound_QT::StopMixing(RageSound *snd) { - LockMutex L(SOUNDMAN->lock); - LOG->Trace("StopMixing(). Stopping sound 0x%X", reinterpret_cast(snd)); - /* Find the sound. */ - int size = playing_channels.size(); - int i; - channelInfo *info; + LockMutex L(SOUNDMAN->lock); + unsigned size = sounds.size(); - for (i=0; i<=size; ++i) - if ((info=playing_channels[i])->snd == snd) - break; - if (i==size) { - LOG->Trace("Not stopping a sound because it's not playing."); - return; - } - - playing_channels.erase(playing_channels.begin()+i); - SndCommand cmd; - cmd.cmd = flushCmd; - cmd.param1 = 0; - cmd.param2 = 0; - LOG->Trace("Flushing channel with song %X", reinterpret_cast(snd)); - SndDoImmediate(info->channel, &cmd); - cmd.cmd = quietCmd; - SndDoImmediate(info->channel, &cmd); - free_channels.push(info); + for (unsigned i=0; isnd != snd) + continue; + if (s->movie[0]) { + StopMovie(s->movie[0]); + DisposeMovie(s->movie[0]); + } + if (s->movie[1]) { + StopMovie(s->movie[1]); + DisposeMovie(s->movie[1]); + } + DisposeHandle(Handle(sHdl)); + } } void RageSound_QT::Update(float delta) { #pragma unused (delta) - LockMutex L(SOUNDMAN->lock); + LockMutex L(SOUNDMAN->lock); + unsigned size = sounds.size(); + vector snds = sounds; - vector playing = playing_channels; - int size = playing.size(); - for (int i=0; istopping != 0) - continue; - if (GetPosition(info->snd) < info->flush_pos) - continue; - info->snd->StopPlaying(); - } + for (unsigned i=0; imovie[0] && !IsMovieDone(s->movie[0])) || + (s->movie[1] && !IsMovieDone(s->movie[1]))) + continue; + s->snd->StopPlaying(); + } } int RageSound_QT::GetPosition(const RageSound *snd) const { - LockMutex L(SOUNDMAN->lock); - TimeRecord tr; - ClockGetTime(soundClock, &tr); - UInt64 time = tr.value.hi; - time <<= 32; - time |= tr.value.lo; - - /* Find the info */ - int size = playing_channels.size(); - int i; - channelInfo *info; - for (i=0; isnd == 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(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(time % 0x00000000FFFFFFFFLL); + LockMutex L(SOUNDMAN->lock); + soundInfoPtr s; + unsigned size = sounds.size(); + /* Find the info */ + for (unsigned i=0; isnd != snd) + continue; + if (s->finishedBuffers == -1) + return 0; /* It hasn't started yet. */ + return s->finishedBuffers * samples + GetMovieTime(s->movie[!s->fillme], NULL); + } + RageException::Throw("Can't get the position of a sound that isn't playing"); + return 0; /* unrechable statement */ } -/* 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; -*/ +void CallBack(QTCallBack cb, long refCon) { + LockMutex L(SOUNDMAN->lock); + soundInfoPtr s = soundInfoPtr(refCon); -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; + s->finishedBuffers++; + if (!cb) + GetData(s); + long playme = !s->fillme; + SetMovieVolume(s->movie[playme], kFullVolume); + GoToBeginningOfMovie(s->movie[playme]); + StartMovie(s->movie[playme]); + if (s->stopPos == 0) { + /* Not stopping */ + GetData(s); + QTCallBack callBack = NewCallBack(GetMovieTimeBase(s->movie[playme]), callBackAtExtremes); + CallMeWhen(callBack, CallBack, long(s), 0, 0, 0); + } + DisposeCallBack(cb); } + +inline void GetData(soundInfoPtr s) { + static long flags = newMovieActive; + Handle hdl, dataRef = NULL; + Track targetTrack = NULL; + TimeValue addedDuration = 0; + long outFlags = 0; + OSErr err; + ComponentResult result; + Movie &movie = s->movie[s->fillme]; + char buffer[buffersize]; + int got = s->snd->GetPCM(buffer, buffersize, s->lastPos); + + if (got < buffersize) { + s->stopPos = got / samplesize + s->lastPos; + if (got == 0) + return; + } + s->lastPos += samples; + + /* I really don' understand this part. Oh well. */ + hdl = NewHandle(got); + HLock(hdl); + BlockMove(buffer, *hdl, got); + err = PtrToHand(&hdl, &dataRef, sizeof(Handle)); /* um... */ + ASSERT(err == noErr); + HUnlock(hdl); + /* end unknown section. */ + + if (movie) + DisposeMovie(movie); + + movie = NewMovie(flags); + SetMovieTimeScale(movie, freq); + if (GetMoviesError() != noErr) + RageException::Throw("Couldn't create new movie"); + result = MovieImportDataRef(miComponent, dataRef, HandleDataHandlerSubType, + movie, NULL, &targetTrack, NULL, &addedDuration, + movieImportCreateTrack, &outFlags); + s->fillme = !s->fillme; +} +