2009-01-29 00:18:34 +08:00
|
|
|
/*
|
2010-01-07 04:37:29 +08:00
|
|
|
* 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.
|
2009-01-29 00:18:34 +08:00
|
|
|
*
|
2010-01-07 04:37:29 +08:00
|
|
|
* 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:
|
2009-01-29 00:18:34 +08:00
|
|
|
*
|
2010-01-07 04:37:29 +08:00
|
|
|
* 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.
|
2009-01-29 00:18:34 +08:00
|
|
|
*
|
2010-01-07 04:37:29 +08:00
|
|
|
* 2. Altered source versions must be plainly marked as such,
|
|
|
|
* and must not be misrepresented as being the original software.
|
2009-01-29 00:18:34 +08:00
|
|
|
*
|
2010-01-07 04:37:29 +08:00
|
|
|
* 3. This notice may not be removed or altered from any
|
|
|
|
* source distribution.
|
2009-01-29 00:18:34 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
module dsfml.audio.soundstream;
|
|
|
|
|
|
|
|
import dsfml.system.alloc;
|
|
|
|
import dsfml.system.common;
|
|
|
|
import dsfml.system.vector3;
|
|
|
|
import dsfml.system.linkedlist;
|
|
|
|
import dsfml.system.lock;
|
2010-03-04 06:04:19 +08:00
|
|
|
|
2010-01-07 04:25:45 +08:00
|
|
|
import core.thread;
|
2010-03-04 06:04:19 +08:00
|
|
|
import core.sync.mutex;
|
2009-01-29 00:18:34 +08:00
|
|
|
|
|
|
|
import dsfml.audio.sound;
|
2010-01-12 05:15:23 +08:00
|
|
|
import dsfml.audio.soundsource;
|
2009-01-29 00:18:34 +08:00
|
|
|
|
|
|
|
/**
|
2010-01-07 04:37:29 +08:00
|
|
|
* 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;
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ------------------------
|
2009-01-29 00:18:34 +08:00
|
|
|
*/
|
2010-01-12 05:15:23 +08:00
|
|
|
abstract class SoundStream : SoundSource!("sfSoundStream")
|
2009-01-29 00:18:34 +08:00
|
|
|
{
|
2010-01-07 04:37:29 +08:00
|
|
|
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);
|
|
|
|
|
2010-03-13 09:56:39 +08:00
|
|
|
if (status != SoundStatus.Paused)
|
2010-01-07 04:37:29 +08:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-01-12 05:48:15 +08:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2010-01-07 04:37:29 +08:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2010-01-07 04:25:45 +08:00
|
|
|
|
2009-01-29 00:18:34 +08:00
|
|
|
protected:
|
2010-01-07 04:37:29 +08:00
|
|
|
/**
|
|
|
|
* Protected constructor
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* channelsCount = number of channel
|
|
|
|
* sampleRate = sample rate of the stream
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
this(uint channelsCount, uint sampleRate)
|
|
|
|
{
|
|
|
|
m_channelsCount = channelsCount;
|
|
|
|
m_sampleRate = sampleRate;
|
2010-01-12 05:15:23 +08:00
|
|
|
|
2010-01-14 01:42:05 +08:00
|
|
|
super(sfSoundStream_Create(&externalOnGetData, &externalOnSeek, channelsCount, sampleRate, &m_id));
|
2010-01-07 04:37:29 +08:00
|
|
|
|
|
|
|
m_mutex = new Mutex();
|
|
|
|
|
|
|
|
m_samples = new LinkedList!(Data);
|
|
|
|
|
|
|
|
m_t = new Thread(&this.threadPoll);
|
|
|
|
|
|
|
|
m_id = ++s_seed;
|
|
|
|
s_instances[m_id] = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-01-14 01:42:05 +08:00
|
|
|
* Called each time the stream is seeked
|
2010-01-07 04:37:29 +08:00
|
|
|
*/
|
2010-01-14 01:42:05 +08:00
|
|
|
abstract void onSeek();
|
2010-01-07 04:37:29 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
2009-01-29 00:18:34 +08:00
|
|
|
private:
|
|
|
|
|
2010-01-07 04:37:29 +08:00
|
|
|
// Called sync when user calling play()
|
2010-01-14 01:42:05 +08:00
|
|
|
// TODO: check if it's correct that way
|
2010-01-12 05:48:15 +08:00
|
|
|
extern(C) static void externalOnSeek(float t, void* user)
|
2010-01-07 04:37:29 +08:00
|
|
|
{
|
|
|
|
int id;
|
|
|
|
if ((id = *cast(int*) user) in s_instances)
|
|
|
|
{
|
|
|
|
SoundStream temp = s_instances[id];
|
2010-01-14 01:42:05 +08:00
|
|
|
return (temp.onSeek());
|
2010-01-07 04:37:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2010-03-04 06:04:19 +08:00
|
|
|
Thread.sleep(10_000_0); // 10ms
|
2010-01-07 04:37:29 +08:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2010-03-04 06:04:19 +08:00
|
|
|
Thread.sleep(100_000_0); // 100ms
|
2010-01-07 04:37:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2009-01-29 00:18:34 +08:00
|
|
|
// External ====================================================================
|
|
|
|
|
2010-01-07 04:37:29 +08:00
|
|
|
extern (C)
|
|
|
|
{
|
|
|
|
struct sfSoundStreamChunk{ short* Samples; uint NbSamples; }
|
|
|
|
|
2010-01-12 05:48:15 +08:00
|
|
|
alias void function(float, void*) sfSoundStreamSeekCallback;
|
2010-01-07 04:37:29 +08:00
|
|
|
alias int function (sfSoundStreamChunk*, void*) sfSoundStreamGetDataCallback;
|
|
|
|
|
2010-03-16 07:35:53 +08:00
|
|
|
alias SFMLClass function(sfSoundStreamGetDataCallback, sfSoundStreamSeekCallback, uint, uint, void*) pf_sfSoundStream_Create;
|
|
|
|
alias void function(SFMLClass) pf_sfSoundStream_Destroy;
|
|
|
|
alias void function(SFMLClass) pf_sfSoundStream_Play;
|
|
|
|
alias void function(SFMLClass) pf_sfSoundStream_Pause;
|
|
|
|
alias void function(SFMLClass) pf_sfSoundStream_Stop;
|
|
|
|
alias uint function(SFMLClass) pf_sfSoundStream_GetChannelsCount;
|
|
|
|
alias uint function(SFMLClass) pf_sfSoundStream_GetSampleRate;
|
|
|
|
alias float function(SFMLClass) pf_sfSoundStream_GetPlayingOffset;
|
|
|
|
alias void function(SFMLClass, float) pf_sfSoundStream_SetPlayingOffset;
|
|
|
|
alias int function(SFMLClass) pf_sfSoundStream_GetLoop;
|
|
|
|
alias void function(SFMLClass, int) pf_sfSoundStream_SetLoop;
|
2010-01-07 04:37:29 +08:00
|
|
|
|
|
|
|
|
|
|
|
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;
|
2010-01-12 05:48:15 +08:00
|
|
|
static pf_sfSoundStream_SetPlayingOffset sfSoundStream_SetPlayingOffset;
|
2010-01-07 04:37:29 +08:00
|
|
|
static pf_sfSoundStream_GetLoop sfSoundStream_GetLoop;
|
|
|
|
static pf_sfSoundStream_SetLoop sfSoundStream_SetLoop;
|
|
|
|
}
|
2009-01-29 00:18:34 +08:00
|
|
|
|
2010-01-07 04:37:29 +08:00
|
|
|
static this()
|
|
|
|
{
|
2010-01-07 04:25:45 +08:00
|
|
|
debug
|
|
|
|
DllLoader dll = DllLoader.load("csfml-audio-d");
|
|
|
|
else
|
|
|
|
DllLoader dll = DllLoader.load("csfml-audio");
|
2010-01-07 04:37:29 +08:00
|
|
|
|
|
|
|
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");
|
2010-01-12 05:48:15 +08:00
|
|
|
sfSoundStream_SetPlayingOffset = cast(pf_sfSoundStream_SetPlayingOffset)dll.getSymbol("sfSoundStream_SetPlayingOffset");
|
2010-01-07 04:37:29 +08:00
|
|
|
sfSoundStream_GetLoop = cast(pf_sfSoundStream_GetLoop)dll.getSymbol("sfSoundStream_GetLoop");
|
|
|
|
sfSoundStream_SetLoop = cast(pf_sfSoundStream_SetLoop)dll.getSymbol("sfSoundStream_SetLoop");
|
|
|
|
}
|
2010-01-07 04:25:45 +08:00
|
|
|
}
|