Skip to content

Commit ff3217d

Browse files
authored
Merge branch 'LiuYunPlayer:master' into master
2 parents f4d0ea4 + c52da1f commit ff3217d

32 files changed

+855
-208
lines changed

TuneLab/App.axaml.cs

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
using TuneLab.Utils;
1818
using TuneLab.I18N;
1919
using System.Linq;
20+
using System.Diagnostics.CodeAnalysis;
21+
using System.Threading.Tasks;
22+
using System.Threading;
23+
using System.IO.Pipes;
2024

2125
namespace TuneLab;
2226

@@ -45,23 +49,56 @@ public override void OnFrameworkInitializationCompleted()
4549

4650
// init audio engine
4751
AudioUtils.Init(new NAudioCodec());
48-
AudioEngine.Init(new SDLPlaybackHandler());
52+
AudioEngine.SampleRate.Value = Settings.SampleRate;
53+
AudioEngine.BufferSize.Value = Settings.BufferSize;
54+
if (!string.IsNullOrEmpty(Settings.AudioDriver)) AudioEngine.CurrentDriver.Value = Settings.AudioDriver;
55+
if (!string.IsNullOrEmpty(Settings.AudioDevice)) AudioEngine.CurrentDevice.Value = Settings.AudioDevice;
56+
AudioEngine.Init();
4957
AudioEngine.LoadKeySamples(Settings.PianoKeySamplesPath);
5058
AudioEngine.MasterGain = Settings.MasterGain;
5159
Settings.PianoKeySamplesPath.Modified.Subscribe(() => AudioEngine.LoadKeySamples(Settings.PianoKeySamplesPath));
5260
Settings.MasterGain.Modified.Subscribe(() => { AudioEngine.MasterGain = Settings.MasterGain; });
61+
Settings.BufferSize.Modified.Subscribe(() => { AudioEngine.BufferSize.Value = Settings.BufferSize; });
62+
Settings.SampleRate.Modified.Subscribe(() => { AudioEngine.SampleRate.Value = Settings.SampleRate; });
63+
Settings.AudioDriver.Modified.Subscribe(() => { AudioEngine.CurrentDriver.Value = Settings.AudioDriver; });
64+
Settings.AudioDevice.Modified.Subscribe(() => { AudioEngine.CurrentDevice.Value = Settings.AudioDevice; });
5365

5466
ExtensionManager.LoadExtensions();
55-
var mainWindow = new MainWindow();
56-
desktop.MainWindow = mainWindow;
67+
mMainWindow = new MainWindow();
68+
desktop.MainWindow = mMainWindow;
5769

5870
// 检测启动参数
5971
var args = Environment.GetCommandLineArgs();
60-
if (args.Length > 1)
72+
Log.Info($"Command line args:");
73+
for (int i = 1; i < args.Length; i++)
6174
{
62-
var filePath = args[1];
63-
mainWindow.Editor.OpenProjectByPath(filePath);
75+
Log.Info(args[i]);
76+
HandleArg(args[i]);
6477
}
78+
79+
// 获取主线程SynchronizationContext
80+
var context = SynchronizationContext.Current ?? throw new InvalidOperationException("SynchronizationContext.Current is null");
81+
82+
// 监听其他实例的启动参数
83+
Task.Run(() =>
84+
{
85+
while (true)
86+
{
87+
var pipeServer = new NamedPipeServerStream("TuneLab", PipeDirection.In);
88+
pipeServer.WaitForConnection();
89+
90+
using var reader = new StreamReader(pipeServer);
91+
while (pipeServer.IsConnected)
92+
{
93+
var arg = reader.ReadLine();
94+
if (arg == null)
95+
continue;
96+
97+
Log.Info($"Received from another instance: {arg}");
98+
context.Post(_ => HandleArg(arg), null);
99+
}
100+
}
101+
});
65102
}
66103
catch (Exception ex)
67104
{
@@ -92,4 +129,11 @@ public override void OnFrameworkInitializationCompleted()
92129

93130
base.OnFrameworkInitializationCompleted();
94131
}
132+
133+
public void HandleArg(string arg)
134+
{
135+
mMainWindow?.Editor.OpenProjectByPath(arg);
136+
}
137+
138+
MainWindow? mMainWindow = null;
95139
}

TuneLab/Audio/AudioEngine.cs

Lines changed: 109 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using System.Threading;
88
using System.Threading.Channels;
99
using System.Threading.Tasks;
10+
using TuneLab.Audio.SDL2;
11+
using TuneLab.Base.Event;
1012
using TuneLab.Base.Science;
1113
using TuneLab.Base.Utils;
1214
using TuneLab.Utils;
@@ -17,53 +19,78 @@ internal static class AudioEngine
1719
{
1820
public static event Action? PlayStateChanged;
1921
public static event Action? ProgressChanged;
20-
public static bool IsPlaying => mAudioProvider!.IsPlaying;
21-
public static int SamplingRate => mAudioProvider!.SamplingRate;
22-
public static double CurrentTime => mAudioProvider!.CurrentTime;
22+
public static bool IsPlaying => mAudioSampleProvider.IsPlaying;
23+
public static INotifiableProperty<int> SampleRate { get; } = new NotifiableProperty<int>(44100);
24+
public static double CurrentTime => mAudioSampleProvider.CurrentTime;
2325
public static double MasterGain { get; set; } = 0;
2426
public static double EndTime => AudioGraph.EndTime;
27+
public static INotifiableProperty<int> BufferSize { get; } = new NotifiableProperty<int>(1024);
28+
public static INotifiableProperty<string> CurrentDriver { get; } = new NotifiableProperty<string>(string.Empty);
29+
public static INotifiableProperty<string> CurrentDevice { get; } = new NotifiableProperty<string>(string.Empty);
30+
public static IReadOnlyList<string> GetAllDrivers() => mAudioPlaybackHandler!.GetAllDrivers();
31+
public static IReadOnlyList<string> GetAllDevices() => mAudioPlaybackHandler!.GetAllDevices();
2532

26-
public static void Init(IAudioPlaybackHandler playbackHandler)
33+
public static void Init()
2734
{
28-
mAudioProvider = new(44100);
29-
30-
mAudioPlaybackHandler = playbackHandler;
31-
mAudioPlaybackHandler.Init(mAudioProvider);
35+
mAudioSampleProvider.SampleRate = SampleRate.Value;
36+
mAudioPlaybackHandler = new SDLPlaybackHandler() { SampleRate = SampleRate.Value, BufferSize = BufferSize.Value, ChannelCount = 2 };
37+
mAudioPlaybackHandler.Init(mAudioSampleProvider);
38+
SetDriverToPlaybackHandler();
39+
SetDeviceToPlaybackHandler();
3240
mAudioPlaybackHandler.ProgressChanged += () => { if (IsPlaying) ProgressChanged?.Invoke(); };
41+
mAudioPlaybackHandler.CurrentDeviceChanged += () =>
42+
{
43+
CurrentDevice.Value = mAudioPlaybackHandler.CurrentDevice;
44+
mAudioPlaybackHandler.Start();
45+
};
46+
mAudioPlaybackHandler.DevicesChanged += () =>
47+
{
48+
SetDeviceToPlaybackHandler();
49+
};
3350

3451
ProgressChanged += OnProgressChanged;
52+
SampleRate.Modified.Subscribe(OnSampleRateModified);
53+
BufferSize.Modified.Subscribe(OnBufferSizeModified);
54+
CurrentDriver.Modified.Subscribe(OnCurrentDriverModified);
55+
CurrentDevice.Modified.Subscribe(OnCurrentDeviceModified);
3556

3657
mAudioPlaybackHandler.Start();
3758
}
3859

3960
public static void Destroy()
4061
{
4162
if (mAudioPlaybackHandler == null)
42-
throw new Exception("Engine is not inited!");
63+
{
64+
Log.Error("Engine is not inited!");
65+
return;
66+
}
4367

4468
mAudioPlaybackHandler.Stop();
4569

4670
ProgressChanged -= OnProgressChanged;
71+
SampleRate.Modified.Unsubscribe(OnSampleRateModified);
72+
CurrentDriver.Modified.Unsubscribe(OnCurrentDriverModified);
73+
CurrentDevice.Modified.Unsubscribe(OnCurrentDeviceModified);
4774

4875
mAudioPlaybackHandler.Destroy();
4976
mAudioPlaybackHandler = null;
5077
}
5178

5279
public static void Play()
5380
{
54-
mAudioProvider!.IsPlaying = true;
81+
mAudioSampleProvider.IsPlaying = true;
5582
PlayStateChanged?.Invoke();
5683
}
5784

5885
public static void Pause()
5986
{
60-
mAudioProvider!.IsPlaying = false;
87+
mAudioSampleProvider.IsPlaying = false;
6188
PlayStateChanged?.Invoke();
6289
}
6390

6491
public static void Seek(double time)
6592
{
66-
mAudioProvider?.Seek(time);
93+
mAudioSampleProvider.Seek(time);
6794
ProgressChanged?.Invoke();
6895
}
6996

@@ -82,19 +109,19 @@ public static void ExportTrack(string filePath, IAudioTrack track, bool isStereo
82109
double endTime = track.EndTime;
83110
endTime = Math.Max(endTime, 0);
84111
endTime += 1;
85-
int endPosition = (endTime * SamplingRate).Ceil();
112+
int endPosition = (endTime * SampleRate.Value).Ceil();
86113
float[] buffer = new float[isStereo ? endPosition * 2 : endPosition];
87114
AudioGraph.AddData(track, 0, endPosition, isStereo, buffer, 0);
88-
AudioUtils.EncodeToWav(filePath, buffer, SamplingRate, 16, isStereo ? 2 : 1);
115+
AudioUtils.EncodeToWav(filePath, buffer, SampleRate.Value, 16, isStereo ? 2 : 1);
89116
}
90117

91118
public static void ExportMaster(string filePath, bool isStereo)
92119
{
93120
var endTime = AudioGraph.EndTime;
94-
int endPosition = (endTime * SamplingRate).Ceil();
121+
int endPosition = (endTime * SampleRate.Value).Ceil();
95122
float[] buffer = new float[isStereo ? endPosition * 2 : endPosition];
96123
AudioGraph.MixData(0, endPosition, isStereo, buffer, 0);
97-
AudioUtils.EncodeToWav(filePath, buffer, SamplingRate, 16, isStereo ? 2 : 1);
124+
AudioUtils.EncodeToWav(filePath, buffer, SampleRate.Value, 16, isStereo ? 2 : 1);
98125
}
99126

100127
public static void InvokeRealtimeAmplitude(IAudioTrack track,out Tuple<double,double>? amplitude)
@@ -121,7 +148,7 @@ double Amplitude2Db(double amplitude)
121148
{
122149
int sampleWindow = 64;
123150
float[] buffer = new float[sampleWindow * 2];
124-
int position = (CurrentTime * SamplingRate).Ceil();
151+
int position = (CurrentTime * SampleRate.Value).Ceil();
125152
AudioGraph.AddData(track, position, position + sampleWindow, true, buffer, 0);
126153
for (int i = 0; i < sampleWindow * 2; i += 2) { amp[0] = (float)Math.Max(amp[0], Sample2Amplitude(buffer[i])); amp[1] = (float)Math.Max(amp[1], Sample2Amplitude(buffer[i + 1])); };
127154
}
@@ -133,6 +160,7 @@ double Amplitude2Db(double amplitude)
133160

134161
public static void LoadKeySamples(string path)
135162
{
163+
mKeySamplesPath = path;
136164
try
137165
{
138166
mKeySamples.Fill(null);
@@ -149,8 +177,8 @@ public static void LoadKeySamples(string path)
149177
{
150178
try
151179
{
152-
int samplingRate = SamplingRate;
153-
var data = AudioUtils.Decode(file, ref samplingRate);
180+
int sampleRate = SampleRate.Value;
181+
var data = AudioUtils.Decode(file, ref sampleRate);
154182
switch (data.Length)
155183
{
156184
case 1:
@@ -194,17 +222,67 @@ static void OnProgressChanged()
194222
Pause();
195223
}
196224

197-
class AudioProvider(int samplingRate) : IAudioProvider
225+
static void OnSampleRateModified()
226+
{
227+
mAudioSampleProvider.SampleRate = SampleRate.Value;
228+
if (mAudioPlaybackHandler != null)
229+
mAudioPlaybackHandler.SampleRate = SampleRate.Value;
230+
if (mKeySamplesPath != null)
231+
LoadKeySamples(mKeySamplesPath);
232+
}
233+
234+
static void OnBufferSizeModified()
235+
{
236+
if (mAudioPlaybackHandler != null)
237+
mAudioPlaybackHandler.BufferSize = BufferSize.Value;
238+
}
239+
240+
static void OnCurrentDriverModified()
241+
{
242+
SetDriverToPlaybackHandler();
243+
SetDeviceToPlaybackHandler();
244+
mAudioPlaybackHandler?.Start();
245+
}
246+
247+
static void OnCurrentDeviceModified()
198248
{
199-
public int SamplingRate => mAudioGraph.SamplingRate;
200-
public int ChannelCount => 2;
201-
public int SamplesPerChannel => int.MaxValue;
249+
SetDeviceToPlaybackHandler();
250+
mAudioPlaybackHandler?.Start();
251+
}
252+
253+
static void SetDriverToPlaybackHandler()
254+
{
255+
var drivers = mAudioPlaybackHandler!.GetAllDrivers();
256+
if (drivers.IsEmpty())
257+
return;
202258

259+
var driver = drivers.Contains(CurrentDriver.Value) ? CurrentDriver.Value : drivers[0];
260+
if (mAudioPlaybackHandler.CurrentDriver != driver)
261+
mAudioPlaybackHandler.CurrentDriver = driver;
262+
263+
CurrentDriver.Value = mAudioPlaybackHandler.CurrentDriver;
264+
}
265+
266+
static void SetDeviceToPlaybackHandler()
267+
{
268+
var devices = mAudioPlaybackHandler!.GetAllDevices();
269+
if (devices.IsEmpty())
270+
return;
271+
272+
var device = devices.Contains(CurrentDevice.Value) ? CurrentDevice.Value : devices[0];
273+
if (mAudioPlaybackHandler.CurrentDevice != device)
274+
mAudioPlaybackHandler.CurrentDevice = device;
275+
276+
CurrentDevice.Value = mAudioPlaybackHandler.CurrentDevice;
277+
}
278+
279+
class AudioSampleProvider() : IAudioSampleProvider
280+
{
203281
public AudioGraph AudioGraph => mAudioGraph;
204282
public AudioPlayer AudioPlayer => mAudioPlayer;
205-
283+
public int SampleRate { get => mAudioGraph.SampleRate; set => mAudioGraph.SampleRate = value; }
206284
public bool IsPlaying { get; set; }
207-
public double CurrentTime => (double)mGraphPosition / SamplingRate;
285+
public double CurrentTime => (double)mGraphPosition / SampleRate;
208286

209287
public void Read(float[] buffer, int offset, int count)
210288
{
@@ -239,20 +317,21 @@ public void Seek(double time)
239317
{
240318
lock (mSeekLockObject)
241319
{
242-
mGraphPosition = (int)(time * SamplingRate);
320+
mGraphPosition = (int)(time * SampleRate);
243321
}
244322
}
245323

246324
readonly AudioPlayer mAudioPlayer = new();
247-
readonly AudioGraph mAudioGraph = new(samplingRate);
325+
readonly AudioGraph mAudioGraph = new();
248326
int mGraphPosition = 0;
249327
readonly object mSeekLockObject = new();
250328
}
251329

252330
static IAudioPlaybackHandler? mAudioPlaybackHandler;
253-
static AudioProvider? mAudioProvider;
254-
static AudioGraph AudioGraph => mAudioProvider!.AudioGraph;
255-
static AudioPlayer AudioPlayer => mAudioProvider!.AudioPlayer;
331+
static AudioSampleProvider mAudioSampleProvider = new();
332+
static AudioGraph AudioGraph => mAudioSampleProvider.AudioGraph;
333+
static AudioPlayer AudioPlayer => mAudioSampleProvider.AudioPlayer;
256334

257335
static readonly IAudioData?[] mKeySamples = new IAudioData?[MusicTheory.PITCH_COUNT];
336+
static string? mKeySamplesPath = null;
258337
}

TuneLab/Audio/AudioGraph.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,26 @@
66

77
namespace TuneLab.Audio;
88

9-
internal class AudioGraph(int samplingRate = 44100)
9+
internal class AudioGraph()
1010
{
11-
public int SamplingRate => samplingRate;
11+
public int SampleRate
12+
{
13+
get => mSampleRate;
14+
set
15+
{
16+
mSampleRate = value;
17+
lock (mTrackLockObject)
18+
{
19+
foreach (var track in mTracks)
20+
{
21+
foreach (var audioSource in track.AudioSources)
22+
{
23+
audioSource.OnSampleRateChanged();
24+
}
25+
}
26+
}
27+
}
28+
}
1229

1330
public IReadOnlyCollection<IAudioTrack> Tracks => mTracks;
1431

@@ -36,7 +53,7 @@ public void AddData(IAudioTrack track, int position, int endPosition, bool isSte
3653
float rightVolume = (float)(volume * (1 + pan));
3754
foreach (var audioSource in track.AudioSources)
3855
{
39-
int audioSourceStart = (int)(audioSource.StartTime * SamplingRate);
56+
int audioSourceStart = (int)(audioSource.StartTime * SampleRate);
4057
int audioSourceEnd = audioSourceStart + audioSource.SampleCount;
4158
if (audioSourceEnd < position)
4259
continue;
@@ -106,7 +123,7 @@ public double EndTime
106123
}
107124
}
108125

109-
126+
int mSampleRate = 44100;
110127
List<IAudioTrack> mTracks = [];
111128

112129
readonly object mTrackLockObject = new();

0 commit comments

Comments
 (0)