77using System . Threading ;
88using System . Threading . Channels ;
99using System . Threading . Tasks ;
10+ using TuneLab . Audio . SDL2 ;
11+ using TuneLab . Base . Event ;
1012using TuneLab . Base . Science ;
1113using TuneLab . Base . Utils ;
1214using 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}
0 commit comments