Merge branch 'develop' of github.com:Flamefire/Vocaluxe into develop
This commit is contained in:
commit
ed076865bc
61 files changed
+2942
-4405
No files matched your search
@@ -1,253 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "GstreamerAudio.h"
|
||||
|
||||
#define DllExport extern "C" __declspec(dllexport)
|
||||
|
||||
map<int, GstreamerAudioStream*> Streams;
|
||||
queue<int> IDs;
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
DllExport void SetLogCallback (LogCallback Callback)
|
||||
{
|
||||
Log = Callback;
|
||||
}
|
||||
|
||||
DllExport bool Init()
|
||||
{
|
||||
for(int i = 0; i < 1000; i++)
|
||||
{
|
||||
IDs.push(i);
|
||||
}
|
||||
#if _WIN64
|
||||
SetDllDirectory(L".\\x64\\gstreamer");
|
||||
#else
|
||||
SetDllDirectory(L".\\x86\\gstreamer");
|
||||
#endif
|
||||
|
||||
gst_init(NULL, NULL);
|
||||
GstRegistry* registry = gst_registry_get();
|
||||
|
||||
#if _WIN64
|
||||
gst_registry_scan_path(registry, ".\\x64\\gstreamer");
|
||||
#else
|
||||
gst_registry_scan_path(registry, ".\\x86\\gstreamer");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
DllExport void SetGlobalVolume(float Volume)
|
||||
{
|
||||
map<int, GstreamerAudioStream*>::iterator p;
|
||||
|
||||
for(p = Streams.begin(); p != Streams.end(); p++) {
|
||||
p->second->SetStreamVolume(Volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DllExport int GetStreamCount(void)
|
||||
{
|
||||
return Streams.size();
|
||||
}
|
||||
|
||||
|
||||
DllExport void CloseAll(void)
|
||||
{
|
||||
map<int, GstreamerAudioStream*>::iterator p;
|
||||
|
||||
for(p = Streams.begin(); p != Streams.end(); p++) {
|
||||
p->second->Close();
|
||||
}
|
||||
}
|
||||
|
||||
DllExport int Load(const wchar_t* Media)
|
||||
{
|
||||
GstreamerAudioStream *s = new GstreamerAudioStream(IDs.front());
|
||||
Streams.insert(pair<int, GstreamerAudioStream*> (IDs.front(), s));
|
||||
IDs.pop();
|
||||
return s->Load(Media);
|
||||
}
|
||||
|
||||
DllExport int LoadPrescan(const wchar_t* Media, bool Prescan)
|
||||
{
|
||||
GstreamerAudioStream *s = new GstreamerAudioStream(IDs.front());
|
||||
Streams.insert(pair<int, GstreamerAudioStream*> (IDs.front(), s));
|
||||
IDs.pop();
|
||||
return s->Load(Media, Prescan);
|
||||
}
|
||||
|
||||
DllExport void Close(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
delete it->second;
|
||||
IDs.push(it->first);
|
||||
Streams.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void Play(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
it->second->Play();
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void PlayLoop(int Stream, bool Loop)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
it->second->Play(Loop);
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void Pause(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
it->second->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void Stop(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
it->second->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void Fade(int Stream, float TargetVolume, float Seconds)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
it->second->Fade(TargetVolume, Seconds);
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void FadeAndPause(int Stream, float TargetVolume, float Seconds)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
it->second->FadeAndPause(TargetVolume, Seconds);
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void FadeAndStop(int Stream, float TargetVolume, float Seconds)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
it->second->FadeAndStop(TargetVolume, Seconds);
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void SetStreamVolume(int Stream, float Volume)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
return it->second->SetStreamVolume(Volume);
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void SetStreamVolumeMax(int Stream, float Volume)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
return it->second->SetStreamVolumeMax(Volume);
|
||||
}
|
||||
}
|
||||
|
||||
DllExport float GetLength(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
return it->second->GetLength();
|
||||
}
|
||||
else return -1.0;
|
||||
}
|
||||
|
||||
DllExport float GetPosition(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
return it->second->GetPosition();
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
|
||||
DllExport bool IsPlaying(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
return it->second->IsPlaying();
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
DllExport bool IsPaused(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
return it->second->IsPaused();
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
DllExport bool IsFinished(int Stream)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
return it->second->IsFinished();
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
DllExport void SetPosition(int Stream, float Position)
|
||||
{
|
||||
map<int,GstreamerAudioStream*>::iterator it = Streams.find(Stream);
|
||||
if(it != Streams.end())
|
||||
{
|
||||
return it->second->SetPosition(Position);
|
||||
}
|
||||
}
|
||||
|
||||
DllExport void Update(void)
|
||||
{
|
||||
map<int, GstreamerAudioStream*>::iterator p = Streams.begin();
|
||||
|
||||
while(p != Streams.end())
|
||||
{
|
||||
if(p->second->Closed)
|
||||
{
|
||||
delete p->second;
|
||||
IDs.push(p->first);
|
||||
Streams.erase(p++);
|
||||
}
|
||||
else {
|
||||
p->second->Update();
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LogError(const char* msg)
|
||||
{
|
||||
Log(msg);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include "gst/gst.h"
|
||||
#include "GstreamerAudioStream.h"
|
||||
#include "gst\gst.h"
|
||||
|
||||
#define DllExport extern "C" __declspec(dllexport)
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef void (__stdcall * LogCallback)(const char* text);
|
||||
static LogCallback Log;
|
||||
|
||||
DllExport void SetLogCallback(LogCallback Callback);
|
||||
|
||||
DllExport bool Init();
|
||||
DllExport void SetGlobalVolume(float Volume);
|
||||
DllExport int GetStreamCount(void);
|
||||
DllExport void CloseAll(void);
|
||||
|
||||
//Stream handling
|
||||
DllExport int Load(const wchar_t* Media);
|
||||
DllExport int LoadPrescan(const wchar_t* Media, bool Prescan);
|
||||
DllExport void Close(int Stream);
|
||||
|
||||
DllExport void Play(int Stream);
|
||||
DllExport void PlayLoop(int Stream, bool Loop);
|
||||
DllExport void Pause(int Stream);
|
||||
DllExport void Stop(int Stream);
|
||||
DllExport void Fade(int Stream, float TargetVolume, float Seconds);
|
||||
DllExport void FadeAndPause(int Stream, float TargetVolume, float Seconds);
|
||||
DllExport void FadeAndStop(int Stream, float TargetVolume, float Seconds);
|
||||
DllExport void SetStreamVolume(int Stream, float Volume);
|
||||
DllExport void SetStreamVolumeMax(int Stream, float Volume);
|
||||
|
||||
DllExport float GetLength(int Stream);
|
||||
DllExport float GetPosition(int Stream);
|
||||
|
||||
DllExport bool IsPlaying(int Stream);
|
||||
DllExport bool IsPaused(int Stream);
|
||||
DllExport bool IsFinished(int Stream);
|
||||
|
||||
DllExport void SetPosition(int Stream, float Position);
|
||||
|
||||
DllExport void Update();
|
||||
|
||||
void LogError(const char* msg);
|
||||
@@ -1,338 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "GstreamerAudioStream.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
GST_PLAY_FLAG_VIDEO = (1 << 0),
|
||||
GST_PLAY_FLAG_AUDIO = (1 << 1),
|
||||
GST_PLAY_FLAG_TEXT = (1 << 2),
|
||||
GST_PLAY_FLAG_VIS = (1 << 3),
|
||||
GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4),
|
||||
GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5),
|
||||
GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6),
|
||||
GST_PLAY_FLAG_DOWNLOAD = (1 << 7),
|
||||
GST_PLAY_FLAG_BUFFERING = (1 << 8),
|
||||
GST_PLAY_FLAG_DEINTERLACE = (1 << 9),
|
||||
GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
|
||||
} GstPlayFlags;
|
||||
|
||||
GstreamerAudioStream::GstreamerAudioStream(int id)
|
||||
{
|
||||
ID = id;
|
||||
MaxVolume = 1.0;
|
||||
Volume = 1.0;
|
||||
Duration = -1;
|
||||
Loop = false;
|
||||
Running = true;
|
||||
|
||||
Finished = false;
|
||||
|
||||
//Fading
|
||||
FadeTimer = g_timer_new();
|
||||
FadeTime = 0.0;
|
||||
TargetVolume = 1.0;
|
||||
StartVolume = 1.0;
|
||||
CloseStreamAfterFade = false;
|
||||
PauseStreamAfterFade = false;
|
||||
Fading = false;
|
||||
|
||||
Closed = false;
|
||||
}
|
||||
|
||||
|
||||
GstreamerAudioStream::~GstreamerAudioStream(void)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
// Loads a stream but does not wait for it to get initialized,
|
||||
// so querying duration or position directly after initialization might
|
||||
// return strange values! To work around this use the prescan load function!
|
||||
int GstreamerAudioStream::Load(const wchar_t* Media)
|
||||
{
|
||||
Element = gst_element_factory_make("playbin", "playbin");
|
||||
|
||||
Convert = gst_element_factory_make("audioconvert", "audioconvert");
|
||||
Audiosink = gst_element_factory_make("directsoundsink", "directsoundsink");
|
||||
SinkBin = gst_bin_new("SinkBin");
|
||||
gst_bin_add_many (GST_BIN (SinkBin), Convert, Audiosink, NULL);
|
||||
gst_element_link_many (Convert, Audiosink, NULL);
|
||||
Pad = gst_element_get_static_pad (Convert, "sink");
|
||||
GhostPad = gst_ghost_pad_new ("sink", Pad);
|
||||
gst_pad_set_active (GhostPad, TRUE);
|
||||
gst_element_add_pad (SinkBin, GhostPad);
|
||||
|
||||
Bus = gst_element_get_bus(Element);
|
||||
if(!Element)
|
||||
{
|
||||
LogError ("Could not create element!");
|
||||
return -1;
|
||||
}
|
||||
Running = true;
|
||||
gst_element_set_state(Element, GST_STATE_NULL);
|
||||
g_object_set(Element, "uri", Media, NULL);
|
||||
g_object_set(Element, "audio-sink", SinkBin, NULL);
|
||||
g_object_set(Element, "flags", GST_PLAY_FLAG_AUDIO, NULL);
|
||||
|
||||
gst_element_set_state(Element, GST_STATE_PAUSED);
|
||||
|
||||
return ID;
|
||||
}
|
||||
|
||||
//Loads a stream and waits for its initialization
|
||||
int GstreamerAudioStream::Load(const wchar_t* Media, bool Prescan)
|
||||
{
|
||||
Load(Media);
|
||||
if(Prescan){
|
||||
gst_bus_timed_pop_filtered(Bus, -1, GST_MESSAGE_ASYNC_DONE);
|
||||
//Workaround: Waiting for the stream to be loaded (Length>0)
|
||||
for(int i=0;i<40;i++){
|
||||
if(GetLength()>0.f)
|
||||
break;
|
||||
Sleep(5);
|
||||
}
|
||||
}
|
||||
return ID;
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::Close(void)
|
||||
{
|
||||
if(Element)
|
||||
{
|
||||
gst_element_set_state(Element, GST_STATE_NULL);
|
||||
gst_object_unref(GST_OBJECT (Element));
|
||||
}
|
||||
|
||||
/* without that there are no crashes any more. but i don't know if there is a memory leak if we do that.
|
||||
if(Bus)
|
||||
gst_object_unref(Bus);
|
||||
if(Message)
|
||||
gst_message_unref(Message);
|
||||
if(Convert)
|
||||
gst_object_unref(Convert);
|
||||
if(Audiosink)
|
||||
gst_object_unref(Audiosink);
|
||||
if(Pad)
|
||||
gst_object_unref(Pad);
|
||||
if(GhostPad)
|
||||
gst_object_unref(GhostPad);
|
||||
if(SinkBin)
|
||||
gst_object_unref(SinkBin);
|
||||
*/
|
||||
|
||||
if(FadeTimer)
|
||||
g_timer_destroy (FadeTimer);
|
||||
|
||||
Running = false;
|
||||
Closed = true;
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::Play(void)
|
||||
{
|
||||
if(Element)
|
||||
{
|
||||
PauseStreamAfterFade = false;
|
||||
Running = true;
|
||||
if(gst_element_set_state(Element, GST_STATE_PLAYING) == GST_STATE_CHANGE_ASYNC)
|
||||
gst_bus_timed_pop_filtered(Bus, -1, GST_MESSAGE_ASYNC_DONE);
|
||||
SetStreamVolume(Volume * 100.0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::Play(bool Loop)
|
||||
{
|
||||
this->Loop = Loop;
|
||||
Play();
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::Pause()
|
||||
{
|
||||
Running = true;
|
||||
if(Element)
|
||||
gst_element_set_state(Element, GST_STATE_PAUSED);
|
||||
SetStreamVolume(Volume * 100.0);
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::Stop()
|
||||
{
|
||||
if(Element)
|
||||
{
|
||||
gst_element_set_state(Element, GST_STATE_NULL);
|
||||
gst_element_seek_simple(Element, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::SetStreamVolume(float Volume)
|
||||
{
|
||||
this->Volume = Volume / 100.0;
|
||||
if(Element)
|
||||
{
|
||||
g_object_set(this->Element, "volume", (gdouble)(Volume / 100.0) * MaxVolume, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::SetStreamVolumeMax(float MaxVolume)
|
||||
{
|
||||
this->MaxVolume = (gdouble) (MaxVolume / 100.0);
|
||||
if(Element)
|
||||
{
|
||||
g_object_set(this->Element, "volume", Volume * this->MaxVolume, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
float GstreamerAudioStream::GetLength()
|
||||
{
|
||||
if(Duration <= 0.f)
|
||||
RefreshDuration();
|
||||
return Duration;
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::RefreshDuration()
|
||||
{
|
||||
if(Element)
|
||||
{
|
||||
gint64 time = -1000;
|
||||
if(gst_element_query_duration(Element, GST_FORMAT_TIME, &time))
|
||||
Duration = (gfloat)((gdouble)time/GST_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
float GstreamerAudioStream::GetPosition()
|
||||
{
|
||||
if(Element)
|
||||
{
|
||||
gint64 time = 0;
|
||||
if (gst_element_query_position(Element, GST_FORMAT_TIME, &time))
|
||||
return (float)((gdouble)time/GST_SECOND);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::SetPosition(float Position)
|
||||
{
|
||||
if(Element)
|
||||
{
|
||||
//Not sure if we need GST_SEEK_FLAG_ACCURATE
|
||||
if(!gst_element_seek_simple(Element, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), Position * GST_SECOND))
|
||||
LogError("Seek failed");
|
||||
}
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::Fade(float TargetVolume, float Seconds)
|
||||
{
|
||||
Fading = true;
|
||||
g_timer_stop(FadeTimer);
|
||||
StartVolume = Volume;
|
||||
this->TargetVolume = TargetVolume / 100.0;
|
||||
FadeTime = (gdouble)Seconds;
|
||||
g_timer_start(FadeTimer);
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::FadeAndPause(float TargetVolume, float Seconds)
|
||||
{
|
||||
PauseStreamAfterFade = true;
|
||||
|
||||
Fade(TargetVolume, Seconds);
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::FadeAndStop(float TargetVolume, float Seconds)
|
||||
{
|
||||
CloseStreamAfterFade = true;
|
||||
|
||||
Fade(TargetVolume, Seconds);
|
||||
}
|
||||
|
||||
bool GstreamerAudioStream::IsPlaying()
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
|
||||
//This might block!!!
|
||||
ret = gst_element_get_state (Element, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
return current == GST_STATE_PLAYING && !Finished;
|
||||
}
|
||||
|
||||
bool GstreamerAudioStream::IsPaused()
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
|
||||
//This might block!!!
|
||||
ret = gst_element_get_state (Element, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
return current == GST_STATE_PAUSED && !Finished;
|
||||
}
|
||||
|
||||
bool GstreamerAudioStream::IsFinished()
|
||||
{
|
||||
return Finished;
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::UpdateVolume()
|
||||
{
|
||||
if (Fading)
|
||||
{
|
||||
if (g_timer_elapsed(FadeTimer, NULL) < FadeTime) {
|
||||
gdouble vol = (StartVolume + (TargetVolume - StartVolume) * (g_timer_elapsed(FadeTimer, NULL) / FadeTime));
|
||||
SetStreamVolume(100.0 * vol);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetStreamVolume (TargetVolume * 100.0);
|
||||
g_timer_stop(FadeTimer);
|
||||
Fading = false;
|
||||
|
||||
if (CloseStreamAfterFade)
|
||||
{
|
||||
Closed = true;
|
||||
}
|
||||
|
||||
if (PauseStreamAfterFade)
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GstreamerAudioStream::Update()
|
||||
{
|
||||
if(Running) {
|
||||
UpdateVolume();
|
||||
Message = gst_bus_pop (Bus);
|
||||
|
||||
/* Parse message */
|
||||
if (Message != NULL) {
|
||||
GError *err;
|
||||
gchar *debug_info;
|
||||
string m;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (Message)) {
|
||||
case GST_MESSAGE_ERROR:
|
||||
gst_message_parse_error (Message, &err, &debug_info);
|
||||
m = err->message;
|
||||
LogError(m.c_str());
|
||||
g_clear_error (&err);
|
||||
g_free (debug_info);
|
||||
break;
|
||||
case GST_MESSAGE_DURATION_CHANGED:
|
||||
RefreshDuration();
|
||||
break;
|
||||
case GST_MESSAGE_STATE_CHANGED:
|
||||
break;
|
||||
case GST_MESSAGE_EOS:
|
||||
if(Loop)
|
||||
SetPosition(0);
|
||||
else
|
||||
Finished = true;
|
||||
break;
|
||||
default:
|
||||
/* We should not reach here */
|
||||
//LogError ("Unexpected message received.\n");
|
||||
break;
|
||||
}
|
||||
gst_message_unref (Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
#pragma warning(disable: 4244) // possible loss of data
|
||||
#pragma warning(disable: 4267) // possible loss of data
|
||||
#pragma warning(disable: 4800)
|
||||
|
||||
#include <string>
|
||||
#include "gst/gst.h"
|
||||
#include "GstreamerAudio.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class GstreamerAudioStream
|
||||
{
|
||||
public:
|
||||
int ID;
|
||||
bool Closed;
|
||||
|
||||
GstreamerAudioStream(int ID);
|
||||
~GstreamerAudioStream(void);
|
||||
|
||||
int Load(const wchar_t* Media);
|
||||
int Load(const wchar_t* Media, bool Prescan);
|
||||
void Close();
|
||||
|
||||
void Play();
|
||||
void Play(bool Loop);
|
||||
void Pause();
|
||||
void Stop();
|
||||
|
||||
void Fade(float TargetVolume, float Seconds);
|
||||
void FadeAndPause(float TargetVolume, float Seconds);
|
||||
void FadeAndStop(float TargetVolume, float Seconds);
|
||||
void SetStreamVolume(float Volume);
|
||||
void SetStreamVolumeMax(float Volume);
|
||||
|
||||
float GetLength();
|
||||
float GetPosition();
|
||||
void SetPosition(float Position);
|
||||
|
||||
bool IsPlaying();
|
||||
bool IsPaused();
|
||||
bool IsFinished();
|
||||
|
||||
void Update();
|
||||
private:
|
||||
GstElement *Element, *Audiosink, *Convert, *SinkBin;
|
||||
GstPad *Pad, *GhostPad;
|
||||
GstBus *Bus;
|
||||
GstMessage *Message;
|
||||
|
||||
gboolean Running;
|
||||
gboolean Loop;
|
||||
|
||||
gboolean Finished;
|
||||
|
||||
gdouble MaxVolume;
|
||||
gdouble Volume;
|
||||
|
||||
gfloat Duration;
|
||||
|
||||
GTimer *FadeTimer;
|
||||
|
||||
gdouble FadeTime;
|
||||
gdouble TargetVolume;
|
||||
gdouble StartVolume;
|
||||
gboolean CloseStreamAfterFade;
|
||||
gboolean PauseStreamAfterFade;
|
||||
gboolean Fading;
|
||||
|
||||
void RefreshDuration();
|
||||
void UpdateVolume();
|
||||
};
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugWin|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;GSTREAMERHELPER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@@ -282,47 +282,13 @@
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GstreamerAudio.h" />
|
||||
<ClInclude Include="GstreamerAudioStream.h" />
|
||||
<ClInclude Include="GstreamerVideo.h" />
|
||||
<ClInclude Include="GstreamerVideoStream.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="VocaluxeClock.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='DebugWin|Win32'">false</CompileAsManaged>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='DebugWin|x64'">false</CompileAsManaged>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='DebugWin|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='DebugWin|x64'">
|
||||
</PrecompiledHeader>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='ReleaseWin|Win32'">false</CompileAsManaged>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='InstallerWin|Win32'">false</CompileAsManaged>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='ReleaseWin|x64'">false</CompileAsManaged>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='InstallerWin|x64'">false</CompileAsManaged>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseWin|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='InstallerWin|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseWin|x64'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='InstallerWin|x64'">
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GstreamerAudio.cpp" />
|
||||
<ClCompile Include="GstreamerAudioStream.cpp" />
|
||||
<ClCompile Include="GstreamerVideo.cpp" />
|
||||
<ClCompile Include="GstreamerVideoStream.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='DebugWin|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='DebugWin|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseWin|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='InstallerWin|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseWin|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='InstallerWin|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VocaluxeClock.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
@@ -15,18 +15,6 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GstreamerAudioStream.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GstreamerAudio.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GstreamerVideo.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
@@ -38,18 +26,6 @@
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GstreamerAudioStream.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GstreamerAudio.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GstreamerVideo.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "GstreamerVideo.h"
|
||||
#include "GstreamerVideoStream.h"
|
||||
|
||||
map<int, GstreamerVideoStream*> VideoStreams;
|
||||
queue<int> VideoIDs;
|
||||
int NextID;
|
||||
|
||||
DllExport void SetVideoLogCallback (LogCallback Callback)
|
||||
{
|
||||
@@ -12,15 +11,7 @@ DllExport void SetVideoLogCallback (LogCallback Callback)
|
||||
|
||||
DllExport bool InitVideo()
|
||||
{
|
||||
for(int i = 0; i < 1000; i++)
|
||||
{
|
||||
VideoIDs.push(i);
|
||||
}
|
||||
#if _WIN64
|
||||
SetDllDirectory(L".\\x64\\gstreamer");
|
||||
#else
|
||||
SetDllDirectory(L".\\x86\\gstreamer");
|
||||
#endif
|
||||
NextID = 0;
|
||||
|
||||
gst_init(NULL, NULL);
|
||||
|
||||
@@ -28,8 +19,10 @@ DllExport bool InitVideo()
|
||||
|
||||
#if _WIN64
|
||||
gst_registry_scan_path(registry, ".\\x64\\gstreamer");
|
||||
#else
|
||||
#elif _WIN32
|
||||
gst_registry_scan_path(registry, ".\\x86\\gstreamer");
|
||||
#else
|
||||
#error Define either _WIN64 or _WIN32!
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -45,9 +38,8 @@ DllExport void CloseAllVideos(void)
|
||||
|
||||
DllExport int LoadVideo(const wchar_t* Media)
|
||||
{
|
||||
GstreamerVideoStream *s = new GstreamerVideoStream(VideoIDs.front());
|
||||
VideoStreams.insert(pair<int, GstreamerVideoStream*> (VideoIDs.front(), s));
|
||||
VideoIDs.pop();
|
||||
GstreamerVideoStream *s = new GstreamerVideoStream(NextID++);
|
||||
VideoStreams.insert(pair<int, GstreamerVideoStream*> (s->ID, s));
|
||||
return s->LoadVideo(Media);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "stdafx.h"
|
||||
#include "GstreamerVideoStream.h"
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <thread>
|
||||
#include "gst/gst.h"
|
||||
#include "GstreamerVideo.h"
|
||||
#include "gst\app\gstappsink.h"
|
||||
#include "gst/app/gstappsink.h"
|
||||
#include "VocaluxeClock.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// dllmain.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung.
|
||||
#include "stdafx.h"
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// stdafx.cpp : Quelldatei, die nur die Standard-Includes einbindet.
|
||||
// GstreamerHelper.pch ist der vorkompilierte Header.
|
||||
// stdafx.obj enthält die vorkompilierten Typinformationen.
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: Auf zusätzliche Header verweisen, die in STDAFX.H
|
||||
// und nicht in dieser Datei erforderlich sind.
|
||||
@@ -1,16 +0,0 @@
|
||||
// stdafx.h : Includedatei für Standardsystem-Includedateien
|
||||
// oder häufig verwendete projektspezifische Includedateien,
|
||||
// die nur in unregelmäßigen Abständen geändert werden.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Selten verwendete Teile der Windows-Header nicht einbinden.
|
||||
// Windows-Headerdateien:
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
|
||||
// TODO: Hier auf zusätzliche Header, die das Programm erfordert, verweisen.
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Durch Einbeziehen von"SDKDDKVer.h" wird die höchste verfügbare Windows-Plattform definiert.
|
||||
|
||||
// Wenn Sie die Anwendung für eine frühere Windows-Plattform erstellen möchten, schließen Sie "WinSDKVer.h" ein, und
|
||||
// legen Sie das _WIN32_WINNT-Makro auf die zu unterstützende Plattform fest, bevor Sie "SDKDDKVer.h" einschließen.
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
@@ -379,7 +379,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
{
|
||||
base.OnClose();
|
||||
CBase.BackgroundMusic.SetStatus(false);
|
||||
CBase.Sound.FadeAndStop(_PreviewStream, 0f, 0.5f);
|
||||
CBase.Sound.FadeAndClose(_PreviewStream, 0f, 0.5f);
|
||||
}
|
||||
|
||||
private void _CreateFields()
|
||||
@@ -503,7 +503,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
|
||||
CBase.BackgroundMusic.SetStatus(true);
|
||||
|
||||
_PreviewStream = CBase.Sound.Load(song.GetMP3(), false);
|
||||
_PreviewStream = CBase.Sound.Load(song.GetMP3());
|
||||
CBase.Sound.SetPosition(_PreviewStream, song.Preview.StartTime);
|
||||
CBase.Sound.SetStreamVolume(_PreviewStream, 0f);
|
||||
CBase.Sound.Play(_PreviewStream);
|
||||
@@ -527,7 +527,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
CSong song = CBase.Songs.GetSongByID(songID);
|
||||
CBase.BackgroundMusic.SetStatus(true);
|
||||
|
||||
_PreviewStream = CBase.Sound.Load(song.GetMP3(), false);
|
||||
_PreviewStream = CBase.Sound.Load(song.GetMP3());
|
||||
CBase.Sound.SetPosition(_PreviewStream, song.Preview.StartTime);
|
||||
CBase.Sound.SetStreamVolume(_PreviewStream, 0f);
|
||||
CBase.Sound.Play(_PreviewStream);
|
||||
@@ -574,7 +574,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
{
|
||||
_GameData.NumJokerRandom[teamNr]--;
|
||||
if (!CBase.Sound.IsFinished(_PreviewStream))
|
||||
CBase.Sound.FadeAndStop(_PreviewStream, 0, 1);
|
||||
CBase.Sound.FadeAndClose(_PreviewStream, 0, 1);
|
||||
_FieldSelected(true);
|
||||
}
|
||||
break;
|
||||
@@ -586,7 +586,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
_GameData.NumJokerRetry[teamNr]--;
|
||||
_GameData.Team = teamNr;
|
||||
if (!CBase.Sound.IsFinished(_PreviewStream))
|
||||
CBase.Sound.FadeAndStop(_PreviewStream, 0, 1);
|
||||
CBase.Sound.FadeAndClose(_PreviewStream, 0, 1);
|
||||
_Status = EStatus.JokerRetry;
|
||||
_OldSelectedField = _SelectedField;
|
||||
_SelectedField = -1;
|
||||
@@ -627,7 +627,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
|
||||
private void _NextRound()
|
||||
{
|
||||
CBase.Sound.FadeAndStop(_PreviewStream, 0f, 0.5f);
|
||||
CBase.Sound.FadeAndClose(_PreviewStream, 0f, 0.5f);
|
||||
_Data.ScreenMain.Rounds = _GameData.Rounds;
|
||||
_Data.ScreenMain.FadeToNameSelection = false;
|
||||
_Data.ScreenMain.FadeToSinging = true;
|
||||
@@ -640,7 +640,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
|
||||
private void _EndParty()
|
||||
{
|
||||
CBase.Sound.FadeAndStop(_PreviewStream, 0f, 0.5f);
|
||||
CBase.Sound.FadeAndClose(_PreviewStream, 0f, 0.5f);
|
||||
_FadeTo(EScreens.ScreenParty);
|
||||
}
|
||||
|
||||
@@ -659,7 +659,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
|
||||
private void _Back()
|
||||
{
|
||||
CBase.Sound.FadeAndStop(_PreviewStream, 0f, 0.5f);
|
||||
CBase.Sound.FadeAndClose(_PreviewStream, 0f, 0.5f);
|
||||
_Data.ScreenMain.FadeToNameSelection = true;
|
||||
_Data.ScreenMain.FadeToSinging = false;
|
||||
_Data.ScreenMain.SingRoundNr = _SelectedField;
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Vocaluxe.Base
|
||||
|
||||
private static bool _OwnSongsAvailable;
|
||||
private static EBackgroundMusicSource _MusicSource;
|
||||
private static float _Volume;
|
||||
private static bool _VideoEnabled;
|
||||
|
||||
private static CPlaylistElement _CurrentPlaylistElement; //Currently played music
|
||||
@@ -146,12 +145,32 @@ namespace Vocaluxe.Base
|
||||
|
||||
_PreviousMusicIndex = -1;
|
||||
IsPlaying = false;
|
||||
_Volume = CConfig.BackgroundMusicVolume;
|
||||
_VideoEnabled = (CConfig.VideoBackgrounds == EOffOn.TR_CONFIG_ON && CConfig.VideosToBackground == EOffOn.TR_CONFIG_ON);
|
||||
SetMusicSource(CConfig.BackgroundMusicSource);
|
||||
_Initialized = true;
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
if (_CurrentMusicStream >= 0)
|
||||
{
|
||||
CSound.Close(_CurrentMusicStream);
|
||||
_CurrentMusicStream = -1;
|
||||
}
|
||||
if (_Video != -1)
|
||||
{
|
||||
CVideo.Close(_Video);
|
||||
CDraw.RemoveTexture(ref _CurrentVideoTexture);
|
||||
_Video = -1;
|
||||
}
|
||||
_BGMusicFiles.Clear();
|
||||
_NotPlayedFiles.Clear();
|
||||
_PreviousFiles.Clear();
|
||||
|
||||
IsPlaying = false;
|
||||
_Initialized = false;
|
||||
}
|
||||
|
||||
public static void Play()
|
||||
{
|
||||
if (IsPlaying || CConfig.BackgroundMusic == EOffOn.TR_CONFIG_OFF)
|
||||
@@ -161,7 +180,7 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
//Resume
|
||||
CSound.SetStreamVolume(_CurrentMusicStream, 0f);
|
||||
CSound.Fade(_CurrentMusicStream, _Volume, CSettings.BackgroundMusicFadeTime);
|
||||
CSound.Fade(_CurrentMusicStream, 100f, CSettings.BackgroundMusicFadeTime);
|
||||
CSound.Play(_CurrentMusicStream);
|
||||
if (_VideoEnabled && _Video != -1)
|
||||
CVideo.Resume(_Video);
|
||||
@@ -255,8 +274,12 @@ namespace Vocaluxe.Base
|
||||
Debug.Assert(_CurrentMusicStream != -1 && _CurrentPlaylistElement != null);
|
||||
|
||||
float timeToPlay;
|
||||
if (Math.Abs(_CurrentPlaylistElement.Finish) < 0.001) //No End-Tag defined
|
||||
timeToPlay = CSound.GetLength(_CurrentMusicStream) - CSound.GetPosition(_CurrentMusicStream);
|
||||
if (Math.Abs(_CurrentPlaylistElement.Finish) < 0.001)
|
||||
{
|
||||
//No End-Tag defined
|
||||
float len = CSound.GetLength(_CurrentMusicStream);
|
||||
timeToPlay = (len > 0f) ? len - CSound.GetPosition(_CurrentMusicStream) : CSettings.BackgroundMusicFadeTime + 1f;
|
||||
}
|
||||
else //End-Tag found
|
||||
timeToPlay = _CurrentPlaylistElement.Finish - CSound.GetPosition(_CurrentMusicStream);
|
||||
|
||||
@@ -312,13 +335,6 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetVolume(float volume)
|
||||
{
|
||||
_Volume = volume;
|
||||
if (_CurrentMusicStream >= 0)
|
||||
CSound.SetStreamVolume(_CurrentMusicStream, _Volume);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Re-)Starts the _CurrentPlaylistElement
|
||||
/// </summary>
|
||||
@@ -333,7 +349,7 @@ namespace Vocaluxe.Base
|
||||
if (_CurrentMusicStream < 0)
|
||||
return;
|
||||
CSound.SetStreamVolume(_CurrentMusicStream, 0f);
|
||||
CSound.Fade(_CurrentMusicStream, _Volume, CSettings.BackgroundMusicFadeTime);
|
||||
CSound.Fade(_CurrentMusicStream, 100f, CSettings.BackgroundMusicFadeTime);
|
||||
}
|
||||
//Seek to #Start-Tag, if found
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
|
||||
+26
-11
@@ -95,15 +95,15 @@ namespace Vocaluxe.Base
|
||||
public static ELyricStyle LyricStyle = ELyricStyle.Slide;
|
||||
|
||||
// Sound
|
||||
public static EPlaybackLib PlayBackLib = EPlaybackLib.Gstreamer;
|
||||
public static EPlaybackLib PlayBackLib = EPlaybackLib.GstreamerSharp;
|
||||
public static EBufferSize AudioBufferSize = EBufferSize.B2048;
|
||||
public static int AudioLatency;
|
||||
private static int _BackgroundMusicVolume = 30;
|
||||
public static EOffOn BackgroundMusic = EOffOn.TR_CONFIG_ON;
|
||||
public static EBackgroundMusicSource BackgroundMusicSource = EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC;
|
||||
public static EOffOn BackgroundMusicUseStart = EOffOn.TR_CONFIG_ON;
|
||||
public static int PreviewMusicVolume = 50;
|
||||
public static int GameMusicVolume = 80;
|
||||
private static int _PreviewMusicVolume = 50;
|
||||
private static int _GameMusicVolume = 80;
|
||||
|
||||
//Folders
|
||||
public static readonly List<string> SongFolders = new List<string>
|
||||
@@ -171,12 +171,27 @@ namespace Vocaluxe.Base
|
||||
get { return _BackgroundMusicVolume; }
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
_BackgroundMusicVolume = 0;
|
||||
else if (value > 100)
|
||||
_BackgroundMusicVolume = 100;
|
||||
else
|
||||
_BackgroundMusicVolume = value;
|
||||
_BackgroundMusicVolume = value.Clamp(0, 100);
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
public static int GameMusicVolume
|
||||
{
|
||||
get { return _GameMusicVolume; }
|
||||
set
|
||||
{
|
||||
_GameMusicVolume = value.Clamp(0, 100);
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
public static int PreviewMusicVolume
|
||||
{
|
||||
get { return _PreviewMusicVolume; }
|
||||
set
|
||||
{
|
||||
_PreviewMusicVolume = value.Clamp(0, 100);
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
@@ -248,8 +263,8 @@ namespace Vocaluxe.Base
|
||||
xmlReader.TryGetIntValueRange("//root/Sound/BackgroundMusicVolume", ref _BackgroundMusicVolume);
|
||||
xmlReader.TryGetEnumValue("//root/Sound/BackgroundMusicSource", ref BackgroundMusicSource);
|
||||
xmlReader.TryGetEnumValue("//root/Sound/BackgroundMusicUseStart", ref BackgroundMusicUseStart);
|
||||
xmlReader.TryGetIntValueRange("//root/Sound/PreviewMusicVolume", ref PreviewMusicVolume);
|
||||
xmlReader.TryGetIntValueRange("//root/Sound/GameMusicVolume", ref GameMusicVolume);
|
||||
xmlReader.TryGetIntValueRange("//root/Sound/PreviewMusicVolume", ref _PreviewMusicVolume);
|
||||
xmlReader.TryGetIntValueRange("//root/Sound/GameMusicVolume", ref _GameMusicVolume);
|
||||
#endregion Sound
|
||||
|
||||
#region Game
|
||||
|
||||
+14
-11
@@ -18,9 +18,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Draw;
|
||||
|
||||
@@ -35,7 +32,6 @@ namespace Vocaluxe.Base
|
||||
|
||||
static class CCover
|
||||
{
|
||||
private static readonly XmlWriterSettings _Settings = new XmlWriterSettings();
|
||||
private static readonly Dictionary<string, CTexture> _Covers = new Dictionary<string, CTexture>();
|
||||
private static readonly List<SCoverTheme> _CoverThemes = new List<SCoverTheme>();
|
||||
|
||||
@@ -43,14 +39,16 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
_Settings.Indent = true;
|
||||
_Settings.Encoding = Encoding.UTF8;
|
||||
_Settings.ConformanceLevel = ConformanceLevel.Document;
|
||||
|
||||
_LoadCoverThemes();
|
||||
_LoadCover(CConfig.CoverTheme);
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
_UnloadCovers();
|
||||
_CoverThemes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of CoverThemes-Names
|
||||
/// </summary>
|
||||
@@ -106,18 +104,23 @@ namespace Vocaluxe.Base
|
||||
/// <summary>
|
||||
/// Reloads cover to use a new cover-theme
|
||||
/// </summary>
|
||||
public static void ReloadCover()
|
||||
public static void ReloadCovers()
|
||||
{
|
||||
_UnloadCovers();
|
||||
_LoadCover(CConfig.CoverTheme);
|
||||
}
|
||||
|
||||
private static void _UnloadCovers()
|
||||
{
|
||||
lock (_Covers)
|
||||
{
|
||||
foreach (string key in _Covers.Keys.ToList())
|
||||
foreach (string key in _Covers.Keys)
|
||||
{
|
||||
CTexture texture = _Covers[key];
|
||||
CDraw.RemoveTexture(ref texture);
|
||||
}
|
||||
_Covers.Clear();
|
||||
}
|
||||
_LoadCover(CConfig.CoverTheme);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+39
-1483
File diff suppressed because it is too large.
Load diff
@@ -28,7 +28,7 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
private static IDraw _Draw;
|
||||
|
||||
public static bool InitDraw()
|
||||
public static bool Init()
|
||||
{
|
||||
if (_Draw != null)
|
||||
return false;
|
||||
|
||||
@@ -106,6 +106,13 @@ namespace Vocaluxe.Base
|
||||
CLog.StopBenchmark("Load Theme");
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
if (_CurrentPopupScreen != EPopupScreens.NoPopup)
|
||||
_PopupScreens[(int)_CurrentPopupScreen].OnClose();
|
||||
_Screens[(int)_CurrentScreen].OnClose();
|
||||
}
|
||||
|
||||
public static void LoadTheme()
|
||||
{
|
||||
_Cursor.LoadTextures();
|
||||
@@ -408,6 +415,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
if (popupVolumeControlAllowed)
|
||||
{
|
||||
//TODO: Handle this in the volume popup
|
||||
int diff = 0;
|
||||
if ((keyEvent.ModShift && (keyEvent.Key == Keys.Add || keyEvent.Key == Keys.PageUp)) || (keyEvent.Sender == ESender.WiiMote && keyEvent.Key == Keys.Add))
|
||||
diff = 5;
|
||||
@@ -423,23 +431,23 @@ namespace Vocaluxe.Base
|
||||
if (CSongs.IsInCategory)
|
||||
{
|
||||
CConfig.PreviewMusicVolume += diff;
|
||||
_Screens[(int)_CurrentScreen].ApplyVolume();
|
||||
CSound.SetGlobalVolume(CConfig.PreviewMusicVolume);
|
||||
}
|
||||
else
|
||||
{
|
||||
CConfig.BackgroundMusicVolume += diff;
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
}
|
||||
break;
|
||||
|
||||
case EScreens.ScreenSing:
|
||||
CConfig.GameMusicVolume += diff;
|
||||
_Screens[(int)_CurrentScreen].ApplyVolume();
|
||||
CSound.SetGlobalVolume(CConfig.GameMusicVolume);
|
||||
break;
|
||||
|
||||
default:
|
||||
CConfig.BackgroundMusicVolume += diff;
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
break;
|
||||
}
|
||||
CConfig.SaveConfig();
|
||||
@@ -521,13 +529,8 @@ namespace Vocaluxe.Base
|
||||
|
||||
bool handled = false;
|
||||
if (_CurrentPopupScreen != EPopupScreens.NoPopup)
|
||||
{
|
||||
handled = _PopupScreens[(int)_CurrentPopupScreen].HandleMouse(mouseEvent);
|
||||
|
||||
if (handled && _CurrentPopupScreen == EPopupScreens.PopupVolumeControl)
|
||||
_Screens[(int)_CurrentScreen].ApplyVolume();
|
||||
}
|
||||
|
||||
if (!handled && _Fading == null && (_Cursor.IsActive || mouseEvent.LB || mouseEvent.RB || mouseEvent.MB))
|
||||
resume &= _Screens[(int)_CurrentScreen].HandleMouse(mouseEvent);
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Base
|
||||
@@ -42,7 +40,6 @@ namespace Vocaluxe.Base
|
||||
|
||||
static class CLanguage
|
||||
{
|
||||
private static readonly XmlWriterSettings _Settings = new XmlWriterSettings();
|
||||
private static List<SLanguage> _Languages;
|
||||
private static int _CurrentLanguage;
|
||||
private static int _FallbackLanguage;
|
||||
@@ -75,9 +72,6 @@ namespace Vocaluxe.Base
|
||||
public static void Init()
|
||||
{
|
||||
_Languages = new List<SLanguage>();
|
||||
_Settings.Indent = true;
|
||||
_Settings.Encoding = Encoding.UTF8;
|
||||
_Settings.ConformanceLevel = ConformanceLevel.Document;
|
||||
|
||||
var files = new List<string>();
|
||||
files.AddRange(CHelper.ListFiles(CSettings.FolderLanguages, "*.xml", true, true));
|
||||
|
||||
@@ -773,9 +773,9 @@ namespace Vocaluxe.Base
|
||||
|
||||
class CBsound : ISound
|
||||
{
|
||||
public int Load(string soundFile, bool prescan)
|
||||
public int Load(string soundFile, bool loop = false, bool prescan = false)
|
||||
{
|
||||
return CSound.Load(soundFile, prescan);
|
||||
return CSound.Load(soundFile, loop, prescan);
|
||||
}
|
||||
|
||||
public void SetPosition(int soundStream, float newPosition)
|
||||
@@ -808,7 +808,7 @@ namespace Vocaluxe.Base
|
||||
return CSound.GetLength(soundStream);
|
||||
}
|
||||
|
||||
public void FadeAndStop(int soundStream, float targetVolume, float duration)
|
||||
public void FadeAndClose(int soundStream, float targetVolume, float duration)
|
||||
{
|
||||
CSound.FadeAndClose(soundStream, targetVolume, duration);
|
||||
}
|
||||
@@ -817,11 +817,6 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
CSound.SetStreamVolume(soundStream, volume);
|
||||
}
|
||||
|
||||
public void SetStreamVolumeMax(int soundStream, float maxVolume)
|
||||
{
|
||||
CSound.SetStreamVolumeMax(soundStream, maxVolume);
|
||||
}
|
||||
}
|
||||
|
||||
class CBcover : ICover
|
||||
|
||||
+26
-30
@@ -18,11 +18,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Vocaluxe.Lib.Sound.Playback;
|
||||
using Vocaluxe.Lib.Sound.Playback.Gstreamer;
|
||||
using Vocaluxe.Lib.Sound.Playback.GstreamerSharp;
|
||||
using Vocaluxe.Lib.Sound.Playback.OpenAL;
|
||||
using Vocaluxe.Lib.Sound.Playback.PortAudio;
|
||||
using VocaluxeLib;
|
||||
using System.Threading;
|
||||
|
||||
namespace Vocaluxe.Base
|
||||
{
|
||||
@@ -50,10 +50,6 @@ namespace Vocaluxe.Base
|
||||
_Playback = new COpenALPlay();
|
||||
break;
|
||||
|
||||
case EPlaybackLib.Gstreamer:
|
||||
_Playback = new CGstreamerAudio();
|
||||
break;
|
||||
|
||||
case EPlaybackLib.GstreamerSharp:
|
||||
_Playback = new CGstreamerSharpAudio();
|
||||
break;
|
||||
@@ -89,14 +85,9 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
|
||||
#region Stream Handling
|
||||
public static int Load(string media)
|
||||
public static int Load(string media, bool loop=false, bool prescan=false)
|
||||
{
|
||||
return _Playback.Load(media);
|
||||
}
|
||||
|
||||
public static int Load(string media, bool prescan)
|
||||
{
|
||||
return _Playback.Load(media, prescan);
|
||||
return _Playback.Load(media, loop, prescan);
|
||||
}
|
||||
|
||||
public static void Close(int stream)
|
||||
@@ -109,11 +100,6 @@ namespace Vocaluxe.Base
|
||||
_Playback.Play(stream);
|
||||
}
|
||||
|
||||
public static void Play(int stream, bool loop)
|
||||
{
|
||||
_Playback.Play(stream, loop);
|
||||
}
|
||||
|
||||
public static void Pause(int stream)
|
||||
{
|
||||
_Playback.Pause(stream);
|
||||
@@ -124,24 +110,24 @@ namespace Vocaluxe.Base
|
||||
_Playback.Stop(stream);
|
||||
}
|
||||
|
||||
public static void Fade(int stream, float targetVolume, float seconds)
|
||||
public static void Fade(int stream, float targetVolume, float seconds, EStreamAction afterFadeAction = EStreamAction.Nothing)
|
||||
{
|
||||
_Playback.Fade(stream, targetVolume, seconds);
|
||||
_Playback.Fade(stream, targetVolume, seconds, afterFadeAction);
|
||||
}
|
||||
|
||||
public static void FadeAndPause(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
_Playback.FadeAndPause(stream, targetVolume, seconds);
|
||||
Fade(stream, targetVolume, seconds, EStreamAction.Pause);
|
||||
}
|
||||
|
||||
public static void FadeAndClose(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
_Playback.FadeAndClose(stream, targetVolume, seconds);
|
||||
Fade(stream, targetVolume, seconds, EStreamAction.Close);
|
||||
}
|
||||
|
||||
public static void FadeAndStop(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
_Playback.FadeAndStop(stream, targetVolume, seconds);
|
||||
Fade(stream, targetVolume, seconds, EStreamAction.Stop);
|
||||
}
|
||||
|
||||
public static void SetStreamVolume(int stream, float volume)
|
||||
@@ -149,11 +135,6 @@ namespace Vocaluxe.Base
|
||||
_Playback.SetStreamVolume(stream, volume);
|
||||
}
|
||||
|
||||
public static void SetStreamVolumeMax(int stream, float volume)
|
||||
{
|
||||
_Playback.SetStreamVolumeMax(stream, volume);
|
||||
}
|
||||
|
||||
public static float GetLength(int stream)
|
||||
{
|
||||
return _Playback.GetLength(stream);
|
||||
@@ -208,11 +189,26 @@ namespace Vocaluxe.Base
|
||||
if (!File.Exists(file))
|
||||
return -1;
|
||||
|
||||
int stream = Load(file, true);
|
||||
float length = GetLength(stream);
|
||||
int stream = Load(file, false, fade);
|
||||
if (stream < 0)
|
||||
return -1;
|
||||
Play(stream);
|
||||
if (fade)
|
||||
FadeAndClose(stream, 100f, length);
|
||||
{
|
||||
float length = -1f;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
length = GetLength(stream);
|
||||
if (length >= 0f)
|
||||
break;
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
if (length > 0f)
|
||||
{
|
||||
SetStreamVolume(stream, 0f);
|
||||
Fade(stream, 100f, length);
|
||||
}
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
#endregion Sounds
|
||||
|
||||
@@ -19,7 +19,6 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Menu;
|
||||
@@ -28,8 +27,6 @@ namespace Vocaluxe.Base.Fonts
|
||||
{
|
||||
static class CFonts
|
||||
{
|
||||
private static readonly XmlWriterSettings _Settings = new XmlWriterSettings();
|
||||
|
||||
private static readonly List<SFont> _Fonts = new List<SFont>();
|
||||
private static int _CurrentFont;
|
||||
private static float _Height = 1f;
|
||||
@@ -58,10 +55,6 @@ namespace Vocaluxe.Base.Fonts
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
_Settings.Indent = true;
|
||||
_Settings.Encoding = Encoding.UTF8;
|
||||
_Settings.ConformanceLevel = ConformanceLevel.Document;
|
||||
|
||||
_CurrentFont = 0;
|
||||
PartyModeID = -1;
|
||||
_BuildFonts();
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vocaluxe.Base;
|
||||
using VocaluxeLib.Draw;
|
||||
#if WIN
|
||||
using System.Data.SQLite;
|
||||
|
||||
#else
|
||||
using Mono.Data.Sqlite;
|
||||
#endif
|
||||
|
||||
namespace Vocaluxe.Lib.Database
|
||||
{
|
||||
public class CCoverDB : CDatabaseBase
|
||||
{
|
||||
//You have to lock all actions using cover connection or transaction, otherwhise order is not guaranted
|
||||
private readonly object _CoverMutex = new object();
|
||||
private SQLiteTransaction _TransactionCover;
|
||||
|
||||
public CCoverDB(string filePath) : base(filePath) {}
|
||||
|
||||
public override bool Init()
|
||||
{
|
||||
if (!base.Init())
|
||||
return false;
|
||||
|
||||
if (_Version < 0)
|
||||
return _CreateCoverDB();
|
||||
if (_Version < CSettings.DatabaseCoverVersion)
|
||||
throw new NotImplementedException("Upgrading of cover DB not implemented");
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
//Do commit and close atomicly otherwhise we may loose changes
|
||||
lock (_CoverMutex)
|
||||
{
|
||||
_CommitCovers();
|
||||
|
||||
base.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetCover(string coverPath, ref CTexture tex, int maxSize)
|
||||
{
|
||||
if (_Connection == null)
|
||||
return false;
|
||||
if (!File.Exists(coverPath))
|
||||
{
|
||||
CLog.LogError("Can't find File: " + coverPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (_CoverMutex)
|
||||
{
|
||||
//Double check here because we may have just closed our connection
|
||||
if (_Connection == null)
|
||||
return false;
|
||||
using (var command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.CommandText = "SELECT id, width, height FROM Cover WHERE [Path] = @path";
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = coverPath;
|
||||
|
||||
SQLiteDataReader reader = command.ExecuteReader();
|
||||
|
||||
if (reader != null && reader.HasRows)
|
||||
{
|
||||
reader.Read();
|
||||
int id = reader.GetInt32(0);
|
||||
int w = reader.GetInt32(1);
|
||||
int h = reader.GetInt32(2);
|
||||
reader.Close();
|
||||
|
||||
command.CommandText = "SELECT Data FROM CoverData WHERE CoverID = @id";
|
||||
command.Parameters.Add("@id", DbType.Int32).Value = id;
|
||||
reader = command.ExecuteReader();
|
||||
|
||||
if (reader.HasRows)
|
||||
{
|
||||
reader.Read();
|
||||
byte[] data = _GetBytes(reader);
|
||||
reader.Dispose();
|
||||
tex = CDraw.EnqueueTexture(w, h, data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reader != null)
|
||||
reader.Close();
|
||||
|
||||
if (_TransactionCover == null)
|
||||
_TransactionCover = _Connection.BeginTransaction();
|
||||
|
||||
Bitmap origin;
|
||||
try
|
||||
{
|
||||
origin = new Bitmap(coverPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error loading Texture: " + coverPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
int w = maxSize;
|
||||
int h = maxSize;
|
||||
byte[] data;
|
||||
|
||||
try
|
||||
{
|
||||
if (origin.Width >= origin.Height && origin.Width > w)
|
||||
h = (int)Math.Round((float)w / origin.Width * origin.Height);
|
||||
else if (origin.Height > origin.Width && origin.Height > h)
|
||||
w = (int)Math.Round((float)h / origin.Height * origin.Width);
|
||||
|
||||
using (var bmp = new Bitmap(w, h))
|
||||
{
|
||||
using (Graphics g = Graphics.FromImage(bmp))
|
||||
g.DrawImage(origin, new Rectangle(0, 0, w, h));
|
||||
|
||||
data = new byte[w * h * 4];
|
||||
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
Marshal.Copy(bmpData.Scan0, data, 0, w * h * 4);
|
||||
bmp.UnlockBits(bmpData);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
origin.Dispose();
|
||||
}
|
||||
|
||||
tex = CDraw.EnqueueTexture(w, h, data);
|
||||
|
||||
command.CommandText = "INSERT INTO Cover (Path, width, height) VALUES (@path, @w, @h)";
|
||||
command.Parameters.Add("@w", DbType.Int32).Value = w;
|
||||
command.Parameters.Add("@h", DbType.Int32).Value = h;
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = coverPath;
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "SELECT id FROM Cover WHERE [Path] = @path";
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = coverPath;
|
||||
reader = command.ExecuteReader();
|
||||
|
||||
if (reader != null)
|
||||
{
|
||||
reader.Read();
|
||||
int id = reader.GetInt32(0);
|
||||
reader.Dispose();
|
||||
command.CommandText = "INSERT INTO CoverData (CoverID, Data) VALUES (@id, @data)";
|
||||
command.Parameters.Add("@id", DbType.Int32).Value = id;
|
||||
command.Parameters.Add("@data", DbType.Binary, 20).Value = data;
|
||||
command.ExecuteReader();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CommitCovers()
|
||||
{
|
||||
lock (_CoverMutex)
|
||||
{
|
||||
_CommitCovers();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// You have to hold the CoverMutex when calling this!
|
||||
/// </summary>
|
||||
private void _CommitCovers()
|
||||
{
|
||||
if (_TransactionCover == null)
|
||||
return;
|
||||
_TransactionCover.Commit();
|
||||
_TransactionCover.Dispose();
|
||||
_TransactionCover = null;
|
||||
}
|
||||
|
||||
private bool _CreateCoverDB()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.CommandText = "CREATE TABLE IF NOT EXISTS Version (Value INTEGER NOT NULL);";
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "INSERT INTO Version (Value) VALUES(@Value)";
|
||||
command.Parameters.Add("@Value", DbType.Int32).Value = CSettings.DatabaseCoverVersion;
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "CREATE TABLE IF NOT EXISTS Cover ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
|
||||
"Path TEXT NOT NULL, width INTEGER NOT NULL, height INTEGER NOT NULL);";
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "CREATE TABLE IF NOT EXISTS CoverData ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
|
||||
"CoverID INTEGER NOT NULL, Data BLOB NOT NULL);";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CLog.LogError("Error creating Cover DB " + e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Vocaluxe.Base;
|
||||
#if WIN
|
||||
using System.Data.SQLite;
|
||||
|
||||
#else
|
||||
using Mono.Data.Sqlite;
|
||||
#endif
|
||||
|
||||
namespace Vocaluxe.Lib.Database
|
||||
{
|
||||
public class CDatabaseBase
|
||||
{
|
||||
protected readonly string _FilePath;
|
||||
protected SQLiteConnection _Connection;
|
||||
|
||||
protected int _Version; //Version of the database (value of row "Value" in table "Version") or -1 if none
|
||||
|
||||
public CDatabaseBase(string filePath)
|
||||
{
|
||||
_FilePath = filePath;
|
||||
}
|
||||
|
||||
public virtual bool Init()
|
||||
{
|
||||
_Connection = new SQLiteConnection("Data Source=" + _FilePath);
|
||||
try
|
||||
{
|
||||
_Connection.Open();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_Connection.Dispose();
|
||||
_Connection = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.CommandText = "SELECT Value FROM Version";
|
||||
|
||||
SQLiteDataReader reader = null;
|
||||
|
||||
try
|
||||
{
|
||||
reader = command.ExecuteReader();
|
||||
}
|
||||
catch (Exception) {}
|
||||
|
||||
if (reader == null || !reader.Read() || reader.FieldCount == 0)
|
||||
_Version = -1;
|
||||
else
|
||||
_Version = reader.GetInt32(0);
|
||||
|
||||
if (reader != null)
|
||||
reader.Dispose();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
if (_Connection != null)
|
||||
{
|
||||
_Connection.Dispose();
|
||||
_Connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected static long _UnixTimeToTicks(int unixTime)
|
||||
{
|
||||
var t70 = new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||
t70 = t70.AddSeconds(unixTime);
|
||||
return t70.Ticks;
|
||||
}
|
||||
|
||||
protected static byte[] _GetBytes(SQLiteDataReader reader, int field = 0)
|
||||
{
|
||||
const int chunkSize = 2 * 1024;
|
||||
var buffer = new byte[chunkSize];
|
||||
long fieldOffset = 0;
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
int bytesRead;
|
||||
while ((bytesRead = (int)reader.GetBytes(field, fieldOffset, buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
stream.Write(buffer, 0, bytesRead);
|
||||
fieldOffset += bytesRead;
|
||||
}
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,253 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vocaluxe.Base;
|
||||
using VocaluxeLib.Draw;
|
||||
#if WIN
|
||||
using System.Data.SQLite;
|
||||
|
||||
#else
|
||||
using Mono.Data.Sqlite;
|
||||
#endif
|
||||
|
||||
namespace Vocaluxe.Lib.Database
|
||||
{
|
||||
public class CResourceDB : CDatabaseBase
|
||||
{
|
||||
#if DEBUG
|
||||
private readonly string[] _FilesV1 = new string[]
|
||||
{
|
||||
"Logo_voc.png", "PerfectNoteStar.png", "redDot.png", "blueDot.png", "brunzel.png", "Darkice.png", "flokuep.png", "flamefire.png", "bohning.png", "mesand.png",
|
||||
"babene03.png", "pantero.png", "Pinky007.png"
|
||||
};
|
||||
#endif
|
||||
public CResourceDB(string filePath) : base(filePath) {}
|
||||
|
||||
public override bool Init()
|
||||
{
|
||||
if (!base.Init())
|
||||
return false;
|
||||
if (_Version < 0)
|
||||
{
|
||||
CLog.LogError("Can't find Ressource-DB!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_Version < CSettings.DatabaseCreditsRessourcesVersion)
|
||||
{
|
||||
#if DEBUG
|
||||
return _CreateDB() && _UpdateV1();
|
||||
#else
|
||||
CLog.LogError("Upgrading Ressource-DB not possible");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GetCreditsRessource(string fileName, ref CTexture tex)
|
||||
{
|
||||
if (_Connection == null)
|
||||
return false;
|
||||
bool result = false;
|
||||
|
||||
using (var command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.CommandText = "SELECT id, width, height FROM Images WHERE [Path] = @path";
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = fileName;
|
||||
|
||||
SQLiteDataReader reader = command.ExecuteReader();
|
||||
|
||||
if (reader != null && reader.HasRows)
|
||||
{
|
||||
reader.Read();
|
||||
int id = reader.GetInt32(0);
|
||||
int w = reader.GetInt32(1);
|
||||
int h = reader.GetInt32(2);
|
||||
reader.Close();
|
||||
|
||||
command.CommandText = "SELECT Data FROM ImageData WHERE ImageID = @id";
|
||||
command.Parameters.Add("@id", DbType.Int32).Value = id;
|
||||
reader = command.ExecuteReader();
|
||||
|
||||
if (reader.HasRows)
|
||||
{
|
||||
result = true;
|
||||
reader.Read();
|
||||
byte[] data = _GetBytes(reader);
|
||||
tex = CDraw.AddTexture(w, h, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (reader != null)
|
||||
reader.Dispose();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
//Update from V0 (empty) to V1
|
||||
private bool _UpdateV1()
|
||||
{
|
||||
if (_Connection == null || _Version >= 1)
|
||||
return false;
|
||||
using (SQLiteTransaction transaction = _Connection.BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (string file in _FilesV1)
|
||||
{
|
||||
string filePath = Path.Combine(Environment.CurrentDirectory, file);
|
||||
if (!_AddImageToCreditsDB(filePath, transaction))
|
||||
{
|
||||
transaction.Rollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
using (SQLiteCommand command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.Transaction = transaction;
|
||||
command.CommandText = "Update Version SET Value=@Value)";
|
||||
command.Parameters.Add("@Value", DbType.Int32).Value = 1;
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
transaction.Commit();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//If you want to add an image to db, call this method!
|
||||
private bool _AddImageToCreditsDB(string imagePath, SQLiteTransaction transaction)
|
||||
{
|
||||
if (_Connection == null || !File.Exists(imagePath))
|
||||
return false;
|
||||
|
||||
Bitmap origin;
|
||||
try
|
||||
{
|
||||
origin = new Bitmap(imagePath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error loading image: " + imagePath);
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
int w = origin.Width;
|
||||
int h = origin.Height;
|
||||
byte[] data = new byte[w * h * 4];
|
||||
|
||||
BitmapData bmpData = origin.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
Marshal.Copy(bmpData.Scan0, data, 0, w * h * 4);
|
||||
origin.UnlockBits(bmpData);
|
||||
|
||||
using (SQLiteCommand command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.Transaction = transaction;
|
||||
command.CommandText = "INSERT INTO Images (Path, width, height) VALUES (@path, @w, @h)";
|
||||
command.Parameters.Add("@path", DbType.String).Value = Path.GetFileName(imagePath);
|
||||
command.Parameters.Add("@w", DbType.Int32).Value = w;
|
||||
command.Parameters.Add("@h", DbType.Int32).Value = h;
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
int id = -1;
|
||||
using (SQLiteCommand command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.Transaction = transaction;
|
||||
command.CommandText = "SELECT id FROM Images WHERE [Path] = @path";
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = Path.GetFileName(imagePath);
|
||||
using (SQLiteDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
if (reader != null)
|
||||
{
|
||||
reader.Read();
|
||||
id = reader.GetInt32(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (id < 0)
|
||||
return false;
|
||||
using (SQLiteCommand command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.Transaction = transaction;
|
||||
command.CommandText = "INSERT INTO ImageData (ImageID, Data) VALUES (@id, @data)";
|
||||
command.Parameters.Add("@id", DbType.Int32).Value = id;
|
||||
command.Parameters.Add("@data", DbType.Binary).Value = data;
|
||||
command.ExecuteNonQuery();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
origin.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _CreateDB()
|
||||
{
|
||||
if (_Connection == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using (SQLiteCommand command = new SQLiteCommand(_Connection))
|
||||
{
|
||||
command.CommandText = "CREATE TABLE IF NOT EXISTS Version (Value INTEGER NOT NULL);";
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "INSERT INTO Version (Value) VALUES(0)";
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "CREATE TABLE IF NOT EXISTS Images ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
|
||||
"Path TEXT NOT NULL, width INTEGER NOT NULL, height INTEGER NOT NULL);";
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "CREATE TABLE IF NOT EXISTS ImageData ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
|
||||
"ImageID INTEGER NOT NULL, Data BLOB NOT NULL);";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CLog.LogError("Error creating Ressource DB " + e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback
|
||||
{
|
||||
abstract class CAudioStreamBase : IAudioStream
|
||||
{
|
||||
private readonly int _ID;
|
||||
protected readonly string _Medium;
|
||||
protected readonly bool _Loop;
|
||||
|
||||
protected ICloseStreamListener _CloseStreamListener;
|
||||
|
||||
protected EStreamAction _AfterFadeAction = EStreamAction.Nothing;
|
||||
protected CFading _Fading;
|
||||
|
||||
public int ID
|
||||
{
|
||||
get { return _ID; }
|
||||
}
|
||||
|
||||
public virtual float Volume { get; set; }
|
||||
public virtual float VolumeMax { get; set; }
|
||||
public virtual float Length { get; protected set; }
|
||||
public abstract float Position { get; set; }
|
||||
public abstract bool IsPaused { get; set; }
|
||||
public abstract bool IsFinished { get; }
|
||||
|
||||
public CAudioStreamBase(int id, string medium, bool loop)
|
||||
{
|
||||
_ID = id;
|
||||
_Medium = medium;
|
||||
_Loop = loop;
|
||||
}
|
||||
|
||||
~CAudioStreamBase()
|
||||
{
|
||||
_Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void _Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
CBase.Log.LogDebug("Audio stream " + _Medium + " was not closed.");
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void SetOnCloseListener(ICloseStreamListener listener)
|
||||
{
|
||||
_CloseStreamListener = listener;
|
||||
}
|
||||
|
||||
public void Fade(float targetVolume, float seconds, EStreamAction afterFadeAction)
|
||||
{
|
||||
_Fading = new CFading(Volume, targetVolume, seconds);
|
||||
_AfterFadeAction = afterFadeAction;
|
||||
}
|
||||
|
||||
public void CancelFading()
|
||||
{
|
||||
_Fading = null;
|
||||
}
|
||||
|
||||
public virtual void Update()
|
||||
{
|
||||
if (_Fading != null)
|
||||
{
|
||||
bool finished;
|
||||
Volume = _Fading.GetValue(out finished);
|
||||
if (finished)
|
||||
{
|
||||
switch (_AfterFadeAction)
|
||||
{
|
||||
case EStreamAction.Close:
|
||||
Close();
|
||||
break;
|
||||
case EStreamAction.Stop:
|
||||
Stop();
|
||||
break;
|
||||
case EStreamAction.Pause:
|
||||
IsPaused = true;
|
||||
break;
|
||||
}
|
||||
_Fading = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract bool Open(bool prescan);
|
||||
public abstract void Play();
|
||||
public abstract void Stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback
|
||||
{
|
||||
public abstract class CPlaybackBase : IPlayback, ICloseStreamListener
|
||||
{
|
||||
protected bool _Initialized;
|
||||
protected readonly List<IAudioStream> _Streams = new List<IAudioStream>();
|
||||
private readonly List<IAudioStream> _StreamsToDelete = new List<IAudioStream>();
|
||||
private bool _InUpdate;
|
||||
private int _NextID;
|
||||
protected float _GlobalVolume = 1f;
|
||||
|
||||
public abstract bool Init();
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
List<IAudioStream> streams = new List<IAudioStream>();
|
||||
//Get all streams but do not modify list without holding the lock
|
||||
//Calling dispose from within the lock may deadlock in closeproc (if a thread is already there)
|
||||
lock (_Streams)
|
||||
{
|
||||
streams.AddRange(_Streams);
|
||||
}
|
||||
foreach (IAudioStream stream in streams)
|
||||
stream.Dispose();
|
||||
while (_Streams.Count > 0)
|
||||
Thread.Sleep(5);
|
||||
_Initialized = false;
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
foreach (IAudioStream stream in _Streams)
|
||||
stream.Close();
|
||||
_Streams.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public float GetGlobalVolume()
|
||||
{
|
||||
return (_Initialized) ? _GlobalVolume * 100f : 100f;
|
||||
}
|
||||
|
||||
public void SetGlobalVolume(float volume)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
volume = volume.Clamp(0f, 100f) / 100f;
|
||||
foreach (IAudioStream stream in _Streams)
|
||||
stream.VolumeMax = volume;
|
||||
_GlobalVolume = volume;
|
||||
}
|
||||
|
||||
public int GetStreamCount()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
lock (_Streams)
|
||||
{
|
||||
return _Streams.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
_InUpdate = true;
|
||||
foreach (IAudioStream stream in _Streams)
|
||||
stream.Update();
|
||||
//This is required because a stream may call the close listener from inside the update method
|
||||
//lock() only protects from different threads not from the same, so we use StreamsToDelete to not modify _Streams while iterating it
|
||||
_InUpdate = false;
|
||||
if (_StreamsToDelete.Count > 0)
|
||||
{
|
||||
foreach (IAudioStream stream in _StreamsToDelete)
|
||||
_Streams.Remove(stream);
|
||||
_StreamsToDelete.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Stream Handling
|
||||
//Factory method to get a stream instance
|
||||
protected abstract IAudioStream _CreateStream(int id, string media, bool loop);
|
||||
|
||||
public int Load(string medium, bool loop = false, bool prescan = false)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return -1;
|
||||
IAudioStream stream = _CreateStream(_NextID, medium, loop);
|
||||
|
||||
if (stream.Open(prescan))
|
||||
{
|
||||
lock (_Streams)
|
||||
{
|
||||
stream.Volume = 1f;
|
||||
stream.VolumeMax = _GlobalVolume;
|
||||
stream.SetOnCloseListener(this);
|
||||
_Streams.Add(stream);
|
||||
_NextID++;
|
||||
return stream.ID;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Close(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
int index = _GetStreamIndex(streamID);
|
||||
if (index >= 0)
|
||||
{
|
||||
_Streams[index].Close();
|
||||
_Streams.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Play(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
_Streams[_GetStreamIndex(streamID)].Play();
|
||||
}
|
||||
}
|
||||
|
||||
public void Pause(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
_Streams[_GetStreamIndex(streamID)].IsPaused = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
_Streams[_GetStreamIndex(streamID)].Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void Fade(int streamID, float targetVolume, float seconds, EStreamAction afterFadeAction = EStreamAction.Nothing)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
targetVolume = targetVolume.Clamp(0f, 100f) / 100f;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
_Streams[_GetStreamIndex(streamID)].Fade(targetVolume, seconds, afterFadeAction);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStreamVolume(int streamID, float volume)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
volume = volume.Clamp(0f, 100f) / 100f;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
{
|
||||
_Streams[_GetStreamIndex(streamID)].Volume = volume;
|
||||
_Streams[_GetStreamIndex(streamID)].CancelFading();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float GetLength(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return -1f;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
return _Streams[_GetStreamIndex(streamID)].Length;
|
||||
}
|
||||
return -1f;
|
||||
}
|
||||
|
||||
public float GetPosition(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return -1f;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
return _Streams[_GetStreamIndex(streamID)].Position;
|
||||
}
|
||||
|
||||
return -1f;
|
||||
}
|
||||
|
||||
public void SetPosition(int streamID, float position)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
_Streams[_GetStreamIndex(streamID)].Position = position;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPlaying(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
return !_Streams[_GetStreamIndex(streamID)].IsPaused && !_Streams[_GetStreamIndex(streamID)].IsFinished;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsPaused(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
return _Streams[_GetStreamIndex(streamID)].IsPaused;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsFinished(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return true;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
return _Streams[_GetStreamIndex(streamID)].IsFinished;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if stream with given id is found
|
||||
/// MUST hold _Stream-lock
|
||||
/// </summary>
|
||||
/// <param name="streamID">Stream id</param>
|
||||
/// <returns></returns>
|
||||
protected bool _StreamExists(int streamID)
|
||||
{
|
||||
return _GetStreamIndex(streamID) != -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the index of the stream with the given id
|
||||
/// MUST hold _Stream-lock
|
||||
/// </summary>
|
||||
/// <param name="streamID">Stream id</param>
|
||||
/// <returns></returns>
|
||||
protected int _GetStreamIndex(int streamID)
|
||||
{
|
||||
for (int i = 0; i < _Streams.Count; i++)
|
||||
{
|
||||
if (_Streams[i].ID == streamID)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endregion Stream Handling
|
||||
|
||||
public void OnCloseStream(IAudioStream stream)
|
||||
{
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_InUpdate) //This is only possible if this is called from inside the update method
|
||||
_StreamsToDelete.Add(stream);
|
||||
else
|
||||
_Streams.Remove(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,21 @@
|
||||
using System;
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback
|
||||
{
|
||||
|
||||
@@ -34,9 +34,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
private string _FileName;
|
||||
private bool _FileOpened;
|
||||
|
||||
public void Init() {}
|
||||
|
||||
public void Open(string fileName)
|
||||
public bool Open(string fileName)
|
||||
{
|
||||
_FileName = fileName;
|
||||
|
||||
@@ -49,14 +47,15 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error opening audio file: " + _FileName);
|
||||
return;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!_Instance.Opened)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
int audioStreamIndex;
|
||||
@@ -71,13 +70,13 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
{
|
||||
CLog.LogError("Error opening audio file (can't find decoder): " + _FileName);
|
||||
Close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (audioStreamIndex < 0)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
_FormatInfo = new SFormatInfo
|
||||
@@ -93,9 +92,10 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
{
|
||||
CLog.LogError("Unsupported BitDepth in file " + fileName);
|
||||
Close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
_FileOpened = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
|
||||
@@ -26,10 +26,9 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
|
||||
interface IAudioDecoder
|
||||
{
|
||||
void Init();
|
||||
void Close();
|
||||
|
||||
void Open(string fileName);
|
||||
bool Open(string fileName);
|
||||
SFormatInfo GetFormatInfo();
|
||||
|
||||
float GetLength();
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using Vocaluxe.Base;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
{
|
||||
class CGstreamerAudio : IPlayback
|
||||
{
|
||||
//static float LastPosition;
|
||||
private readonly CGstreamerAudioWrapper.LogCallback _LogCallback;
|
||||
|
||||
#region log
|
||||
private static void _LogHandler(string text)
|
||||
{
|
||||
CLog.LogError(text);
|
||||
}
|
||||
#endregion log
|
||||
|
||||
public CGstreamerAudio()
|
||||
{
|
||||
_LogCallback = _LogHandler;
|
||||
//Is this really needed? CodaAnalyzer complains about it...
|
||||
//GC.SuppressFinalize(_LogCallback);
|
||||
CGstreamerAudioWrapper.SetLogCallback(_LogCallback);
|
||||
}
|
||||
|
||||
public bool Init()
|
||||
{
|
||||
return CGstreamerAudioWrapper.Init();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
CloseAll();
|
||||
}
|
||||
|
||||
public void SetGlobalVolume(float volume)
|
||||
{
|
||||
CGstreamerAudioWrapper.SetGlobalVolume(volume);
|
||||
}
|
||||
|
||||
public int GetStreamCount()
|
||||
{
|
||||
return CGstreamerAudioWrapper.GetStreamCount();
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
CGstreamerAudioWrapper.CloseAll();
|
||||
}
|
||||
|
||||
public int Load(string media)
|
||||
{
|
||||
var u = new Uri(media);
|
||||
int i = CGstreamerAudioWrapper.Load(u.AbsoluteUri);
|
||||
return i;
|
||||
}
|
||||
|
||||
public int Load(string media, bool prescan)
|
||||
{
|
||||
var u = new Uri(media);
|
||||
int i = CGstreamerAudioWrapper.Load(u.AbsoluteUri, prescan);
|
||||
return i;
|
||||
}
|
||||
|
||||
public void Close(int stream)
|
||||
{
|
||||
CGstreamerAudioWrapper.Close(stream);
|
||||
}
|
||||
|
||||
public void Play(int stream)
|
||||
{
|
||||
CGstreamerAudioWrapper.Play(stream);
|
||||
}
|
||||
|
||||
public void Play(int stream, bool loop)
|
||||
{
|
||||
CGstreamerAudioWrapper.Play(stream, loop);
|
||||
}
|
||||
|
||||
public void Pause(int stream)
|
||||
{
|
||||
CGstreamerAudioWrapper.Pause(stream);
|
||||
}
|
||||
|
||||
public void Stop(int stream)
|
||||
{
|
||||
CGstreamerAudioWrapper.Stop(stream);
|
||||
}
|
||||
|
||||
public void Fade(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
CGstreamerAudioWrapper.Fade(stream, targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void FadeAndPause(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
CGstreamerAudioWrapper.FadeAndPause(stream, targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void FadeAndClose(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
CGstreamerAudioWrapper.FadeAndStop(stream, targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void SetStreamVolume(int stream, float volume)
|
||||
{
|
||||
CGstreamerAudioWrapper.SetStreamVolume(stream, volume);
|
||||
}
|
||||
|
||||
public void SetStreamVolumeMax(int stream, float volume)
|
||||
{
|
||||
CGstreamerAudioWrapper.SetStreamVolumeMax(stream, volume);
|
||||
}
|
||||
|
||||
public float GetLength(int stream)
|
||||
{
|
||||
return CGstreamerAudioWrapper.GetLength(stream);
|
||||
}
|
||||
|
||||
public float GetPosition(int stream)
|
||||
{
|
||||
return CGstreamerAudioWrapper.GetPosition(stream);
|
||||
}
|
||||
|
||||
public bool IsPlaying(int stream)
|
||||
{
|
||||
return CGstreamerAudioWrapper.IsPlaying(stream);
|
||||
}
|
||||
|
||||
public bool IsPaused(int stream)
|
||||
{
|
||||
return CGstreamerAudioWrapper.IsPaused(stream);
|
||||
}
|
||||
|
||||
public bool IsFinished(int stream)
|
||||
{
|
||||
return CGstreamerAudioWrapper.IsFinished(stream);
|
||||
}
|
||||
|
||||
public void SetPosition(int stream, float position)
|
||||
{
|
||||
CGstreamerAudioWrapper.SetPosition(stream, position);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
CGstreamerAudioWrapper.Update();
|
||||
}
|
||||
|
||||
public void FadeAndStop(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
{
|
||||
public static class CGstreamerAudioWrapper
|
||||
{
|
||||
#region arch
|
||||
#if ARCH_X86
|
||||
#if WIN
|
||||
private const string _Dll = "x86\\gstreamer\\GstreamerHelper.dll";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ARCH_X64
|
||||
#if WIN
|
||||
private const string _Dll = "x64\\gstreamer\\GstreamerHelper.dll";
|
||||
#endif
|
||||
#endif
|
||||
#endregion arch
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
public delegate void LogCallback(string message);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void SetLogCallback(LogCallback c);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool Init();
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void SetGlobalVolume(float volume);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int GetStreamCount();
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void CloseAll();
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int Load(string media);
|
||||
|
||||
[DllImport(_Dll, EntryPoint = "LoadPrescan", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int Load(string media,
|
||||
[MarshalAs(UnmanagedType.U1)] bool prescan);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Close(int stream);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Play(int stream);
|
||||
|
||||
[DllImport(_Dll, EntryPoint = "PlayLoop")]
|
||||
public static extern void Play(int stream,
|
||||
[MarshalAs(UnmanagedType.U1)] bool loop);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Pause(int stream);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Stop(int stream);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Fade(int stream, float targetVolume, float seconds);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void FadeAndPause(int stream, float targetVolume, float seconds);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void FadeAndStop(int stream, float targetVolume, float seconds);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void SetStreamVolume(int stream, float volume);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void SetStreamVolumeMax(int stream, float volume);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern float GetLength(int stream);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern float GetPosition(int stream);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool IsPlaying(int stream);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool IsPaused(int stream);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool IsFinished(int stream);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void SetPosition(int stream, float position);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Update();
|
||||
}
|
||||
}
|
||||
@@ -15,19 +15,15 @@
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Gst;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
{
|
||||
public class CGstreamerSharpAudio : IPlayback
|
||||
public class CGstreamerSharpAudio : CPlaybackBase
|
||||
{
|
||||
private readonly Dictionary<int, CGstreamerSharpAudioStream> _Streams = new Dictionary<int, CGstreamerSharpAudioStream>();
|
||||
private static int _IDCount;
|
||||
|
||||
public bool Init()
|
||||
public override bool Init()
|
||||
{
|
||||
if (Application.IsInitialized)
|
||||
if (_Initialized)
|
||||
return false;
|
||||
#if ARCH_X86
|
||||
const string path = ".\\x86\\gstreamer";
|
||||
@@ -40,176 +36,21 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
Registry reg = Registry.Get();
|
||||
reg.ScanPath(path);
|
||||
|
||||
return Application.IsInitialized;
|
||||
_Initialized = Application.IsInitialized;
|
||||
return _Initialized;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
public override void Close()
|
||||
{
|
||||
if (!Application.IsInitialized)
|
||||
if (!_Initialized)
|
||||
return;
|
||||
CloseAll();
|
||||
base.Close();
|
||||
Application.Deinit();
|
||||
}
|
||||
|
||||
public void SetGlobalVolume(float volume)
|
||||
protected override IAudioStream _CreateStream(int id, string media, bool loop)
|
||||
{
|
||||
foreach (CGstreamerSharpAudioStream stream in _Streams.Values)
|
||||
stream.Volume = volume;
|
||||
}
|
||||
|
||||
public int GetStreamCount()
|
||||
{
|
||||
return _Streams.Count;
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
foreach (CGstreamerSharpAudioStream stream in _Streams.Values)
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
public int Load(string media)
|
||||
{
|
||||
return Load(media, false);
|
||||
}
|
||||
|
||||
public int Load(string media, bool prescan)
|
||||
{
|
||||
var stream = new CGstreamerSharpAudioStream(media, prescan);
|
||||
_Streams[_IDCount] = stream;
|
||||
return _IDCount++;
|
||||
}
|
||||
|
||||
public void Close(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Close();
|
||||
}
|
||||
|
||||
public void Play(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Playing = true;
|
||||
}
|
||||
|
||||
public void Play(int stream, bool loop)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
Play(stream, loop);
|
||||
}
|
||||
|
||||
public void Pause(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Paused = true;
|
||||
}
|
||||
|
||||
public void Stop(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Stop();
|
||||
}
|
||||
|
||||
public void Fade(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Fade(targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void FadeAndPause(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].FadeAndPause(targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void FadeAndClose(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].FadeAndClose(targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void FadeAndStop(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].FadeAndStop(targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void SetStreamVolume(int stream, float volume)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Volume = volume;
|
||||
}
|
||||
|
||||
public void SetStreamVolumeMax(int stream, float volume)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].MaxVolume = volume;
|
||||
}
|
||||
|
||||
public float GetLength(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return -1;
|
||||
return _Streams[stream].Length;
|
||||
}
|
||||
|
||||
public float GetPosition(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return -1f;
|
||||
return _Streams[stream].Position;
|
||||
}
|
||||
|
||||
public bool IsPlaying(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return false;
|
||||
return _Streams[stream].Playing;
|
||||
}
|
||||
|
||||
public bool IsPaused(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return false;
|
||||
return _Streams[stream].Paused;
|
||||
}
|
||||
|
||||
public bool IsFinished(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return true;
|
||||
return _Streams[stream].Finished;
|
||||
}
|
||||
|
||||
public void SetPosition(int stream, float position)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Position = position;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var streamsToDelete = new List<int>();
|
||||
foreach (KeyValuePair<int, CGstreamerSharpAudioStream> stream in _Streams)
|
||||
{
|
||||
if (stream.Value.Closed)
|
||||
streamsToDelete.Add(stream.Key);
|
||||
stream.Value.Update();
|
||||
}
|
||||
foreach (int key in streamsToDelete)
|
||||
_Streams.Remove(key);
|
||||
return new CGstreamerSharpAudioStream(id, media, loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,39 +19,115 @@ using System;
|
||||
using GLib;
|
||||
using Gst;
|
||||
using Vocaluxe.Base;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
{
|
||||
public class CGstreamerSharpAudioStream
|
||||
class CGstreamerSharpAudioStream : CAudioStreamBase
|
||||
{
|
||||
private Element _Element;
|
||||
private bool _Loop;
|
||||
public volatile bool Closed = true;
|
||||
public volatile bool Finished;
|
||||
private bool _FileOpened;
|
||||
|
||||
private CFading _Fading;
|
||||
private EStreamAction _AfterFadeAction;
|
||||
private float _Volume = 1f;
|
||||
private volatile bool _IsFinished;
|
||||
|
||||
private float _MaxVolume = 1f;
|
||||
|
||||
private volatile float _Duration = -1f;
|
||||
private volatile float _Position;
|
||||
private volatile bool _QueryingDuration;
|
||||
private System.Threading.Thread _QueryDurationThread;
|
||||
|
||||
public CGstreamerSharpAudioStream(string media, bool prescan)
|
||||
private readonly object _DurationLock = new object(); //For atomic check-and-update of _QueryingDuration
|
||||
private readonly object _ElementLock = new object(); //Hold this if you set _Element=null or when accessing _Element from outside the main thread
|
||||
|
||||
public override float Volume
|
||||
{
|
||||
get { return base.Volume; }
|
||||
set
|
||||
{
|
||||
base.Volume = value;
|
||||
_SetElementVolume();
|
||||
}
|
||||
}
|
||||
|
||||
public override float VolumeMax
|
||||
{
|
||||
get { return base.VolumeMax; }
|
||||
set
|
||||
{
|
||||
base.VolumeMax = value;
|
||||
_SetElementVolume();
|
||||
}
|
||||
}
|
||||
|
||||
public override float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (base.Length < 0 && _QueryDurationThread == null && _Element != null)
|
||||
{
|
||||
// _QueryDurationThread does not get reset so it is created only once!
|
||||
_QueryDurationThread = new System.Threading.Thread(_UpdateDuration) {Name = "GSt Update Duration"};
|
||||
_QueryDurationThread.Start();
|
||||
}
|
||||
return base.Length >= 0f ? base.Length : 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Element != null)
|
||||
{
|
||||
long position;
|
||||
if (!_Element.QueryPosition(Format.Time, out position))
|
||||
CLog.LogError("Could not query position");
|
||||
else
|
||||
_Position = ((float)position / Constants.SECOND);
|
||||
}
|
||||
return _Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SeekSimple(Format.Time, SeekFlags.Accurate | SeekFlags.Flush, (long)(value * Constants.SECOND));
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsFinished
|
||||
{
|
||||
get { return _Element == null || _IsFinished; }
|
||||
}
|
||||
|
||||
public override bool IsPaused
|
||||
{
|
||||
get { return _Element == null || _Element.TargetState == State.Paused; }
|
||||
set
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(value ? State.Paused : State.Playing);
|
||||
}
|
||||
}
|
||||
|
||||
public CGstreamerSharpAudioStream(int id, string medium, bool loop) : base(id, medium, loop) {}
|
||||
|
||||
public override bool Open(bool prescan)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(!_FileOpened);
|
||||
if (_FileOpened)
|
||||
return false;
|
||||
Length = -1;
|
||||
Element convert = ElementFactory.Make("audioconvert", "convert");
|
||||
Element audiosink = ElementFactory.Make("directsoundsink", "audiosink");
|
||||
var audioSinkBin = new Bin("Audiosink");
|
||||
|
||||
if (convert == null || audiosink == null)
|
||||
{
|
||||
CLog.LogError("Could not create pipeline");
|
||||
return;
|
||||
if (convert != null)
|
||||
convert.Dispose();
|
||||
if (audiosink != null)
|
||||
audiosink.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
var audioSinkBin = new Bin("Audiosink");
|
||||
audioSinkBin.Add(convert);
|
||||
audioSinkBin.Add(audiosink);
|
||||
convert.Link(audiosink);
|
||||
@@ -61,33 +137,46 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
if (pad == null)
|
||||
{
|
||||
CLog.LogError("Could not create pads");
|
||||
return;
|
||||
convert.Dispose();
|
||||
audiosink.Dispose();
|
||||
audioSinkBin.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ghostpad.SetActive(true))
|
||||
{
|
||||
CLog.LogError("Could not link pads");
|
||||
return;
|
||||
convert.Dispose();
|
||||
audiosink.Dispose();
|
||||
audioSinkBin.Dispose();
|
||||
ghostpad.Dispose();
|
||||
return false;
|
||||
}
|
||||
if (!audioSinkBin.AddPad(ghostpad))
|
||||
{
|
||||
CLog.LogError("Could not add pad");
|
||||
return;
|
||||
convert.Dispose();
|
||||
audiosink.Dispose();
|
||||
audioSinkBin.Dispose();
|
||||
ghostpad.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
_Element = ElementFactory.Make("playbin", "playbin");
|
||||
if (_Element == null)
|
||||
{
|
||||
CLog.LogError("Could not create playbin");
|
||||
return;
|
||||
convert.Dispose();
|
||||
audiosink.Dispose();
|
||||
audioSinkBin.Dispose();
|
||||
ghostpad.Dispose();
|
||||
return false;
|
||||
}
|
||||
_Element["audio-sink"] = audioSinkBin;
|
||||
_Element["flags"] = 1 << 1;
|
||||
_Element["uri"] = new Uri(media).AbsoluteUri;
|
||||
_Element["uri"] = new Uri(_Medium).AbsoluteUri;
|
||||
_Element.SetState(State.Paused);
|
||||
|
||||
Closed = false; //Enable stream
|
||||
|
||||
// Passing CLOCK_TIME_NONE here causes the pipeline to block for a long time so with
|
||||
// prescan enabled the pipeline will wait 500ms for stream to initialize and then continue
|
||||
// if it takes more than 500ms, duration queries will be performed asynchronously
|
||||
@@ -97,6 +186,58 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
if (msg.Handle != IntPtr.Zero)
|
||||
_UpdateDuration();
|
||||
}
|
||||
_FileOpened = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void _Dispose(bool disposing)
|
||||
{
|
||||
if (_Element == null)
|
||||
return;
|
||||
Element element;
|
||||
lock (_ElementLock)
|
||||
{
|
||||
//Atomic get and set
|
||||
if (_Element == null)
|
||||
return;
|
||||
element = _Element;
|
||||
_Element = null; //Now everything "seems" closed
|
||||
}
|
||||
if (element.TargetState == State.Playing)
|
||||
element.SetState(State.Paused); //Stop output
|
||||
if (_CloseStreamListener != null)
|
||||
_CloseStreamListener.OnCloseStream(this);
|
||||
//Now really close it in the background
|
||||
var t = new System.Threading.Thread(() => _TerminateStream(element)) {Name = "GSt Terminate"};
|
||||
t.Start();
|
||||
}
|
||||
|
||||
private static void _TerminateStream(Element element)
|
||||
{
|
||||
if (element == null)
|
||||
return;
|
||||
//One of these might take a bit, so better do this in the background to avoid lags
|
||||
element.SetState(State.Null);
|
||||
element.Dispose();
|
||||
}
|
||||
|
||||
public override void Play()
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(State.Playing);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(State.Ready);
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
private void _SetElementVolume()
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element["volume"] = Volume * VolumeMax;
|
||||
}
|
||||
|
||||
private void _OnMessage(Message msg)
|
||||
@@ -109,10 +250,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
if (_Loop)
|
||||
Position = 0;
|
||||
else
|
||||
{
|
||||
Finished = true;
|
||||
Close();
|
||||
}
|
||||
_IsFinished = true;
|
||||
break;
|
||||
case MessageType.Error:
|
||||
GException error;
|
||||
@@ -121,194 +259,41 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
CLog.LogError("Gstreamer error: message" + error.Message + ", code" + error.Code + " ,debug information" + debug);
|
||||
break;
|
||||
case MessageType.DurationChanged:
|
||||
if (!_QueryingDuration)
|
||||
_UpdateDuration();
|
||||
_UpdateDuration();
|
||||
break;
|
||||
}
|
||||
msg.Unref();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (!Closed)
|
||||
{
|
||||
Closed = true;
|
||||
Finished = true;
|
||||
var t = new System.Threading.Thread(_TerminateStream) {Name = "GSt Terminate"};
|
||||
t.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void _TerminateStream()
|
||||
{
|
||||
if (_Element != null)
|
||||
{
|
||||
_Element.SetState(State.Null);
|
||||
_Element.Dispose();
|
||||
_Element = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Play(bool loop = false)
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(State.Playing);
|
||||
_Loop = loop;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(State.Ready);
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public void Fade(float targetVolume, float seconds)
|
||||
{
|
||||
targetVolume.Clamp(0f, 100f);
|
||||
_Fading = new CFading(_Volume, targetVolume / 100f, seconds);
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
}
|
||||
|
||||
public void FadeAndPause(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Pause;
|
||||
}
|
||||
|
||||
public void FadeAndClose(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Close;
|
||||
}
|
||||
|
||||
public void FadeAndStop(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Stop;
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get { return _Volume * 100f; }
|
||||
set
|
||||
{
|
||||
value.Clamp(0f, 100f);
|
||||
_Volume = value / 100f;
|
||||
_SetElementVolume();
|
||||
}
|
||||
}
|
||||
|
||||
private void _SetElementVolume()
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element["volume"] = (_Volume * _MaxVolume);
|
||||
}
|
||||
|
||||
public float MaxVolume
|
||||
{
|
||||
get { return _MaxVolume * 100f; }
|
||||
set
|
||||
{
|
||||
value.Clamp(0f, 100f);
|
||||
_MaxVolume = value / 100f;
|
||||
_SetElementVolume();
|
||||
}
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Duration < 0 && !_QueryingDuration)
|
||||
{
|
||||
_QueryingDuration = true; // Set this to avoid race conditions
|
||||
var t = new System.Threading.Thread(_UpdateDuration) {Name = "GSt Update Duration"};
|
||||
t.Start();
|
||||
}
|
||||
return _Duration > 0 ? _Duration : -1;
|
||||
}
|
||||
}
|
||||
|
||||
public float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
long position;
|
||||
if (!_Element.QueryPosition(Format.Time, out position))
|
||||
CLog.LogError("Could not query position");
|
||||
else
|
||||
_Position = (float)(position / (double)Constants.SECOND);
|
||||
return _Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SeekSimple(Format.Time, SeekFlags.Accurate | SeekFlags.Flush, (long)(value * (double)Constants.SECOND));
|
||||
}
|
||||
}
|
||||
|
||||
public bool Paused
|
||||
{
|
||||
get { return _Element == null || _Element.TargetState == State.Paused; }
|
||||
set
|
||||
{
|
||||
if (value && _Element != null)
|
||||
_Element.SetState(State.Paused);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Playing
|
||||
{
|
||||
get { return _Element != null && (_Element.TargetState == State.Playing && !Finished); }
|
||||
set
|
||||
{
|
||||
if (value && _Element != null)
|
||||
_Element.SetState(State.Playing);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
while (_Element != null && _Element.Bus != null && _Element.Bus.HavePending())
|
||||
_OnMessage(_Element.Bus.Pop());
|
||||
|
||||
if (_Fading != null)
|
||||
{
|
||||
bool finished;
|
||||
_Volume = _Fading.GetValue(out finished);
|
||||
_SetElementVolume();
|
||||
if (finished)
|
||||
{
|
||||
switch (_AfterFadeAction)
|
||||
{
|
||||
case EStreamAction.Pause:
|
||||
Paused = true;
|
||||
break;
|
||||
case EStreamAction.Stop:
|
||||
Stop();
|
||||
break;
|
||||
case EStreamAction.Close:
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
_Fading = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _UpdateDuration()
|
||||
{
|
||||
_QueryingDuration = true;
|
||||
long duration = -1;
|
||||
while (duration < 0 && !Closed && !Finished && _Element != null)
|
||||
if (_QueryingDuration)
|
||||
return;
|
||||
lock (_DurationLock)
|
||||
{
|
||||
if (_Element.QueryDuration(Format.Time, out duration))
|
||||
if (_QueryingDuration)
|
||||
return;
|
||||
_QueryingDuration = true;
|
||||
}
|
||||
long duration = -1;
|
||||
while (duration < 0 && !IsFinished)
|
||||
{
|
||||
lock (_ElementLock)
|
||||
{
|
||||
_Duration = duration / (float)Constants.SECOND;
|
||||
_QueryingDuration = false;
|
||||
if (_Element == null)
|
||||
break;
|
||||
if (_Element.QueryDuration(Format.Time, out duration))
|
||||
Length = duration / (float)Constants.SECOND;
|
||||
}
|
||||
}
|
||||
_QueryingDuration = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback
|
||||
{
|
||||
public interface ICloseStreamListener
|
||||
{
|
||||
void OnCloseStream(IAudioStream stream);
|
||||
}
|
||||
|
||||
public interface IAudioStream : IDisposable
|
||||
{
|
||||
int ID { get; }
|
||||
float Volume { get; set; }
|
||||
float VolumeMax { get; set; }
|
||||
float Length { get; }
|
||||
float Position { get; set; }
|
||||
bool IsPaused { get; set; }
|
||||
bool IsFinished { get; }
|
||||
|
||||
void SetOnCloseListener(ICloseStreamListener listener);
|
||||
|
||||
void Update();
|
||||
|
||||
bool Open(bool prescan);
|
||||
void Close();
|
||||
void Play();
|
||||
void Stop();
|
||||
void Fade(float targetVolume, float seconds, EStreamAction afterFadeAction);
|
||||
void CancelFading();
|
||||
}
|
||||
}
|
||||
@@ -25,52 +25,47 @@ namespace Vocaluxe.Lib.Sound.Playback
|
||||
Close
|
||||
}
|
||||
|
||||
struct SAudioStreams
|
||||
{
|
||||
public int Handle;
|
||||
|
||||
public SAudioStreams(int stream)
|
||||
{
|
||||
Handle = stream;
|
||||
}
|
||||
}
|
||||
|
||||
delegate void Closeproc(int stream);
|
||||
|
||||
interface IPlayback
|
||||
{
|
||||
bool Init();
|
||||
void Close();
|
||||
float GetGlobalVolume();
|
||||
|
||||
/// <summary>
|
||||
/// Set the global (maximum) volume<br />
|
||||
/// All streams (existent and future ones) will have their maximum volume set to this value
|
||||
/// </summary>
|
||||
/// <param name="volume">Volume in percent</param>
|
||||
void SetGlobalVolume(float volume);
|
||||
|
||||
int GetStreamCount();
|
||||
void CloseAll();
|
||||
|
||||
#region Stream Handling
|
||||
int Load(string media);
|
||||
int Load(string media, bool prescan);
|
||||
void Close(int stream);
|
||||
#region stream Handling
|
||||
int Load(string medium, bool loop = false, bool prescan = false);
|
||||
void Close(int streamID);
|
||||
|
||||
void Play(int stream);
|
||||
void Play(int stream, bool loop);
|
||||
void Pause(int stream);
|
||||
void Stop(int stream);
|
||||
void Fade(int stream, float targetVolume, float seconds);
|
||||
void FadeAndPause(int stream, float targetVolume, float seconds);
|
||||
void FadeAndClose(int stream, float targetVolume, float seconds);
|
||||
void FadeAndStop(int stream, float targetVolume, float seconds);
|
||||
void SetStreamVolume(int stream, float volume);
|
||||
void SetStreamVolumeMax(int stream, float volume);
|
||||
void Play(int streamID);
|
||||
void Pause(int streamID);
|
||||
void Stop(int streamID);
|
||||
void Fade(int streamID, float targetVolume, float seconds, EStreamAction afterFadeAction = EStreamAction.Nothing);
|
||||
|
||||
float GetLength(int stream);
|
||||
float GetPosition(int stream);
|
||||
/// <summary>
|
||||
/// Set the streams current volume. Cancels fading
|
||||
/// </summary>
|
||||
/// <param name="streamID">Id of the stream (optained by Load)</param>
|
||||
/// <param name="volume">Volume in percent</param>
|
||||
void SetStreamVolume(int streamID, float volume);
|
||||
|
||||
bool IsPlaying(int stream);
|
||||
bool IsPaused(int stream);
|
||||
bool IsFinished(int stream);
|
||||
float GetLength(int streamID);
|
||||
float GetPosition(int streamID);
|
||||
void SetPosition(int streamID, float position);
|
||||
|
||||
void SetPosition(int stream, float position);
|
||||
bool IsPlaying(int streamID);
|
||||
bool IsPaused(int streamID);
|
||||
bool IsFinished(int streamID);
|
||||
|
||||
void Update();
|
||||
#endregion Stream Handling
|
||||
#endregion stream Handling
|
||||
}
|
||||
}
|
||||
@@ -15,359 +15,38 @@
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using OpenTK.Audio;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback.OpenAL
|
||||
{
|
||||
class COpenALPlay : IPlayback, IDisposable
|
||||
class COpenALPlay : CPlaybackBase
|
||||
{
|
||||
private bool _Initialized;
|
||||
private readonly List<COpenAlStream> _Decoder = new List<COpenAlStream>();
|
||||
private Closeproc _Closeproc;
|
||||
private int _Count = 1;
|
||||
private AudioContext _Context;
|
||||
|
||||
private readonly Object _MutexDecoder = new Object();
|
||||
|
||||
private List<SAudioStreams> _Streams;
|
||||
|
||||
~COpenALPlay()
|
||||
{
|
||||
_Dispose(false);
|
||||
}
|
||||
|
||||
public bool Init()
|
||||
public override bool Init()
|
||||
{
|
||||
if (_Initialized)
|
||||
return false;
|
||||
|
||||
_Context = new AudioContext();
|
||||
|
||||
_Context.MakeCurrent();
|
||||
|
||||
|
||||
_Closeproc = _CloseProc;
|
||||
_Initialized = true;
|
||||
|
||||
_Streams = new List<SAudioStreams>();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
for (int i = 0; i < _Decoder.Count; i++)
|
||||
_Decoder[i].Free(_Closeproc, i + 1, _MutexDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetGlobalVolume(float volume)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
//Bass.BASS_SetVolume(Volume / 100f);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetStreamCount()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
return _Streams.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
foreach (COpenAlStream stream in _Decoder)
|
||||
stream.UploadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Stream Handling
|
||||
public int Load(string media)
|
||||
{
|
||||
return Load(media, false);
|
||||
}
|
||||
|
||||
public int Load(string media, bool prescan)
|
||||
{
|
||||
var stream = new SAudioStreams(0);
|
||||
var decoder = new COpenAlStream();
|
||||
|
||||
if (decoder.Open(media) > -1)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
_Decoder.Add(decoder);
|
||||
stream.Handle = _Count++;
|
||||
_Streams.Add(stream);
|
||||
return stream.Handle;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Close(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Free(_Closeproc, stream, _MutexDecoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Play(int stream)
|
||||
{
|
||||
Play(stream, false);
|
||||
}
|
||||
|
||||
public void Play(int stream, bool loop)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
{
|
||||
_Decoder[_GetStreamIndex(stream)].Loop = loop;
|
||||
_Decoder[_GetStreamIndex(stream)].Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Pause(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop(int stream)
|
||||
{
|
||||
Pause(stream);
|
||||
}
|
||||
|
||||
public void Fade(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Fade(targetVolume, seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndPause(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].FadeAndPause(targetVolume, seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndClose(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].FadeAndStop(targetVolume, seconds, _Closeproc, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStreamVolume(int stream, float volume)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Volume = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStreamVolumeMax(int stream, float volume)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].VolumeMax = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float GetLength(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Decoder[_GetStreamIndex(stream)].Length;
|
||||
}
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public float GetPosition(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Decoder[_GetStreamIndex(stream)].Position;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public bool IsPlaying(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return !_Decoder[_GetStreamIndex(stream)].Paused && !_Decoder[_GetStreamIndex(stream)].Finished;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsPaused(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Decoder[_GetStreamIndex(stream)].Paused;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsFinished(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Decoder[_GetStreamIndex(stream)].Finished;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetPosition(int stream, float position)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Skip(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion Stream Handling
|
||||
|
||||
private bool _AlreadyAdded(int stream)
|
||||
{
|
||||
return _Streams.Any(st => st.Handle == stream);
|
||||
}
|
||||
|
||||
private int _GetStreamIndex(int stream)
|
||||
{
|
||||
for (int i = 0; i < _Streams.Count; i++)
|
||||
{
|
||||
if (_Streams[i].Handle == stream)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void _CloseProc(int streamID)
|
||||
public override void Close()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(streamID))
|
||||
{
|
||||
int index = _GetStreamIndex(streamID);
|
||||
_Decoder.RemoveAt(index);
|
||||
_Streams.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
base.Close();
|
||||
_Context.Dispose();
|
||||
_Context = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override IAudioStream _CreateStream(int id, string media, bool loop)
|
||||
{
|
||||
_Dispose(true);
|
||||
}
|
||||
|
||||
private void _Dispose(bool disposing)
|
||||
{
|
||||
CloseAll();
|
||||
if (disposing)
|
||||
{
|
||||
while (_Decoder.Count > 0)
|
||||
Thread.Sleep(5);
|
||||
}
|
||||
if (_Context != null)
|
||||
{
|
||||
_Context.Dispose();
|
||||
_Context = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndStop(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return new COpenAlStream(id, media, loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,57 +22,41 @@ using System.Threading;
|
||||
using OpenTK.Audio;
|
||||
using Vocaluxe.Base;
|
||||
using Vocaluxe.Lib.Sound.Playback.Decoder;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback.OpenAL
|
||||
{
|
||||
class COpenAlStream
|
||||
class COpenAlStream : CAudioStreamBase
|
||||
{
|
||||
private const int _BufferSize = 2048;
|
||||
private const int _BufferCount = 5;
|
||||
private const int _Bufsize = 50000;
|
||||
|
||||
private Object _CloseMutex;
|
||||
private const int _Bufsize = 500000;
|
||||
private const int _BeginRefill = 50000;
|
||||
|
||||
private int[] _Buffers;
|
||||
private int _State;
|
||||
private byte[] _SampleBuf;
|
||||
private int _Source;
|
||||
private SFormatInfo _Format;
|
||||
|
||||
private bool _Initialized;
|
||||
private int _ByteCount = 4;
|
||||
private float _Volume = 1f;
|
||||
private float _VolumeMax = 1f;
|
||||
|
||||
private EStreamAction _AfterFadeAction;
|
||||
private CFading _Fading;
|
||||
|
||||
private readonly Stopwatch _Timer = new Stopwatch();
|
||||
|
||||
private Closeproc _Closeproc;
|
||||
private int _StreamID;
|
||||
private IAudioDecoder _Decoder;
|
||||
private int _ByteCount;
|
||||
private float _BytesPerSecond;
|
||||
private bool _NoMoreData;
|
||||
|
||||
private bool _FileOpened;
|
||||
|
||||
private bool _Waiting;
|
||||
private bool _Skip;
|
||||
|
||||
private bool _Loop;
|
||||
private float _Duration;
|
||||
private float _CurrentTime;
|
||||
private float _TimeCode;
|
||||
|
||||
private bool _Paused;
|
||||
|
||||
private CRingBuffer _Data;
|
||||
private float _SetStart;
|
||||
private volatile float _SetStart;
|
||||
private float _Start;
|
||||
private bool _SetLoop;
|
||||
private bool _SetSkip;
|
||||
private bool _Terminated;
|
||||
private volatile bool _SetSkip;
|
||||
private volatile bool _Terminated;
|
||||
|
||||
private Thread _DecoderThread;
|
||||
|
||||
@@ -81,199 +65,156 @@ namespace Vocaluxe.Lib.Sound.Playback.OpenAL
|
||||
private readonly Object _MutexData = new Object();
|
||||
private readonly Object _MutexSyncSignals = new Object();
|
||||
|
||||
public void Free(Closeproc closeProc, int streamID, Object closeMutex)
|
||||
{
|
||||
_Closeproc = closeProc;
|
||||
_StreamID = streamID;
|
||||
_Terminated = true;
|
||||
_CloseMutex = closeMutex;
|
||||
_EventDecode.Set();
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_FileOpened)
|
||||
return _Duration;
|
||||
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Finished
|
||||
public override bool IsFinished
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_MutexData)
|
||||
{
|
||||
return _NoMoreData && _Data.BytesNotRead == 0L;
|
||||
return _NoMoreData && _Data.BytesNotRead == 0 && Position >= Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get { return _Volume * 100f; }
|
||||
set
|
||||
{
|
||||
lock (_MutexData)
|
||||
{
|
||||
value.Clamp(0f, 100f);
|
||||
_Volume = value / 100f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float VolumeMax
|
||||
{
|
||||
get { return _VolumeMax * 100f; }
|
||||
set
|
||||
{
|
||||
lock (_MutexData)
|
||||
{
|
||||
value.Clamp(0f, 100f);
|
||||
_VolumeMax = value / 100f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Position
|
||||
public override float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Finished)
|
||||
float time = _CurrentTime + _Timer.ElapsedMilliseconds / 1000f;
|
||||
if (time > Length)
|
||||
{
|
||||
_Timer.Stop();
|
||||
//TODO: Why not use Decoder.GetPosition()?
|
||||
return _CurrentTime + _Timer.ElapsedMilliseconds / 1000f;
|
||||
time = Length;
|
||||
}
|
||||
return time;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_SetStart = value;
|
||||
_SetSkip = true;
|
||||
}
|
||||
//Set position here in case we immediately request it, it will be reset in Update method
|
||||
_CurrentTime = value;
|
||||
_Timer.Restart();
|
||||
_EventDecode.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Paused
|
||||
public override bool IsPaused
|
||||
{
|
||||
get { return _Paused; }
|
||||
set
|
||||
{
|
||||
_Paused = value;
|
||||
lock (_MutexSyncSignals)
|
||||
if (_Paused)
|
||||
{
|
||||
if (_Paused)
|
||||
{
|
||||
_Timer.Stop();
|
||||
AL.SourceStop(_Source);
|
||||
}
|
||||
else
|
||||
{
|
||||
_Timer.Start();
|
||||
_EventDecode.Set();
|
||||
AL.SourcePlay(_Source);
|
||||
}
|
||||
_Timer.Stop();
|
||||
AL.SourceStop(_Source);
|
||||
}
|
||||
else
|
||||
{
|
||||
_Timer.Start();
|
||||
_EventDecode.Set();
|
||||
AL.SourcePlay(_Source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Fade(float targetVolume, float fadeTime)
|
||||
public COpenAlStream(int id, string medium, bool loop) : base(id, medium, loop) {}
|
||||
|
||||
protected override void _Dispose(bool disposing)
|
||||
{
|
||||
targetVolume.Clamp(0f, 100f);
|
||||
_Fading = new CFading(_Volume, targetVolume / 100f, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
base._Dispose(disposing);
|
||||
if (!_Terminated)
|
||||
{
|
||||
_Terminated = true;
|
||||
if (_DecoderThread != null)
|
||||
_EventDecode.Set();
|
||||
else
|
||||
_DoFree();
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndPause(float targetVolume, float fadeTime)
|
||||
public override void Play()
|
||||
{
|
||||
Fade(targetVolume, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Pause;
|
||||
IsPaused = false;
|
||||
}
|
||||
|
||||
public void FadeAndStop(float targetVolume, float fadeTime, Closeproc closeProc, int streamID)
|
||||
public override void Stop()
|
||||
{
|
||||
_Closeproc = closeProc;
|
||||
_StreamID = streamID;
|
||||
Fade(targetVolume, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Stop;
|
||||
IsPaused = true;
|
||||
Position = 0f;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
Paused = false;
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
AL.SourcePlay(_Source);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
AL.SourceStop(_Source);
|
||||
Skip(0f);
|
||||
}
|
||||
|
||||
public bool Loop
|
||||
{
|
||||
get { return _SetLoop; }
|
||||
set { _SetLoop = value; }
|
||||
}
|
||||
|
||||
public int Open(string fileName)
|
||||
public override bool Open(bool prescan)
|
||||
{
|
||||
Debug.Assert(!_FileOpened);
|
||||
if (_FileOpened)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
return -1;
|
||||
|
||||
_Decoder = new CAudioDecoderFFmpeg();
|
||||
_Decoder.Init();
|
||||
if (!File.Exists(_Medium))
|
||||
{
|
||||
Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
try
|
||||
{
|
||||
_Source = AL.GenSource();
|
||||
_Buffers = new int[_BufferCount];
|
||||
for (int i = 0; i < _BufferCount; i++)
|
||||
{
|
||||
_Buffers[i] = AL.GenBuffer();
|
||||
|
||||
_State = 0;
|
||||
//AL.SourceQueueBuffers(_source, _buffers.Length, _buffers);
|
||||
ok = ok && _Buffers[i] != 0;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_Initialized = false;
|
||||
ok = false;
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
Dispose();
|
||||
CLog.LogError("Error Init OpenAL Playback");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
_Decoder.Open(fileName);
|
||||
_Duration = _Decoder.GetLength();
|
||||
|
||||
_Decoder = new CAudioDecoderFFmpeg();
|
||||
if (!_Decoder.Open(_Medium))
|
||||
{
|
||||
Dispose();
|
||||
CLog.LogError("Error opening audio file: " + _Medium);
|
||||
return false;
|
||||
}
|
||||
_Format = _Decoder.GetFormatInfo();
|
||||
if (_Format.SamplesPerSecond == 0)
|
||||
{
|
||||
Dispose();
|
||||
CLog.LogError("Error Init OpenAL Playback (samples=0)");
|
||||
return false;
|
||||
}
|
||||
|
||||
Length = _Decoder.GetLength();
|
||||
|
||||
_ByteCount = 2 * _Format.ChannelCount;
|
||||
_BytesPerSecond = _Format.SamplesPerSecond * _ByteCount;
|
||||
|
||||
_CurrentTime = 0f;
|
||||
_TimeCode = 0f;
|
||||
_Timer.Reset();
|
||||
_Data = new CRingBuffer(_Bufsize);
|
||||
_NoMoreData = false;
|
||||
_SampleBuf = new byte[(int)CConfig.AudioBufferSize];
|
||||
//From now on closing the driver and the decoder is handled by the thread ONLY!
|
||||
|
||||
var stream = new SAudioStreams(0) {Handle = _Buffers[0]};
|
||||
|
||||
if (stream.Handle != 0)
|
||||
{
|
||||
_FileOpened = true;
|
||||
_Data = new CRingBuffer(_Bufsize);
|
||||
_NoMoreData = false;
|
||||
_DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(fileName)};
|
||||
_DecoderThread.Start();
|
||||
|
||||
return stream.Handle;
|
||||
}
|
||||
_Initialized = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public bool Skip(float time)
|
||||
{
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_SetStart = time;
|
||||
_SetSkip = true;
|
||||
}
|
||||
_EventDecode.Set();
|
||||
_DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(_Medium)};
|
||||
_DecoderThread.Start();
|
||||
|
||||
_FileOpened = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -281,10 +222,7 @@ namespace Vocaluxe.Lib.Sound.Playback.OpenAL
|
||||
private void _DoSkip()
|
||||
{
|
||||
_Decoder.SetPosition(_Start);
|
||||
_CurrentTime = _Start;
|
||||
_TimeCode = _Start;
|
||||
_Timer.Reset();
|
||||
_Waiting = false;
|
||||
|
||||
lock (_MutexData)
|
||||
{
|
||||
@@ -297,30 +235,25 @@ namespace Vocaluxe.Lib.Sound.Playback.OpenAL
|
||||
{
|
||||
while (!_Terminated)
|
||||
{
|
||||
_Waiting = false;
|
||||
if (_EventDecode.WaitOne(1) || !_Waiting)
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
if (_Skip)
|
||||
if (_SetSkip)
|
||||
{
|
||||
_DoSkip();
|
||||
_Skip = false;
|
||||
}
|
||||
|
||||
_DoDecode();
|
||||
}
|
||||
if (!_Terminated)
|
||||
{
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
if (_SetSkip)
|
||||
_Skip = true;
|
||||
|
||||
_Skip = true;
|
||||
_SetSkip = false;
|
||||
|
||||
_Start = _SetStart;
|
||||
_Loop = _SetLoop;
|
||||
}
|
||||
|
||||
_Start = _SetStart;
|
||||
}
|
||||
|
||||
if (_Skip)
|
||||
{
|
||||
_DoSkip();
|
||||
_Skip = false;
|
||||
}
|
||||
|
||||
_DoDecode();
|
||||
_EventDecode.WaitOne();
|
||||
}
|
||||
|
||||
_DoFree();
|
||||
@@ -328,40 +261,25 @@ namespace Vocaluxe.Lib.Sound.Playback.OpenAL
|
||||
|
||||
private void _DoDecode()
|
||||
{
|
||||
if (!_FileOpened)
|
||||
return;
|
||||
|
||||
if (_Paused)
|
||||
return;
|
||||
|
||||
if (_Terminated)
|
||||
if (_Paused || _Terminated || _NoMoreData)
|
||||
return;
|
||||
|
||||
float timecode;
|
||||
byte[] buffer;
|
||||
|
||||
bool doIt = false;
|
||||
lock (_MutexData)
|
||||
{
|
||||
if (_Bufsize - 10000L > _Data.BytesNotRead)
|
||||
doIt = true;
|
||||
if (_Data.BytesNotRead > _BeginRefill)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!doIt)
|
||||
return;
|
||||
|
||||
_Decoder.Decode(out buffer, out timecode);
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
if (_Loop)
|
||||
{
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_CurrentTime = 0f;
|
||||
_Start = 0f;
|
||||
}
|
||||
|
||||
_Start = 0f;
|
||||
_DoSkip();
|
||||
}
|
||||
else
|
||||
@@ -373,118 +291,101 @@ namespace Vocaluxe.Lib.Sound.Playback.OpenAL
|
||||
{
|
||||
_Data.Write(buffer);
|
||||
_TimeCode = timecode;
|
||||
if (_Data.BytesNotRead < _BeginRefill)
|
||||
_EventDecode.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private void _DoFree()
|
||||
{
|
||||
if (_Initialized)
|
||||
if (_Source != 0)
|
||||
{
|
||||
lock (_CloseMutex)
|
||||
AL.SourceStop(_Source);
|
||||
if (_Buffers != null)
|
||||
{
|
||||
Stop();
|
||||
AL.DeleteSource(_Source);
|
||||
AL.DeleteBuffers(_Buffers);
|
||||
_Buffers = null;
|
||||
}
|
||||
_Decoder.Close();
|
||||
AL.DeleteSource(_Source);
|
||||
_Source = 0;
|
||||
}
|
||||
|
||||
_Closeproc(_StreamID);
|
||||
if (_DecoderThread != null)
|
||||
{
|
||||
if (Thread.CurrentThread.ManagedThreadId != _DecoderThread.ManagedThreadId)
|
||||
throw new Exception("Another thread should never free the decoder thread!");
|
||||
_DecoderThread = null;
|
||||
}
|
||||
if (_Decoder != null)
|
||||
{
|
||||
_Decoder.Close();
|
||||
_Decoder = null;
|
||||
}
|
||||
_SampleBuf = null;
|
||||
if (_EventDecode != null)
|
||||
{
|
||||
_EventDecode.Dispose();
|
||||
_EventDecode.Close();
|
||||
_EventDecode = null;
|
||||
}
|
||||
if (_CloseStreamListener != null)
|
||||
_CloseStreamListener.OnCloseStream(this);
|
||||
}
|
||||
#endregion Threading
|
||||
|
||||
private void _Update()
|
||||
public override void Update()
|
||||
{
|
||||
if (_Fading != null)
|
||||
{
|
||||
bool finished;
|
||||
_Volume = _Fading.GetValue(out finished);
|
||||
if (finished)
|
||||
{
|
||||
switch (_AfterFadeAction)
|
||||
{
|
||||
case EStreamAction.Close:
|
||||
case EStreamAction.Stop:
|
||||
_Terminated = true;
|
||||
break;
|
||||
case EStreamAction.Pause:
|
||||
Paused = true;
|
||||
break;
|
||||
}
|
||||
_Fading = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
base.Update();
|
||||
|
||||
public void UploadData()
|
||||
{
|
||||
_Update();
|
||||
|
||||
if (_Paused)
|
||||
if (_Paused || _Terminated || IsFinished)
|
||||
return;
|
||||
|
||||
int queuedCount;
|
||||
bool doit = true;
|
||||
bool useQueuedBuffer = false;
|
||||
AL.GetSource(_Source, ALGetSourcei.BuffersQueued, out queuedCount);
|
||||
|
||||
int processedCount = _BufferCount;
|
||||
int freeBufferCt = _BufferCount;
|
||||
if (queuedCount > 0)
|
||||
{
|
||||
AL.GetSource(_Source, ALGetSourcei.BuffersProcessed, out processedCount);
|
||||
doit = false;
|
||||
AL.GetSource(_Source, ALGetSourcei.BuffersProcessed, out freeBufferCt);
|
||||
useQueuedBuffer = true;
|
||||
//Console.WriteLine("Buffers Processed on Stream " + _Source + " = " + processedCount);
|
||||
if (processedCount < 1)
|
||||
if (freeBufferCt < 1)
|
||||
return;
|
||||
}
|
||||
|
||||
var buf = new byte[_BufferSize];
|
||||
|
||||
lock (_MutexData)
|
||||
{
|
||||
while (processedCount > 0)
|
||||
queuedCount = 0;
|
||||
for (int j = 0; j < freeBufferCt; j++)
|
||||
{
|
||||
if (_Data.BytesNotRead >= buf.Length)
|
||||
if (_Data.BytesNotRead < _SampleBuf.Length && !(_NoMoreData && _Data.BytesNotRead > 0))
|
||||
break;
|
||||
_Data.Read(_SampleBuf);
|
||||
|
||||
float volume = Volume * VolumeMax;
|
||||
//We want to scale all values. No matter how many channels we have (_ByteCount=2 or 4) we have short values
|
||||
//So just process 2 bytes a time
|
||||
for (int i = 0; i < _SampleBuf.Length; i += 2)
|
||||
{
|
||||
_Data.Read(buf);
|
||||
|
||||
//We want to scale all values. No matter how many channels we have (_ByteCount=2 or 4) we have short values
|
||||
//So just process 2 bytes a time
|
||||
for (int i = 0; i < buf.Length; i += 2)
|
||||
{
|
||||
byte[] b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(buf, i) * _Volume * _VolumeMax));
|
||||
buf[i] = b[0];
|
||||
buf[i + 1] = b[1];
|
||||
}
|
||||
|
||||
int buffer;
|
||||
if (!doit)
|
||||
buffer = AL.SourceUnqueueBuffer(_Source);
|
||||
else
|
||||
{
|
||||
buffer = _Buffers[queuedCount];
|
||||
queuedCount++;
|
||||
}
|
||||
|
||||
if (buffer != 0)
|
||||
{
|
||||
ALFormat alFormat = (_Format.ChannelCount == 2) ? ALFormat.Stereo16 : ALFormat.Mono16;
|
||||
AL.BufferData(buffer, alFormat, buf, buf.Length, _Format.SamplesPerSecond);
|
||||
AL.SourceQueueBuffer(_Source, buffer);
|
||||
}
|
||||
byte[] b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(_SampleBuf, i) * volume));
|
||||
_SampleBuf[i] = b[0];
|
||||
_SampleBuf[i + 1] = b[1];
|
||||
}
|
||||
processedCount--;
|
||||
|
||||
int buffer = useQueuedBuffer ? AL.SourceUnqueueBuffer(_Source) : _Buffers[queuedCount];
|
||||
|
||||
|
||||
if (buffer != 0)
|
||||
{
|
||||
ALFormat alFormat = (_Format.ChannelCount == 2) ? ALFormat.Stereo16 : ALFormat.Mono16;
|
||||
AL.BufferData(buffer, alFormat, _SampleBuf, _SampleBuf.Length, _Format.SamplesPerSecond);
|
||||
AL.SourceQueueBuffer(_Source, buffer);
|
||||
}
|
||||
queuedCount++;
|
||||
}
|
||||
AL.GetSource(_Source, ALGetSourcei.SourceState, out _State);
|
||||
if ((ALSourceState)_State != ALSourceState.Playing)
|
||||
AL.SourcePlay(_Source);
|
||||
}
|
||||
|
||||
_CurrentTime = _TimeCode - _Data.BytesNotRead / _BytesPerSecond - 0.1f;
|
||||
float latency = CConfig.AudioLatency / 1000f + queuedCount * _SampleBuf.Length / _BytesPerSecond + 0.1f;
|
||||
_CurrentTime = _TimeCode - _Data.BytesNotRead / _BytesPerSecond - latency;
|
||||
_Timer.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,11 @@
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
{
|
||||
class CPortAudioPlay : IPlayback
|
||||
class CPortAudioPlay : CPlaybackBase
|
||||
{
|
||||
private volatile bool _Initialized;
|
||||
private readonly List<CPortAudioStream> _Streams = new List<CPortAudioStream>();
|
||||
private int _NextID;
|
||||
|
||||
public bool Init()
|
||||
public override bool Init()
|
||||
{
|
||||
if (_Initialized)
|
||||
return false;
|
||||
@@ -35,311 +29,9 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
protected override IAudioStream _CreateStream(int id, string media, bool loop)
|
||||
{
|
||||
_Initialized = false; //Set this first, so threads don't call closeproc anymore
|
||||
List<CPortAudioStream> streams = new List<CPortAudioStream>();
|
||||
//Get all streams but do not modify list without holding the lock
|
||||
//Calling dispose from within the lock may deadlock in closeproc (if a thread is already there)
|
||||
lock (_Streams)
|
||||
{
|
||||
streams.AddRange(_Streams);
|
||||
_Streams.Clear();
|
||||
}
|
||||
foreach (CPortAudioStream stream in streams)
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
lock (_Streams)
|
||||
{
|
||||
foreach (CPortAudioStream stream in _Streams)
|
||||
stream.Close();
|
||||
_Streams.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetGlobalVolume(float volume)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
//Bass.BASS_SetVolume(Volume / 100f);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetStreamCount()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
lock (_Streams)
|
||||
{
|
||||
return _Streams.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update() {}
|
||||
|
||||
#region Stream Handling
|
||||
public int Load(string media)
|
||||
{
|
||||
return Load(media, false);
|
||||
}
|
||||
|
||||
public int Load(string media, bool prescan)
|
||||
{
|
||||
//Note: We ignore prescan here as the stream does it always
|
||||
|
||||
var stream = new CPortAudioStream(_NextID++, _CloseProc);
|
||||
|
||||
if (stream.Open(media))
|
||||
{
|
||||
lock (_Streams)
|
||||
{
|
||||
_Streams.Add(stream);
|
||||
return stream.ID;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Close(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
int index = _GetStreamIndex(stream);
|
||||
if (index >= 0)
|
||||
{
|
||||
_Streams[index].Close();
|
||||
_Streams.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Play(int stream)
|
||||
{
|
||||
Play(stream, false);
|
||||
}
|
||||
|
||||
public void Play(int stream, bool loop)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
int index = _GetStreamIndex(stream);
|
||||
if (index >= 0)
|
||||
{
|
||||
_Streams[index].Loop = loop;
|
||||
_Streams[index].Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Pause(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].Paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void Fade(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].Fade(targetVolume, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndPause(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].FadeAndPause(targetVolume, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndClose(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].FadeAndClose(targetVolume, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndStop(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].FadeAndStop(targetVolume, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStreamVolume(int stream, float volume)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].Volume = volume;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStreamVolumeMax(int stream, float volume)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].VolumeMax = volume;
|
||||
}
|
||||
}
|
||||
|
||||
public float GetLength(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0f;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Streams[_GetStreamIndex(stream)].Length;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public float GetPosition(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0f;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Streams[_GetStreamIndex(stream)].Position;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public bool IsPlaying(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return !_Streams[_GetStreamIndex(stream)].Paused && !_Streams[_GetStreamIndex(stream)].Finished;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsPaused(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Streams[_GetStreamIndex(stream)].Paused;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsFinished(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return true;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Streams[_GetStreamIndex(stream)].Finished;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetPosition(int stream, float position)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams[_GetStreamIndex(stream)].Skip(position);
|
||||
}
|
||||
}
|
||||
#endregion Stream Handling
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if strem with given id is found
|
||||
/// MUST hold _Stream-lock
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream id</param>
|
||||
/// <returns></returns>
|
||||
private bool _AlreadyAdded(int stream)
|
||||
{
|
||||
return _GetStreamIndex(stream) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the index of the stream with the given id
|
||||
/// MUST hold _Stream-lock
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream id</param>
|
||||
/// <returns></returns>
|
||||
private int _GetStreamIndex(int stream)
|
||||
{
|
||||
for (int i = 0; i < _Streams.Count; i++)
|
||||
{
|
||||
if (_Streams[i].ID == stream)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the stream with the given handle
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream handle</param>
|
||||
private void _CloseProc(int stream)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Streams.RemoveAt(_GetStreamIndex(stream));
|
||||
}
|
||||
return new CPortAudioStream(id, media, loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,48 +16,38 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Vocaluxe.Base;
|
||||
using Vocaluxe.Lib.Sound.Playback.Decoder;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
{
|
||||
class CPortAudioStream : IDisposable
|
||||
class CPortAudioStream : CAudioStreamBase
|
||||
{
|
||||
private const int _Bufsize = 1000000;
|
||||
private const int _Beginrefill = 800000;
|
||||
private const int _BeginRefill = 800000;
|
||||
|
||||
private readonly CSyncTimer _SyncTimer = new CSyncTimer(0f, 1f, 0.02f);
|
||||
private int _ByteCount;
|
||||
|
||||
public readonly int ID;
|
||||
private readonly Closeproc _Closeproc;
|
||||
private CPortAudioHandle _PaHandle;
|
||||
|
||||
private readonly CSyncTimer _SyncTimer;
|
||||
private int _ByteCount = 4;
|
||||
private float _Volume = 1f;
|
||||
private float _VolumeMax = 1f;
|
||||
|
||||
private EStreamAction _AfterFadeAction;
|
||||
private CFading _Fading;
|
||||
|
||||
private static PortAudioSharp.PortAudio.PaHostApiInfo _ApiInfo;
|
||||
private static PortAudioSharp.PortAudio.PaDeviceInfo _OutputDeviceInfo;
|
||||
private PortAudioSharp.PortAudio.PaHostApiInfo _ApiInfo;
|
||||
private PortAudioSharp.PortAudio.PaDeviceInfo _OutputDeviceInfo;
|
||||
private IntPtr _Stream = IntPtr.Zero;
|
||||
|
||||
private PortAudioSharp.PortAudio.PaStreamCallbackDelegate _PaStreamCallback;
|
||||
private IAudioDecoder _Decoder;
|
||||
private float _BytesPerSecond;
|
||||
private float _Latency;
|
||||
private bool _NoMoreData;
|
||||
|
||||
private bool _FileOpened;
|
||||
|
||||
private bool _Skip;
|
||||
|
||||
private bool _Loop;
|
||||
private float _Duration;
|
||||
private float _CurrentTime;
|
||||
private volatile float _TimeCode;
|
||||
|
||||
private volatile bool _Paused;
|
||||
@@ -65,7 +55,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
private CRingBuffer _Data;
|
||||
private volatile float _SetStart;
|
||||
private float _Start;
|
||||
private volatile bool _SetLoop;
|
||||
private volatile bool _SetSkip;
|
||||
private volatile bool _Terminated;
|
||||
|
||||
@@ -76,94 +65,54 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
private readonly Object _LockData = new Object();
|
||||
private readonly Object _LockSyncSignals = new Object();
|
||||
|
||||
public CPortAudioStream(int id, Closeproc closeproc)
|
||||
{
|
||||
ID = id;
|
||||
_Closeproc = closeproc;
|
||||
_SyncTimer = new CSyncTimer(0f, 1f, 0.02f);
|
||||
}
|
||||
|
||||
~CPortAudioStream()
|
||||
{
|
||||
_Dispose(false);
|
||||
if (_DecoderThread != null)
|
||||
_DecoderThread.Join();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_Terminated = true;
|
||||
_EventDecode.Set();
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_FileOpened)
|
||||
return _Duration;
|
||||
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Finished
|
||||
public override float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_LockData)
|
||||
{
|
||||
return _NoMoreData && _Data.BytesNotRead == 0 && _SyncTimer.Time >= _Duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get { return _Volume * 100f; }
|
||||
set
|
||||
{
|
||||
value.Clamp(0f, 100f);
|
||||
_Volume = value / 100f;
|
||||
}
|
||||
}
|
||||
|
||||
public float VolumeMax
|
||||
{
|
||||
get { return _VolumeMax * 100f; }
|
||||
set
|
||||
{
|
||||
value.Clamp(0f, 100f);
|
||||
_VolumeMax = value / 100f;
|
||||
}
|
||||
}
|
||||
|
||||
public float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_LockData)
|
||||
{
|
||||
if (Finished)
|
||||
// Decoder may return wrong timestamps. This is why we use the synctimer
|
||||
// If you change this do exessive testing for monoton timestamps espacially for ogg files
|
||||
float time = Math.Max(0f, _SyncTimer.Time);
|
||||
if (time > Length)
|
||||
{
|
||||
_SyncTimer.Pause();
|
||||
//Decoder may return wrong timestamps. If you change this do exessive testing for monoton timestamps espacially for ogg files
|
||||
return _SyncTimer.Time;
|
||||
time = Length;
|
||||
}
|
||||
return time;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_LockSyncSignals)
|
||||
{
|
||||
_SetStart = value;
|
||||
_SetSkip = true;
|
||||
_EventDecode.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Paused
|
||||
public override bool IsPaused
|
||||
{
|
||||
get { return _Paused; }
|
||||
set
|
||||
{
|
||||
if (_Paused == value)
|
||||
return;
|
||||
if (!_FileOpened || _Terminated)
|
||||
return;
|
||||
_Paused = value;
|
||||
lock (_LockData)
|
||||
{
|
||||
if (_Paused)
|
||||
{
|
||||
_PaHandle.CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream));
|
||||
_SyncTimer.Pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
_PaHandle.CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream));
|
||||
_SyncTimer.Resume();
|
||||
_EventDecode.Set();
|
||||
}
|
||||
@@ -171,57 +120,30 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
}
|
||||
}
|
||||
|
||||
public void Fade(float targetVolume, float fadeTime)
|
||||
public override bool IsFinished
|
||||
{
|
||||
targetVolume = targetVolume.Clamp(0f, 100f);
|
||||
_Fading = new CFading(_Volume, targetVolume / 100f, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
get
|
||||
{
|
||||
lock (_LockData)
|
||||
{
|
||||
return _NoMoreData && _Data.BytesNotRead == 0 && _SyncTimer.Time >= Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndPause(float targetVolume, float fadeTime)
|
||||
{
|
||||
Fade(targetVolume, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Pause;
|
||||
}
|
||||
public CPortAudioStream(int id, string medium, bool loop) : base(id, medium, loop) {}
|
||||
|
||||
public void FadeAndClose(float targetVolume, float fadeTime)
|
||||
{
|
||||
Fade(targetVolume, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Close;
|
||||
}
|
||||
|
||||
public void FadeAndStop(float targetVolume, float fadeTime)
|
||||
{
|
||||
Fade(targetVolume, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Stop;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
Paused = false;
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
_PaHandle.CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_PaHandle.CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream));
|
||||
Skip(0f);
|
||||
}
|
||||
|
||||
public bool Loop
|
||||
{
|
||||
get { return _SetLoop; }
|
||||
set { _SetLoop = value; }
|
||||
}
|
||||
|
||||
public bool Open(string fileName)
|
||||
public override bool Open(bool prescan)
|
||||
{
|
||||
Debug.Assert(!_FileOpened);
|
||||
if (_FileOpened)
|
||||
return false;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
if (!File.Exists(_Medium))
|
||||
{
|
||||
Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -237,36 +159,32 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (_PaHandle != null)
|
||||
{
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
}
|
||||
Dispose();
|
||||
CLog.LogError("Error Init PortAudio Playback");
|
||||
return false;
|
||||
}
|
||||
|
||||
_Decoder = new CAudioDecoderFFmpeg();
|
||||
_Decoder.Init();
|
||||
_Decoder.Open(fileName);
|
||||
if (!_Decoder.Open(_Medium))
|
||||
{
|
||||
Dispose();
|
||||
CLog.LogError("Error opening audio file: " + _Medium);
|
||||
return false;
|
||||
}
|
||||
|
||||
SFormatInfo format = _Decoder.GetFormatInfo();
|
||||
if (format.SamplesPerSecond == 0)
|
||||
{
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
_Decoder.Close();
|
||||
_Decoder = null;
|
||||
Dispose();
|
||||
CLog.LogError("Error Init PortAudio Playback (samples=0)");
|
||||
return false;
|
||||
}
|
||||
|
||||
_Duration = _Decoder.GetLength();
|
||||
Length = _Decoder.GetLength();
|
||||
_ByteCount = 2 * format.ChannelCount;
|
||||
_BytesPerSecond = format.SamplesPerSecond * _ByteCount;
|
||||
_CurrentTime = 0f;
|
||||
_SyncTimer.Pause();
|
||||
_SyncTimer.Time = _CurrentTime;
|
||||
_SyncTimer.Time = 0f;
|
||||
|
||||
PortAudioSharp.PortAudio.PaStreamParameters? outputParams = new PortAudioSharp.PortAudio.PaStreamParameters
|
||||
{
|
||||
@@ -281,51 +199,69 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
out _Stream,
|
||||
ref outputParams,
|
||||
format.SamplesPerSecond,
|
||||
(uint)CConfig.AudioBufferSize,
|
||||
(uint)CConfig.AudioBufferSize / 2,
|
||||
PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag,
|
||||
_PaStreamCallback,
|
||||
IntPtr.Zero) || _Stream == IntPtr.Zero)
|
||||
{
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
_Decoder.Close();
|
||||
_Decoder = null;
|
||||
Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
_Latency = CConfig.AudioLatency / 1000f + (float)PortAudioSharp.PortAudio.Pa_GetStreamInfo(_Stream).outputLatency;
|
||||
|
||||
//From now on closing the driver and the decoder is handled by the thread ONLY!
|
||||
|
||||
_Paused = true;
|
||||
_FileOpened = true;
|
||||
_Data = new CRingBuffer(_Bufsize);
|
||||
_NoMoreData = false;
|
||||
_DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(fileName)};
|
||||
_DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(_Medium)};
|
||||
_DecoderThread.Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Skip(float time)
|
||||
protected override void _Dispose(bool disposing)
|
||||
{
|
||||
lock (_LockSyncSignals)
|
||||
base._Dispose(disposing);
|
||||
if (!_Terminated)
|
||||
{
|
||||
_SetStart = time;
|
||||
_SetSkip = true;
|
||||
_EventDecode.Set();
|
||||
_Terminated = true;
|
||||
if (_DecoderThread != null)
|
||||
_EventDecode.Set();
|
||||
else
|
||||
_DoFree();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Play()
|
||||
{
|
||||
if (!_FileOpened || _Terminated || !IsPaused)
|
||||
return;
|
||||
IsPaused = false;
|
||||
_PaHandle.CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream));
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (!_FileOpened || _Terminated)
|
||||
return;
|
||||
_Paused = true;
|
||||
_PaHandle.CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream));
|
||||
Position = 0f;
|
||||
}
|
||||
|
||||
#region Threading
|
||||
private void _DoSkip()
|
||||
{
|
||||
_Decoder.SetPosition(_Start);
|
||||
lock (_LockData)
|
||||
{
|
||||
_Decoder.SetPosition(_Start);
|
||||
_CurrentTime = _Start;
|
||||
_SyncTimer.Time = _Start;
|
||||
_TimeCode = _Start;
|
||||
_Data.Reset();
|
||||
_NoMoreData = false;
|
||||
_SyncTimer.Time = _Start;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +278,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
}
|
||||
|
||||
_Start = _SetStart;
|
||||
_Loop = _SetLoop;
|
||||
}
|
||||
|
||||
if (_Skip)
|
||||
@@ -352,7 +287,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
}
|
||||
|
||||
_DoDecode();
|
||||
_Update();
|
||||
_EventDecode.WaitOne();
|
||||
}
|
||||
|
||||
@@ -361,37 +295,25 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
|
||||
private void _DoDecode()
|
||||
{
|
||||
if (_Paused)
|
||||
return;
|
||||
|
||||
if (_Terminated)
|
||||
if (_Paused || _Terminated || _NoMoreData)
|
||||
return;
|
||||
|
||||
float timecode;
|
||||
byte[] buffer;
|
||||
|
||||
bool doIt = false;
|
||||
lock (_LockData)
|
||||
{
|
||||
if (!_Skip && _Beginrefill > _Data.BytesNotRead)
|
||||
doIt = true;
|
||||
if (_Data.BytesNotRead > _BeginRefill)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!doIt)
|
||||
return;
|
||||
|
||||
_Decoder.Decode(out buffer, out timecode);
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
if (_Loop)
|
||||
{
|
||||
lock (_LockSyncSignals)
|
||||
{
|
||||
_CurrentTime = 0f;
|
||||
_Start = 0f;
|
||||
}
|
||||
|
||||
_Start = 0f;
|
||||
_DoSkip();
|
||||
}
|
||||
else
|
||||
@@ -403,20 +325,38 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
{
|
||||
_Data.Write(buffer);
|
||||
_TimeCode = timecode;
|
||||
if (_Data.BytesNotRead < _Beginrefill)
|
||||
if (_Data.BytesNotRead < _BeginRefill)
|
||||
_EventDecode.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private void _DoFree()
|
||||
{
|
||||
_PaHandle.CloseStream(_Stream);
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
_Decoder.Close();
|
||||
_Decoder = null;
|
||||
|
||||
_Closeproc(ID);
|
||||
if (_PaHandle != null)
|
||||
{
|
||||
if (_Stream != IntPtr.Zero)
|
||||
_PaHandle.CloseStream(_Stream);
|
||||
_PaHandle.Close();
|
||||
_PaHandle = null;
|
||||
}
|
||||
if (_DecoderThread != null)
|
||||
{
|
||||
if (Thread.CurrentThread.ManagedThreadId != _DecoderThread.ManagedThreadId)
|
||||
throw new Exception("Another thread should never free the decoder thread!");
|
||||
_DecoderThread = null;
|
||||
}
|
||||
if (_Decoder != null)
|
||||
{
|
||||
_Decoder.Close();
|
||||
_Decoder = null;
|
||||
}
|
||||
if (_EventDecode != null)
|
||||
{
|
||||
_EventDecode.Close();
|
||||
_EventDecode = null;
|
||||
}
|
||||
if (_CloseStreamListener != null)
|
||||
_CloseStreamListener.OnCloseStream(this);
|
||||
}
|
||||
#endregion Threading
|
||||
|
||||
@@ -446,27 +386,27 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
|
||||
lock (_LockData)
|
||||
{
|
||||
if (_NoMoreData || _Data.BytesNotRead >= buf.Length)
|
||||
int dataLen = _Data.BytesNotRead;
|
||||
if ((_NoMoreData && dataLen > 0) || dataLen >= buf.Length)
|
||||
{
|
||||
dataLen = Math.Min(dataLen, buf.Length);
|
||||
_Data.Read(buf);
|
||||
//We want to scale all values. No matter how many channels we have (_ByteCount=2 or 4) we have short values
|
||||
//So just process 2 bytes a time
|
||||
for (int i = 0; i < buf.Length; i += 2)
|
||||
float volume = Volume * VolumeMax;
|
||||
for (int i = 0; i < dataLen; i += 2)
|
||||
{
|
||||
byte[] b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(buf, i) * _Volume * _VolumeMax));
|
||||
byte[] b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(buf, i) * volume));
|
||||
buf[i] = b[0];
|
||||
buf[i + 1] = b[1];
|
||||
}
|
||||
float latency = buf.Length / _BytesPerSecond + _Latency;
|
||||
float time = _TimeCode - _Data.BytesNotRead / _BytesPerSecond - latency;
|
||||
_SyncTimer.Update(time);
|
||||
}
|
||||
|
||||
if (_Data.BytesNotRead < _Beginrefill && !_NoMoreData)
|
||||
if (_Data.BytesNotRead < _BeginRefill && !_NoMoreData)
|
||||
_EventDecode.Set();
|
||||
|
||||
float latency = buf.Length / _BytesPerSecond + CConfig.AudioLatency / 1000f;
|
||||
float time = _TimeCode - _Data.BytesNotRead / _BytesPerSecond - latency;
|
||||
|
||||
if (!_NoMoreData)
|
||||
_CurrentTime = _SyncTimer.Update(time);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -481,54 +421,5 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
return PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue;
|
||||
}
|
||||
#endregion Callbacks
|
||||
|
||||
private void _Update()
|
||||
{
|
||||
if (_Fading != null)
|
||||
{
|
||||
bool finished;
|
||||
_Volume = _Fading.GetValue(out finished);
|
||||
if (finished)
|
||||
{
|
||||
switch (_AfterFadeAction)
|
||||
{
|
||||
case EStreamAction.Close:
|
||||
_Terminated = true;
|
||||
break;
|
||||
case EStreamAction.Stop:
|
||||
Stop();
|
||||
break;
|
||||
case EStreamAction.Pause:
|
||||
Paused = true;
|
||||
break;
|
||||
}
|
||||
_Fading = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void _Dispose(bool disposing)
|
||||
{
|
||||
Close();
|
||||
if (disposing)
|
||||
{
|
||||
if (_DecoderThread != null)
|
||||
{
|
||||
_DecoderThread.Join();
|
||||
_DecoderThread = null;
|
||||
}
|
||||
if (_EventDecode != null)
|
||||
{
|
||||
_EventDecode.Close();
|
||||
_EventDecode = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,8 +65,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
float diff = et - dt;
|
||||
if (Math.Abs(diff) > 0.05f)
|
||||
{
|
||||
_Timer.Reset();
|
||||
_Timer.Start();
|
||||
_Timer.Restart();
|
||||
_SetValue = dt + diff;
|
||||
dt = _SetValue;
|
||||
//Console.WriteLine("DRIFTED!!! " + diff.ToString());
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
|
||||
~CBuffer()
|
||||
{
|
||||
_Dispose();
|
||||
_Dispose(false);
|
||||
}
|
||||
|
||||
public int GetNumHalfTones()
|
||||
@@ -110,13 +110,13 @@ namespace Vocaluxe.Lib.Sound.Record
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_Dispose();
|
||||
_Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void _Dispose()
|
||||
private void _Dispose(bool disposing)
|
||||
{
|
||||
if (_PitchTracker != null)
|
||||
if (disposing && _PitchTracker != null)
|
||||
{
|
||||
_PitchTracker.Dispose();
|
||||
_PitchTracker = null;
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using SlimDX.DirectSound;
|
||||
using Vocaluxe.Base;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Record.DirectSound
|
||||
{
|
||||
|
||||
+26
-24
@@ -107,26 +107,17 @@ namespace Vocaluxe
|
||||
// Create folders
|
||||
CSettings.CreateFolders();
|
||||
|
||||
Application.DoEvents();
|
||||
_SplashScreen = new CSplashScreen();
|
||||
Application.DoEvents();
|
||||
|
||||
// Init Draw
|
||||
CLog.StartBenchmark("Init Draw");
|
||||
if (!CDraw.InitDraw())
|
||||
if (!CDraw.Init())
|
||||
throw new CLoadingException("drawing");
|
||||
CLog.StopBenchmark("Init Draw");
|
||||
|
||||
Application.DoEvents();
|
||||
|
||||
// Init Database
|
||||
CLog.StartBenchmark("Init Database");
|
||||
if (!CDataBase.Init())
|
||||
throw new CLoadingException("database");
|
||||
CLog.StopBenchmark("Init Database");
|
||||
|
||||
Application.DoEvents();
|
||||
|
||||
// Init Playback
|
||||
CLog.StartBenchmark("Init Playback");
|
||||
if (!CSound.Init())
|
||||
@@ -143,6 +134,22 @@ namespace Vocaluxe
|
||||
|
||||
Application.DoEvents();
|
||||
|
||||
// Init VideoDecoder
|
||||
CLog.StartBenchmark("Init Videodecoder");
|
||||
if (!CVideo.Init())
|
||||
throw new CLoadingException("video");
|
||||
CLog.StopBenchmark("Init Videodecoder");
|
||||
|
||||
Application.DoEvents();
|
||||
|
||||
// Init Database
|
||||
CLog.StartBenchmark("Init Database");
|
||||
if (!CDataBase.Init())
|
||||
throw new CLoadingException("database");
|
||||
CLog.StopBenchmark("Init Database");
|
||||
|
||||
Application.DoEvents();
|
||||
|
||||
//Init Webcam
|
||||
CLog.StartBenchmark("Init Webcam");
|
||||
if (!CWebcam.Init())
|
||||
@@ -172,14 +179,6 @@ namespace Vocaluxe
|
||||
|
||||
Application.DoEvents();
|
||||
|
||||
// Init VideoDecoder
|
||||
CLog.StartBenchmark("Init Videodecoder");
|
||||
if (!CVideo.Init())
|
||||
throw new CLoadingException("video");
|
||||
CLog.StopBenchmark("Init Videodecoder");
|
||||
|
||||
Application.DoEvents();
|
||||
|
||||
// Load Cover
|
||||
CLog.StartBenchmark("Init Cover");
|
||||
CCover.Init();
|
||||
@@ -257,19 +256,22 @@ namespace Vocaluxe
|
||||
|
||||
private static void _CloseProgram()
|
||||
{
|
||||
// Unloading
|
||||
// Unloading in reverse order
|
||||
try
|
||||
{
|
||||
CVocaluxeServer.Close();
|
||||
CController.Close();
|
||||
CVocaluxeServer.Close();
|
||||
CGraphics.Close();
|
||||
CCover.Close();
|
||||
CBackgroundMusic.Close();
|
||||
CWebcam.Close();
|
||||
CDataBase.Close();
|
||||
CVideo.Close();
|
||||
CRecord.Close();
|
||||
CSound.Close();
|
||||
CVideo.Close();
|
||||
CDraw.Unload();
|
||||
CDataBase.Close();
|
||||
CWebcam.Close();
|
||||
GC.Collect(); // Do a GC run here before we close logs to have finalizers run
|
||||
CLog.CloseAll();
|
||||
CLog.CloseAll(); // Do this last, so we get all log entries!
|
||||
}
|
||||
catch (Exception) {}
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
|
||||
@@ -107,49 +107,49 @@ namespace Vocaluxe.Screens
|
||||
|
||||
private void _SaveConfig()
|
||||
{
|
||||
int volume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
switch (CGraphics.CurrentScreen)
|
||||
{
|
||||
case EScreens.ScreenSong:
|
||||
if (CSongs.IsInCategory)
|
||||
CConfig.PreviewMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
CConfig.PreviewMusicVolume = volume;
|
||||
else
|
||||
{
|
||||
CConfig.BackgroundMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
}
|
||||
CConfig.BackgroundMusicVolume = volume;
|
||||
break;
|
||||
|
||||
case EScreens.ScreenSing:
|
||||
CConfig.GameMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
CConfig.GameMusicVolume = volume;
|
||||
break;
|
||||
|
||||
default:
|
||||
CConfig.BackgroundMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
CConfig.BackgroundMusicVolume = volume;
|
||||
break;
|
||||
}
|
||||
CConfig.SaveConfig();
|
||||
CSound.SetGlobalVolume(volume);
|
||||
}
|
||||
|
||||
private void _UpdateSlides()
|
||||
{
|
||||
int volume;
|
||||
switch (CGraphics.CurrentScreen)
|
||||
{
|
||||
case EScreens.ScreenSong:
|
||||
if (CSongs.IsInCategory)
|
||||
_SelectSlides[_SelectSlideVolume].Selection = CConfig.PreviewMusicVolume / 5;
|
||||
volume = CConfig.PreviewMusicVolume;
|
||||
else
|
||||
_SelectSlides[_SelectSlideVolume].Selection = CConfig.BackgroundMusicVolume / 5;
|
||||
volume = CConfig.BackgroundMusicVolume;
|
||||
break;
|
||||
|
||||
case EScreens.ScreenSing:
|
||||
_SelectSlides[_SelectSlideVolume].Selection = CConfig.GameMusicVolume / 5;
|
||||
volume = CConfig.GameMusicVolume;
|
||||
break;
|
||||
|
||||
default:
|
||||
_SelectSlides[_SelectSlideVolume].Selection = CConfig.BackgroundMusicVolume / 5;
|
||||
volume = CConfig.BackgroundMusicVolume;
|
||||
break;
|
||||
}
|
||||
_SelectSlides[_SelectSlideVolume].Selection = volume / 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ namespace Vocaluxe.Screens
|
||||
CConfig.SaveConfig();
|
||||
|
||||
CBackgroundMusic.SetMusicSource(CConfig.BackgroundMusicSource);
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
if (CConfig.BackgroundMusic == EOffOn.TR_CONFIG_ON)
|
||||
CBackgroundMusic.Play();
|
||||
else
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Vocaluxe.Screens
|
||||
CConfig.SaveConfig();
|
||||
|
||||
if (_OldCoverTheme != _SelectSlides[_SelectSlideCover].Selection)
|
||||
CCover.ReloadCover();
|
||||
CCover.ReloadCovers();
|
||||
|
||||
if (_OldTheme != _SelectSlides[_SelectSlideTheme].Selection)
|
||||
{
|
||||
|
||||
@@ -258,11 +258,8 @@ namespace Vocaluxe.Screens
|
||||
case Keys.PageUp:
|
||||
if (keyEvent.ModShift)
|
||||
{
|
||||
CConfig.GameMusicVolume = CConfig.GameMusicVolume + 5;
|
||||
if (CConfig.GameMusicVolume > 100)
|
||||
CConfig.GameMusicVolume = 100;
|
||||
CConfig.SaveConfig();
|
||||
ApplyVolume();
|
||||
CConfig.GameMusicVolume += 5;
|
||||
CSound.SetGlobalVolume(CConfig.GameMusicVolume);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -270,11 +267,8 @@ namespace Vocaluxe.Screens
|
||||
case Keys.PageDown:
|
||||
if (keyEvent.ModShift)
|
||||
{
|
||||
CConfig.GameMusicVolume = CConfig.GameMusicVolume - 5;
|
||||
if (CConfig.GameMusicVolume < 0)
|
||||
CConfig.GameMusicVolume = 0;
|
||||
CConfig.SaveConfig();
|
||||
ApplyVolume();
|
||||
CConfig.GameMusicVolume -= 5;
|
||||
CSound.SetGlobalVolume(CConfig.GameMusicVolume);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -441,6 +435,7 @@ namespace Vocaluxe.Screens
|
||||
|
||||
CBackgroundMusic.Disabled = true;
|
||||
_CloseSong();
|
||||
CSound.SetGlobalVolume(CConfig.GameMusicVolume);
|
||||
}
|
||||
|
||||
public override void OnShowFinish()
|
||||
@@ -449,7 +444,6 @@ namespace Vocaluxe.Screens
|
||||
|
||||
CGame.Start();
|
||||
_LoadNextSong();
|
||||
CBackgroundMusic.Disabled = true;
|
||||
}
|
||||
|
||||
public override bool Draw()
|
||||
@@ -540,22 +534,20 @@ namespace Vocaluxe.Screens
|
||||
public override void OnClose()
|
||||
{
|
||||
base.OnClose();
|
||||
CBackgroundMusic.Disabled = false;
|
||||
_CloseSong();
|
||||
if (_Webcam)
|
||||
CWebcam.Stop();
|
||||
_CloseSong();
|
||||
}
|
||||
|
||||
public override void ApplyVolume()
|
||||
{
|
||||
if (_CurrentStream > -1)
|
||||
CSound.SetStreamVolumeMax(_CurrentStream, CConfig.GameMusicVolume);
|
||||
CBackgroundMusic.Disabled = false;
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
}
|
||||
|
||||
private void _CloseSong()
|
||||
{
|
||||
if (_CurrentStream > -1)
|
||||
{
|
||||
CSound.FadeAndClose(_CurrentStream, 0f, 0.5f);
|
||||
_CurrentStream = -1;
|
||||
}
|
||||
CRecord.Stop();
|
||||
if (_CurrentVideo != -1)
|
||||
{
|
||||
@@ -605,8 +597,7 @@ namespace Vocaluxe.Screens
|
||||
songname += " (" + CGame.RoundNr + "/" + rounds + ")";
|
||||
_Texts[_TextSongName].Text = songname;
|
||||
|
||||
_CurrentStream = CSound.Load(song.GetMP3(), true);
|
||||
CSound.SetStreamVolumeMax(_CurrentStream, CConfig.GameMusicVolume);
|
||||
_CurrentStream = CSound.Load(song.GetMP3(), false, true);
|
||||
CSound.SetStreamVolume(_CurrentStream, _Volume);
|
||||
CSound.SetPosition(_CurrentStream, song.Start);
|
||||
_CurrentTime = song.Start;
|
||||
|
||||
@@ -148,8 +148,6 @@ namespace Vocaluxe.Screens
|
||||
_Playlists[_Playlist].Init();
|
||||
|
||||
_AvailableGameModes.Clear();
|
||||
|
||||
ApplyVolume();
|
||||
}
|
||||
|
||||
public override bool HandleInput(SKeyEvent keyEvent)
|
||||
@@ -365,21 +363,23 @@ namespace Vocaluxe.Screens
|
||||
}
|
||||
}
|
||||
|
||||
int volumeDiff = 0;
|
||||
if (keyEvent.ModShift && (keyEvent.Key == Keys.Add || keyEvent.Key == Keys.PageUp))
|
||||
{
|
||||
CConfig.PreviewMusicVolume = CConfig.PreviewMusicVolume + 5;
|
||||
if (CConfig.PreviewMusicVolume > 100)
|
||||
CConfig.PreviewMusicVolume = 100;
|
||||
CConfig.SaveConfig();
|
||||
ApplyVolume();
|
||||
}
|
||||
volumeDiff = 5;
|
||||
else if (keyEvent.ModShift && (keyEvent.Key == Keys.Subtract || keyEvent.Key == Keys.PageDown))
|
||||
volumeDiff = -5;
|
||||
if (volumeDiff != 0)
|
||||
{
|
||||
CConfig.PreviewMusicVolume = CConfig.PreviewMusicVolume - 5;
|
||||
if (CConfig.PreviewMusicVolume < 0)
|
||||
CConfig.PreviewMusicVolume = 0;
|
||||
CConfig.SaveConfig();
|
||||
ApplyVolume();
|
||||
if (CSongs.IsInCategory)
|
||||
{
|
||||
CConfig.PreviewMusicVolume += volumeDiff;
|
||||
CSound.SetGlobalVolume(CConfig.PreviewMusicVolume);
|
||||
}
|
||||
else
|
||||
{
|
||||
CConfig.BackgroundMusicVolume += volumeDiff;
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -643,6 +643,8 @@ namespace Vocaluxe.Screens
|
||||
_Playlists[_Playlist].DragAndDropSongID = -1;
|
||||
|
||||
UpdateGame();
|
||||
if (CSongs.IsInCategory)
|
||||
CSound.SetGlobalVolume(CConfig.PreviewMusicVolume);
|
||||
}
|
||||
|
||||
public override bool UpdateGame()
|
||||
@@ -700,14 +702,10 @@ namespace Vocaluxe.Screens
|
||||
{
|
||||
base.OnClose();
|
||||
CBackgroundMusic.Disabled = false;
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
_SongMenus[_SongMenu].OnHide();
|
||||
}
|
||||
|
||||
public override void ApplyVolume()
|
||||
{
|
||||
_SongMenus[_SongMenu].ApplyVolume(CConfig.PreviewMusicVolume);
|
||||
}
|
||||
|
||||
private void _HandlePartySongSelection(int songNr)
|
||||
{
|
||||
if ((CSongs.Category >= 0) && (songNr >= 0))
|
||||
|
||||
@@ -219,8 +219,15 @@
|
||||
<Compile Include="Base\CPlaylistElement.cs" />
|
||||
<Compile Include="Base\CRecord.cs" />
|
||||
<Compile Include="COSFunctions.cs" />
|
||||
<Compile Include="Lib\Database\CCoverDB.cs" />
|
||||
<Compile Include="Lib\Database\CDatabaseBase.cs" />
|
||||
<Compile Include="Lib\Database\CHighscoreDB.cs" />
|
||||
<Compile Include="Lib\Database\CResourceDB.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\CAudioStreamBase.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\CPlaybackBase.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\CRingBuffer.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\GstreamerSharp\CGstreamerSharpAudioStream.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\IAudioStream.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\OpenAL\COpenAlStream.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\PortAudio\CPortAudioStream.cs" />
|
||||
<Compile Include="Lib\Sound\CPortAudioHandle.cs" />
|
||||
@@ -305,14 +312,12 @@
|
||||
<Compile Include="Lib\Input\WiiMote\WiiMoteLib.cs" />
|
||||
<Compile Include="Lib\Playlist\CPlaylistFile.cs" />
|
||||
<Compile Include="Lib\Sound\Record\DirectSound\CDirectSoundRecord.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\Gstreamer\CGstreamerAudio.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\OpenAL\COpenALPlay.cs" />
|
||||
<Compile Include="Base\CSound.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\PortAudio\CPortAudioPlay.cs" />
|
||||
<Compile Include="Lib\Sound\Record\PortAudio\CPortAudioRecord.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\Decoder\CAudioDecoderFFmpeg.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\Decoder\IAudioDecoder.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\Gstreamer\CGstreamerAudioWrapper.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\IPlayback.cs" />
|
||||
<Compile Include="Lib\Sound\Record\IRecord.cs" />
|
||||
<Compile Include="Lib\Video\Acinerella\CAcinerella.cs" />
|
||||
|
||||
@@ -144,7 +144,6 @@ namespace VocaluxeLib
|
||||
{
|
||||
PortAudio,
|
||||
OpenAL,
|
||||
Gstreamer,
|
||||
GstreamerSharp
|
||||
}
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ namespace VocaluxeLib
|
||||
|
||||
public interface ISound
|
||||
{
|
||||
int Load(string soundFile, bool prescan);
|
||||
int Load(string soundFile, bool loop = false, bool prescan = false);
|
||||
void SetPosition(int soundStream, float newPosition);
|
||||
void Play(int soundStream);
|
||||
void Fade(int soundStream, float targetVolume, float duration);
|
||||
@@ -264,10 +264,9 @@ namespace VocaluxeLib
|
||||
bool IsFinished(int soundStream);
|
||||
float GetPosition(int soundStream);
|
||||
float GetLength(int soundStream);
|
||||
void FadeAndStop(int soundStream, float targetVolume, float duration);
|
||||
void FadeAndClose(int soundStream, float targetVolume, float duration);
|
||||
|
||||
void SetStreamVolume(int soundStream, float volume);
|
||||
void SetStreamVolumeMax(int soundStream, float maxVolume);
|
||||
}
|
||||
|
||||
public interface ICover
|
||||
|
||||
@@ -218,14 +218,18 @@ namespace VocaluxeLib.Menu
|
||||
if (weights == null || weights.Length == 0 || _Bars == null)
|
||||
return;
|
||||
if (volume < 0.001)
|
||||
{
|
||||
for (int i = 0; i < _Bars.Length; i++)
|
||||
_Bars[i] = 0f;
|
||||
return;
|
||||
}
|
||||
if (volume > _MaxVolume)
|
||||
_MaxVolume = volume;
|
||||
_MaxBar = 0;
|
||||
float maxVal = -99f;
|
||||
for (int i = 0; i < _Bars.Length; i++)
|
||||
{
|
||||
if (weights.Length > i)
|
||||
if (i < weights.Length)
|
||||
{
|
||||
if (_Theme.DrawNegative == EOffOn.TR_CONFIG_OFF && weights[i] < 0)
|
||||
_Bars[i] = 0f;
|
||||
@@ -261,10 +265,12 @@ namespace VocaluxeLib.Menu
|
||||
return;
|
||||
|
||||
float dx = Rect.W / _Bars.Length;
|
||||
float scaleVal = (_Bars[_MaxBar] < 0.00001f) ? 0f : 1 / _Bars[_MaxBar];
|
||||
|
||||
for (int i = 0; i < _Bars.Length; i++)
|
||||
{
|
||||
var bar = new SRectF(Rect.X + dx * i, Rect.Y + Rect.H - _Bars[i] * Rect.H, dx - Space, _Bars[i] * Rect.H, Rect.Z);
|
||||
float value = _Bars[i] * scaleVal;
|
||||
var bar = new SRectF(Rect.X + dx * i, Rect.Y + Rect.H - value * Rect.H, dx - Space, value * Rect.H, Rect.Z);
|
||||
SColorF color = Color;
|
||||
if (i == _MaxBar)
|
||||
color = MaxColor;
|
||||
|
||||
@@ -81,11 +81,6 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
_SongMenu.Draw();
|
||||
}
|
||||
|
||||
public void ApplyVolume(float volumeMax)
|
||||
{
|
||||
_SongMenu.ApplyVolume(volumeMax);
|
||||
}
|
||||
|
||||
public bool IsActive()
|
||||
{
|
||||
return _SongMenu.IsActive();
|
||||
|
||||
@@ -402,7 +402,6 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
{
|
||||
_PreviewSongStream = -1;
|
||||
_Vidtex = null;
|
||||
ApplyVolume(CBase.Config.GetPreviewMusicVolume());
|
||||
}
|
||||
|
||||
public virtual void OnHide()
|
||||
@@ -423,14 +422,6 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
CBase.Drawing.DrawTexture(_Vidtex, new SRectF(0, 0, 1280, 720, 0));
|
||||
}
|
||||
|
||||
public virtual void ApplyVolume(float volumeMax)
|
||||
{
|
||||
_MaxVolume = volumeMax;
|
||||
|
||||
foreach (int stream in _Streams)
|
||||
CBase.Sound.SetStreamVolumeMax(stream, _MaxVolume);
|
||||
}
|
||||
|
||||
public virtual bool IsActive()
|
||||
{
|
||||
return _Active;
|
||||
@@ -532,9 +523,7 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
if (song == null)
|
||||
return;
|
||||
|
||||
int stream = CBase.Sound.Load(Path.Combine(song.Folder, song.MP3FileName), true);
|
||||
CBase.Sound.SetStreamVolumeMax(stream, _MaxVolume);
|
||||
CBase.Sound.SetStreamVolume(stream, 0f);
|
||||
int stream = CBase.Sound.Load(Path.Combine(song.Folder, song.MP3FileName), true, true);
|
||||
|
||||
float startposition = song.Preview.StartTime;
|
||||
float length = CBase.Sound.GetLength(stream);
|
||||
@@ -549,7 +538,7 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
|
||||
CBase.Sound.SetPosition(stream, startposition);
|
||||
CBase.Sound.Play(stream);
|
||||
CBase.Sound.SetStreamVolumeMax(stream, CBase.Config.GetPreviewMusicVolume());
|
||||
CBase.Sound.SetStreamVolume(stream, 0f);
|
||||
CBase.Sound.Fade(stream, 100f, 3f);
|
||||
_Streams.Add(stream);
|
||||
_PreviewSongStream = stream;
|
||||
@@ -570,7 +559,7 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
protected void _Reset()
|
||||
{
|
||||
foreach (int stream in _Streams)
|
||||
CBase.Sound.FadeAndStop(stream, 0f, 0.75f);
|
||||
CBase.Sound.FadeAndClose(stream, 0f, 0.75f);
|
||||
_Streams.Clear();
|
||||
|
||||
CBase.Video.Close(_PreviewVideoStream);
|
||||
|
||||
@@ -67,11 +67,11 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
{
|
||||
set
|
||||
{
|
||||
_PlaySong(value);
|
||||
_PreviewIdInternal = value;
|
||||
if (value == base._PreviewId)
|
||||
return;
|
||||
base._PreviewId = value;
|
||||
_UpdatePreview();
|
||||
}
|
||||
get { return _PreviewIdInternal; }
|
||||
}
|
||||
|
||||
public override int GetActualSelection()
|
||||
@@ -373,8 +373,7 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
if (!keyEvent.Handled)
|
||||
{
|
||||
_ActualSelection = _Locked;
|
||||
if (_PreviewId != _Locked)
|
||||
_PreviewId = _Locked;
|
||||
_PreviewId = _Locked;
|
||||
|
||||
for (int i = 0; i < _Tiles.Count; i++)
|
||||
_Tiles[i].Selected = _Locked == i + _Offset;
|
||||
|
||||
@@ -29,8 +29,6 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
void HandleMouse(ref SMouseEvent mouseEvent, SScreenSongOptions songOptions);
|
||||
void Draw();
|
||||
|
||||
void ApplyVolume(float volumeMax);
|
||||
|
||||
int GetSelectedSong();
|
||||
CStatic GetSelectedSongCover();
|
||||
void SetSelectedSong(int visibleSongNr);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "stdafx.h"
|
||||
#include "VocaluxeClock.h"
|
||||
|
||||
static void vocaluxe_clock_class_init (VocaluxeClockClass * klass);
|
||||
|
||||
Reference in new issue
Block a user