SFML/DSFML/import/dsfml/audio/soundstream.d

387 lines
10 KiB
D
Raw Normal View History

/*
* DSFML - SFML Library wrapper for the D programming language.
* Copyright (C) 2008 Julien Dagorn (sirjulio13@gmail.com)
* Copyright (C) 2010 Andreas Hollandt
*
* This software is provided 'as-is', without any express or
* implied warranty. In no event will the authors be held
* liable for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute
* it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented;
* you must not claim that you wrote the original software.
* If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but
* is not required.
*
* 2. Altered source versions must be plainly marked as such,
* and must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any
* source distribution.
*/
module dsfml.audio.soundstream;
import dsfml.system.alloc;
import dsfml.system.common;
import dsfml.system.vector3;
import dsfml.system.linkedlist;
import dsfml.system.lock;
import dsfml.system.mutex;
import dsfml.system.sleep;
//import dsfml.system.thread;
import core.thread;
import dsfml.audio.sound;
import dsfml.audio.soundsource;
/**
* SoundStream is a streamed sound, ie samples are acquired
* while the sound is playing. Use it for big sounds that would
* require hundreds of MB in memory, or for streaming sound from the network.
*
* SoundStream is a base class and cannot be instanciated directly.
*
* $(B onGetData override will be called by a different thread, take care of synchronization issues.) onStart is called by the thread which called .play().
*
* ------------------------
* class MySoundStream : SoundStream
* {
* this()
* {
* super(2, 44100); // you need to initialize the base class before any operation.
* }
* protected bool onGetData(out short[] data)
* {
* //You need to fill data array with some samples
*
* return true; //or false if you want to stop playback
* }
*
* protected bool onStart()
* {
* return true;
* }
* }
* ------------------------
*/
abstract class SoundStream : SoundSource!("sfSoundStream")
{
override void dispose()
{
stop();
sfSoundStream_Destroy(m_ptr);
s_instances.remove(m_id);
}
/**
* Start playing the stream
*/
void play()
{
m_flag = true;
sfSoundStream_Play(m_ptr);
if (getStatus() != SoundStatus.Paused)
{
m_t = new Thread(&threadPoll);
m_t.start();
}
}
/**
* Pause the stream
*/
void pause()
{
sfSoundStream_Pause(m_ptr);
}
/**
* Stop the stream
*/
void stop()
{
m_flag = false;
sfSoundStream_Stop(m_ptr);
m_t.join();
if (m_dummy !is null)
delete m_dummy;
}
/**
* Get number of channels of the stream
*
* Returns:
* number of channels
*/
uint getChannelsCount()
{
return m_channelsCount;
}
/**
* Get the sample rate of the stream
*
* Returns:
* sample rate
*/
uint getSampleRate()
{
return m_sampleRate;
}
/**
* Get the current playing offset of the stream
*
* Returns:
* current playing offset, in seconds.
*/
float getPlayingOffset()
{
return sfSoundStream_GetPlayingOffset(m_ptr);
}
/**
* Set the current playing position of a music
*
* Params:
* timeOffset = New playing position, expressed in seconds
*/
void setPlayingOffset(float timeOffset)
{
sfSoundStream_SetPlayingOffset(m_ptr, timeOffset);
}
/**
* Tell whether or not the stream is looping
*
* Returns:
* True if the music is looping, false otherwise
*/
bool getLoop()
{
if (m_ptr !is null)
return cast(bool)sfSoundStream_GetLoop(m_ptr);
return false;
}
/**
* Set the stream loop state.
*
* Disabled by default.
*
* Params:
* loop = true to play in loop, false to play once
*/
void setLoop(bool loop)
{
if (m_ptr !is null)
sfSoundStream_SetLoop(m_ptr, loop);
}
protected:
/**
* Protected constructor
*
* Params:
* channelsCount = number of channel
* sampleRate = sample rate of the stream
*
*/
this(uint channelsCount, uint sampleRate)
{
m_channelsCount = channelsCount;
m_sampleRate = sampleRate;
m_ptr = sfSoundStream_Create(&externalOnGetData, &externalOnSeek, channelsCount, sampleRate, &m_id); // TODO: hack
m_mutex = new Mutex();
m_samples = new LinkedList!(Data);
m_t = new Thread(&this.threadPoll);
m_id = ++s_seed;
s_instances[m_id] = this;
}
/**
* Called each time the stream restart
*
* Returns:
* false to abort the playback
*/
abstract bool onStart();
/**
* Called each time the stream needs new data.
* This method will be call by an other thread, take care of possible synchronisation issues.
*
* Params:
* data = array of samples to stream
*
* Returns:
* true to continue streaming, false to stop
*/
abstract bool onGetData(out short[] data);
private:
// Called sync when user calling play()
// FIXME: this needs to be transformed from OnStart to OnSeek
extern(C) static void externalOnSeek(float t, void* user)
{
int id;
if ((id = *cast(int*) user) in s_instances)
{
SoundStream temp = s_instances[id];
return (temp.m_flag = temp.onStart());
}
// return true;
}
// C Thread callback (no allocation can be done)
extern (C) static int externalOnGetData(sfSoundStreamChunk* data, void* user)
{
int id, flag = false;
// Get calling soundStream
if ((id = *cast(int*) user) in s_instances)
{
SoundStream temp = s_instances[id];
//if no samples are available but streaming is not stopped, we sleep the thread
while (temp.m_samples.empty && temp.m_flag)
sleep(0.01f);
scope Lock l = new Lock(temp.m_mutex);
if (!temp.m_samples.empty)
{
if (temp.m_dummy !is null)
delete temp.m_dummy;
temp.m_dummy = temp.m_samples.dequeue;
if ((flag = temp.m_dummy.Flag) == true)
{
data.Samples = temp.m_dummy.Samples.ptr;
data.NbSamples = temp.m_dummy.Samples.length;
}
else
{
data.Samples = null;
data.NbSamples = 0;
}
}
}
return flag;
}
// Managed thread loop
void threadPoll()
{
short[] data;
bool ret = true;
// while streaming is active ...
while (ret && m_flag)
{
{
scope Lock l = new Lock(m_mutex);
// see how many samples are available (keep always 2 samples ready)
if (m_samples.getCount < 2)
{
// if we need new samples, lock and call derived class
ret = onGetData(data);
m_samples.enqueue(new Data(data, ret));
}
}
sleep(0.1f);
}
}
private class Data
{
short[] Samples;
bool Flag;
mixin Alloc;
this (short[] samples, bool flag)
{
this.Samples = samples;
this.Flag = flag;
}
}
Thread m_t;
Mutex m_mutex;
LinkedList!(Data) m_samples;
Data m_dummy;
bool m_flag;
uint m_channelsCount;
uint m_sampleRate;
int m_id;
static SoundStream[int] s_instances;
static int s_seed = 0;
// External ====================================================================
extern (C)
{
struct sfSoundStreamChunk{ short* Samples; uint NbSamples; }
alias void function(float, void*) sfSoundStreamSeekCallback;
alias int function (sfSoundStreamChunk*, void*) sfSoundStreamGetDataCallback;
alias void* function(sfSoundStreamGetDataCallback, sfSoundStreamSeekCallback, uint, uint, void*) pf_sfSoundStream_Create;
alias void function(void*) pf_sfSoundStream_Destroy;
alias void function(void*) pf_sfSoundStream_Play;
alias void function(void*) pf_sfSoundStream_Pause;
alias void function(void*) pf_sfSoundStream_Stop;
alias uint function(void*) pf_sfSoundStream_GetChannelsCount;
alias uint function(void*) pf_sfSoundStream_GetSampleRate;
alias float function(void*) pf_sfSoundStream_GetPlayingOffset;
alias void function(void*, float) pf_sfSoundStream_SetPlayingOffset;
alias int function(void*) pf_sfSoundStream_GetLoop;
alias void function(void*, int) pf_sfSoundStream_SetLoop;
static pf_sfSoundStream_Create sfSoundStream_Create;
static pf_sfSoundStream_Destroy sfSoundStream_Destroy;
static pf_sfSoundStream_Play sfSoundStream_Play;
static pf_sfSoundStream_Pause sfSoundStream_Pause;
static pf_sfSoundStream_Stop sfSoundStream_Stop;
static pf_sfSoundStream_GetChannelsCount sfSoundStream_GetChannelsCount;
static pf_sfSoundStream_GetSampleRate sfSoundStream_GetSampleRate;
static pf_sfSoundStream_GetPlayingOffset sfSoundStream_GetPlayingOffset;
static pf_sfSoundStream_SetPlayingOffset sfSoundStream_SetPlayingOffset;
static pf_sfSoundStream_GetLoop sfSoundStream_GetLoop;
static pf_sfSoundStream_SetLoop sfSoundStream_SetLoop;
}
static this()
{
debug
DllLoader dll = DllLoader.load("csfml-audio-d");
else
DllLoader dll = DllLoader.load("csfml-audio");
sfSoundStream_Create = cast(pf_sfSoundStream_Create)dll.getSymbol("sfSoundStream_Create");
sfSoundStream_Destroy = cast(pf_sfSoundStream_Destroy)dll.getSymbol("sfSoundStream_Destroy");
sfSoundStream_Play = cast(pf_sfSoundStream_Play)dll.getSymbol("sfSoundStream_Play");
sfSoundStream_Pause = cast(pf_sfSoundStream_Pause)dll.getSymbol("sfSoundStream_Pause");
sfSoundStream_Stop = cast(pf_sfSoundStream_Stop)dll.getSymbol("sfSoundStream_Stop");
sfSoundStream_GetChannelsCount = cast(pf_sfSoundStream_GetChannelsCount)dll.getSymbol("sfSoundStream_GetChannelsCount");
sfSoundStream_GetSampleRate = cast(pf_sfSoundStream_GetSampleRate)dll.getSymbol("sfSoundStream_GetSampleRate");
sfSoundStream_GetPlayingOffset = cast(pf_sfSoundStream_GetPlayingOffset)dll.getSymbol("sfSoundStream_GetPlayingOffset");
sfSoundStream_SetPlayingOffset = cast(pf_sfSoundStream_SetPlayingOffset)dll.getSymbol("sfSoundStream_SetPlayingOffset");
sfSoundStream_GetLoop = cast(pf_sfSoundStream_GetLoop)dll.getSymbol("sfSoundStream_GetLoop");
sfSoundStream_SetLoop = cast(pf_sfSoundStream_SetLoop)dll.getSymbol("sfSoundStream_SetLoop");
}
}