private async void EncodingStart(object o, DialogOpenedEventArgs args) { StopPlayback(); using (cancelEncoding = new CancellationTokenSource()) { await Task.Run(() => { Bass.ChannelSetPosition(fxStream, 0); int encoder = BassEnc_Mp3.Start(fxStream, "-b 192", EncodeFlags.Default, saveFilename); long i = 0; while (i < audioLength - 1) { if (cancelEncoding.Token.IsCancellationRequested) { return; } Bass.ChannelSetPosition(fxStream, i); Bass.ChannelUpdate(fxStream, buffer_length); encodingDialog.Dispatcher?.Invoke(() => { encodingDialog.UpdateProgress((double)i / audioLength * 100.0); }); long len = Bass.ChannelSeconds2Bytes(fxStream, buffer_length / 1000.0); i += len; } BassEnc.EncodeStop(encoder); }, cancelEncoding.Token); } dialogHost.IsOpen = false; }
/// <summary> /// 切鸡鸡 /// </summary> /// <param name="dir_path"></param> /// <param name="specific_bms_file_name">可选,钦定文件夹下某个bms谱面文件,如果不钦定就随机选取一个</param> /// <param name="start_time">起始时间,单位毫秒或者百分比,默认最初</param> /// <param name="end_time">终止时间,单位毫秒或者百分比,默认谱面末尾</param> /// <param name="encoder_command_line">编码命令</param> /// <param name="save_file_name">保存的文件名</param> public static bool GeneratePreviewAudio( string dir_path, string specific_bms_file_name = null, string start_time = null, string end_time = null, string encoder_command_line = "", string save_file_name = "preview_auto_generator.ogg", int fade_out = 0, int fade_in = 0, bool check_vaild = false, bool fast_clip = false, bool no_skip = false) { var created_audio_handles = new HashSet <int>(); var sync_record = new HashSet <int>(); int mixer = 0; try { save_file_name = string.IsNullOrWhiteSpace(save_file_name) ? "preview_auto_generator.ogg" : save_file_name; if (!Directory.Exists(dir_path)) { throw new Exception($"Directory {dir_path} not found."); } var bms_file_path = string.IsNullOrWhiteSpace(specific_bms_file_name) ? Directory.EnumerateFiles(dir_path, "*.bm*", SearchOption.TopDirectoryOnly).Where(x => support_bms_format.Any(y => x.EndsWith(y, StringComparison.InvariantCultureIgnoreCase))).FirstOrDefault() : Path.Combine(dir_path, specific_bms_file_name); if (!File.Exists(bms_file_path)) { throw new Exception($"BMS file {bms_file_path} not found."); } Console.WriteLine($"BMS file path:{bms_file_path}"); var content = File.ReadAllText(bms_file_path); if (((check_vaild && CheckBeforeFileVaild(dir_path, save_file_name)) || CheckSkipable(dir_path, content)) && !no_skip) { Console.WriteLine("This bms contains preview audio file, skiped."); return(true); } var chart = bms_file_path.EndsWith(".bmson", StringComparison.InvariantCultureIgnoreCase) ? new BmsonChart(content) as Chart : new BMSChart(content); chart.Parse(ParseType.Header); chart.Parse(ParseType.Resources); chart.Parse(ParseType.Content); var audio_map = chart.IterateResourceData(ResourceType.wav) .Select(x => (x.resourceId, Directory.EnumerateFiles(dir_path, $"{Path.GetFileNameWithoutExtension(x.dataPath)}.*").FirstOrDefault())) .Select(x => (x.resourceId, LoadAudio(x.Item2))) .ToDictionary(x => x.resourceId, x => x.Item2); var bms_evemts = chart.Events .Where(e => e.type == BMSEventType.WAV || e.type == BMSEventType.Note || e.type == BMSEventType.LongNoteEnd || e.type == BMSEventType.LongNoteStart) .OrderBy(x => x.time) .Where(x => audio_map.ContainsKey(x.data2))//filter .ToArray(); //init mixer mixer = BassMix.CreateMixerStream(44100, 2, BassFlags.Decode | BassFlags.MixerNonStop); //build triggers var mixer_events = new List <MixEventBase>(bms_evemts.Select(x => new AudioMixEvent() { Time = x.time, Duration = TimeSpan.FromSeconds(Bass.ChannelBytes2Seconds(audio_map[x.data2], Bass.ChannelGetLength(audio_map[x.data2]))), PlayOffset = TimeSpan.Zero, AudioHandle = audio_map[x.data2] })); #region Calculate and Adjust StartTime/EndTime var full_audio_duration = mixer_events.OfType <AudioMixEvent>().Max(x => x.Duration + x.Time).Add(TimeSpan.FromSeconds(1)); var actual_end_time = string.IsNullOrWhiteSpace(end_time) ? full_audio_duration : (end_time.EndsWith("%") ? TimeSpan.FromMilliseconds(float.Parse(end_time.TrimEnd('%')) / 100.0f * full_audio_duration.TotalMilliseconds) : TimeSpan.FromMilliseconds(int.Parse(end_time))); var actual_start_time = string.IsNullOrWhiteSpace(start_time) ? TimeSpan.Zero : (start_time.EndsWith("%") ? TimeSpan.FromMilliseconds(float.Parse(start_time.TrimEnd('%')) / 100.0f * full_audio_duration.TotalMilliseconds) : TimeSpan.FromMilliseconds(int.Parse(start_time))); actual_start_time = actual_start_time < TimeSpan.Zero ? TimeSpan.Zero : actual_start_time; actual_start_time = actual_start_time > full_audio_duration ? full_audio_duration : actual_start_time; actual_end_time = actual_end_time < TimeSpan.Zero ? TimeSpan.Zero : actual_end_time; actual_end_time = actual_end_time > full_audio_duration ? full_audio_duration : actual_end_time; if (actual_end_time < actual_start_time) { var t = actual_end_time; actual_end_time = actual_start_time; actual_start_time = t; } Console.WriteLine($"Actual clip({(int)full_audio_duration.TotalMilliseconds}ms):{(int)actual_start_time.TotalMilliseconds}ms ~ {(int)actual_end_time.TotalMilliseconds}ms"); #endregion if (fast_clip) { FastClipEvent(mixer_events, ref actual_start_time, ref actual_end_time); } //add special events to control encorder and mixer mixer_events.Add(new StopMixEvent { Time = actual_end_time }); mixer_events.Add(new StartMixEvent() { Time = actual_start_time }); //save encoder handle int encoder = 0; #region apply fade in/out var effect = new VolumeParameters(); var fx = Bass.ChannelSetFX(mixer, effect.FXType, 0); if (fade_in != 0) { var fade_in_evt = new FadeMixEvent(false, fade_in) { Time = actual_start_time }; mixer_events.Add(fade_in_evt); } if (fade_out != 0) { var fade_out_evt = new FadeMixEvent(true, fade_out) { Time = actual_end_time.Subtract(TimeSpan.FromMilliseconds(fade_out)) }; mixer_events.Add(fade_out_evt); } #endregion foreach (var evt in mixer_events) { var trigger_position = Bass.ChannelSeconds2Bytes(mixer, evt.Time.TotalSeconds); sync_record.Add(Bass.ChannelSetSync(mixer, SyncFlags.Position | SyncFlags.Mixtime, trigger_position, (nn, mm, ss, ll) => { if (evt is StopMixEvent && encoder != 0) { Bass.ChannelStop(mixer); BassEnc.EncodeStop(encoder); encoder = 0; } else if (evt is StartMixEvent && encoder == 0) { var output_path = Path.Combine(dir_path, save_file_name); Console.WriteLine($"Encoding output file path:{output_path}"); encoder = BassEnc_Ogg.Start(mixer, encoder_command_line, EncodeFlags.AutoFree, output_path); } else if (evt is AudioMixEvent audio) { var handle = audio.AudioHandle; BassMix.MixerRemoveChannel(handle); Bass.ChannelSetPosition(handle, Bass.ChannelSeconds2Bytes(handle, audio.PlayOffset.TotalSeconds)); BassMix.MixerAddChannel(mixer, handle, BassFlags.Default); } else if (evt is FadeMixEvent fade) { effect.fTime = fade.Duration / 1000.0f; if (fade.FadeOut) { effect.fCurrent = 1; effect.fTarget = 0; } else { effect.fCurrent = 0; effect.fTarget = 1; } Bass.FXSetParameters(fx, effect); } })); } WaitChannelDataProcessed(mixer); Console.WriteLine("Success!"); return(true); } catch (Exception ex) { Console.WriteLine($"Failed.\n{ex.Message}\n{ex.StackTrace}"); return(false); } finally { #region Clean Resource foreach (var record in sync_record) { Bass.ChannelRemoveSync(mixer, record); } foreach (var handle in created_audio_handles) { Bass.StreamFree(handle); } if (mixer != 0) { Bass.StreamFree(mixer); } #endregion } int LoadAudio(string item2) { var handle = Bass.CreateStream(item2, 0, 0, BassFlags.Decode | BassFlags.Float); created_audio_handles.Add(handle); return(handle); } }