mirror of https://github.com/Washi1337/Emux.git
Audio mixer window
This commit is contained in:
parent
9db0b5024d
commit
1b4df02938
|
@ -1,4 +1,6 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Navigation;
|
||||
using Emux.Gui;
|
||||
|
||||
namespace Emux
|
||||
{
|
||||
|
@ -11,7 +13,7 @@ namespace Emux
|
|||
{
|
||||
DeviceManager = new DeviceManager();
|
||||
}
|
||||
|
||||
|
||||
public new static App Current
|
||||
{
|
||||
get { return (App) Application.Current; }
|
||||
|
@ -21,5 +23,7 @@ namespace Emux
|
|||
{
|
||||
get;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,35 @@
|
|||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Emux.GameBoy.Audio;
|
||||
using NAudio.Wave;
|
||||
|
||||
namespace Emux.Audio
|
||||
{
|
||||
public class GameBoyAudioMixer : IWaveProvider
|
||||
public class GameBoyAudioMixer : IWaveProvider, INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private readonly MixingWaveProvider32 _mixer = new MixingWaveProvider32();
|
||||
private bool _enabled = true;
|
||||
private WaveFileWriter _writer;
|
||||
private bool _isRecording;
|
||||
|
||||
public GameBoyAudioMixer()
|
||||
{
|
||||
Channels = new List<NAudioChannelOutput>
|
||||
{
|
||||
new NAudioChannelOutput(this),
|
||||
new NAudioChannelOutput(this),
|
||||
new NAudioChannelOutput(this),
|
||||
new NAudioChannelOutput(this),
|
||||
new NAudioChannelOutput(this, "Square + Sweep"),
|
||||
new NAudioChannelOutput(this, "Square"),
|
||||
new NAudioChannelOutput(this, "Wave"),
|
||||
new NAudioChannelOutput(this, "Noise"),
|
||||
}.AsReadOnly();
|
||||
|
||||
foreach (var channel in Channels)
|
||||
_mixer.AddInputStream(channel);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public WaveFormat WaveFormat
|
||||
|
@ -35,20 +42,87 @@ namespace Emux.Audio
|
|||
get;
|
||||
}
|
||||
|
||||
public bool IsRecording
|
||||
{
|
||||
get { return _isRecording; }
|
||||
private set
|
||||
{
|
||||
if (_isRecording != value)
|
||||
{
|
||||
_isRecording = value;
|
||||
OnPropertyChanged(nameof(IsRecording));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get { return _enabled; }
|
||||
set
|
||||
{
|
||||
if (_enabled != value)
|
||||
{
|
||||
_enabled = value;
|
||||
OnPropertyChanged(nameof(Enabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
_mixer.Read(buffer, offset, count);
|
||||
if (!Enabled)
|
||||
Array.Clear(buffer, offset, count);
|
||||
|
||||
lock (this)
|
||||
{
|
||||
_writer?.Write(buffer, offset, count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public void Connect(GameBoySpu spu)
|
||||
{
|
||||
for (var i = 0; i < spu.Channels.Count; i++)
|
||||
spu.Channels[i].ChannelOutput = Channels[i];
|
||||
}
|
||||
|
||||
public void StartRecording(Stream outputStream)
|
||||
{
|
||||
if (outputStream == null)
|
||||
throw new ArgumentNullException(nameof(outputStream));
|
||||
|
||||
lock (this)
|
||||
{
|
||||
var channel = spu.Channels[i];
|
||||
if (channel != null) // TODO: remove null check if all channels are implemented.
|
||||
channel.ChannelOutput = Channels[i];
|
||||
if (IsRecording)
|
||||
throw new InvalidOperationException("Cannot start a recording when a recording is already happening.");
|
||||
_writer = new WaveFileWriter(outputStream, WaveFormat);
|
||||
IsRecording = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void StopRecording()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (IsRecording)
|
||||
throw new InvalidOperationException("Cannot stop a recording when a recording is not happening.");
|
||||
try
|
||||
{
|
||||
_writer.Flush();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_writer.Dispose();
|
||||
_writer = null;
|
||||
IsRecording = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnPropertyChanged(string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,47 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Emux.GameBoy.Audio;
|
||||
using NAudio.Wave;
|
||||
|
||||
namespace Emux.Audio
|
||||
{
|
||||
public class NAudioChannelOutput : BufferedWaveProvider, IAudioChannelOutput
|
||||
public class NAudioChannelOutput : BufferedWaveProvider, IAudioChannelOutput, INotifyPropertyChanged
|
||||
{
|
||||
private readonly GameBoyAudioMixer _mixer;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public NAudioChannelOutput(GameBoyAudioMixer mixer)
|
||||
private readonly GameBoyAudioMixer _mixer;
|
||||
private bool _enabled;
|
||||
|
||||
public NAudioChannelOutput(GameBoyAudioMixer mixer, string name)
|
||||
: base(mixer.WaveFormat)
|
||||
{
|
||||
if (mixer == null)
|
||||
throw new ArgumentNullException(nameof(mixer));
|
||||
_mixer = mixer;
|
||||
Name = name;
|
||||
Enabled = true;
|
||||
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get;
|
||||
set;
|
||||
get { return _enabled; }
|
||||
set
|
||||
{
|
||||
if (_enabled != value)
|
||||
{
|
||||
_enabled = value;
|
||||
OnPropertyChanged(nameof(Enabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public int SampleRate
|
||||
{
|
||||
get { return WaveFormat.SampleRate; }
|
||||
|
@ -35,5 +54,10 @@ namespace Emux.Audio
|
|||
Buffer.BlockCopy(sampleData, offset * sizeof(float), newSampleData, 0, length * sizeof(float));
|
||||
AddSamples(newSampleData, 0, newSampleData.Length);
|
||||
}
|
||||
|
||||
protected virtual void OnPropertyChanged(string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,6 +66,9 @@
|
|||
<DependentUpon>AboutDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Audio\NAudioChannelOutput.cs" />
|
||||
<Compile Include="Gui\AudioMixerWindow.xaml.cs">
|
||||
<DependentUpon>AudioMixerWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Gui\Converters\HexadecimalConverter.cs" />
|
||||
<Compile Include="Gui\Converters\InverseConverter.cs" />
|
||||
<Compile Include="Gui\RegisterItem.cs" />
|
||||
|
@ -91,6 +94,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Gui\AudioMixerWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Gui\TitledOverlay.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Emux.Gui"
|
||||
mc:Ignorable="d"
|
||||
Title="Emux" Height="440.245" Width="707.465" Closing="MainWindow_OnClosing">
|
||||
Title="Emux" Height="440.245" Width="707.465" Closing="MainWindow_OnClosing"
|
||||
x:Name="Root">
|
||||
<Window.CommandBindings>
|
||||
<CommandBinding Command="ApplicationCommands.Open" Executed="OpenCommandOnExecuted" />
|
||||
<CommandBinding Command="local:MainWindow.RunCommand" CanExecute="PausingOnCanExecute" Executed="RunCommandOnExecuted" />
|
||||
|
@ -17,7 +18,8 @@
|
|||
<CommandBinding Command="local:MainWindow.KeyPadCommand" CanExecute="GameBoyExistsCanExecute" Executed="KeyPadCommandOnExecuted" />
|
||||
<CommandBinding Command="local:MainWindow.VideoOutputCommand" CanExecute="GameBoyExistsCanExecute" Executed="VideoOutputCommandOnExecuted" />
|
||||
<CommandBinding Command="local:MainWindow.IOMemoryCommand" CanExecute="GameBoyExistsCanExecute" Executed="IOMemoryCommandOnExecuted" />
|
||||
<CommandBinding Command="local:MainWindow.EnableSoundCommand" CanExecute="GameBoyExistsCanExecute" Executed="EnableSoundCommandOnExecuted" />
|
||||
<CommandBinding Command="local:MainWindow.MixerCommand" CanExecute="GameBoyExistsCanExecute" Executed="MixerCommandOnExecuted" />
|
||||
<CommandBinding Command="local:MainWindow.EnableSoundCommand" CanExecute="GameBoyExistsCanExecute" />
|
||||
<CommandBinding Command="local:MainWindow.SourceCodeCommand" Executed="SourceCodeCommandOnExecuted" />
|
||||
<CommandBinding Command="local:MainWindow.AboutCommand" Executed="AboutCommandOnExecuted" />
|
||||
</Window.CommandBindings>
|
||||
|
@ -49,7 +51,8 @@
|
|||
<MenuItem Header="Video output" Command="local:MainWindow.VideoOutputCommand"/>
|
||||
<MenuItem Header="Virtual keypad" Command="local:MainWindow.KeyPadCommand"/>
|
||||
<MenuItem Header="IO memory view" Command="local:MainWindow.IOMemoryCommand"/>
|
||||
<MenuItem x:Name="EnableSoundsMenuItem" Header="Enable sound" IsCheckable="True" IsChecked="False" Command="local:MainWindow.EnableSoundCommand" />
|
||||
<MenuItem Header="Audio Mixer" Command="local:MainWindow.MixerCommand"/>
|
||||
<MenuItem x:Name="EnableSoundsMenuItem" Header="Enable sound" IsCheckable="True" IsChecked="{Binding ElementName=Root, Path=DeviceManager.AudioMixer.Enabled}" Command="local:MainWindow.EnableSoundCommand" />
|
||||
</MenuItem.Items>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Help">
|
||||
|
|
|
@ -75,7 +75,11 @@ namespace Emux.Gui
|
|||
public static readonly RoutedUICommand IOMemoryCommand = new RoutedUICommand(
|
||||
"Open the IO memory view",
|
||||
"IO Memory",
|
||||
typeof(MainWindow));
|
||||
typeof(MainWindow),
|
||||
new InputGestureCollection(new[]
|
||||
{
|
||||
new KeyGesture(Key.F3)
|
||||
}));
|
||||
|
||||
public static readonly RoutedUICommand KeyPadCommand = new RoutedUICommand(
|
||||
"Open the virtual keypad window",
|
||||
|
@ -86,6 +90,15 @@ namespace Emux.Gui
|
|||
new KeyGesture(Key.F12)
|
||||
}));
|
||||
|
||||
public static readonly RoutedUICommand MixerCommand = new RoutedUICommand(
|
||||
"Open the audio mixer",
|
||||
"Audio Mixer",
|
||||
typeof(MainWindow),
|
||||
new InputGestureCollection(new[]
|
||||
{
|
||||
new KeyGesture(Key.F4)
|
||||
}));
|
||||
|
||||
public static readonly RoutedUICommand EnableSoundCommand = new RoutedUICommand(
|
||||
"Enable or disable sound",
|
||||
"Enable sound",
|
||||
|
@ -105,22 +118,42 @@ namespace Emux.Gui
|
|||
new KeyGesture(Key.F1)
|
||||
}));
|
||||
|
||||
public static readonly DependencyProperty DeviceManagerProperty = DependencyProperty.Register(
|
||||
"DeviceManager", typeof(DeviceManager), typeof(MainWindow),
|
||||
new PropertyMetadata((o, e) => ((MainWindow) o).OnDeviceManagerChanged(e)));
|
||||
|
||||
private readonly VideoWindow _videoWindow;
|
||||
private readonly AudioMixerWindow _mixerWindow;
|
||||
private readonly KeypadWindow _keypadWindow;
|
||||
private readonly IOWindow _ioWindow;
|
||||
private GameBoy.GameBoy _currentDevice;
|
||||
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
_videoWindow = new VideoWindow();
|
||||
_mixerWindow = new AudioMixerWindow();
|
||||
_keypadWindow = new KeypadWindow();
|
||||
_ioWindow = new IOWindow();
|
||||
|
||||
App.Current.DeviceManager.DeviceChanged += DeviceManagerOnDeviceChanged;
|
||||
DeviceManager = App.Current.DeviceManager;
|
||||
}
|
||||
|
||||
private void DeviceManagerOnDeviceChanged(object sender, EventArgs e)
|
||||
public DeviceManager DeviceManager
|
||||
{
|
||||
get { return (DeviceManager) GetValue(DeviceManagerProperty); }
|
||||
set { SetValue(DeviceManagerProperty, value); }
|
||||
}
|
||||
|
||||
private void OnDeviceManagerChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.OldValue != null)
|
||||
((DeviceManager) e.OldValue).DeviceChanged -= OnDeviceChanged;
|
||||
if (e.NewValue != null)
|
||||
((DeviceManager) e.NewValue).DeviceChanged += OnDeviceChanged;
|
||||
}
|
||||
|
||||
private void OnDeviceChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_currentDevice != null)
|
||||
{
|
||||
|
@ -129,7 +162,7 @@ namespace Emux.Gui
|
|||
RunningOverlay.DisableOverlay();
|
||||
}
|
||||
|
||||
_currentDevice = App.Current.DeviceManager.CurrentDevice;
|
||||
_currentDevice = DeviceManager.CurrentDevice;
|
||||
_currentDevice.Cpu.Paused += GameBoyOnPaused;
|
||||
_currentDevice.Cpu.Resumed += GameBoyOnResumed;
|
||||
_currentDevice.Gpu.VideoOutput = _videoWindow;
|
||||
|
@ -137,15 +170,11 @@ namespace Emux.Gui
|
|||
_videoWindow.Device = _currentDevice;
|
||||
_keypadWindow.Device = _currentDevice;
|
||||
_ioWindow.Device = _currentDevice;
|
||||
|
||||
_mixerWindow.Mixer = DeviceManager.AudioMixer;
|
||||
|
||||
_videoWindow.Show();
|
||||
|
||||
RefreshView();
|
||||
|
||||
if (EnableSoundsMenuItem.IsChecked)
|
||||
_currentDevice.Spu.ActivateAllChannels();
|
||||
else
|
||||
_currentDevice.Spu.DeactivateAllChannels();
|
||||
}
|
||||
|
||||
public void RefreshView()
|
||||
|
@ -301,22 +330,28 @@ namespace Emux.Gui
|
|||
_videoWindow.Device = null;
|
||||
_keypadWindow.Device = null;
|
||||
_ioWindow.Device = null;
|
||||
_mixerWindow.Mixer = null;
|
||||
_videoWindow.Close();
|
||||
_keypadWindow.Close();
|
||||
_ioWindow.Close();
|
||||
_mixerWindow.Close();
|
||||
}
|
||||
|
||||
private void EnableSoundCommandOnExecuted(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
if (EnableSoundsMenuItem.IsChecked)
|
||||
_currentDevice.Spu.ActivateAllChannels();
|
||||
else
|
||||
_currentDevice.Spu.DeactivateAllChannels();
|
||||
App.Current.DeviceManager.AudioMixer.Enabled = EnableSoundsMenuItem.IsChecked;
|
||||
}
|
||||
|
||||
private void IOMemoryCommandOnExecuted(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
_ioWindow.Show();
|
||||
_ioWindow.Focus();
|
||||
}
|
||||
|
||||
private void MixerCommandOnExecuted(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
_mixerWindow.Show();
|
||||
_mixerWindow.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue