@@ -18,6 +18,20 @@ struct AudioData {
18
18
sample_rate : u32 ,
19
19
}
20
20
21
+ #[ derive( Debug , Clone ) ]
22
+ enum FilteredNoteType {
23
+ Don ,
24
+ Ka ,
25
+ DrumRoll { duration : f64 } ,
26
+ Balloon { duration : f64 } ,
27
+ }
28
+
29
+ #[ derive( Debug , Clone ) ]
30
+ struct FilteredNote {
31
+ note_type : FilteredNoteType ,
32
+ timestamp : f64 ,
33
+ }
34
+
21
35
fn main ( ) {
22
36
let args: Vec < String > = env:: args ( ) . collect ( ) ;
23
37
if args. len ( ) < 5 {
@@ -251,11 +265,25 @@ fn merge_audio_files(
251
265
ka_data. sample_rate = sample_rate;
252
266
}
253
267
254
- // Create output buffer with same length as music
255
- let mut output_samples = music_data. samples . clone ( ) ;
268
+ let output_samples = merge_samples (
269
+ & music_data. samples ,
270
+ & don_data. samples ,
271
+ & ka_data. samples ,
272
+ sample_rate,
273
+ course_data,
274
+ branch,
275
+ ) ;
276
+
277
+ // Write output file using the detected sample rate
278
+ write_audio_file ( output_path, & output_samples, sample_rate) ?;
279
+
280
+ Ok ( ( ) )
281
+ }
282
+
283
+ fn filter_notes ( course_data : & tja:: Chart , branch : Option < & str > ) -> Vec < FilteredNote > {
284
+ let mut filtered_notes = Vec :: new ( ) ;
256
285
257
- // Process each segment
258
- for ( seg_idx, segment) in course_data. segments . clone ( ) . into_iter ( ) . enumerate ( ) {
286
+ for ( seg_idx, segment) in course_data. segments . iter ( ) . enumerate ( ) {
259
287
// Skip if branch doesn't match
260
288
if let Some ( branch_name) = branch {
261
289
if let Some ( segment_branch) = & segment. branch {
@@ -268,11 +296,10 @@ fn merge_audio_files(
268
296
let mut i = 0 ;
269
297
while i < segment. notes . len ( ) {
270
298
let note = & segment. notes [ i] ;
271
- let sample_pos = ( note. timestamp * sample_rate as f64 ) as usize * 2 ;
272
299
273
300
match note. note_type {
274
301
NoteType :: Roll | NoteType :: RollBig | NoteType :: Balloon | NoteType :: BalloonAlt => {
275
- // Find the corresponding EndOf note by searching through current and subsequent segments
302
+ // Find the corresponding EndOf note
276
303
let mut end_time: Option < f64 > = None ;
277
304
278
305
// Search in current segment first
@@ -286,7 +313,6 @@ fn merge_audio_files(
286
313
// If not found in current segment, search in subsequent segments
287
314
if end_time. is_none ( ) {
288
315
for future_segment in course_data. segments [ seg_idx + 1 ..] . iter ( ) {
289
- // Skip segments that don't match the branch if branch is specified
290
316
if let Some ( branch_name) = branch {
291
317
if let Some ( segment_branch) = & future_segment. branch {
292
318
if segment_branch != branch_name {
@@ -309,25 +335,19 @@ fn merge_audio_files(
309
335
310
336
if let Some ( end_time) = end_time {
311
337
let duration = end_time - note. timestamp ;
312
- let hits = ( duration * 15.0 ) as usize ;
313
- let interval = duration / hits as f64 ;
314
-
315
- for hit in 0 ..hits {
316
- let hit_time = note. timestamp + ( interval * hit as f64 ) ;
317
- let hit_pos = ( hit_time * sample_rate as f64 ) as usize * 2 ;
318
-
319
- let volume = if note. gogo { 1.2 } else { 1.0 } ;
320
- for ( j, & sample) in don_data. samples . iter ( ) . enumerate ( ) {
321
- if hit_pos + j >= output_samples. len ( ) {
322
- break ;
323
- }
324
- output_samples[ hit_pos + j] = clamp (
325
- output_samples[ hit_pos + j] + ( sample * volume) ,
326
- -1.0 ,
327
- 1.0 ,
328
- ) ;
338
+ let filtered_type = match note. note_type {
339
+ NoteType :: Roll | NoteType :: RollBig => {
340
+ FilteredNoteType :: DrumRoll { duration }
329
341
}
330
- }
342
+ NoteType :: Balloon | NoteType :: BalloonAlt => {
343
+ FilteredNoteType :: Balloon { duration }
344
+ }
345
+ _ => unreachable ! ( ) ,
346
+ } ;
347
+ filtered_notes. push ( FilteredNote {
348
+ note_type : filtered_type,
349
+ timestamp : note. timestamp ,
350
+ } ) ;
331
351
} else {
332
352
eprintln ! (
333
353
"Warning: No end marker found for roll/balloon starting at {}s" ,
@@ -336,41 +356,89 @@ fn merge_audio_files(
336
356
}
337
357
}
338
358
NoteType :: Don | NoteType :: DonBig => {
339
- let volume = if note. gogo { 1.2 } else { 1.0 } ;
340
- for ( j, & sample) in don_data. samples . iter ( ) . enumerate ( ) {
341
- if sample_pos + j >= output_samples. len ( ) {
342
- break ;
343
- }
344
- output_samples[ sample_pos + j] = clamp (
345
- output_samples[ sample_pos + j] + ( sample * volume) ,
346
- -1.0 ,
347
- 1.0 ,
348
- ) ;
349
- }
359
+ filtered_notes. push ( FilteredNote {
360
+ note_type : FilteredNoteType :: Don ,
361
+ timestamp : note. timestamp ,
362
+ } ) ;
350
363
}
351
364
NoteType :: Ka | NoteType :: KaBig => {
352
- let volume = if note. gogo { 1.2 } else { 1.0 } ;
353
- for ( j, & sample) in ka_data. samples . iter ( ) . enumerate ( ) {
354
- if sample_pos + j >= output_samples. len ( ) {
355
- break ;
356
- }
357
- output_samples[ sample_pos + j] = clamp (
358
- output_samples[ sample_pos + j] + ( sample * volume) ,
359
- -1.0 ,
360
- 1.0 ,
361
- ) ;
362
- }
365
+ filtered_notes. push ( FilteredNote {
366
+ note_type : FilteredNoteType :: Ka ,
367
+ timestamp : note. timestamp ,
368
+ } ) ;
363
369
}
364
370
_ => { }
365
371
}
366
372
i += 1 ;
367
373
}
368
374
}
369
375
370
- // Write output file using the detected sample rate
371
- write_audio_file ( output_path , & output_samples , sample_rate ) ? ;
376
+ filtered_notes
377
+ }
372
378
373
- Ok ( ( ) )
379
+ fn merge_samples (
380
+ music_samples : & [ f32 ] ,
381
+ don_samples : & [ f32 ] ,
382
+ ka_samples : & [ f32 ] ,
383
+ sample_rate : u32 ,
384
+ course_data : & tja:: Chart ,
385
+ branch : Option < & str > ,
386
+ ) -> Vec < f32 > {
387
+ let mut output_samples = music_samples. to_vec ( ) ;
388
+ let filtered_notes = filter_notes ( course_data, branch) ;
389
+
390
+ for note in filtered_notes {
391
+ let sample_pos = ( note. timestamp * sample_rate as f64 ) as usize * 2 ;
392
+
393
+ match note. note_type {
394
+ FilteredNoteType :: DrumRoll { duration } | FilteredNoteType :: Balloon { duration } => {
395
+ let hits = ( duration * 15.0 ) as usize ;
396
+ let interval = duration / hits as f64 ;
397
+
398
+ for hit in 0 ..hits {
399
+ let hit_time = note. timestamp + ( interval * hit as f64 ) ;
400
+ let hit_pos = ( hit_time * sample_rate as f64 ) as usize * 2 ;
401
+
402
+ let volume = 1.0 ;
403
+ for ( j, & sample) in don_samples. iter ( ) . enumerate ( ) {
404
+ if hit_pos + j >= output_samples. len ( ) {
405
+ break ;
406
+ }
407
+ output_samples[ hit_pos + j] =
408
+ clamp ( output_samples[ hit_pos + j] + ( sample * volume) , -1.0 , 1.0 ) ;
409
+ }
410
+ }
411
+ }
412
+ FilteredNoteType :: Don => {
413
+ let volume = 1.0 ;
414
+ for ( j, & sample) in don_samples. iter ( ) . enumerate ( ) {
415
+ if sample_pos + j >= output_samples. len ( ) {
416
+ break ;
417
+ }
418
+ output_samples[ sample_pos + j] = clamp (
419
+ output_samples[ sample_pos + j] + ( sample * volume) ,
420
+ -1.0 ,
421
+ 1.0 ,
422
+ ) ;
423
+ }
424
+ }
425
+ FilteredNoteType :: Ka => {
426
+ let volume = 1.0 ;
427
+ for ( j, & sample) in ka_samples. iter ( ) . enumerate ( ) {
428
+ if sample_pos + j >= output_samples. len ( ) {
429
+ break ;
430
+ }
431
+ output_samples[ sample_pos + j] = clamp (
432
+ output_samples[ sample_pos + j] + ( sample * volume) ,
433
+ -1.0 ,
434
+ 1.0 ,
435
+ ) ;
436
+ }
437
+ }
438
+ }
439
+ }
440
+
441
+ output_samples
374
442
}
375
443
376
444
fn write_audio_file (
0 commit comments