diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/AppWidget.cs b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/AppWidget.cs index f2b4a13..bcec75d 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/AppWidget.cs +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/AppWidget.cs @@ -21,9 +21,12 @@ public class AppWidget : AppWidgetProvider public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - var me = new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget)).Name); - - appWidgetManager.UpdateAppWidget(me, BuildRemoteViews(context, appWidgetIds)); + //https://developer.android.com/guide/topics/appwidgets + //var me = new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget)).Name); + foreach (var appWidgetId in appWidgetIds) + { + appWidgetManager.UpdateAppWidget(appWidgetId, BuildRemoteViews(context, appWidgetIds)); + } //base.OnUpdate(context, appWidgetManager, appWidgetIds); } @@ -50,12 +53,10 @@ public override async void OnReceive(Context context, Intent intent) { base.OnReceive(context, intent); - await PlayTileService.PlayDummyAudioIfRequired(); - switch (intent.Action) { case ACTION_SELECTED: - await Manager.Tts.SpeakLeftRight(); + await PlayTileService.PlayLeftRightSound(); break; } } diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/Manager/Tts.cs b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/Manager/Tts.cs index fc72f6f..28e4363 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/Manager/Tts.cs +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/Manager/Tts.cs @@ -85,8 +85,10 @@ static async Task SpeakLeftOrRightCommon(LeftRight leftRight) private static async Task SpeakWordInEnglish(string text, TextToSpeechOptions options) { - var op = new TextToSpeechOptions(options); - op.Locale = TextToSpeechImplementation.GetLocaleFromJavaLocale(Java.Util.Locale.English); + var op = new TextToSpeechOptions(options) + { + Locale = TextToSpeechImplementation.GetLocaleFromJavaLocale(Java.Util.Locale.English) + }; await Content.SpeakAsync(new (string Text, Dependency.TextToSpeechOptions Options)[] { (text, op) }); } @@ -107,8 +109,10 @@ public static async Task SpeakLeftRight() textLeft = string.IsNullOrWhiteSpace(textLeftOr) ? textLeft : textLeftOr; textRight = string.IsNullOrWhiteSpace(textRightOr) ? textRight : textRightOr; option.Locale = TextToSpeechImplementation.GetLocaleFromJavaLocale(locale); - var optionLeft = new TextToSpeechOptions(option); - optionLeft.Pan = -option.Pan; + var optionLeft = new TextToSpeechOptions(option) + { + Pan = -option.Pan + }; if (textLeft.Equals("Left", StringComparison.InvariantCultureIgnoreCase) && (await Content.GetLocalesAsync()).Any(a => a.Language == "English")) goto English; await Content.SpeakAsync(new (string Text, Dependency.TextToSpeechOptions Options)[] { (textLeft, optionLeft), (textRight, option) }); return; @@ -117,8 +121,10 @@ public static async Task SpeakLeftRight() English:; { option.Locale = TextToSpeechImplementation.GetLocaleFromJavaLocale(Java.Util.Locale.English); - var optionLeft = new TextToSpeechOptions(option); - optionLeft.Pan = -option.Pan; + var optionLeft = new TextToSpeechOptions(option) + { + Pan = -option.Pan + }; await Content.SpeakAsync(new (string Text, Dependency.TextToSpeechOptions Options)[] { ("Left", optionLeft), ("Right", option) }); return; } diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/PlayTileService.cs b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/PlayTileService.cs index 663388b..8bb5077 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/PlayTileService.cs +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/PlayTileService.cs @@ -53,9 +53,12 @@ public static IAudioTest AudioTest public override async void OnClick() { base.OnClick(); - Storages.StatusBarManagerStorage.IsTileAdded = true; + await PlayLeftRightSound(); + } + public static async System.Threading.Tasks.Task PlayLeftRightSound() + { if (Storages.ConfigStorage.TileForceBeep.Value) { var audioTest = AudioTest; @@ -80,26 +83,6 @@ await audioTest.Register((sample, actualSampleRate, channel) => amplifications = new[] { 0, Math.Max(Math.Min(1, 1 - (t - duration * 3) / easing), 0) }; return (amplifications[channel] * Math.Sin(2.0 * Math.PI * freqR * (t - duration * 2)), false); } - - //if (sample < actualSampleRate * Math.Floor(duration * freqL * 2) / freqL / 2) - //{ - // amplifications = new[] { 1, 0 }; - // return (amplifications[channel] * Math.Sin(2.0 * Math.PI * freqL * t), false); - //} - //else if (sample < duration * actualSampleRate * 2) - //{ - // return (0, false); - //} - //else if (sample < actualSampleRate * (duration * 2 + Math.Floor(duration * freqR * 2) / freqR / 2)) - //{ - // amplifications = new[] { 0, 1 }; - // return (amplifications[channel] * Math.Sin(2.0 * Math.PI * freqR * (t - duration * 2)), false); - //} - //else - //{ - // var temp = Math.Sin(2.0 * Math.PI * freqR * (t - duration * 2)); - // return (0, false); - //} }, duration * 3 + easing, sample); await System.Threading.Tasks.Task.Run(() => { try { audioTest.Play(); } catch { } }); } diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/custom.aprof b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/custom.aprof index 23326ca..383af76 100644 Binary files a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/custom.aprof and b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight.Android/custom.aprof differ diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Licenses/nuget.csv b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Licenses/nuget.csv index 9935d95..15e4f0a 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Licenses/nuget.csv +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Licenses/nuget.csv @@ -1,12 +1,11 @@ #TYPE NuGet.PackageManagement.PowerShellCmdlets.PowerShellInstalledPackage "ProjectName","Id","Versions","AsyncLazyVersions","Version","AllVersions","LicenseUrl" -"EarphoneLeftAndRight","Octokit","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","0.51.0","False","https://licenses.nuget.org/MIT" -"EarphoneLeftAndRight","Xamarin.Essentials","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","1.7.3","False","https://aka.ms/deprecateLicenseUrl" -"EarphoneLeftAndRight","NETStandard.Library","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","2.0.3","False","https://github.com/dotnet/standard/blob/master/LICENSE.TXT" -"EarphoneLeftAndRight","CsvHelper","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","27.2.1","False","https://licenses.nuget.org/MS-PL%20OR%20Apache-2.0" -"EarphoneLeftAndRight","Xamarin.Forms","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","5.0.0.2401","False","https://licenses.nuget.org/MIT" -"EarphoneLeftAndRight.Android","Xamarin.AndroidX.Lifecycle.LiveData","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","2.4.1.1","False","https://licenses.nuget.org/MIT" -"EarphoneLeftAndRight.Android","Xamarin.Forms","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","5.0.0.2401","False","https://licenses.nuget.org/MIT" -"EarphoneLeftAndRight.Android","Xamarin.Essentials","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","1.7.3","False","https://aka.ms/deprecateLicenseUrl" -"EarphoneLeftAndRight.Android","Xamarin.Google.UserMessagingPlatform","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","2.0.0","False","https://licenses.nuget.org/MIT%20AND%20Apache-2.0" -"EarphoneLeftAndRight.Android","Xamarin.GooglePlayServices.Ads","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","120.4.0","False","https://go.microsoft.com/fwlink/?linkid=865373" +"EarphoneLeftAndRight.Android","Xamarin.AndroidX.Lifecycle.LiveData","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","2.6.1.3","False","https://licenses.nuget.org/MIT%20AND%20Apache-2.0" +"EarphoneLeftAndRight.Android","Xamarin.Forms","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","5.0.0.2612","False","https://licenses.nuget.org/MIT" +"EarphoneLeftAndRight.Android","Xamarin.Essentials","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","1.8.0","False","https://aka.ms/deprecateLicenseUrl" +"EarphoneLeftAndRight.Android","Xamarin.Google.UserMessagingPlatform","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","2.0.0.5","False","https://licenses.nuget.org/MIT%20AND%20Apache-2.0" +"EarphoneLeftAndRight.Android","Xamarin.GooglePlayServices.Ads","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","122.1.0.2","False","https://go.microsoft.com/fwlink/?linkid=865373" +"EarphoneLeftAndRight","Xamarin.Essentials","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","1.8.0","False","https://aka.ms/deprecateLicenseUrl" +"EarphoneLeftAndRight","Octokit","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","7.1.0","False","https://licenses.nuget.org/MIT" +"EarphoneLeftAndRight","CsvHelper","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","30.0.1","False","https://licenses.nuget.org/MS-PL%20OR%20Apache-2.0" +"EarphoneLeftAndRight","Xamarin.Forms","System.Linq.Enumerable+d__25`1[NuGet.Versioning.NuGetVersion]","NuGet.Versioning.NuGetVersion[]","5.0.0.2612","False","https://licenses.nuget.org/MIT" diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Licenses/readme.md b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Licenses/readme.md new file mode 100644 index 0000000..c18f80f --- /dev/null +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Licenses/readme.md @@ -0,0 +1,5 @@ +「nugetパッケージマネージャ」→「パッケージマネージャコンソール」 + +```ps +Get-Package | Export-Csv -path nuget.csv +``` diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Storages/AudioStorage.cs b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Storages/AudioStorage.cs index dc0daec..34aa9e1 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Storages/AudioStorage.cs +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Storages/AudioStorage.cs @@ -8,327 +8,327 @@ namespace EarphoneLeftAndRight.Storages; public static class AudioStorage { - private static Dependency.IAudioTest? _AudioTest; - public static Dependency.IAudioTest AudioTest { get => _AudioTest ??= DependencyService.Get(); } + private static Dependency.IAudioTest? _AudioTest; + public static Dependency.IAudioTest AudioTest { get => _AudioTest ??= DependencyService.Get(); } - public static async Task RegisterWave(double frequency, Func generator, double duration, double ampLeft, double ampRight, int sampleRate = 44100) - { - double[] amplifications = new[] { ampLeft, ampRight }; - if (frequency >= sampleRate) - { - //You can not play the audio above the sampleRate - await AudioTest.Register((sample, samplerate, channel) => - { - return (0, false); - }, duration, sampleRate); + public static async Task RegisterWave(double frequency, Func generator, double duration, double ampLeft, double ampRight, int sampleRate = 44100) + { + double[] amplifications = new[] { ampLeft, ampRight }; + if (frequency >= sampleRate) + { + //You can not play the audio above the sampleRate + await AudioTest.Register((sample, samplerate, channel) => + { + return (0, false); + }, duration, sampleRate); - } - else if (frequency >= sampleRate * 5.0 / 12.0) - { - //If frequency is between 1 and 5/12 (middle of 1/2 and 1/3) * sampleRate, we assume it's 1/2 * sampleRate. - await AudioTest.Register((sample, actualSampleRate, channel) => - { - //We have to make sure sampleRate is actually accepted. - if (frequency >= sampleRate) - { - return (0, false); - } - if (frequency >= actualSampleRate * 5.0 / 12.0 && frequency < actualSampleRate) - { - return (amplifications[channel] * (1 - ((sample + 1) % 2) * 2), false); - } - else - { - return generator(sample, actualSampleRate, channel); - } - }, duration, sampleRate); + } + else if (frequency >= sampleRate * 5.0 / 12.0) + { + //If frequency is between 1 and 5/12 (middle of 1/2 and 1/3) * sampleRate, we assume it's 1/2 * sampleRate. + await AudioTest.Register((sample, actualSampleRate, channel) => + { + //We have to make sure sampleRate is actually accepted. + if (frequency >= sampleRate) + { + return (0, false); + } + if (frequency >= actualSampleRate * 5.0 / 12.0 && frequency < actualSampleRate) + { + return (amplifications[channel] * (1 - ((sample + 1) % 2) * 2), false); + } + else + { + return generator(sample, actualSampleRate, channel); + } + }, duration, sampleRate); - } - else - { - await AudioTest.Register(generator, duration, sampleRate); - } + } + else + { + await AudioTest.Register(generator, duration, sampleRate); + } - } + } - public static bool HiDefTested = false; - public static bool HiDefSupported096kHz = false; - public static bool HiDefSupported192kHz = false; + public static bool HiDefTested = false; + public static bool HiDefSupported096kHz = false; + public static bool HiDefSupported192kHz = false; - public static async Task TestSupportHiDef() - { - if (HiDefTested) return; - try - { - await RegisterWave(200, 0.01, 0, 0, WaveKinds.Square, 96000); - HiDefSupported096kHz = AudioTest.ActualSampleRate == 96000; - await RegisterWave(200, 0.01, 0, 0, WaveKinds.Square, 192000); - HiDefSupported192kHz = AudioTest.ActualSampleRate == 192000; - HiDefTested = true; - } - catch - { - HiDefSupported096kHz = false; - HiDefSupported192kHz = false; - } - } + public static async Task TestSupportHiDef() + { + if (HiDefTested) return; + try + { + await RegisterWave(200, 0.01, 0, 0, WaveKinds.Square, 96000); + HiDefSupported096kHz = AudioTest.ActualSampleRate == 96000; + await RegisterWave(200, 0.01, 0, 0, WaveKinds.Square, 192000); + HiDefSupported192kHz = AudioTest.ActualSampleRate == 192000; + HiDefTested = true; + } + catch + { + HiDefSupported096kHz = false; + HiDefSupported192kHz = false; + } + } - public enum SweepChanneldOrder - { - Both, LeftThenRight, RightThenLeft - } + public enum SweepChanneldOrder + { + Both, LeftThenRight, RightThenLeft + } - public static async Task RegisterSweepSemitoneAsync(double freqFrom, double freqTo, bool exp, double duration, double amp = 0.5, SweepChanneldOrder channeldOrder = SweepChanneldOrder.Both, int sampleRate = 44100) - { - //var start = equal ? Helper.FreqConverters.HzToNoteNumberEqualTemperament(freqFrom) : Helper.FreqConverters.HzToOctaveJustIntonation(freqFrom).noteNumber; - //var end = equal ? Helper.FreqConverters.HzToNoteNumberEqualTemperament(freqTo) : Helper.FreqConverters.HzToOctaveJustIntonation(freqTo).noteNumber; - var start = Helper.FreqConverters.HzToNoteNumberEqualTemperament(freqFrom); - var end = Helper.FreqConverters.HzToNoteNumberEqualTemperament(freqTo); + public static async Task RegisterSweepSemitoneAsync(double freqFrom, double freqTo, bool exp, double duration, double amp = 0.5, SweepChanneldOrder channeldOrder = SweepChanneldOrder.Both, int sampleRate = 44100, bool invertRight = false) + { + //var start = equal ? Helper.FreqConverters.HzToNoteNumberEqualTemperament(freqFrom) : Helper.FreqConverters.HzToOctaveJustIntonation(freqFrom).noteNumber; + //var end = equal ? Helper.FreqConverters.HzToNoteNumberEqualTemperament(freqTo) : Helper.FreqConverters.HzToOctaveJustIntonation(freqTo).noteNumber; + var start = Helper.FreqConverters.HzToNoteNumberEqualTemperament(freqFrom); + var end = Helper.FreqConverters.HzToNoteNumberEqualTemperament(freqTo); - if (Math.Floor(start) == Math.Floor(end)) - { - await RegisterWave(Helper.FreqConverters.NoteNumberToHzEqualTemperament(Math.Floor(start)), duration, amp, amp, WaveKinds.Sine, sampleRate); - return; - } - else if (start > end) - { - start = Math.Floor(start); - end = Math.Ceiling(end); - } - else - { - start = Math.Ceiling(start); - end = Math.Floor(end); - } - int noteCounts = (int)Math.Abs(start - end) + 1; - double[] hzTable = new double[noteCounts + 1]; - for (int i = 0; i <= noteCounts; i++) - { - hzTable[i] = Helper.FreqConverters.NoteNumberToHzEqualTemperament(i * Math.Sign(end - start) + start); - } - if (exp) - { - double[] noteVirtualDuration = new double[noteCounts]; - double len = duration / noteCounts; - double currentTimeVirtual = 0; - noteVirtualDuration[0] = 0; - for (int index = 1; index < noteCounts; index++) - { - currentTimeVirtual += len * 2.0 * Math.PI * hzTable[index - 1]; - noteVirtualDuration[index] = currentTimeVirtual; - } - await AudioTest.Register((sample, actualSampleRate, channel) => - { - double tActual = (double)sample / actualSampleRate; - double t = tActual % duration; - double cnt = Math.Floor(t / len); - double nn = cnt * Math.Sign(end - start) + start; - double rem = t - len * cnt; - double ampActual = channeldOrder switch - { - SweepChanneldOrder.LeftThenRight => tActual < duration ^ channel == 1 ? amp : 0, - SweepChanneldOrder.RightThenLeft => tActual < duration ^ channel == 0 ? amp : 0, - _ or SweepChanneldOrder.Both => amp, - }; - return (ampActual * Math.Sin(Math.PI * 2.0 * rem * hzTable[(int)cnt] + noteVirtualDuration[(int)cnt]), false); - }, duration * channeldOrder switch { SweepChanneldOrder.LeftThenRight or SweepChanneldOrder.RightThenLeft => 2, _ => 1 }, sampleRate); - return; - } - else - { - double secPerHz = duration / Math.Abs(hzTable[^1] - hzTable[0]); - double[] noteDuration = new double[noteCounts + 1]; - double[] noteDurationVirtual = new double[noteCounts + 1]; - double currentTime = 0; - double currentTimeVirtual = 0; - noteDuration[0] = 0; - noteDurationVirtual[0] = 0; + if (Math.Floor(start) == Math.Floor(end)) + { + await RegisterWave(Helper.FreqConverters.NoteNumberToHzEqualTemperament(Math.Floor(start)), duration, amp, amp * (invertRight ? -1 : 1), WaveKinds.Sine, sampleRate); + return; + } + else if (start > end) + { + start = Math.Floor(start); + end = Math.Ceiling(end); + } + else + { + start = Math.Ceiling(start); + end = Math.Floor(end); + } + int noteCounts = (int)Math.Abs(start - end) + 1; + double[] hzTable = new double[noteCounts + 1]; + for (int i = 0; i <= noteCounts; i++) + { + hzTable[i] = Helper.FreqConverters.NoteNumberToHzEqualTemperament(i * Math.Sign(end - start) + start); + } + if (exp) + { + double[] noteVirtualDuration = new double[noteCounts]; + double len = duration / noteCounts; + double currentTimeVirtual = 0; + noteVirtualDuration[0] = 0; + for (int index = 1; index < noteCounts; index++) + { + currentTimeVirtual += len * 2.0 * Math.PI * hzTable[index - 1]; + noteVirtualDuration[index] = currentTimeVirtual; + } + await AudioTest.Register((sample, actualSampleRate, channel) => + { + double tActual = (double)sample / actualSampleRate; + double t = tActual % duration; + double cnt = Math.Floor(t / len); + double nn = cnt * Math.Sign(end - start) + start; + double rem = t - len * cnt; + double ampActual = channeldOrder switch + { + SweepChanneldOrder.LeftThenRight => tActual < duration ^ channel == 1 ? amp : 0, + SweepChanneldOrder.RightThenLeft => tActual < duration ^ channel == 0 ? amp : 0, + _ or SweepChanneldOrder.Both => amp, + } * (channel == 1 && invertRight ? -1 : 1); + return (ampActual * Math.Sin(Math.PI * 2.0 * rem * hzTable[(int)cnt] + noteVirtualDuration[(int)cnt]), false); + }, duration * channeldOrder switch { SweepChanneldOrder.LeftThenRight or SweepChanneldOrder.RightThenLeft => 2, _ => 1 }, sampleRate); + return; + } + else + { + double secPerHz = duration / Math.Abs(hzTable[^1] - hzTable[0]); + double[] noteDuration = new double[noteCounts + 1]; + double[] noteDurationVirtual = new double[noteCounts + 1]; + double currentTime = 0; + double currentTimeVirtual = 0; + noteDuration[0] = 0; + noteDurationVirtual[0] = 0; - for (int i = 0; i < noteCounts; i++) - { - var len = Math.Abs(hzTable[i + 1] - hzTable[i]) * secPerHz; - currentTime += len; - noteDuration[i + 1] = currentTime; - currentTimeVirtual += len * 2.0 * Math.PI * hzTable[i]; - noteDurationVirtual[i + 1] = currentTimeVirtual; - } - await AudioTest.Register((sample, actualSampleRate, channel) => - { - double tActual = (double)sample / actualSampleRate; - double t = tActual % duration; - int cnt = 0; - while (cnt < noteCounts) - { - if (noteDuration[cnt + 1] > t) break; - cnt++; - } - double rem = t - noteDuration[cnt]; - double freq = hzTable[cnt]; - double ampActual = channeldOrder switch - { - SweepChanneldOrder.LeftThenRight => tActual < duration ^ channel == 1 ? amp : 0, - SweepChanneldOrder.RightThenLeft => tActual < duration ^ channel == 0 ? amp : 0, - _ or SweepChanneldOrder.Both => amp, - }; - return (ampActual * Math.Sin(Math.PI * 2.0 * rem * hzTable[cnt] + noteDurationVirtual[cnt]), false); - }, duration * channeldOrder switch { SweepChanneldOrder.LeftThenRight or SweepChanneldOrder.RightThenLeft => 2, _ => 1 }, sampleRate); - return; - } - } + for (int i = 0; i < noteCounts; i++) + { + var len = Math.Abs(hzTable[i + 1] - hzTable[i]) * secPerHz; + currentTime += len; + noteDuration[i + 1] = currentTime; + currentTimeVirtual += len * 2.0 * Math.PI * hzTable[i]; + noteDurationVirtual[i + 1] = currentTimeVirtual; + } + await AudioTest.Register((sample, actualSampleRate, channel) => + { + double tActual = (double)sample / actualSampleRate; + double t = tActual % duration; + int cnt = 0; + while (cnt < noteCounts) + { + if (noteDuration[cnt + 1] > t) break; + cnt++; + } + double rem = t - noteDuration[cnt]; + double freq = hzTable[cnt]; + double ampActual = channeldOrder switch + { + SweepChanneldOrder.LeftThenRight => tActual < duration ^ channel == 1 ? amp : 0, + SweepChanneldOrder.RightThenLeft => tActual < duration ^ channel == 0 ? amp : 0, + _ or SweepChanneldOrder.Both => amp, + } * (channel == 1 && invertRight ? -1 : 1); + return (ampActual * Math.Sin(Math.PI * 2.0 * rem * hzTable[cnt] + noteDurationVirtual[cnt]), false); + }, duration * channeldOrder switch { SweepChanneldOrder.LeftThenRight or SweepChanneldOrder.RightThenLeft => 2, _ => 1 }, sampleRate); + return; + } + } - public static async Task RegisterSweepAsync(double freqFrom, double freqTo, bool exp, double duration, double amp = 0.5, SweepChanneldOrder channeldOrder = SweepChanneldOrder.Both, int sampleRate = 44100) - { - switch (channeldOrder) - { - case SweepChanneldOrder.Both: - if (exp) - { - var freqFromLog = Math.Log(freqFrom); - var freqToLog = Math.Log(freqTo); - double freqDifLog = (freqToLog - freqFromLog) / duration; - await AudioTest.Register((sample, actualSampleRate, channel) => - { - double t = (double)(sample) / actualSampleRate; - //Thanks, Wolfram! - //https://www.wolframalpha.com/input?i2d=true&i=Integrate%5B2*pi*exp%5C%2840%29a%2Bbt%5C%2841%29%2C%7Bt%2C0%2Cu%7D%5D - return (amp * Math.Sin(2.0 * Math.PI * Math.Exp(freqFromLog) * (Math.Exp(freqDifLog * t) - 1) / freqDifLog), false); - }, duration, sampleRate); - } - else - { - double freqDif = (freqTo - freqFrom) / duration; - await AudioTest.Register((sample, actualSampleRate, channel) => - { - double t = (double)(sample) / actualSampleRate; - //I can handle basic integral. But just in case... - //https://www.wolframalpha.com/input?i2d=true&i=Integrate[a%2Bbt%2C{t%2C0%2Cu}] - return (amp * Math.Sin(Math.PI * 2 * (freqFrom * t + freqDif * t * t / 2.0)), false); - }, duration, sampleRate); - } - break; - case SweepChanneldOrder.LeftThenRight: - case SweepChanneldOrder.RightThenLeft: - bool leftStart = channeldOrder == SweepChanneldOrder.LeftThenRight; - if (exp) - { - var freqFromLog = Math.Log(freqFrom); - var freqToLog = Math.Log(freqTo); - double freqDifLog = (freqToLog - freqFromLog) / duration; - await AudioTest.Register((sample, actualSampleRate, channel) => - { - double t = (double)(sample) / actualSampleRate; - bool isFirst = t < duration; - t = isFirst ? t : t - duration; - return ((isFirst ^ leftStart ^ (channel == 0) ? amp : 0) * Math.Sin(2.0 * Math.PI * Math.Exp(freqFromLog) * (Math.Exp(freqDifLog * t) - 1) / freqDifLog), false); - }, duration * 2, sampleRate); - } - else - { - double freqDif = (freqTo - freqFrom) / duration; - await AudioTest.Register((sample, actualSampleRate, channel) => - { - double t = (double)(sample) / actualSampleRate; - bool isFirst = t < duration; - t = isFirst ? t : t - duration; - return ((isFirst ^ leftStart ^ (channel == 0) ? amp : 0) * Math.Sin(Math.PI * 2 * (freqFrom * t + freqDif * t * t / 2.0)), false); - }, duration * 2, sampleRate); + public static async Task RegisterSweepAsync(double freqFrom, double freqTo, bool exp, double duration, double amp = 0.5, SweepChanneldOrder channeldOrder = SweepChanneldOrder.Both, int sampleRate = 44100, bool invertRight = false) + { + switch (channeldOrder) + { + case SweepChanneldOrder.Both: + if (exp) + { + var freqFromLog = Math.Log(freqFrom); + var freqToLog = Math.Log(freqTo); + double freqDifLog = (freqToLog - freqFromLog) / duration; + await AudioTest.Register((sample, actualSampleRate, channel) => + { + double t = (double)(sample) / actualSampleRate; + //Thanks, Wolfram! + //https://www.wolframalpha.com/input?i2d=true&i=Integrate%5B2*pi*exp%5C%2840%29a%2Bbt%5C%2841%29%2C%7Bt%2C0%2Cu%7D%5D + return (amp * (channel == 1 && invertRight ? -1 : 1) * Math.Sin(2.0 * Math.PI * Math.Exp(freqFromLog) * (Math.Exp(freqDifLog * t) - 1) / freqDifLog), false); + }, duration, sampleRate); + } + else + { + double freqDif = (freqTo - freqFrom) / duration; + await AudioTest.Register((sample, actualSampleRate, channel) => + { + double t = (double)(sample) / actualSampleRate; + //I can handle basic integral. But just in case... + //https://www.wolframalpha.com/input?i2d=true&i=Integrate[a%2Bbt%2C{t%2C0%2Cu}] + return (amp * (channel == 1 && invertRight ? -1 : 1) * Math.Sin(Math.PI * 2 * (freqFrom * t + freqDif * t * t / 2.0)), false); + }, duration, sampleRate); + } + break; + case SweepChanneldOrder.LeftThenRight: + case SweepChanneldOrder.RightThenLeft: + bool leftStart = channeldOrder == SweepChanneldOrder.LeftThenRight; + if (exp) + { + var freqFromLog = Math.Log(freqFrom); + var freqToLog = Math.Log(freqTo); + double freqDifLog = (freqToLog - freqFromLog) / duration; + await AudioTest.Register((sample, actualSampleRate, channel) => + { + double t = (double)(sample) / actualSampleRate; + bool isFirst = t < duration; + t = isFirst ? t : t - duration; + return ((isFirst ^ leftStart ^ (channel == 0) ? amp : 0) * (channel == 1 && invertRight ? -1 : 1) * Math.Sin(2.0 * Math.PI * Math.Exp(freqFromLog) * (Math.Exp(freqDifLog * t) - 1) / freqDifLog), false); + }, duration * 2, sampleRate); + } + else + { + double freqDif = (freqTo - freqFrom) / duration; + await AudioTest.Register((sample, actualSampleRate, channel) => + { + double t = (double)(sample) / actualSampleRate; + bool isFirst = t < duration; + t = isFirst ? t : t - duration; + return ((isFirst ^ leftStart ^ (channel == 0) ? amp : 0) * (channel == 1 && invertRight ? -1 : 1) * Math.Sin(Math.PI * 2 * (freqFrom * t + freqDif * t * t / 2.0)), false); + }, duration * 2, sampleRate); - } - break; - } - } + } + break; + } + } - public static async Task RegisterWave(double frequency, double duration, double ampLeft, double ampRight, WaveKinds kinds = WaveKinds.Sine, int sampleRate = 44100) - { - double[] amplifications = new[] { ampLeft, ampRight }; - switch (kinds) - { - case WaveKinds.Sine: - default: - await RegisterWave(frequency, (sample, actualSampleRate, channel) => - { - double t = (double)(sample) / actualSampleRate; - return (amplifications[channel] * Math.Sin(2.0 * Math.PI * frequency * t), false); - }, duration, ampLeft, ampRight, sampleRate); - break; - case WaveKinds.Square: - await RegisterWave(frequency, (sample, actualSampleRate, channel) => - { - double p1 = (sample * frequency) % actualSampleRate; - double p2 = p1 / actualSampleRate; - return (amplifications[channel] * (p2 < 0.5 ? 1 : -1), (int)p1 == 0); - }, duration, ampLeft, ampRight, sampleRate); - break; - case WaveKinds.Sawtooth: - await RegisterWave(frequency, (sample, actualSampleRate, channel) => - { - double p1 = (sample * frequency) % actualSampleRate; - double p2 = p1 / actualSampleRate; - return (amplifications[channel] * (-1 + 2 * p2), (int)p1 == 0); - }, duration, ampLeft, ampRight, sampleRate); - break; - case WaveKinds.Ramp: - await RegisterWave(frequency, (sample, actualSampleRate, channel) => - { - double p1 = (sample * frequency) % actualSampleRate; - double p2 = p1 / actualSampleRate; + public static async Task RegisterWave(double frequency, double duration, double ampLeft, double ampRight, WaveKinds kinds = WaveKinds.Sine, int sampleRate = 44100) + { + double[] amplifications = new[] { ampLeft, ampRight }; + switch (kinds) + { + case WaveKinds.Sine: + default: + await RegisterWave(frequency, (sample, actualSampleRate, channel) => + { + double t = (double)(sample) / actualSampleRate; + return (amplifications[channel] * Math.Sin(2.0 * Math.PI * frequency * t), false); + }, duration, ampLeft, ampRight, sampleRate); + break; + case WaveKinds.Square: + await RegisterWave(frequency, (sample, actualSampleRate, channel) => + { + double p1 = (sample * frequency) % actualSampleRate; + double p2 = p1 / actualSampleRate; + return (amplifications[channel] * (p2 < 0.5 ? 1 : -1), (int)p1 == 0); + }, duration, ampLeft, ampRight, sampleRate); + break; + case WaveKinds.Sawtooth: + await RegisterWave(frequency, (sample, actualSampleRate, channel) => + { + double p1 = (sample * frequency) % actualSampleRate; + double p2 = p1 / actualSampleRate; + return (amplifications[channel] * (-1 + 2 * p2), (int)p1 == 0); + }, duration, ampLeft, ampRight, sampleRate); + break; + case WaveKinds.Ramp: + await RegisterWave(frequency, (sample, actualSampleRate, channel) => + { + double p1 = (sample * frequency) % actualSampleRate; + double p2 = p1 / actualSampleRate; - //Sample 0 must be 0 for niceCuttingFrame. - return (amplifications[channel] * (p2 switch - { - < 0.25 => -p2 * 4, - < 0.75 => -2 + p2 * 4, - _ => -p2 * 4 + 4 - }), (int)p1 == 0); - }, duration, ampLeft, ampRight, sampleRate); - break; - } - } + //Sample 0 must be 0 for niceCuttingFrame. + return (amplifications[channel] * (p2 switch + { + < 0.25 => -p2 * 4, + < 0.75 => -2 + p2 * 4, + _ => -p2 * 4 + 4 + }), (int)p1 == 0); + }, duration, ampLeft, ampRight, sampleRate); + break; + } + } - public enum ShiftDirection - { - LeftToRight, RightToLeft - } + public enum ShiftDirection + { + LeftToRight, RightToLeft + } - public enum WaveKinds - { - Sine, Square, Sawtooth, Ramp, - } + public enum WaveKinds + { + Sine, Square, Sawtooth, Ramp, + } - public static async Task RegisterSineWaveStereoShift(double frequency, double duration, ShiftDirection direction, int sampleRate = 44100) - { - if (frequency >= sampleRate) - { - //You can not play the audio above the sampleRate - await AudioTest.Register((sample, samplerate, channel) => - { - return (0, false); - }, duration, sampleRate); + public static async Task RegisterSineWaveStereoShift(double frequency, double duration, ShiftDirection direction, int sampleRate = 44100) + { + if (frequency >= sampleRate) + { + //You can not play the audio above the sampleRate + await AudioTest.Register((sample, samplerate, channel) => + { + return (0, false); + }, duration, sampleRate); - }//We omit 22.05kHz operation here. - else - { - int directionNumber = direction switch - { - ShiftDirection.LeftToRight => 1, - ShiftDirection.RightToLeft => -1, - _ => 0, - }; - await AudioTest.Register((sample, actualSampleRate, channel) => - { - double t = (double)(sample) / actualSampleRate; - double d = Math.Cos(Math.PI * t / duration) / 2; - return (channel switch - { - 0 => 0.5 + d * directionNumber, - 1 => 0.5 - d * directionNumber, - _ => 0, - } * Math.Sin(2.0 * Math.PI * frequency * t), false); - }, duration, sampleRate); - } + }//We omit 22.05kHz operation here. + else + { + int directionNumber = direction switch + { + ShiftDirection.LeftToRight => 1, + ShiftDirection.RightToLeft => -1, + _ => 0, + }; + await AudioTest.Register((sample, actualSampleRate, channel) => + { + double t = (double)(sample) / actualSampleRate; + double d = Math.Cos(Math.PI * t / duration) / 2; + return (channel switch + { + 0 => 0.5 + d * directionNumber, + 1 => 0.5 - d * directionNumber, + _ => 0, + } * Math.Sin(2.0 * Math.PI * frequency * t), false); + }, duration, sampleRate); + } - } + } } diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Storages/StatusBarManagerStorage.cs b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Storages/StatusBarManagerStorage.cs index da777e0..f690acb 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Storages/StatusBarManagerStorage.cs +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Storages/StatusBarManagerStorage.cs @@ -13,7 +13,7 @@ public static class StatusBarManagerStorage //https://learn.microsoft.com/xamarin/community-toolkit/helpers/weakeventmanagert //https://learn.microsoft.com/dotnet/api/system.windows.weakeventmanager //Or just hit F12 on Command. - static readonly WeakEventManager _weakEventManager = new WeakEventManager(); + static readonly WeakEventManager _weakEventManager = new(); public static bool IsTileAdded { diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/ViewModels/BeepSweepViewModel.cs b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/ViewModels/BeepSweepViewModel.cs index 096b850..b8b7fa6 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/ViewModels/BeepSweepViewModel.cs +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/ViewModels/BeepSweepViewModel.cs @@ -10,187 +10,190 @@ namespace EarphoneLeftAndRight.ViewModels; public class BeepSweepViewModel : BaseViewModel { - private double _Duration = 10.0; - public double Duration - { - get => _Duration; set - { - if (value < 0.1) return; - SetProperty(ref _Duration, value); - } - } - - - private double _FrequencyStart = 20.0; - public double FrequencyStart - { - get => _FrequencyStart; set - { - if (value < FrequencyMinimum) return; - if (value > FrequencyMaximum) return; - SetProperty(ref _FrequencyStart, value); - } - } - - - private double _FrequencyEnd = 20000.0; - public double FrequencyEnd - { - get => _FrequencyEnd; set - { - if (value < FrequencyMinimum) return; - if (value > FrequencyMaximum) return; - SetProperty(ref _FrequencyEnd, value); - } - } - - - private double _FrequencyMinimum = 5.0; - public double FrequencyMinimum { get => _FrequencyMinimum; set => SetProperty(ref _FrequencyMinimum, value); } - - - private double _FrequencyMaximum = 96000.0; - public double FrequencyMaximum { get => _FrequencyMaximum; set => SetProperty(ref _FrequencyMaximum, value); } - - - private bool _EachChannel = false; - public bool EachChannel { get => _EachChannel; set => SetProperty(ref _EachChannel, value); } - - - private bool _Exponential = true; - public bool Exponential { get => _Exponential; set => SetProperty(ref _Exponential, value); } - - - private bool _Semitone = false; - - public BeepSweepViewModel() - { - PlayCommand = new Command(async () => - { - await Storages.AudioStorage.TestSupportHiDef(); - if (Semitone) await Storages.AudioStorage.RegisterSweepSemitoneAsync(this.FrequencyStart, FrequencyEnd, this.Exponential, this.Duration, 0.5, EachChannel ? Storages.AudioStorage.SweepChanneldOrder.LeftThenRight : Storages.AudioStorage.SweepChanneldOrder.Both, Storages.AudioStorage.HiDefSupported192kHz ? 192000 : (Storages.AudioStorage.HiDefSupported096kHz ? 96000 : 44100)); - else await Storages.AudioStorage.RegisterSweepAsync(this.FrequencyStart, FrequencyEnd, this.Exponential, this.Duration, 0.5, EachChannel ? Storages.AudioStorage.SweepChanneldOrder.LeftThenRight : Storages.AudioStorage.SweepChanneldOrder.Both, Storages.AudioStorage.HiDefSupported192kHz ? 192000 : (Storages.AudioStorage.HiDefSupported096kHz ? 96000 : 44100)); - await Task.Run(() => Storages.AudioStorage.AudioTest.Play()); - }); - - SetFrequencyCommand = new Command((arg) => - { - var args = arg?.ToString()?.Split(':')?.Select(a => double.Parse(a, System.Globalization.CultureInfo.InvariantCulture)).ToArray(); - if (args is null || args.Length < 2) return; - if (args[0] >= 0) FrequencyStart = args[0]; - if (args[1] >= 0) FrequencyEnd = args[1]; - }); - - SetFrequencyMaxCommand = new Command(() => - { - FrequencyStart = FrequencyMinimum; - FrequencyEnd = (Storages.AudioStorage.HiDefSupported192kHz ? 192000 : (Storages.AudioStorage.HiDefSupported096kHz ? 96000 : 44100)) / 2.0; - }); - - SetFrequencyInvertCommand = new Command(() => - { - (FrequencyEnd, FrequencyStart) = (FrequencyStart, FrequencyEnd); - }); - - DurationAddCommand = new Command((arg) => - { - if (!double.TryParse(arg.ToString(), System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out double t)) return; - this.Duration += t; - }); - } - - public bool Semitone { get => _Semitone; set => SetProperty(ref _Semitone, value); } - - public ICommand PlayCommand { get; } - public ICommand SetFrequencyCommand { get; } - public ICommand SetFrequencyInvertCommand { get; } - public ICommand SetFrequencyMaxCommand { get; } - public ICommand DurationAddCommand { get; } - - public CurrentHzProvider GetCurrentHzProvider() - { - return new CurrentHzProvider() - { - Duration = Duration, - FrequencyEnd = FrequencyEnd, - FrequencyStart = FrequencyStart, - Exponential = Exponential, - EachChannel = EachChannel, - Semitone = Semitone - }; - } - - public class CurrentHzProvider - { - public double Duration { get; set; } - public double FrequencyStart { get; set; } - public double FrequencyEnd { get; set; } - public bool Exponential { get; set; } - public bool EachChannel { get; set; } - public bool Semitone { get; set; } - - public double ActualDuration => EachChannel ? Duration * 2 : Duration; - - public double SecondsToHz(double sec) - { - if ((EachChannel && sec > Duration * 2) || ((!EachChannel) && sec > Duration)) - { - return 0; - } - sec %= Duration; - if (Semitone) - { - var start = Helper.FreqConverters.HzToNoteNumberEqualTemperament(FrequencyStart); - var end = Helper.FreqConverters.HzToNoteNumberEqualTemperament(FrequencyEnd); - - if (Math.Floor(start) == Math.Floor(end)) - { - return Helper.FreqConverters.NoteNumberToHzEqualTemperament(Math.Floor(start)); - } - else if (start > end) - { - start = Math.Floor(start); - end = Math.Ceiling(end); - } - else - { - start = Math.Ceiling(start); - end = Math.Floor(end); - } - - if (Exponential) - { - return Helper.FreqConverters.NoteNumberToHzEqualTemperament(start + Math.Sign(end - start) * Math.Floor(sec / Duration * (Math.Abs(end - start) + 1))); - } - else if (start < end) - { - var hzS = Helper.FreqConverters.NoteNumberToHzEqualTemperament(start); - double hzPerSec = (Helper.FreqConverters.NoteNumberToHzEqualTemperament(end + 1) - hzS) / Duration; - return Helper.FreqConverters.NoteNumberToHzEqualTemperament(Math.Floor(Helper.FreqConverters.HzToNoteNumberEqualTemperament(hzS + hzPerSec * sec))); - } - else - { - if (sec == 0) return Helper.FreqConverters.NoteNumberToHzEqualTemperament(start); - var hzS = Helper.FreqConverters.NoteNumberToHzEqualTemperament(start + 1); - double hzPerSec = (hzS - Helper.FreqConverters.NoteNumberToHzEqualTemperament(end)) / Duration; - return Helper.FreqConverters.NoteNumberToHzEqualTemperament(Math.Floor(Helper.FreqConverters.HzToNoteNumberEqualTemperament(hzS - hzPerSec * sec))); - - } - } - else - { - if (Exponential) - { - double slog = Math.Log(FrequencyStart); - double elog = Math.Log(FrequencyEnd); - double dlog = (elog - slog) / Duration; - return Math.Pow(Math.E, slog + dlog * sec); - } - else - { - return FrequencyStart + (FrequencyEnd - FrequencyStart) * sec / Duration; - } - } - } - } + private double _Duration = 10.0; + public double Duration + { + get => _Duration; set + { + if (value < 0.1) return; + SetProperty(ref _Duration, value); + } + } + + + private double _FrequencyStart = 20.0; + public double FrequencyStart + { + get => _FrequencyStart; set + { + if (value < FrequencyMinimum) return; + if (value > FrequencyMaximum) return; + SetProperty(ref _FrequencyStart, value); + } + } + + + private double _FrequencyEnd = 20000.0; + public double FrequencyEnd + { + get => _FrequencyEnd; set + { + if (value < FrequencyMinimum) return; + if (value > FrequencyMaximum) return; + SetProperty(ref _FrequencyEnd, value); + } + } + + + private double _FrequencyMinimum = 5.0; + public double FrequencyMinimum { get => _FrequencyMinimum; set => SetProperty(ref _FrequencyMinimum, value); } + + + private double _FrequencyMaximum = 96000.0; + public double FrequencyMaximum { get => _FrequencyMaximum; set => SetProperty(ref _FrequencyMaximum, value); } + + + private bool _EachChannel = false; + public bool EachChannel { get => _EachChannel; set => SetProperty(ref _EachChannel, value); } + + + private bool _Exponential = true; + public bool Exponential { get => _Exponential; set => SetProperty(ref _Exponential, value); } + + + private bool _Semitone = false; + + public BeepSweepViewModel() + { + PlayCommand = new Command(async () => + { + await Storages.AudioStorage.TestSupportHiDef(); + if (Semitone) await Storages.AudioStorage.RegisterSweepSemitoneAsync(this.FrequencyStart, FrequencyEnd, this.Exponential, this.Duration, 0.5, EachChannel ? Storages.AudioStorage.SweepChanneldOrder.LeftThenRight : Storages.AudioStorage.SweepChanneldOrder.Both, Storages.AudioStorage.HiDefSupported192kHz ? 192000 : (Storages.AudioStorage.HiDefSupported096kHz ? 96000 : 44100), this.OppositePhase); + else await Storages.AudioStorage.RegisterSweepAsync(this.FrequencyStart, FrequencyEnd, this.Exponential, this.Duration, 0.5, EachChannel ? Storages.AudioStorage.SweepChanneldOrder.LeftThenRight : Storages.AudioStorage.SweepChanneldOrder.Both, Storages.AudioStorage.HiDefSupported192kHz ? 192000 : (Storages.AudioStorage.HiDefSupported096kHz ? 96000 : 44100), this.OppositePhase); + await Task.Run(() => Storages.AudioStorage.AudioTest.Play()); + }); + + SetFrequencyCommand = new Command((arg) => + { + var args = arg?.ToString()?.Split(':')?.Select(a => double.Parse(a, System.Globalization.CultureInfo.InvariantCulture)).ToArray(); + if (args is null || args.Length < 2) return; + if (args[0] >= 0) FrequencyStart = args[0]; + if (args[1] >= 0) FrequencyEnd = args[1]; + }); + + SetFrequencyMaxCommand = new Command(() => + { + FrequencyStart = FrequencyMinimum; + FrequencyEnd = (Storages.AudioStorage.HiDefSupported192kHz ? 192000 : (Storages.AudioStorage.HiDefSupported096kHz ? 96000 : 44100)) / 2.0; + }); + + SetFrequencyInvertCommand = new Command(() => + { + (FrequencyEnd, FrequencyStart) = (FrequencyStart, FrequencyEnd); + }); + + DurationAddCommand = new Command((arg) => + { + if (!double.TryParse(arg.ToString(), System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out double t)) return; + this.Duration += t; + }); + } + + public bool Semitone { get => _Semitone; set => SetProperty(ref _Semitone, value); } + + private bool _OppositePhase = false; + public bool OppositePhase { get => _OppositePhase; set => SetProperty(ref _OppositePhase, value); } + + public ICommand PlayCommand { get; } + public ICommand SetFrequencyCommand { get; } + public ICommand SetFrequencyInvertCommand { get; } + public ICommand SetFrequencyMaxCommand { get; } + public ICommand DurationAddCommand { get; } + + public CurrentHzProvider GetCurrentHzProvider() + { + return new CurrentHzProvider() + { + Duration = Duration, + FrequencyEnd = FrequencyEnd, + FrequencyStart = FrequencyStart, + Exponential = Exponential, + EachChannel = EachChannel, + Semitone = Semitone + }; + } + + public class CurrentHzProvider + { + public double Duration { get; set; } + public double FrequencyStart { get; set; } + public double FrequencyEnd { get; set; } + public bool Exponential { get; set; } + public bool EachChannel { get; set; } + public bool Semitone { get; set; } + + public double ActualDuration => EachChannel ? Duration * 2 : Duration; + + public double SecondsToHz(double sec) + { + if ((EachChannel && sec > Duration * 2) || ((!EachChannel) && sec > Duration)) + { + return 0; + } + sec %= Duration; + if (Semitone) + { + var start = Helper.FreqConverters.HzToNoteNumberEqualTemperament(FrequencyStart); + var end = Helper.FreqConverters.HzToNoteNumberEqualTemperament(FrequencyEnd); + + if (Math.Floor(start) == Math.Floor(end)) + { + return Helper.FreqConverters.NoteNumberToHzEqualTemperament(Math.Floor(start)); + } + else if (start > end) + { + start = Math.Floor(start); + end = Math.Ceiling(end); + } + else + { + start = Math.Ceiling(start); + end = Math.Floor(end); + } + + if (Exponential) + { + return Helper.FreqConverters.NoteNumberToHzEqualTemperament(start + Math.Sign(end - start) * Math.Floor(sec / Duration * (Math.Abs(end - start) + 1))); + } + else if (start < end) + { + var hzS = Helper.FreqConverters.NoteNumberToHzEqualTemperament(start); + double hzPerSec = (Helper.FreqConverters.NoteNumberToHzEqualTemperament(end + 1) - hzS) / Duration; + return Helper.FreqConverters.NoteNumberToHzEqualTemperament(Math.Floor(Helper.FreqConverters.HzToNoteNumberEqualTemperament(hzS + hzPerSec * sec))); + } + else + { + if (sec == 0) return Helper.FreqConverters.NoteNumberToHzEqualTemperament(start); + var hzS = Helper.FreqConverters.NoteNumberToHzEqualTemperament(start + 1); + double hzPerSec = (hzS - Helper.FreqConverters.NoteNumberToHzEqualTemperament(end)) / Duration; + return Helper.FreqConverters.NoteNumberToHzEqualTemperament(Math.Floor(Helper.FreqConverters.HzToNoteNumberEqualTemperament(hzS - hzPerSec * sec))); + + } + } + else + { + if (Exponential) + { + double slog = Math.Log(FrequencyStart); + double elog = Math.Log(FrequencyEnd); + double dlog = (elog - slog) / Duration; + return Math.Pow(Math.E, slog + dlog * sec); + } + else + { + return FrequencyStart + (FrequencyEnd - FrequencyStart) * sec / Duration; + } + } + } + } } diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/ViewModels/PlayViewModel.cs b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/ViewModels/PlayViewModel.cs index 719f06b..fe531aa 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/ViewModels/PlayViewModel.cs +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/ViewModels/PlayViewModel.cs @@ -56,23 +56,7 @@ public PlayViewModel() _StatusBarManager = DependencyService.Get(); - //{ - // var command = new Command(() => { _StatusBarManager.RequestAddTileService(); OnPropertyChanged(nameof(IsStatusBarManagerSupported)); }, () => IsStatusBarManagerSupported); - // this.PropertyChanged += (_, e) => - // { - // if (e.PropertyName == nameof(IsStatusBarManagerSupported)) command.ChangeCanExecute(); - // }; - // RequestAddTile = command; - //} RequestAddTile = new Command(_StatusBarManager.RequestAddTileService, () => IsStatusBarManagerSupported); - - //外部から参照され続けるとGCで解放されない気がするからWeakReferenceにしたけどこれ合ってる? - //Storages.StatusBarManagerStorage.IsTileAddedChangedをWeakEventManagerで制御するようにしたのでもう心配する必要はない。 - //次回コミットで削除。 - //WeakReference weak = new(() => OnPropertyChanged(nameof(IsStatusBarManagerSupported)), false); - //Storages.StatusBarManagerStorage.IsTileAddedChanged += (_, _) => { try { if (weak.TryGetTarget(out var action)) action?.Invoke(); } catch { } }; - - //Storages.StatusBarManagerStorage.IsTileAddedChanged += (_, _) => OnPropertyChanged(nameof(IsStatusBarManagerSupported)); } public async static void Speak(object a) @@ -135,7 +119,7 @@ public async static void Speak(object a) new SearchService("YouTube","https://www.youtube.com/results?search_query={0}"), new SearchService("GitHub","https://github.com/search?q={0}"), new SearchService("Amazon","https://www.amazon.com/s?k={0}&tag=kuremastereotest-22"), - new SearchService("Twitter","https://twitter.com/search?q={0}"), + new SearchService("Twitter / X","https://twitter.com/search?q={0}"), new SearchService("Google Play","https://play.google.com/store/search?q={0}"), }; @@ -153,9 +137,7 @@ public SearchService(string title, string uri) readonly Dependency.IStatusBarManager _StatusBarManager; - //IsTileAddedが機能してなさそうなので、とりあえずコメントアウト。 public bool IsStatusBarManagerSupported => _StatusBarManager.RequestAddTileServiceIsSupported && !Storages.StatusBarManagerStorage.IsTileAdded; - //public bool IsStatusBarManagerSupported => _StatusBarManager.RequestAddTileServiceIsSupported; } } diff --git a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Views/BeepTabbed.xaml b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Views/BeepTabbed.xaml index ff62f47..3c26a15 100644 --- a/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Views/BeepTabbed.xaml +++ b/Earphone/EarphoneLeftAndRight/EarphoneLeftAndRight/Views/BeepTabbed.xaml @@ -260,6 +260,8 @@