Skip to content

Commit

Permalink
feat: support presets
Browse files Browse the repository at this point in the history
  • Loading branch information
InuInu2022 committed Nov 2, 2024
1 parent 286fa7f commit c2756fc
Show file tree
Hide file tree
Showing 11 changed files with 401 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,4 @@ YMM4のボイスとして **「VoiSona Talk」(ボイソナトーク)** を使
- SonaBridge - MIT
- FlaUI - MIT
- Epoxy - Apache-2.0 license
- Material.Icons.WPF - MIT
49 changes: 49 additions & 0 deletions src/YMM4VoiSonaPlugin/View/TalkPresetsView.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<UserControl x:Class="YMM4VoiSonaPlugin.View.TalkPresetsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YMM4VoiSonaPlugin.View"
xmlns:viewmodels="clr-namespace:YMM4VoiSonaPlugin.ViewModel"
xmlns:materialIcons="clr-namespace:Material.Icons.WPF;assembly=Material.Icons.WPF"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="300">
<!--
<UserControl.DataContext>
<viewmodels:TalkPresetsViewModel />
</UserControl.DataContext>
-->
<UserControl.Resources>
<!-- DrawingImageをリソースとして定義 -->
<DrawingImage x:Key="MyIcon">
<DrawingImage.Drawing>
<GeometryDrawing
Brush="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
Geometry="M2 12C2 16.97 6.03 21 11 21C13.39 21 15.68 20.06 17.4 18.4L15.9 16.9C14.63 18.25 12.86 19 11 19C4.76 19 1.64 11.46 6.05 7.05C10.46 2.64 18 5.77 18 12H15L19 16H19.1L23 12H20C20 7.03 15.97 3 11 3C6.03 3 2 7.03 2 12Z" />
</DrawingImage.Drawing>
</DrawingImage>
</UserControl.Resources>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>

<ComboBox
x:Name="PresetCombo"
ItemsSource="{Binding Presets}"
SelectedIndex="{Binding PresetIndex}"
Grid.Row="0" Grid.Column="0"
/>
<Button x:Name="ReloadPresets"
Command="{Binding ReloadPresets}"
Grid.Row="0" Grid.Column="1"
Width="26"
Height="26"
>
<Image Source="{StaticResource MyIcon}" />
</Button>
</Grid>

</UserControl>
58 changes: 58 additions & 0 deletions src/YMM4VoiSonaPlugin/View/TalkPresetsView.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Windows;
using System.Windows.Controls;

using YMM4VoiSonaPlugin.ViewModel;

using YukkuriMovieMaker.Commons;

namespace YMM4VoiSonaPlugin.View;

/// <summary>
/// Interaction logic for TalkPresetsView.xaml
/// </summary>
public partial class TalkPresetsView : UserControl, IPropertyEditorControl2
{
public event EventHandler? BeginEdit;
public event EventHandler? EndEdit;
public ItemProperty[]? ItemProperties { get; set; }

public TalkPresetsView()
{
InitializeComponent();
DataContextChanged += TalkPresetsView_DataContextChanged;
}

public void SetEditorInfo(IEditorInfo info)
{
if (DataContext is not TalkPresetsViewModel vm) return;
vm.EditorInfo = info;
}

private void TalkPresetsView_DataContextChanged(
object sender, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is TalkPresetsView oldVm)
{
oldVm.BeginEdit -= TalkPresetsView_BeginEdit;
oldVm.EndEdit -= TalkPresetsView_EndEdit;
}
if (e.NewValue is TalkPresetsView newVm)
{
newVm.BeginEdit += TalkPresetsView_BeginEdit;
newVm.EndEdit += TalkPresetsView_EndEdit;
}
}

private void TalkPresetsView_BeginEdit(object? sender, EventArgs e)
{
BeginEdit?.Invoke(this, e);
}

private void TalkPresetsView_EndEdit(object? sender, EventArgs e)
{
//var vm = DataContext as TalkPresetsViewModel;

EndEdit?.Invoke(this, e);
}

}
38 changes: 38 additions & 0 deletions src/YMM4VoiSonaPlugin/View/VoiSonaTalkPresetDisplayAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using YukkuriMovieMaker.Commons;
using System.Windows;
using YMM4VoiSonaPlugin.ViewModel;

namespace YMM4VoiSonaPlugin.View;

[AttributeUsage(AttributeTargets.Property)]
public sealed class VoiSonaTalkPresetDisplayAttribute : PropertyEditorAttribute2
{
public override void SetBindings(FrameworkElement control, ItemProperty[] itemProperties)
{
if (control is not TalkPresetsView editor) return;

editor.ItemProperties = itemProperties;
if(itemProperties?[0].PropertyOwner is VoiSonaTalkParameter vsParam)
{
int? oldIndex = vsParam.PresetIndex;

editor.DataContext = new TalkPresetsViewModel(vsParam.Preset, oldIndex);
}
if(editor.DataContext is not TalkPresetsViewModel vm) return;
vm.ItemProperties = itemProperties;
}

public override FrameworkElement Create()
{
return new TalkPresetsView();
}

public override void ClearBindings(FrameworkElement control)
{
if (control is not TalkPresetsView editor) return;
editor.ItemProperties = null;
if(editor.DataContext is not TalkPresetsViewModel vm) return;
vm.Dispose();
editor.DataContext = null;
}
}
173 changes: 173 additions & 0 deletions src/YMM4VoiSonaPlugin/ViewModel/TalkPresetsViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Windows;

using Epoxy;

using SonaBridge;
using SonaBridge.Core.Common;

using YmmeUtil.Ymm4;

using YukkuriMovieMaker.Commons;

namespace YMM4VoiSonaPlugin.ViewModel;

[ViewModel]
public class TalkPresetsViewModel: IPropertyEditorControl, IDisposable
{
public ObservableCollection<string> Presets { get; set; } = [];
public int PresetIndex { get; set; }
public Command ReloadPresets { get; set; }

[IgnoreInject]
public IEditorInfo? EditorInfo { get; set; }
public ItemProperty[]? ItemProperties { get; set; }

public event EventHandler? BeginEdit;
public event EventHandler? EndEdit;

static readonly ITalkAutoService _service =
new TalkServiceProvider()
.GetService<ITalkAutoService>();
bool _isPresetLoad;
bool _disposedValue;

public TalkPresetsViewModel(
IList<string>? presets,
int? presetIndex = null
)
{
if(presets is not null)
{
Presets = [..presets];
}
if(presetIndex is null || Presets.Count > presetIndex)
{
PresetIndex = presetIndex ?? -1;
}
ReloadPresets = Command.Factory.Create(async ()=>{
var voice = EditorInfo?.Voice?.Speaker?.SpeakerName;
if(voice is null) return;

// (re)load presets
TaskbarUtil.StartIndeterminate();
var presets = await _service
.GetPresetsAsync(voice)
.ConfigureAwait(true);
TaskbarUtil.FinishIndeterminate();
Presets = [.. presets];
WindowUtil.FocusBack();
PresetIndex = -1;
});
}

[PropertyChanged(nameof(PresetIndex))]
[SuppressMessage("","IDE0051")]
private async ValueTask PresetIndexChangedAsync(int index)
{
if (index < 0) return;
if (Presets[index] is not string preset) return;
if (EditorInfo?.Voice?.Speaker?.SpeakerName is not string voice) return;

_isPresetLoad = true;

TaskbarUtil.StartIndeterminate();
BeginEdit?.Invoke(this, EventArgs.Empty);
await _service.SetPresetsAsync(voice, preset)
.ConfigureAwait(true);

//PresetIndex = -1; //reset combo

if(ItemProperties?[0].PropertyOwner is VoiSonaTalkParameter vsParam)
{
var globalParams = await _service.GetGlobalParamsAsync()
.ConfigureAwait(true);
var styles = await _service.GetStylesAsync(voice)
.ConfigureAwait(true);
var items = styles
.Select(s => new VoiSonaTalkStyleParameter() {
DisplayName = s.Key,
Value = s.Value,
Description = $"Style: {s.Key}",
});
vsParam.PresetIndex = index;
vsParam.Alpha = globalParams["Alpha"];
vsParam.Husky = globalParams["Hus."];
vsParam.Pitch = globalParams["Pitch"];
vsParam.Speed = globalParams["Speed"];
vsParam.Intonation = globalParams["Into."];
vsParam.Volume = globalParams["Volume"];
vsParam.ItemsCollection = [.. items];
}
EndEdit?.Invoke(this, EventArgs.Empty);
TaskbarUtil.FinishIndeterminate();

WindowUtil.FocusBack();
_isPresetLoad = false;
}

[PropertyChanged(nameof(ItemProperties))]
[SuppressMessage("","IDE0051")]
private ValueTask ItemPropertiesChangedAsync(ItemProperty[] value)
{
if(value is null or []){ return default; }

if(ItemProperties?[0].PropertyOwner is VoiSonaTalkParameter vsParam)
{
vsParam.PropertyChanged += ResetPresetSelectionEvent;
}
return default;
}

void ResetPresetSelectionEvent(object? sender, PropertyChangedEventArgs e)
{
var isTargetReset = e.PropertyName switch
{
"Speed" or "Volume" or "Pitch" or "Alpha" or "Into." or "Hus." => true,
"ItemsCollection.Value" => true,
_ => false,
};
if (!isTargetReset) return;
if (_isPresetLoad) return;

// reset combo
PresetIndex = -1;
if(ItemProperties?[0].PropertyOwner is VoiSonaTalkParameter vsParam)
{
vsParam.PresetIndex = -1;
}
}

protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if(ItemProperties?[0].PropertyOwner is VoiSonaTalkParameter vsParam)
{
vsParam.PropertyChanged -= ResetPresetSelectionEvent;
}
_service.Dispose();
}

_disposedValue = true;
}
}

// 'Dispose(bool disposing)' にアンマネージド リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします
// ~TalkPresetsViewModel()
// {
// // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します
// Dispose(disposing: false);
// }

public void Dispose()
{
// このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
1 change: 1 addition & 0 deletions src/YMM4VoiSonaPlugin/ViewModel/TalkSettingViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ async ValueTask PreloadAsync()
IsPreloading = false;
IsPreloadButtonEnabled = true;
TaskbarUtil.FinishIndeterminate();
WindowUtil.FocusBack();
}

static async Task<Process> OpenUrlAsync(string openUrl)
Expand Down
Loading

0 comments on commit c2756fc

Please sign in to comment.