@@ -6,25 +6,20 @@ use std::thread::sleep;
6
6
use std:: time:: Duration ;
7
7
8
8
use crate :: subsystems:: config_manager:: ConfigCommand ;
9
- use anyhow:: { anyhow, Error , Result } ;
9
+
10
+ use anyhow:: { bail, Error , Result } ;
10
11
use glob:: glob;
12
+ use lazy_static:: lazy_static;
11
13
use log:: info;
12
- use rand:: seq:: SliceRandom ;
14
+ use rand_distr:: Distribution ;
15
+ use rand_distr:: WeightedAliasIndex ;
16
+ use regex:: Regex ;
13
17
use rodio:: { Decoder , OutputStream , OutputStreamHandle , Sink } ;
14
18
use tokio:: sync:: mpsc;
15
19
use tokio:: sync:: mpsc:: error:: TryRecvError ;
16
20
use tokio:: sync:: oneshot;
17
21
use tokio_graceful_shutdown:: { IntoSubsystem , SubsystemHandle } ;
18
22
19
- pub struct AudioPlayer {
20
- share_path : PathBuf ,
21
- cache_path : PathBuf ,
22
- bloop_paths : Vec < PathBuf > ,
23
- confirm_paths : Vec < PathBuf > ,
24
- rx : mpsc:: Receiver < PlayerCommand > ,
25
- config : mpsc:: Sender < ConfigCommand > ,
26
- }
27
-
28
23
pub type Done = oneshot:: Sender < ( ) > ;
29
24
30
25
#[ derive( Debug ) ]
@@ -147,6 +142,60 @@ impl InternalPlayer {
147
142
}
148
143
}
149
144
145
+ struct AudioCollection {
146
+ paths : Vec < PathBuf > ,
147
+ dist : WeightedAliasIndex < f64 > ,
148
+ }
149
+
150
+ impl AudioCollection {
151
+ pub fn from_dir ( path : & Path ) -> Result < AudioCollection > {
152
+ let mut paths: Vec < PathBuf > = Vec :: new ( ) ;
153
+
154
+ for entry in glob ( format ! ( "{}/*.mp3" , path. to_str( ) . unwrap( ) ) . as_str ( ) ) . unwrap ( ) {
155
+ paths. push ( entry. unwrap ( ) . as_path ( ) . try_into ( ) ?) ;
156
+ }
157
+
158
+ if paths. is_empty ( ) {
159
+ bail ! ( "Path '{:?}' contains no mp3 files" , path) ;
160
+ }
161
+
162
+ let mut weights: Vec < f64 > = Vec :: new ( ) ;
163
+
164
+ lazy_static ! {
165
+ static ref RE : Regex = Regex :: new( r"\.\[w=(\d+(?:\.\d*)?)\]\.mp3$" ) . unwrap( ) ;
166
+ }
167
+
168
+ for path in & paths {
169
+ let filename = path. file_name ( ) . unwrap ( ) ;
170
+ let cap = RE . captures ( filename. to_str ( ) . unwrap ( ) ) ;
171
+
172
+ weights. push ( if let Some ( cap) = cap {
173
+ cap[ 1 ] . parse :: < f64 > ( ) ?
174
+ } else {
175
+ 100.
176
+ } ) ;
177
+ }
178
+
179
+ Ok ( AudioCollection {
180
+ paths,
181
+ dist : WeightedAliasIndex :: new ( weights) ?,
182
+ } )
183
+ }
184
+
185
+ pub fn choose_random ( & self ) -> & PathBuf {
186
+ & self . paths [ self . dist . sample ( & mut rand:: thread_rng ( ) ) ]
187
+ }
188
+ }
189
+
190
+ pub struct AudioPlayer {
191
+ share_path : PathBuf ,
192
+ cache_path : PathBuf ,
193
+ bloop_collection : AudioCollection ,
194
+ confirm_collection : AudioCollection ,
195
+ rx : mpsc:: Receiver < PlayerCommand > ,
196
+ config : mpsc:: Sender < ConfigCommand > ,
197
+ }
198
+
150
199
impl AudioPlayer {
151
200
pub fn new (
152
201
share_path : PathBuf ,
@@ -157,8 +206,10 @@ impl AudioPlayer {
157
206
Self {
158
207
share_path : share_path. clone ( ) ,
159
208
cache_path,
160
- bloop_paths : Self :: collect_paths ( & share_path, "bloop" ) . expect ( "" ) ,
161
- confirm_paths : Self :: collect_paths ( & share_path, "confirm" ) . expect ( "" ) ,
209
+ bloop_collection : AudioCollection :: from_dir ( & share_path. join ( Path :: new ( "bloop" ) ) )
210
+ . unwrap ( ) ,
211
+ confirm_collection : AudioCollection :: from_dir ( & share_path. join ( Path :: new ( "confirm" ) ) )
212
+ . unwrap ( ) ,
162
213
rx,
163
214
config,
164
215
}
@@ -168,6 +219,8 @@ impl AudioPlayer {
168
219
let ( internal_tx, internal_rx) = mpsc:: channel ( 8 ) ;
169
220
let share_path = self . share_path . to_owned ( ) ;
170
221
222
+ info ! ( "selected: {:?}" , self . bloop_collection. choose_random( ) ) ;
223
+
171
224
thread:: spawn ( move || {
172
225
let internal_player =
173
226
InternalPlayer :: new ( internal_rx, share_path. join ( Path :: new ( "volume-change.mp3" ) ) )
@@ -194,11 +247,7 @@ impl AudioPlayer {
194
247
195
248
match play_command {
196
249
PlayBloop { done } => {
197
- let path = self
198
- . bloop_paths
199
- . choose ( & mut rand:: thread_rng ( ) )
200
- . ok_or_else ( || anyhow ! ( "No boop files available" ) ) ?
201
- . clone ( ) ;
250
+ let path = self . bloop_collection . choose_random ( ) . clone ( ) ;
202
251
internal_tx
203
252
. send ( InternalCommand :: PlayFile {
204
253
path : self . share_path . join ( path) ,
@@ -207,11 +256,7 @@ impl AudioPlayer {
207
256
. await ?;
208
257
}
209
258
PlayConfirm { done } => {
210
- let path = self
211
- . confirm_paths
212
- . choose ( & mut rand:: thread_rng ( ) )
213
- . ok_or_else ( || anyhow ! ( "No confirm files available" ) ) ?
214
- . clone ( ) ;
259
+ let path = self . confirm_collection . choose_random ( ) . clone ( ) ;
215
260
internal_tx
216
261
. send ( InternalCommand :: PlayFile {
217
262
path : self . share_path . join ( path) ,
@@ -280,18 +325,6 @@ impl AudioPlayer {
280
325
281
326
Ok ( ( ) )
282
327
}
283
-
284
- fn collect_paths ( share_path : & Path , dir_name : & str ) -> Result < Vec < PathBuf > > {
285
- let mut paths: Vec < PathBuf > = Vec :: new ( ) ;
286
-
287
- for entry in
288
- glob ( format ! ( "{}/{}/*.mp3" , share_path. to_str( ) . unwrap( ) , dir_name) . as_str ( ) ) . unwrap ( )
289
- {
290
- paths. push ( entry. unwrap ( ) . as_path ( ) . try_into ( ) ?) ;
291
- }
292
-
293
- Ok ( paths)
294
- }
295
328
}
296
329
297
330
#[ async_trait:: async_trait]
0 commit comments