1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-04-12 15:15:45 +02:00

Add a fallback sound notification impl if WMP is unavailable

This commit is contained in:
chylex 2017-03-30 01:55:07 +02:00
parent 8ce92df87a
commit 33d5638bb0
8 changed files with 222 additions and 112 deletions

View File

@ -18,6 +18,7 @@
using TweetDck.Core.Notification.Screenshot;
using TweetDck.Updates.Events;
using System.Diagnostics;
using TweetDck.Core.Notification.Sound;
namespace TweetDck.Core{
sealed partial class FormBrowser : Form{
@ -43,7 +44,7 @@ private static UserConfig Config{
private FormWindowState prevState;
private TweetScreenshotManager notificationScreenshotManager;
private SoundNotification soundNotification;
private ISoundNotificationPlayer soundNotification;
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
InitializeComponent();
@ -273,7 +274,7 @@ private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){
Config.Save();
}
private void soundNotification_PlaybackError(object sender, SoundNotification.PlaybackErrorEventArgs e){
private void soundNotification_PlaybackError(object sender, PlaybackErrorEventArgs e){
e.Ignore = true;
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound."+Environment.NewLine+e.Message, MessageBoxIcon.Error)){
@ -408,7 +409,7 @@ public void PlayNotificationSound(){
}
if (soundNotification == null){
soundNotification = new SoundNotification();
soundNotification = SoundNotification.New();
soundNotification.PlaybackError += soundNotification_PlaybackError;
}

View File

@ -0,0 +1,12 @@
using System;
namespace TweetDck.Core.Notification.Sound{
interface ISoundNotificationPlayer : IDisposable{
string SupportedFormats { get; }
event EventHandler<PlaybackErrorEventArgs> PlaybackError;
void Play(string file);
void Stop();
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace TweetDck.Core.Notification.Sound{
sealed class PlaybackErrorEventArgs : EventArgs{
public string Message { get; private set; }
public bool Ignore { get; set; }
public PlaybackErrorEventArgs(string message){
this.Message = message;
this.Ignore = false;
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.IO;
using System.Media;
namespace TweetDck.Core.Notification.Sound{
sealed class SoundPlayerImplFallback : ISoundNotificationPlayer{
string ISoundNotificationPlayer.SupportedFormats{
get{
return "*.wav";
}
}
public event EventHandler<PlaybackErrorEventArgs> PlaybackError;
private readonly SoundPlayer player;
private bool ignorePlaybackError;
public SoundPlayerImplFallback(){
player = new SoundPlayer{
LoadTimeout = 5000
};
}
void ISoundNotificationPlayer.Play(string file){
if (player.SoundLocation != file){
player.SoundLocation = file;
ignorePlaybackError = false;
}
try{
player.Play();
}catch(FileNotFoundException e){
OnNotificationSoundError("File not found: "+e.FileName);
}catch(InvalidOperationException){
OnNotificationSoundError("File format was not recognized.");
}catch(TimeoutException){
OnNotificationSoundError("File took too long to load.");
}
}
void ISoundNotificationPlayer.Stop(){
player.Stop();
}
void IDisposable.Dispose(){
player.Dispose();
}
private void OnNotificationSoundError(string message){
if (!ignorePlaybackError && PlaybackError != null){
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
PlaybackError(this, args);
ignorePlaybackError = args.Ignore;
}
}
}
}

View File

@ -0,0 +1,109 @@
using System;
using System.Runtime.InteropServices;
using TweetDck.Core.Utils;
using WMPLib;
namespace TweetDck.Core.Notification.Sound{
sealed class SoundPlayerImplWMP : ISoundNotificationPlayer{
string ISoundNotificationPlayer.SupportedFormats{
get{
return "*.wav;*.mp3;*.mp2;*.m4a;*.mid;*.midi;*.rmi;*.wma;*.aif;*.aifc;*.aiff;*.snd;*.au";
}
}
public event EventHandler<PlaybackErrorEventArgs> PlaybackError;
private readonly WindowsMediaPlayer player;
private bool wasTryingToPlay;
private bool ignorePlaybackError;
// changing the player volume also affects the value in the Windows mixer
// however, the initial value is always 50 or some other stupid shit
// so we have to tell the player to set its volume to whatever the mixer is set to
// using the most code required for the least functionality with a sorry excuse for an API
// thanks, Microsoft
public SoundPlayerImplWMP(){
player = new WindowsMediaPlayer();
player.settings.autoStart = false;
player.settings.enableErrorDialogs = false;
player.settings.invokeURLs = false;
player.settings.volume = (int)Math.Floor(100.0*NativeCoreAudio.GetMixerVolumeForCurrentProcess());
player.MediaChange += player_MediaChange;
player.MediaError += player_MediaError;
}
void ISoundNotificationPlayer.Play(string file){
wasTryingToPlay = true;
if (player.URL != file){
player.close();
player.URL = file;
ignorePlaybackError = false;
}
else{
player.controls.stop();
}
player.controls.play();
}
void ISoundNotificationPlayer.Stop(){
player.controls.stop();
}
void IDisposable.Dispose(){
player.close();
Marshal.ReleaseComObject(player);
}
private void player_MediaChange(object item){
IWMPMedia2 media = item as IWMPMedia2;
if (media == null){
OnNotificationSoundError("Unknown error.");
return;
}
// ReSharper disable once CompareOfFloatsByEqualityOperator
else if (media.Error == null && media.duration == 0.0){
OnNotificationSoundError("File does not contain an audio track.");
}
else if (media.Error != null){
OnNotificationSoundError(media.Error);
}
Marshal.ReleaseComObject(media);
}
private void player_MediaError(object pMediaObject){
IWMPMedia2 media = pMediaObject as IWMPMedia2;
if (media == null){
OnNotificationSoundError("Unknown error.");
return;
}
else if (media.Error != null){
OnNotificationSoundError(media.Error);
}
Marshal.ReleaseComObject(media);
}
private void OnNotificationSoundError(IWMPErrorItem error){
OnNotificationSoundError(error.errorCode == -1072885353 ? "Invalid media file." : error.errorDescription);
Marshal.ReleaseComObject(error);
}
private void OnNotificationSoundError(string message){
if (wasTryingToPlay){
wasTryingToPlay = false;
if (!ignorePlaybackError && PlaybackError != null){
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
PlaybackError(this, args);
ignorePlaybackError = args.Ignore;
}
}
}
}
}

View File

@ -1,114 +1,27 @@
using System;
using System.Runtime.InteropServices;
using TweetDck.Core.Utils;
using WMPLib;
using System.Runtime.InteropServices;
using TweetDck.Core.Notification.Sound;
namespace TweetDck.Core.Notification{
sealed class SoundNotification : IDisposable{ // TODO test on windows server
public const string SupportedFormats = "*.wav;*.mp3;*.mp2;*.m4a;*.mid;*.midi;*.rmi;*.wma;*.aif;*.aifc;*.aiff;*.snd;*.au";
static class SoundNotification{
private static bool? IsWMPAvailable;
public event EventHandler<PlaybackErrorEventArgs> PlaybackError;
private readonly WindowsMediaPlayer player;
private bool wasTryingToPlay;
private bool ignorePlaybackError;
// changing the player volume also affects the value in the Windows mixer
// however, the initial value is always 50 or some other stupid shit
// so we have to tell the player to set its volume to whatever the mixer is set to
// using the most code required for the least functionality with a sorry excuse for an API
// thanks, Microsoft
public SoundNotification(){
player = new WindowsMediaPlayer();
player.settings.autoStart = false;
player.settings.enableErrorDialogs = false;
player.settings.invokeURLs = false;
player.settings.volume = (int)Math.Floor(100.0*NativeCoreAudio.GetMixerVolumeForCurrentProcess());
player.MediaChange += player_MediaChange;
player.MediaError += player_MediaError;
}
public void Play(string file){
wasTryingToPlay = true;
if (player.URL != file){
player.close();
player.URL = file;
ignorePlaybackError = false;
}
else{
player.controls.stop();
}
player.controls.play();
}
public void Stop(){
player.controls.stop();
}
private void player_MediaChange(object item){
IWMPMedia2 media = item as IWMPMedia2;
if (media == null){
OnNotificationSoundError("Unknown error.");
return;
}
// ReSharper disable once CompareOfFloatsByEqualityOperator
else if (media.Error == null && media.duration == 0.0){
OnNotificationSoundError("File does not contain an audio track.");
}
else if (media.Error != null){
OnNotificationSoundError(media.Error);
}
Marshal.ReleaseComObject(media);
}
private void player_MediaError(object pMediaObject){
IWMPMedia2 media = pMediaObject as IWMPMedia2;
if (media == null){
OnNotificationSoundError("Unknown error.");
return;
}
else if (media.Error != null){
OnNotificationSoundError(media.Error);
}
Marshal.ReleaseComObject(media);
}
private void OnNotificationSoundError(IWMPErrorItem error){
OnNotificationSoundError(error.errorCode == -1072885353 ? "Invalid media file." : error.errorDescription);
Marshal.ReleaseComObject(error);
}
private void OnNotificationSoundError(string message){
if (wasTryingToPlay){
wasTryingToPlay = false;
if (!ignorePlaybackError && PlaybackError != null){
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
PlaybackError(this, args);
ignorePlaybackError = args.Ignore;
public static ISoundNotificationPlayer New(){
if (IsWMPAvailable.HasValue){
if (IsWMPAvailable.Value){
return new SoundPlayerImplWMP();
}
else{
return new SoundPlayerImplFallback();
}
}
}
public void Dispose(){
player.close();
Marshal.ReleaseComObject(player);
}
public class PlaybackErrorEventArgs : EventArgs{
public string Message { get; private set; }
public bool Ignore { get; set; }
public PlaybackErrorEventArgs(string message){
this.Message = message;
this.Ignore = false;
try{
SoundPlayerImplWMP implWMP = new SoundPlayerImplWMP();
IsWMPAvailable = true;
return implWMP;
}catch(COMException){
IsWMPAvailable = false;
return new SoundPlayerImplFallback();
}
}
}

View File

@ -3,15 +3,16 @@
using System.IO;
using System.Windows.Forms;
using TweetDck.Core.Notification;
using TweetDck.Core.Notification.Sound;
namespace TweetDck.Core.Other.Settings{
partial class TabSettingsSounds : BaseTabSettings{
private readonly SoundNotification soundNotification;
private readonly ISoundNotificationPlayer soundNotification;
public TabSettingsSounds(){
InitializeComponent();
soundNotification = new SoundNotification();
soundNotification = SoundNotification.New();
soundNotification.PlaybackError += sound_PlaybackError;
tbCustomSound.Text = Config.NotificationSoundPath;
@ -42,7 +43,7 @@ private void btnPlaySound_Click(object sender, EventArgs e){
soundNotification.Play(tbCustomSound.Text);
}
private void sound_PlaybackError(object sender, SoundNotification.PlaybackErrorEventArgs e){
private void sound_PlaybackError(object sender, PlaybackErrorEventArgs e){
MessageBox.Show("Could not play custom notification sound."+Environment.NewLine+e.Message, "Notification Sound Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
@ -51,7 +52,7 @@ private void btnBrowseSound_Click(object sender, EventArgs e){
AutoUpgradeEnabled = true,
DereferenceLinks = true,
Title = "Custom Notification Sound",
Filter = "Sound file ("+SoundNotification.SupportedFormats+")|"+SoundNotification.SupportedFormats+"|All files (*.*)|*.*"
Filter = "Sound file ("+soundNotification.SupportedFormats+")|"+soundNotification.SupportedFormats+"|All files (*.*)|*.*"
}){
if (dialog.ShowDialog() == DialogResult.OK){
tbCustomSound.Text = dialog.FileName;

View File

@ -123,6 +123,10 @@
<DependentUpon>FormNotificationTweet.cs</DependentUpon>
</Compile>
<Compile Include="Core\Notification\SoundNotification.cs" />
<Compile Include="Core\Notification\Sound\SoundPlayerImplFallback.cs" />
<Compile Include="Core\Notification\Sound\SoundPlayerImplWMP.cs" />
<Compile Include="Core\Notification\Sound\ISoundNotificationPlayer.cs" />
<Compile Include="Core\Notification\Sound\PlaybackErrorEventArgs.cs" />
<Compile Include="Core\Notification\TweetNotification.cs" />
<Compile Include="Core\Other\FormAbout.cs">
<SubType>Form</SubType>