212 lines
4.9 KiB
D
212 lines
4.9 KiB
D
/+
|
|
+ ui/sound.d
|
|
+
|
|
+ Copyright: 2007 Gerald Stocker
|
|
+
|
|
+ This file is part of twoapple-reboot.
|
|
+
|
|
+ twoapple-reboot 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-reboot 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-reboot; 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 std.conv;
|
|
|
|
import derelict.sdl.sdl;
|
|
import derelict.util.exception;
|
|
|
|
import timer;
|
|
import device.speaker;
|
|
import ui.inputevents;
|
|
|
|
static import host;
|
|
|
|
static this()
|
|
{
|
|
soundCard = new SoundCardNo();
|
|
/+ TODO: fix sound segfault in fillAudio
|
|
if (host.SDL)
|
|
{
|
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
|
|
{
|
|
writefln("%s", to!string(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", to!string(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);
|
|
}
|