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:
Flamefire committed 2014-05-05 02:10:15 +02:00
1 parent 0692fe3c9b
commit a4b1c3eb86
34 files changed
+1160 -1750

No files matched your search

+14 -13
View File
@@ -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);
+1 -1
View File
@@ -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;
+2 -10
View File
@@ -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
View File
@@ -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
+5 -9
View File
@@ -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);
+1 -6
View File
@@ -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
View File
@@ -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);
}
}
}
}
+18 -1
View File
@@ -1,4 +1,21 @@
using System;
#region license
// This file is part of Vocaluxe.
//
// Vocaluxe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vocaluxe is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
namespace Vocaluxe.Lib.Sound.Playback
{
@@ -34,9 +34,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
private string _FileName;
private bool _FileOpened;
public void Init() {}
public void Open(string fileName)
public bool Open(string fileName)
{
_FileName = fileName;
@@ -49,14 +47,15 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
catch (Exception)
{
CLog.LogError("Error opening audio file: " + _FileName);
return;
Close();
return false;
}
if (!_Instance.Opened)
{
Close();
return;
return false;
}
int audioStreamIndex;
@@ -71,13 +70,13 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
{
CLog.LogError("Error opening audio file (can't find decoder): " + _FileName);
Close();
return;
return false;
}
if (audioStreamIndex < 0)
{
Close();
return;
return false;
}
_FormatInfo = new SFormatInfo
@@ -93,9 +92,10 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
{
CLog.LogError("Unsupported BitDepth in file " + fileName);
Close();
return;
return false;
}
_FileOpened = true;
return true;
}
public void Close()
@@ -26,10 +26,9 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder
interface IAudioDecoder
{
void Init();
void Close();
void Open(string fileName);
bool Open(string fileName);
SFormatInfo GetFormatInfo();
float GetLength();
@@ -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();
}
}
+28 -33
View File
@@ -25,52 +25,47 @@ namespace Vocaluxe.Lib.Sound.Playback
Close
}
struct SAudioStreams
{
public int Handle;
public SAudioStreams(int stream)
{
Handle = stream;
}
}
delegate void Closeproc(int stream);
interface IPlayback
{
bool Init();
void Close();
float GetGlobalVolume();
/// <summary>
/// Set the global (maximum) volume<br />
/// All streams (existent and future ones) will have their maximum volume set to this value
/// </summary>
/// <param name="volume">Volume in percent</param>
void SetGlobalVolume(float volume);
int GetStreamCount();
void CloseAll();
#region Stream Handling
int Load(string media);
int Load(string media, bool prescan);
void Close(int stream);
#region stream Handling
int Load(string medium, bool loop = false, bool prescan = false);
void Close(int streamID);
void Play(int stream);
void Play(int stream, bool loop);
void Pause(int stream);
void Stop(int stream);
void Fade(int stream, float targetVolume, float seconds);
void FadeAndPause(int stream, float targetVolume, float seconds);
void FadeAndClose(int stream, float targetVolume, float seconds);
void FadeAndStop(int stream, float targetVolume, float seconds);
void SetStreamVolume(int stream, float volume);
void SetStreamVolumeMax(int stream, float volume);
void Play(int streamID);
void Pause(int streamID);
void Stop(int streamID);
void Fade(int streamID, float targetVolume, float seconds, EStreamAction afterFadeAction = EStreamAction.Nothing);
float GetLength(int stream);
float GetPosition(int stream);
/// <summary>
/// Set the streams current volume. Cancels fading
/// </summary>
/// <param name="streamID">Id of the stream (optained by Load)</param>
/// <param name="volume">Volume in percent</param>
void SetStreamVolume(int streamID, float volume);
bool IsPlaying(int stream);
bool IsPaused(int stream);
bool IsFinished(int stream);
float GetLength(int streamID);
float GetPosition(int streamID);
void SetPosition(int streamID, float position);
void SetPosition(int stream, float position);
bool IsPlaying(int streamID);
bool IsPaused(int streamID);
bool IsFinished(int streamID);
void Update();
#endregion Stream Handling
#endregion stream Handling
}
}
@@ -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
{
+12 -12
View File
@@ -107,49 +107,49 @@ namespace Vocaluxe.Screens
private void _SaveConfig()
{
int volume = _SelectSlides[_SelectSlideVolume].Selection * 5;
switch (CGraphics.CurrentScreen)
{
case EScreens.ScreenSong:
if (CSongs.IsInCategory)
CConfig.PreviewMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
CConfig.PreviewMusicVolume = volume;
else
{
CConfig.BackgroundMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
}
CConfig.BackgroundMusicVolume = volume;
break;
case EScreens.ScreenSing:
CConfig.GameMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
CConfig.GameMusicVolume = volume;
break;
default:
CConfig.BackgroundMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5;
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
CConfig.BackgroundMusicVolume = volume;
break;
}
CConfig.SaveConfig();
CSound.SetGlobalVolume(volume);
}
private void _UpdateSlides()
{
int volume;
switch (CGraphics.CurrentScreen)
{
case EScreens.ScreenSong:
if (CSongs.IsInCategory)
_SelectSlides[_SelectSlideVolume].Selection = CConfig.PreviewMusicVolume / 5;
volume = CConfig.PreviewMusicVolume;
else
_SelectSlides[_SelectSlideVolume].Selection = CConfig.BackgroundMusicVolume / 5;
volume = CConfig.BackgroundMusicVolume;
break;
case EScreens.ScreenSing:
_SelectSlides[_SelectSlideVolume].Selection = CConfig.GameMusicVolume / 5;
volume = CConfig.GameMusicVolume;
break;
default:
_SelectSlides[_SelectSlideVolume].Selection = CConfig.BackgroundMusicVolume / 5;
volume = CConfig.BackgroundMusicVolume;
break;
}
_SelectSlides[_SelectSlideVolume].Selection = volume / 5;
}
}
}
+1 -1
View File
@@ -156,7 +156,7 @@ namespace Vocaluxe.Screens
CConfig.SaveConfig();
CBackgroundMusic.SetMusicSource(CConfig.BackgroundMusicSource);
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
if (CConfig.BackgroundMusic == EOffOn.TR_CONFIG_ON)
CBackgroundMusic.Play();
else
+11 -20
View File
@@ -258,11 +258,8 @@ namespace Vocaluxe.Screens
case Keys.PageUp:
if (keyEvent.ModShift)
{
CConfig.GameMusicVolume = CConfig.GameMusicVolume + 5;
if (CConfig.GameMusicVolume > 100)
CConfig.GameMusicVolume = 100;
CConfig.SaveConfig();
ApplyVolume();
CConfig.GameMusicVolume += 5;
CSound.SetGlobalVolume(CConfig.GameMusicVolume);
}
break;
@@ -270,11 +267,8 @@ namespace Vocaluxe.Screens
case Keys.PageDown:
if (keyEvent.ModShift)
{
CConfig.GameMusicVolume = CConfig.GameMusicVolume - 5;
if (CConfig.GameMusicVolume < 0)
CConfig.GameMusicVolume = 0;
CConfig.SaveConfig();
ApplyVolume();
CConfig.GameMusicVolume -= 5;
CSound.SetGlobalVolume(CConfig.GameMusicVolume);
}
break;
}
@@ -441,6 +435,7 @@ namespace Vocaluxe.Screens
CBackgroundMusic.Disabled = true;
_CloseSong();
CSound.SetGlobalVolume(CConfig.GameMusicVolume);
}
public override void OnShowFinish()
@@ -449,7 +444,6 @@ namespace Vocaluxe.Screens
CGame.Start();
_LoadNextSong();
CBackgroundMusic.Disabled = true;
}
public override bool Draw()
@@ -540,22 +534,20 @@ namespace Vocaluxe.Screens
public override void OnClose()
{
base.OnClose();
CBackgroundMusic.Disabled = false;
_CloseSong();
if (_Webcam)
CWebcam.Stop();
_CloseSong();
}
public override void ApplyVolume()
{
if (_CurrentStream > -1)
CSound.SetStreamVolumeMax(_CurrentStream, CConfig.GameMusicVolume);
CBackgroundMusic.Disabled = false;
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
}
private void _CloseSong()
{
if (_CurrentStream > -1)
{
CSound.FadeAndClose(_CurrentStream, 0f, 0.5f);
_CurrentStream = -1;
}
CRecord.Stop();
if (_CurrentVideo != -1)
{
@@ -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;
+17 -19
View File
@@ -148,8 +148,6 @@ namespace Vocaluxe.Screens
_Playlists[_Playlist].Init();
_AvailableGameModes.Clear();
ApplyVolume();
}
public override bool HandleInput(SKeyEvent keyEvent)
@@ -365,21 +363,23 @@ namespace Vocaluxe.Screens
}
}
int volumeDiff = 0;
if (keyEvent.ModShift && (keyEvent.Key == Keys.Add || keyEvent.Key == Keys.PageUp))
{
CConfig.PreviewMusicVolume = CConfig.PreviewMusicVolume + 5;
if (CConfig.PreviewMusicVolume > 100)
CConfig.PreviewMusicVolume = 100;
CConfig.SaveConfig();
ApplyVolume();
}
volumeDiff = 5;
else if (keyEvent.ModShift && (keyEvent.Key == Keys.Subtract || keyEvent.Key == Keys.PageDown))
volumeDiff = -5;
if (volumeDiff != 0)
{
CConfig.PreviewMusicVolume = CConfig.PreviewMusicVolume - 5;
if (CConfig.PreviewMusicVolume < 0)
CConfig.PreviewMusicVolume = 0;
CConfig.SaveConfig();
ApplyVolume();
if (CSongs.IsInCategory)
{
CConfig.PreviewMusicVolume += volumeDiff;
CSound.SetGlobalVolume(CConfig.PreviewMusicVolume);
}
else
{
CConfig.BackgroundMusicVolume += volumeDiff;
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
}
}
return true;
@@ -643,6 +643,8 @@ namespace Vocaluxe.Screens
_Playlists[_Playlist].DragAndDropSongID = -1;
UpdateGame();
if (CSongs.IsInCategory)
CSound.SetGlobalVolume(CConfig.PreviewMusicVolume);
}
public override bool UpdateGame()
@@ -700,14 +702,10 @@ namespace Vocaluxe.Screens
{
base.OnClose();
CBackgroundMusic.Disabled = false;
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
_SongMenus[_SongMenu].OnHide();
}
public override void ApplyVolume()
{
_SongMenus[_SongMenu].ApplyVolume(CConfig.PreviewMusicVolume);
}
private void _HandlePartySongSelection(int songNr)
{
if ((CSongs.Category >= 0) && (songNr >= 0))
+3
View File
@@ -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" />
+1 -2
View File
@@ -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
-5
View File
@@ -81,11 +81,6 @@ namespace VocaluxeLib.Menu.SongMenu
_SongMenu.Draw();
}
public void ApplyVolume(float volumeMax)
{
_SongMenu.ApplyVolume(volumeMax);
}
public bool IsActive()
{
return _SongMenu.IsActive();
@@ -402,7 +402,6 @@ namespace VocaluxeLib.Menu.SongMenu
{
_PreviewSongStream = -1;
_Vidtex = null;
ApplyVolume(CBase.Config.GetPreviewMusicVolume());
}
public virtual void OnHide()
@@ -423,14 +422,6 @@ namespace VocaluxeLib.Menu.SongMenu
CBase.Drawing.DrawTexture(_Vidtex, new SRectF(0, 0, 1280, 720, 0));
}
public virtual void ApplyVolume(float volumeMax)
{
_MaxVolume = volumeMax;
foreach (int stream in _Streams)
CBase.Sound.SetStreamVolumeMax(stream, _MaxVolume);
}
public virtual bool IsActive()
{
return _Active;
@@ -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);
-2
View File
@@ -29,8 +29,6 @@ namespace VocaluxeLib.Menu.SongMenu
void HandleMouse(ref SMouseEvent mouseEvent, SScreenSongOptions songOptions);
void Draw();
void ApplyVolume(float volumeMax);
int GetSelectedSong();
CStatic GetSelectedSongCover();
void SetSelectedSong(int visibleSongNr);