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);
            }
        }