Merge branch 'develop' of github.com:Flamefire/Vocaluxe into develop

This commit is contained in:
Florian Küpper committed 2014-05-07 15:03:57 +02:00
commit ed076865bc
61 files changed
+2942 -4405

No files matched your search

-253
View File
@@ -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);
}
-49
View File
@@ -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);
-338
View File
@@ -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, &current, &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, &current, &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);
}
}
}
-73
View File
@@ -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();
};
+1 -35
View File
@@ -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>
+7 -15
View File
@@ -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
View File
@@ -1,4 +1,3 @@
#include "stdafx.h"
#include "GstreamerVideoStream.h"
typedef enum {
+1 -1
View File
@@ -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;
-19
View File
@@ -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;
}
-8
View File
@@ -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.
-16
View File
@@ -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.
-8
View File
@@ -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;
+29 -13
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -28,7 +28,7 @@ namespace Vocaluxe.Base
{
private static IDraw _Draw;
public static bool InitDraw()
public static bool Init()
{
if (_Draw != null)
return false;
+12 -9
View File
@@ -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);
-6
View File
@@ -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));
+3 -8
View File
@@ -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
View File
@@ -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
-7
View File
@@ -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();
+234
View File
@@ -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;
}
}
}
+112
View File
@@ -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
+253
View File
@@ -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);
}
}
}
}
+18 -1
View File
@@ -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();
}
}
+28 -33
View File
@@ -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());
+4 -4
View File
@@ -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
View File
@@ -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);
+12 -12
View File
@@ -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;
}
}
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -179,7 +179,7 @@ namespace Vocaluxe.Screens
CConfig.SaveConfig();
if (_OldCoverTheme != _SelectSlides[_SelectSlideCover].Selection)
CCover.ReloadCover();
CCover.ReloadCovers();
if (_OldTheme != _SelectSlides[_SelectSlideTheme].Selection)
{
+12 -21
View File
@@ -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;
+17 -19
View File
@@ -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))
+7 -2
View File
@@ -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" />
-1
View File
@@ -144,7 +144,6 @@ namespace VocaluxeLib
{
PortAudio,
OpenAL,
Gstreamer,
GstreamerSharp
}
+2 -3
View File
@@ -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
+8 -2
View File
@@ -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;
-5
View File
@@ -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;
-2
View File
@@ -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
View File
@@ -1,4 +1,3 @@
#include "stdafx.h"
#include "VocaluxeClock.h"
static void vocaluxe_clock_class_init (VocaluxeClockClass * klass);