Unify playback libs with common base class
Remove duplicate code Fix bugs in implementations (threading, logic) Remove SetStreamVolumeMax and use SetGlobalVolume instead
This commit is contained in:
1 parent
0692fe3c9b
commit
a4b1c3eb86
34 files changed
+1160
-1750
No files matched your search
@@ -5,6 +5,7 @@
|
||||
|
||||
map<int, GstreamerAudioStream*> Streams;
|
||||
queue<int> IDs;
|
||||
float GlobalVolume = 1.f;
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
DllExport void SetLogCallback (LogCallback Callback)
|
||||
{
|
||||
@@ -41,17 +42,20 @@ DllExport void SetGlobalVolume(float Volume)
|
||||
map<int, GstreamerAudioStream*>::iterator p;
|
||||
|
||||
for(p = Streams.begin(); p != Streams.end(); p++) {
|
||||
p->second->SetStreamVolume(Volume);
|
||||
p->second->SetStreamVolumeMax(Volume);
|
||||
}
|
||||
GlobalVolume = Volume;
|
||||
}
|
||||
|
||||
DllExport float GetGlobalVolume(){
|
||||
return GlobalVolume;
|
||||
}
|
||||
|
||||
DllExport int GetStreamCount(void)
|
||||
{
|
||||
return Streams.size();
|
||||
}
|
||||
|
||||
|
||||
DllExport void CloseAll(void)
|
||||
{
|
||||
map<int, GstreamerAudioStream*>::iterator p;
|
||||
@@ -66,7 +70,10 @@ 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);
|
||||
int result = s->Load(Media);
|
||||
s->SetStreamVolume(1.f);
|
||||
s->SetStreamVolumeMax(GlobalVolume);
|
||||
return result;
|
||||
}
|
||||
|
||||
DllExport int LoadPrescan(const wchar_t* Media, bool Prescan)
|
||||
@@ -74,7 +81,10 @@ 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);
|
||||
int result = s->Load(Media, Prescan);
|
||||
s->SetStreamVolume(1.f);
|
||||
s->SetStreamVolumeMax(GlobalVolume);
|
||||
return result;
|
||||
}
|
||||
|
||||
DllExport void Close(int Stream)
|
||||
@@ -160,15 +170,6 @@ DllExport void SetStreamVolume(int Stream, float 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);
|
||||
|
||||
@@ -17,6 +17,7 @@ using namespace std;
|
||||
|
||||
DllExport bool Init();
|
||||
DllExport void SetGlobalVolume(float Volume);
|
||||
DllExport float GetGlobalVolume();
|
||||
DllExport int GetStreamCount(void);
|
||||
DllExport void CloseAll(void);
|
||||
|
||||
@@ -33,7 +34,6 @@ using namespace std;
|
||||
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);
|
||||
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
|
||||
@@ -146,7 +146,6 @@ 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;
|
||||
@@ -161,7 +160,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);
|
||||
@@ -312,13 +311,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 +325,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)
|
||||
|
||||
+25
-10
@@ -102,8 +102,8 @@ namespace Vocaluxe.Base
|
||||
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
|
||||
|
||||
@@ -408,6 +408,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 +424,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 +522,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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-15
@@ -109,11 +109,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 +119,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 +144,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);
|
||||
|
||||
@@ -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,308 @@
|
||||
#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 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)
|
||||
{
|
||||
foreach (IAudioStream stream in _Streams)
|
||||
stream.Update();
|
||||
}
|
||||
}
|
||||
|
||||
#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 0f;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
return _Streams[_GetStreamIndex(streamID)].Length;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public float GetPosition(int streamID)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0f;
|
||||
lock (_Streams)
|
||||
{
|
||||
if (_StreamExists(streamID))
|
||||
return _Streams[_GetStreamIndex(streamID)].Position;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_Streams.Remove(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,21 @@
|
||||
using System;
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback
|
||||
{
|
||||
|
||||
@@ -34,9 +34,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
private string _FileName;
|
||||
private bool _FileOpened;
|
||||
|
||||
public void Init() {}
|
||||
|
||||
public void Open(string fileName)
|
||||
public bool Open(string fileName)
|
||||
{
|
||||
_FileName = fileName;
|
||||
|
||||
@@ -49,14 +47,15 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error opening audio file: " + _FileName);
|
||||
return;
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!_Instance.Opened)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
int audioStreamIndex;
|
||||
@@ -71,13 +70,13 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
{
|
||||
CLog.LogError("Error opening audio file (can't find decoder): " + _FileName);
|
||||
Close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (audioStreamIndex < 0)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
_FormatInfo = new SFormatInfo
|
||||
@@ -93,9 +92,10 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
{
|
||||
CLog.LogError("Unsupported BitDepth in file " + fileName);
|
||||
Close();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
_FileOpened = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
|
||||
@@ -26,10 +26,9 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
|
||||
interface IAudioDecoder
|
||||
{
|
||||
void Init();
|
||||
void Close();
|
||||
|
||||
void Open(string fileName);
|
||||
bool Open(string fileName);
|
||||
SFormatInfo GetFormatInfo();
|
||||
|
||||
float GetLength();
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
{
|
||||
class CGstreamerAudio : IPlayback
|
||||
{
|
||||
//static float LastPosition;
|
||||
private readonly CGstreamerAudioWrapper.LogCallback _LogCallback;
|
||||
|
||||
#region log
|
||||
@@ -35,8 +34,6 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
public CGstreamerAudio()
|
||||
{
|
||||
_LogCallback = _LogHandler;
|
||||
//Is this really needed? CodaAnalyzer complains about it...
|
||||
//GC.SuppressFinalize(_LogCallback);
|
||||
CGstreamerAudioWrapper.SetLogCallback(_LogCallback);
|
||||
}
|
||||
|
||||
@@ -48,6 +45,12 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
public void Close()
|
||||
{
|
||||
CloseAll();
|
||||
CGstreamerAudioWrapper.SetLogCallback(null);
|
||||
}
|
||||
|
||||
public float GetGlobalVolume()
|
||||
{
|
||||
return CGstreamerAudioWrapper.GetGlobalVolume();
|
||||
}
|
||||
|
||||
public void SetGlobalVolume(float volume)
|
||||
@@ -65,17 +68,13 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
CGstreamerAudioWrapper.CloseAll();
|
||||
}
|
||||
|
||||
public int Load(string media)
|
||||
public int Load(string medium, bool loop = false, bool prescan = false)
|
||||
{
|
||||
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);
|
||||
var u = new Uri(medium);
|
||||
int i = CGstreamerAudioWrapper.Load(u.AbsoluteUri, prescan);
|
||||
//Workaround b/c I'm lazy:
|
||||
CGstreamerAudioWrapper.Play(i, loop);
|
||||
CGstreamerAudioWrapper.Pause(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -89,11 +88,6 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
CGstreamerAudioWrapper.Play(stream);
|
||||
}
|
||||
|
||||
public void Play(int stream, bool loop)
|
||||
{
|
||||
CGstreamerAudioWrapper.Play(stream, loop);
|
||||
}
|
||||
|
||||
public void Pause(int stream)
|
||||
{
|
||||
CGstreamerAudioWrapper.Pause(stream);
|
||||
@@ -104,19 +98,25 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
CGstreamerAudioWrapper.Stop(stream);
|
||||
}
|
||||
|
||||
public void Fade(int stream, float targetVolume, float seconds)
|
||||
public void Fade(int streamID, float targetVolume, float seconds, EStreamAction afterFadeAction = EStreamAction.Nothing)
|
||||
{
|
||||
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);
|
||||
switch (afterFadeAction)
|
||||
{
|
||||
case EStreamAction.Nothing:
|
||||
CGstreamerAudioWrapper.Fade(streamID, targetVolume, seconds);
|
||||
break;
|
||||
case EStreamAction.Pause:
|
||||
CGstreamerAudioWrapper.FadeAndPause(streamID, targetVolume, seconds);
|
||||
break;
|
||||
case EStreamAction.Stop:
|
||||
CGstreamerAudioWrapper.FadeAndStop(streamID, targetVolume, seconds);
|
||||
break;
|
||||
case EStreamAction.Close:
|
||||
CGstreamerAudioWrapper.FadeAndStop(streamID, targetVolume, seconds);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("afterFadeAction");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStreamVolume(int stream, float volume)
|
||||
@@ -124,11 +124,6 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
CGstreamerAudioWrapper.SetStreamVolume(stream, volume);
|
||||
}
|
||||
|
||||
public void SetStreamVolumeMax(int stream, float volume)
|
||||
{
|
||||
CGstreamerAudioWrapper.SetStreamVolumeMax(stream, volume);
|
||||
}
|
||||
|
||||
public float GetLength(int stream)
|
||||
{
|
||||
return CGstreamerAudioWrapper.GetLength(stream);
|
||||
@@ -163,10 +158,5 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
{
|
||||
CGstreamerAudioWrapper.Update();
|
||||
}
|
||||
|
||||
public void FadeAndStop(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,9 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void SetGlobalVolume(float volume);
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern float GetGlobalVolume();
|
||||
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int GetStreamCount();
|
||||
|
||||
@@ -57,7 +60,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int Load(string media);
|
||||
|
||||
[DllImport(_Dll, EntryPoint = "LoadPrescan", CallingConvention = CallingConvention.Cdecl)]
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "LoadPrescan")]
|
||||
public static extern int Load(string media,
|
||||
[MarshalAs(UnmanagedType.U1)] bool prescan);
|
||||
|
||||
@@ -67,7 +70,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Play(int stream);
|
||||
|
||||
[DllImport(_Dll, EntryPoint = "PlayLoop")]
|
||||
[DllImport(_Dll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PlayLoop")]
|
||||
public static extern void Play(int stream,
|
||||
[MarshalAs(UnmanagedType.U1)] bool loop);
|
||||
|
||||
@@ -89,9 +92,6 @@ namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
[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);
|
||||
|
||||
|
||||
@@ -20,14 +20,11 @@ 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 +37,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,107 @@ 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 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;
|
||||
|
||||
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 && !_QueryingDuration)
|
||||
{
|
||||
_QueryingDuration = true; // Set this to avoid race conditions
|
||||
var t = new System.Threading.Thread(_UpdateDuration) {Name = "GSt Update Duration"};
|
||||
t.Start();
|
||||
}
|
||||
return base.Length >= 0f ? base.Length : 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
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 _IsFinished; }
|
||||
}
|
||||
|
||||
public override bool IsPaused
|
||||
{
|
||||
get { return _Element == null || _Element.TargetState == State.Paused; }
|
||||
set
|
||||
{
|
||||
if (_Element == null)
|
||||
return;
|
||||
_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)
|
||||
{
|
||||
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 +129,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 +178,55 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
if (msg.Handle != IntPtr.Zero)
|
||||
_UpdateDuration();
|
||||
}
|
||||
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
|
||||
//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 +239,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
if (_Loop)
|
||||
Position = 0;
|
||||
else
|
||||
{
|
||||
Finished = true;
|
||||
Close();
|
||||
}
|
||||
_IsFinished = true;
|
||||
break;
|
||||
case MessageType.Error:
|
||||
GException error;
|
||||
@@ -128,187 +255,35 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp
|
||||
msg.Unref();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (!Closed)
|
||||
{
|
||||
Closed = true;
|
||||
Finished = true;
|
||||
var t = new System.Threading.Thread(_TerminateStream) {Name = "GSt Terminate"};
|
||||
t.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void _TerminateStream()
|
||||
{
|
||||
if (_Element != null)
|
||||
{
|
||||
_Element.SetState(State.Null);
|
||||
_Element.Dispose();
|
||||
_Element = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Play(bool loop = false)
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(State.Playing);
|
||||
_Loop = loop;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(State.Ready);
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public void Fade(float targetVolume, float seconds)
|
||||
{
|
||||
targetVolume.Clamp(0f, 100f);
|
||||
_Fading = new CFading(_Volume, targetVolume / 100f, seconds);
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
}
|
||||
|
||||
public void FadeAndPause(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Pause;
|
||||
}
|
||||
|
||||
public void FadeAndClose(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Close;
|
||||
}
|
||||
|
||||
public void FadeAndStop(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Stop;
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get { return _Volume * 100f; }
|
||||
set
|
||||
{
|
||||
value.Clamp(0f, 100f);
|
||||
_Volume = value / 100f;
|
||||
_SetElementVolume();
|
||||
}
|
||||
}
|
||||
|
||||
private void _SetElementVolume()
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element["volume"] = (_Volume * _MaxVolume);
|
||||
}
|
||||
|
||||
public float MaxVolume
|
||||
{
|
||||
get { return _MaxVolume * 100f; }
|
||||
set
|
||||
{
|
||||
value.Clamp(0f, 100f);
|
||||
_MaxVolume = value / 100f;
|
||||
_SetElementVolume();
|
||||
}
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Duration < 0 && !_QueryingDuration)
|
||||
{
|
||||
_QueryingDuration = true; // Set this to avoid race conditions
|
||||
var t = new System.Threading.Thread(_UpdateDuration) {Name = "GSt Update Duration"};
|
||||
t.Start();
|
||||
}
|
||||
return _Duration > 0 ? _Duration : -1;
|
||||
}
|
||||
}
|
||||
|
||||
public float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
long position;
|
||||
if (!_Element.QueryPosition(Format.Time, out position))
|
||||
CLog.LogError("Could not query position");
|
||||
else
|
||||
_Position = (float)(position / (double)Constants.SECOND);
|
||||
return _Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SeekSimple(Format.Time, SeekFlags.Accurate | SeekFlags.Flush, (long)(value * (double)Constants.SECOND));
|
||||
}
|
||||
}
|
||||
|
||||
public bool Paused
|
||||
{
|
||||
get { return _Element == null || _Element.TargetState == State.Paused; }
|
||||
set
|
||||
{
|
||||
if (value && _Element != null)
|
||||
_Element.SetState(State.Paused);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Playing
|
||||
{
|
||||
get { return _Element != null && (_Element.TargetState == State.Playing && !Finished); }
|
||||
set
|
||||
{
|
||||
if (value && _Element != null)
|
||||
_Element.SetState(State.Playing);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
while (_Element != null && _Element.Bus != null && _Element.Bus.HavePending())
|
||||
_OnMessage(_Element.Bus.Pop());
|
||||
|
||||
if (_Fading != null)
|
||||
{
|
||||
bool finished;
|
||||
_Volume = _Fading.GetValue(out finished);
|
||||
_SetElementVolume();
|
||||
if (finished)
|
||||
{
|
||||
switch (_AfterFadeAction)
|
||||
{
|
||||
case EStreamAction.Pause:
|
||||
Paused = true;
|
||||
break;
|
||||
case EStreamAction.Stop:
|
||||
Stop();
|
||||
break;
|
||||
case EStreamAction.Close:
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
_Fading = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _UpdateDuration()
|
||||
{
|
||||
_QueryingDuration = true;
|
||||
long duration = -1;
|
||||
while (duration < 0 && !Closed && !Finished && _Element != null)
|
||||
if (_QueryingDuration)
|
||||
return;
|
||||
lock (_DurationLock)
|
||||
{
|
||||
if (_Element.QueryDuration(Format.Time, out duration))
|
||||
if (_QueryingDuration)
|
||||
return;
|
||||
_QueryingDuration = true;
|
||||
}
|
||||
long duration = -1;
|
||||
while (duration < 0 && !IsFinished)
|
||||
{
|
||||
lock (_ElementLock)
|
||||
{
|
||||
_Duration = duration / (float)Constants.SECOND;
|
||||
_QueryingDuration = false;
|
||||
if (_Element == null)
|
||||
break;
|
||||
if (_Element.QueryDuration(Format.Time, out duration))
|
||||
Length = duration / (float)Constants.SECOND;
|
||||
}
|
||||
}
|
||||
_QueryingDuration = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Playback
|
||||
{
|
||||
public interface ICloseStreamListener
|
||||
{
|
||||
void OnCloseStream(IAudioStream stream);
|
||||
}
|
||||
|
||||
public interface IAudioStream : IDisposable
|
||||
{
|
||||
int ID { get; }
|
||||
float Volume { get; set; }
|
||||
float VolumeMax { get; set; }
|
||||
float Length { get; }
|
||||
float Position { get; set; }
|
||||
bool IsPaused { get; set; }
|
||||
bool IsFinished { get; }
|
||||
|
||||
void SetOnCloseListener(ICloseStreamListener listener);
|
||||
|
||||
void Update();
|
||||
|
||||
bool Open(bool prescan);
|
||||
void Close();
|
||||
void Play();
|
||||
void Stop();
|
||||
void Fade(float targetVolume, float seconds, EStreamAction afterFadeAction);
|
||||
void CancelFading();
|
||||
}
|
||||
}
|
||||
@@ -25,52 +25,47 @@ namespace Vocaluxe.Lib.Sound.Playback
|
||||
Close
|
||||
}
|
||||
|
||||
struct SAudioStreams
|
||||
{
|
||||
public int Handle;
|
||||
|
||||
public SAudioStreams(int stream)
|
||||
{
|
||||
Handle = stream;
|
||||
}
|
||||
}
|
||||
|
||||
delegate void Closeproc(int stream);
|
||||
|
||||
interface IPlayback
|
||||
{
|
||||
bool Init();
|
||||
void Close();
|
||||
float GetGlobalVolume();
|
||||
|
||||
/// <summary>
|
||||
/// Set the global (maximum) volume<br />
|
||||
/// All streams (existent and future ones) will have their maximum volume set to this value
|
||||
/// </summary>
|
||||
/// <param name="volume">Volume in percent</param>
|
||||
void SetGlobalVolume(float volume);
|
||||
|
||||
int GetStreamCount();
|
||||
void CloseAll();
|
||||
|
||||
#region Stream Handling
|
||||
int Load(string media);
|
||||
int Load(string media, bool prescan);
|
||||
void Close(int stream);
|
||||
#region stream Handling
|
||||
int Load(string medium, bool loop = false, bool prescan = false);
|
||||
void Close(int streamID);
|
||||
|
||||
void Play(int stream);
|
||||
void Play(int stream, bool loop);
|
||||
void Pause(int stream);
|
||||
void Stop(int stream);
|
||||
void Fade(int stream, float targetVolume, float seconds);
|
||||
void FadeAndPause(int stream, float targetVolume, float seconds);
|
||||
void FadeAndClose(int stream, float targetVolume, float seconds);
|
||||
void FadeAndStop(int stream, float targetVolume, float seconds);
|
||||
void SetStreamVolume(int stream, float volume);
|
||||
void SetStreamVolumeMax(int stream, float volume);
|
||||
void Play(int streamID);
|
||||
void Pause(int streamID);
|
||||
void Stop(int streamID);
|
||||
void Fade(int streamID, float targetVolume, float seconds, EStreamAction afterFadeAction = EStreamAction.Nothing);
|
||||
|
||||
float GetLength(int stream);
|
||||
float GetPosition(int stream);
|
||||
/// <summary>
|
||||
/// Set the streams current volume. Cancels fading
|
||||
/// </summary>
|
||||
/// <param name="streamID">Id of the stream (optained by Load)</param>
|
||||
/// <param name="volume">Volume in percent</param>
|
||||
void SetStreamVolume(int streamID, float volume);
|
||||
|
||||
bool IsPlaying(int stream);
|
||||
bool IsPaused(int stream);
|
||||
bool IsFinished(int stream);
|
||||
float GetLength(int streamID);
|
||||
float GetPosition(int streamID);
|
||||
void SetPosition(int streamID, float position);
|
||||
|
||||
void SetPosition(int stream, float position);
|
||||
bool IsPlaying(int streamID);
|
||||
bool IsPaused(int streamID);
|
||||
bool IsFinished(int streamID);
|
||||
|
||||
void Update();
|
||||
#endregion Stream Handling
|
||||
#endregion stream Handling
|
||||
}
|
||||
}
|
||||
@@ -23,351 +23,34 @@ 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,157 @@ 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)
|
||||
{
|
||||
if (_FileOpened)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
return -1;
|
||||
Length = 0f;
|
||||
|
||||
_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 +223,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 +236,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 +262,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 +292,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,29 +21,20 @@ 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;
|
||||
@@ -56,9 +47,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
|
||||
private bool _Skip;
|
||||
|
||||
private bool _Loop;
|
||||
private float _Duration;
|
||||
private float _CurrentTime;
|
||||
private volatile float _TimeCode;
|
||||
|
||||
private volatile bool _Paused;
|
||||
@@ -66,7 +54,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;
|
||||
|
||||
@@ -77,83 +64,35 @@ 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
|
||||
@@ -172,57 +111,31 @@ 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)
|
||||
{
|
||||
if (_FileOpened)
|
||||
return false;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
Length = 0f;
|
||||
|
||||
if (!File.Exists(_Medium))
|
||||
{
|
||||
Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -238,36 +151,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
|
||||
{
|
||||
@@ -282,15 +191,12 @@ 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;
|
||||
}
|
||||
|
||||
@@ -302,33 +208,52 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
_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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,7 +270,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
}
|
||||
|
||||
_Start = _SetStart;
|
||||
_Loop = _SetLoop;
|
||||
}
|
||||
|
||||
if (_Skip)
|
||||
@@ -355,7 +279,6 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio
|
||||
}
|
||||
|
||||
_DoDecode();
|
||||
_Update();
|
||||
_EventDecode.WaitOne();
|
||||
}
|
||||
|
||||
@@ -364,37 +287,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
|
||||
@@ -406,20 +317,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
|
||||
|
||||
@@ -449,27 +378,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 + _Latency;
|
||||
float time = _TimeCode - _Data.BytesNotRead / _BytesPerSecond - latency;
|
||||
|
||||
if (!_NoMoreData)
|
||||
_CurrentTime = _SyncTimer.Update(time);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -484,54 +413,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());
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -107,49 +107,49 @@ namespace Vocaluxe.Screens
|
||||
|
||||
private void _SaveConfig()
|
||||
{
|
||||
int volume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
switch (CGraphics.CurrentScreen)
|
||||
{
|
||||
case EScreens.ScreenSong:
|
||||
if (CSongs.IsInCategory)
|
||||
CConfig.PreviewMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
CConfig.PreviewMusicVolume = volume;
|
||||
else
|
||||
{
|
||||
CConfig.BackgroundMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
}
|
||||
CConfig.BackgroundMusicVolume = volume;
|
||||
break;
|
||||
|
||||
case EScreens.ScreenSing:
|
||||
CConfig.GameMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
CConfig.GameMusicVolume = volume;
|
||||
break;
|
||||
|
||||
default:
|
||||
CConfig.BackgroundMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
CConfig.BackgroundMusicVolume = volume;
|
||||
break;
|
||||
}
|
||||
CConfig.SaveConfig();
|
||||
CSound.SetGlobalVolume(volume);
|
||||
}
|
||||
|
||||
private void _UpdateSlides()
|
||||
{
|
||||
int volume;
|
||||
switch (CGraphics.CurrentScreen)
|
||||
{
|
||||
case EScreens.ScreenSong:
|
||||
if (CSongs.IsInCategory)
|
||||
_SelectSlides[_SelectSlideVolume].Selection = CConfig.PreviewMusicVolume / 5;
|
||||
volume = CConfig.PreviewMusicVolume;
|
||||
else
|
||||
_SelectSlides[_SelectSlideVolume].Selection = CConfig.BackgroundMusicVolume / 5;
|
||||
volume = CConfig.BackgroundMusicVolume;
|
||||
break;
|
||||
|
||||
case EScreens.ScreenSing:
|
||||
_SelectSlides[_SelectSlideVolume].Selection = CConfig.GameMusicVolume / 5;
|
||||
volume = CConfig.GameMusicVolume;
|
||||
break;
|
||||
|
||||
default:
|
||||
_SelectSlides[_SelectSlideVolume].Selection = CConfig.BackgroundMusicVolume / 5;
|
||||
volume = CConfig.BackgroundMusicVolume;
|
||||
break;
|
||||
}
|
||||
_SelectSlides[_SelectSlideVolume].Selection = volume / 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ namespace Vocaluxe.Screens
|
||||
CConfig.SaveConfig();
|
||||
|
||||
CBackgroundMusic.SetMusicSource(CConfig.BackgroundMusicSource);
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
if (CConfig.BackgroundMusic == EOffOn.TR_CONFIG_ON)
|
||||
CBackgroundMusic.Play();
|
||||
else
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -606,7 +598,6 @@ namespace Vocaluxe.Screens
|
||||
_Texts[_TextSongName].Text = songname;
|
||||
|
||||
_CurrentStream = CSound.Load(song.GetMP3(), true);
|
||||
CSound.SetStreamVolumeMax(_CurrentStream, CConfig.GameMusicVolume);
|
||||
CSound.SetStreamVolume(_CurrentStream, _Volume);
|
||||
CSound.SetPosition(_CurrentStream, song.Start);
|
||||
_CurrentTime = song.Start;
|
||||
|
||||
@@ -148,8 +148,6 @@ namespace Vocaluxe.Screens
|
||||
_Playlists[_Playlist].Init();
|
||||
|
||||
_AvailableGameModes.Clear();
|
||||
|
||||
ApplyVolume();
|
||||
}
|
||||
|
||||
public override bool HandleInput(SKeyEvent keyEvent)
|
||||
@@ -365,21 +363,23 @@ namespace Vocaluxe.Screens
|
||||
}
|
||||
}
|
||||
|
||||
int volumeDiff = 0;
|
||||
if (keyEvent.ModShift && (keyEvent.Key == Keys.Add || keyEvent.Key == Keys.PageUp))
|
||||
{
|
||||
CConfig.PreviewMusicVolume = CConfig.PreviewMusicVolume + 5;
|
||||
if (CConfig.PreviewMusicVolume > 100)
|
||||
CConfig.PreviewMusicVolume = 100;
|
||||
CConfig.SaveConfig();
|
||||
ApplyVolume();
|
||||
}
|
||||
volumeDiff = 5;
|
||||
else if (keyEvent.ModShift && (keyEvent.Key == Keys.Subtract || keyEvent.Key == Keys.PageDown))
|
||||
volumeDiff = -5;
|
||||
if (volumeDiff != 0)
|
||||
{
|
||||
CConfig.PreviewMusicVolume = CConfig.PreviewMusicVolume - 5;
|
||||
if (CConfig.PreviewMusicVolume < 0)
|
||||
CConfig.PreviewMusicVolume = 0;
|
||||
CConfig.SaveConfig();
|
||||
ApplyVolume();
|
||||
if (CSongs.IsInCategory)
|
||||
{
|
||||
CConfig.PreviewMusicVolume += volumeDiff;
|
||||
CSound.SetGlobalVolume(CConfig.PreviewMusicVolume);
|
||||
}
|
||||
else
|
||||
{
|
||||
CConfig.BackgroundMusicVolume += volumeDiff;
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -643,6 +643,8 @@ namespace Vocaluxe.Screens
|
||||
_Playlists[_Playlist].DragAndDropSongID = -1;
|
||||
|
||||
UpdateGame();
|
||||
if (CSongs.IsInCategory)
|
||||
CSound.SetGlobalVolume(CConfig.PreviewMusicVolume);
|
||||
}
|
||||
|
||||
public override bool UpdateGame()
|
||||
@@ -700,14 +702,10 @@ namespace Vocaluxe.Screens
|
||||
{
|
||||
base.OnClose();
|
||||
CBackgroundMusic.Disabled = false;
|
||||
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
|
||||
_SongMenus[_SongMenu].OnHide();
|
||||
}
|
||||
|
||||
public override void ApplyVolume()
|
||||
{
|
||||
_SongMenus[_SongMenu].ApplyVolume(CConfig.PreviewMusicVolume);
|
||||
}
|
||||
|
||||
private void _HandlePartySongSelection(int songNr)
|
||||
{
|
||||
if ((CSongs.Category >= 0) && (songNr >= 0))
|
||||
|
||||
@@ -219,8 +219,11 @@
|
||||
<Compile Include="Base\CPlaylistElement.cs" />
|
||||
<Compile Include="Base\CRecord.cs" />
|
||||
<Compile Include="COSFunctions.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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -533,8 +524,6 @@ namespace VocaluxeLib.Menu.SongMenu
|
||||
return;
|
||||
|
||||
int stream = CBase.Sound.Load(Path.Combine(song.Folder, song.MP3FileName), true);
|
||||
CBase.Sound.SetStreamVolumeMax(stream, _MaxVolume);
|
||||
CBase.Sound.SetStreamVolume(stream, 0f);
|
||||
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user