twoapple-reboot/src/ui/sound.d

210 lines
4.9 KiB
D

/+
+ ui/sound.d
+
+ Copyright: 2007 Gerald Stocker
+
+ This file is part of Twoapple.
+
+ Twoapple is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Twoapple is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Twoapple; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module ui.sound;
import std.stdio;
import std.c.string;
import derelict.sdl.sdl;
import derelict.util.exception;
import timer;
import device.speaker;
import ui.inputevents;
static import host;
static this()
{
soundCard = new SoundCardNo();
if (host.SDL)
{
if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
{
writefln("%s", std.string.toString(SDL_GetError()));
return;
}
auto checkCard = new SoundCardYes();
if (checkCard.isActive)
soundCard = checkCard;
}
host.delay.soundCardActive = &soundCard.getIsActive;
host.delay.soundCardHasEnoughData = &soundCard.hasEnoughData;
}
SoundCard soundCard;
private:
class SoundCard
{
bool isActive;
// XXX hack for fast mode testing
bool speedyMode;
bool getIsActive()
{
return isActive;
}
abstract void installSpeaker(Speaker spkr);
abstract void start();
abstract void pause();
abstract void resume();
abstract void process();
abstract bool hasEnoughData();
}
class SoundCardNo : SoundCard
{
this()
{
isActive = false;
}
void installSpeaker(Speaker spkr) {}
void start() {}
void pause() {}
void resume() {}
void process() {}
bool hasEnoughData() { return true; } // XXX Throw exception?
}
class SoundCardYes : SoundCard
{
SDL_AudioSpec audioRequest;
short[] xferBuffer;
short[] zeroBuffer;
uint xferWriteIndex, xferReadIndex;
Timer.Cycle soundCycle;
Speaker speaker;
static const int samplesPerCallback = 1024;
static const int sampleFreq = 44100;
this()
{
audioRequest.freq = sampleFreq;
audioRequest.format = AUDIO_S16SYS;
audioRequest.channels = 1;
audioRequest.samples = samplesPerCallback;
audioRequest.callback = &audioCallback;
audioRequest.userdata = cast(void*)this;
if (SDL_OpenAudio(&audioRequest, null) == -1)
{
writefln("%s", std.string.toString(SDL_GetError()));
return;
}
xferBuffer.length = 8192;
zeroBuffer.length = samplesPerCallback;
isActive = true;
}
void installSpeaker(Speaker spkr)
{
speaker = spkr;
}
void start()
{
SDL_PauseAudio(0);
}
void pause()
{
SDL_PauseAudio(1);
SDL_LockAudio();
}
void resume()
{
SDL_UnlockAudio();
SDL_PauseAudio(0);
}
void process()
{
speaker.update();
if (!speedyMode) // XXX
{
SDL_LockAudio();
int inLength = speaker.mainIndex;
short* inBuffer = speaker.mainBuffer.ptr;
// truncate xfer buffer if it would overflow
if (xferWriteIndex + inLength > xferBuffer.length)
{
memmove(xferBuffer.ptr, xferBuffer.ptr + xferReadIndex,
(xferBuffer.length - xferReadIndex) * 2);
xferWriteIndex -= xferReadIndex;
xferReadIndex = 0;
}
memcpy(xferBuffer.ptr + xferWriteIndex, inBuffer,
inLength * 2);
xferWriteIndex += inLength;
SDL_UnlockAudio();
}
finishProcessing();
}
void finishProcessing()
{
speaker.clearBuffer();
}
bool hasEnoughData()
{
if (speedyMode) return false; // XXX
SDL_LockAudio();
uint bufLen = xferWriteIndex - xferReadIndex;
SDL_UnlockAudio();
return bufLen > samplesPerCallback;
}
void fillAudio(Uint8* stream, int len)
{
int available = (xferWriteIndex - xferReadIndex) * 2;
int readLen = (available > len) ? len : available;
memcpy(stream, xferBuffer.ptr + xferReadIndex, readLen);
if (input.pauseButton.getActive())
{
if (len > readLen)
memcpy(stream + readLen, zeroBuffer.ptr, (len - readLen));
}
xferReadIndex += (readLen / 2);
}
}
extern(C) void audioCallback(void* userdata, Uint8* stream, int len)
{
(cast(SoundCardYes)userdata).fillAudio(stream, len);
}