xref: /MusicPlayer2/MusicPlayer2/BassCore.cpp (revision 8959a120c2c1a8b30f887255062f37449f37dee8)
1 #include "stdafx.h"
2 #include "BassCore.h"
3 #include "Common.h"
4 #include "AudioCommon.h"
5 #include "MusicPlayer2.h"
6 #include "Player.h"
7 #include "CommonDialogMgr.h"
8 
9 CBASSMidiLibrary CBassCore::m_bass_midi_lib;
10 CBassCore::MidiLyricInfo CBassCore::m_midi_lyric;
11 BASS_MIDI_FONT CBassCore::m_sfont{};
12 CCriticalSection CBassCore::m_critical;
13 CBASSEncodeLibrary CBassCore::m_bass_encode_lib;
14 CBASSWmaLibrary CBassCore::m_bass_wma_lib;
15 CBassMixLibrary CBassCore::m_bass_mix_lib;
16 bool CBassCore::m_fading = false;
17 
18 #define CONVERTING_TEMP_FILE_NAME L"converting_5k2019u6271iyt8j"
19 
CBassCore()20 CBassCore::CBassCore()
21 {
22 }
23 
24 
~CBassCore()25 CBassCore::~CBassCore()
26 {
27 }
28 
InitCore()29 void CBassCore::InitCore()
30 {
31     CSingleLock sync(&m_critical, TRUE);
32     //获取当前的音频输出设备
33     BASS_DEVICEINFO device_info;
34     int rtn;
35     int device_index{ 1 };
36     theApp.m_output_devices.clear();
37     DeviceInfo device{};
38     device.index = -1;
39     device.name = theApp.m_str_table.LoadText(L"TXT_OPT_PLAY_DEVICE_NAME_BASS_DEFAULT");
40     theApp.m_output_devices.push_back(device);
41     while (true)
42     {
43         device = DeviceInfo{};
44         rtn = BASS_GetDeviceInfo(device_index, &device_info);
45         if (rtn == 0)
46             break;
47         device.index = device_index;
48         if (device_info.name != nullptr)
49             device.name = CCommon::StrToUnicode(string(device_info.name));
50         if (device_info.driver != nullptr)
51             device.driver = CCommon::StrToUnicode(string(device_info.driver));
52         device.flags = device_info.flags;
53         theApp.m_output_devices.push_back(device);
54         device_index++;
55     }
56 
57     theApp.m_play_setting_data.device_selected = 0;
58     for (size_t i{}; i < theApp.m_output_devices.size(); i++)
59     {
60         if (theApp.m_output_devices[i].name == theApp.m_play_setting_data.output_device)
61         {
62             theApp.m_play_setting_data.device_selected = i;
63             break;
64         }
65     }
66 
67     //初始化BASE音频库
68     BASS_Init(
69         theApp.m_output_devices[theApp.m_play_setting_data.device_selected].index,		//播放设备
70         44100,//输出采样率44100(常用值)
71         BASS_DEVICE_CPSPEAKERS,//信号,BASS_DEVICE_CPSPEAKERS 注释原文如下:
72         /* Use the Windows control panel setting to detect the number of speakers.*/
73         /* Soundcards generally have their own control panel to set the speaker config,*/
74         /* so the Windows control panel setting may not be accurate unless it matches that.*/
75         /* This flag has no effect on Vista, as the speakers are already accurately detected.*/
76         theApp.m_pMainWnd->m_hWnd,//程序窗口,0用于控制台程序
77         NULL//类标识符,0使用默认值
78     );
79 
80     //向支持的文件列表插入原生支持的文件格式
81     CAudioCommon::m_surpported_format.clear();
82     SupportedFormat format;
83     format.description = theApp.m_str_table.LoadText(L"TXT_FILE_TYPE_BASE");
84     format.extensions.insert(format.extensions.end(), theApp.m_nc_setting_data.default_file_type.begin(), theApp.m_nc_setting_data.default_file_type.end());
85     for (const auto& f : theApp.m_nc_setting_data.default_file_type)
86     {
87         format.extensions_list += L"*.";
88         format.extensions_list += f;
89         format.extensions_list += L';';
90     }
91     if (!format.extensions_list.empty())
92         format.extensions_list.pop_back();
93 
94     //format.extensions_list = L"*.mp3;*.wma;*.wav;*.flac;*.ogg;*.oga;*.m4a;*.mp4;*.cue;*.mp2;*.mp1;*.aif;*.aiff";
95     CAudioCommon::m_surpported_format.push_back(format);
96     CAudioCommon::m_all_surpported_extensions = format.extensions;
97     //载入BASS插件
98     wstring plugin_dir;
99     plugin_dir = theApp.m_local_dir + L"Plugins\\";
100     vector<wstring> plugin_files;
101     CCommon::GetFiles(plugin_dir + L"*.dll", plugin_files);		//获取Plugins目录下所有的dll文件的文件名
102     m_plugin_handles.clear();
103     for (const auto& plugin_file : plugin_files)
104     {
105         //加载插件
106         HPLUGIN handle = BASS_PluginLoad((plugin_dir + plugin_file).c_str(), 0);
107         m_plugin_handles.push_back(handle);
108         //获取插件支持的音频文件类型
109         const BASS_PLUGININFO* plugin_info = BASS_PluginGetInfo(handle);
110         if (plugin_info == nullptr)
111             continue;
112         format.file_name = plugin_file;
113         format.description = CCommon::ASCIIToUnicode(plugin_info->formats->name);	//插件支持文件类型的描述
114         format.extensions_list = CCommon::ASCIIToUnicode(plugin_info->formats->exts);	//插件支持文件扩展名列表
115         //解析扩展名列表到vector
116         format.extensions.clear();
117         size_t index = 0, last_index = 0;
118         while (true)
119         {
120             index = format.extensions_list.find(L"*.", index + 1);
121             wstring ext{ format.extensions_list.substr(last_index + 2, index - last_index - 2) };
122             if (!ext.empty() && ext.back() == L';')
123                 ext.pop_back();
124             format.extensions.push_back(ext);
125             if (!CCommon::IsItemInVector(CAudioCommon::m_all_surpported_extensions, ext))
126                 CAudioCommon::m_all_surpported_extensions.push_back(ext);
127             if (index == wstring::npos)
128                 break;
129             last_index = index;
130         }
131         CAudioCommon::m_surpported_format.push_back(format);
132 
133         //载入MIDI音色库,用于播放MIDI
134         if (format.description == L"MIDI")
135         {
136             m_bass_midi_lib.Init(plugin_dir + plugin_file);
137             m_sfont_name = theApp.m_str_table.LoadText(L"UI_TXT_SF2_NAME_NONE");
138             m_sfont.font = 0;
139             if (m_bass_midi_lib.IsSucceed())
140             {
141                 wstring sf2_path = theApp.m_play_setting_data.sf2_path;
142                 if (!CCommon::FileExist(sf2_path))		//如果设置的音色库路径不存在,则从.\Plugins\soundfont\目录下查找音色库文件
143                 {
144                     vector<wstring> sf2s;
145                     CCommon::GetFiles(plugin_dir + L"soundfont\\*.sf2", sf2s);
146                     if (!sf2s.empty())
147                         sf2_path = plugin_dir + L"soundfont\\" + sf2s[0];
148                 }
149                 if (CCommon::FileExist(sf2_path))
150                 {
151                     m_sfont.font = m_bass_midi_lib.BASS_MIDI_FontInit(sf2_path.c_str(), BASS_UNICODE);
152                     if (m_sfont.font == 0)
153                     {
154                         wstring info = theApp.m_str_table.LoadTextFormat(L"LOG_SF2_LOAD_FAILED", { sf2_path });
155                         theApp.WriteLog(info);
156                         m_sfont_name = theApp.m_str_table.LoadText(L"UI_TXT_SF2_NAME_FAILDE");
157                     }
158                     else
159                     {
160                         //获取音色库信息
161                         BASS_MIDI_FONTINFO sfount_info;
162                         m_bass_midi_lib.BASS_MIDI_FontGetInfo(m_sfont.font, &sfount_info);
163                         m_sfont_name = CCommon::StrToUnicode(sfount_info.name);
164                     }
165                     m_sfont.preset = -1;
166                     m_sfont.bank = 0;
167                 }
168             }
169         }
170     }
171 }
172 
UnInitCore()173 void CBassCore::UnInitCore()
174 {
175     CSingleLock sync(&m_critical, TRUE);
176     BASS_Stop();	//停止输出
177     BASS_Free();	//释放Bass资源
178     if (m_bass_midi_lib.IsSucceed() && m_sfont.font != 0)
179         m_bass_midi_lib.BASS_MIDI_FontFree(m_sfont.font);
180     m_bass_midi_lib.UnInit();
181     for (const auto& handle : m_plugin_handles)		//释放插件句柄
182     {
183         BASS_PluginFree(handle);
184     }
185     m_fading = false;
186 }
187 
GetHandle()188 unsigned int CBassCore::GetHandle()
189 {
190     return m_musicStream;
191 }
192 
GetAudioType()193 std::wstring CBassCore::GetAudioType()
194 {
195     return CAudioCommon::GetBASSChannelDescription(m_channel_info.ctype);
196 }
197 
MidiLyricSync(HSYNC handle,DWORD channel,DWORD data,void * user)198 void CBassCore::MidiLyricSync(HSYNC handle, DWORD channel, DWORD data, void * user)
199 {
200     if (!m_bass_midi_lib.IsSucceed())
201         return;
202     m_midi_lyric.midi_no_lyric = false;
203     BASS_MIDI_MARK mark;
204     m_bass_midi_lib.BASS_MIDI_StreamGetMark(channel, (DWORD)user, data, &mark); // get the lyric/text
205     if (mark.text[0] == '@') return; // skip info
206     if (mark.text[0] == '\\')
207     {
208         // clear display
209         m_midi_lyric.midi_lyric.clear();
210     }
211     else if (mark.text[0] == '/')
212     {
213         m_midi_lyric.midi_lyric += L"\r\n";
214         const char* text = mark.text + 1;
215         m_midi_lyric.midi_lyric += CCommon::StrToUnicode(text, CodeType::ANSI);
216     }
217     else
218     {
219         m_midi_lyric.midi_lyric += CCommon::StrToUnicode(mark.text, CodeType::ANSI);
220     }
221 }
222 
MidiEndSync(HSYNC handle,DWORD channel,DWORD data,void * user)223 void CBassCore::MidiEndSync(HSYNC handle, DWORD channel, DWORD data, void * user)
224 {
225     m_midi_lyric.midi_lyric.clear();
226 }
227 
GetMidiPosition()228 void CBassCore::GetMidiPosition()
229 {
230     CSingleLock sync(&m_critical, TRUE);
231     if (m_is_midi)
232     {
233         //获取midi音乐的进度并转换成节拍数。(其中+ (m_midi_info.ppqn / 4)的目的是修正显示的节拍不准确的问题)
234         m_midi_info.midi_position = static_cast<int>((BASS_ChannelGetPosition(m_musicStream, BASS_POS_MIDI_TICK) + (m_midi_info.ppqn / 4)) / m_midi_info.ppqn);
235     }
236 }
237 
SetFXHandle()238 void CBassCore::SetFXHandle()
239 {
240     if (m_musicStream == 0) return;
241     //if (!m_equ_enable) return;
242     //设置每个均衡器通道的句柄
243     for (int i{}; i < EQU_CH_NUM; i++)
244     {
245         m_equ_handle[i] = BASS_ChannelSetFX(m_musicStream, BASS_FX_DX8_PARAMEQ, 1);
246     }
247     //设置混响的句柄
248     m_reverb_handle = BASS_ChannelSetFX(m_musicStream, BASS_FX_DX8_REVERB, 1);
249 }
250 
RemoveFXHandle()251 void CBassCore::RemoveFXHandle()
252 {
253     if (m_musicStream == 0) return;
254     //移除每个均衡器通道的句柄
255     for (int i{}; i < EQU_CH_NUM; i++)
256     {
257         if (m_equ_handle[i] != 0)
258         {
259             BASS_ChannelRemoveFX(m_musicStream, m_equ_handle[i]);
260             m_equ_handle[i] = 0;
261         }
262     }
263     //移除混响的句柄
264     if (m_reverb_handle != 0)
265     {
266         BASS_ChannelRemoveFX(m_musicStream, m_reverb_handle);
267         m_reverb_handle = 0;
268     }
269 }
270 
GetBASSAudioInfo(HSTREAM hStream,SongInfo & song_info,int flag)271 void CBassCore::GetBASSAudioInfo(HSTREAM hStream, SongInfo & song_info, int flag)
272 {
273     //获取长度
274     if (flag&AF_LENGTH)
275         song_info.end_pos = CBassCore::GetBASSSongLength(hStream);
276     //获取比特率
277     if(flag&AF_BITRATE)
278     {
279         float bitrate{};
280         BASS_ChannelGetAttribute(hStream, BASS_ATTRIB_BITRATE, &bitrate);
281         song_info.bitrate = static_cast<int>(bitrate + 0.5f);
282     }
283     //获取采样频率、通道数、位深度
284     if (flag & AF_CHANNEL_INFO)
285     {
286         BASS_CHANNELINFO info{};
287         BASS_ChannelGetInfo(hStream, &info);
288         song_info.freq = info.freq;
289         song_info.bits = static_cast<BYTE>(info.origres);
290         song_info.channels = static_cast<BYTE>(info.chans);
291     }
292     if(flag&AF_TAG_INFO)
293     {
294         CAudioTag audio_tag(song_info, hStream);
295         audio_tag.GetAudioTag();
296         audio_tag.GetAudioRating();
297         //获取midi音乐的标题
298         if (CBassCore::m_bass_midi_lib.IsSucceed() && audio_tag.GetAudioType() == AU_MIDI)
299         {
300             BASS_MIDI_MARK mark;
301             if (CBassCore::m_bass_midi_lib.BASS_MIDI_StreamGetMark(hStream, BASS_MIDI_MARK_TRACK, 0, &mark) && !mark.track)
302             {
303                 song_info.title = CCommon::StrToUnicode(mark.text);
304             }
305         }
306     }
307 }
308 
GetChannels()309 int CBassCore::GetChannels()
310 {
311     return m_channel_info.chans;
312 }
313 
GetFReq()314 int CBassCore::GetFReq()
315 {
316     return m_channel_info.freq;
317 }
318 
GetSoundFontName()319 wstring CBassCore::GetSoundFontName()
320 {
321     return m_sfont_name;
322 }
323 
Open(const wchar_t * file_path)324 void CBassCore::Open(const wchar_t * file_path)
325 {
326     CSingleLock sync(&m_critical, TRUE);
327 
328     m_last_playing_state = PLAYING_STATE_DEFAULT_VALUE;
329     if (m_musicStream != 0)     //打开前如果音频句柄没有关闭,先将其关闭,确保同时只能打开一个音频文件
330         Close();
331 
332     m_file_path = file_path;
333     if (CCommon::IsURL(m_file_path))
334         m_musicStream = BASS_StreamCreateURL(file_path, 0, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE, NULL, NULL);
335     else
336         m_musicStream = BASS_StreamCreateFile(FALSE, /*(GetCurrentFilePath()).c_str()*/file_path, 0, 0, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE);
337     BASS_ChannelGetInfo(m_musicStream, &m_channel_info);
338     m_is_midi = (CAudioCommon::GetAudioTypeByBassChannel(m_channel_info.ctype) == AudioType::AU_MIDI);
339     if (m_bass_midi_lib.IsSucceed() && m_is_midi && m_sfont.font != 0)
340         m_bass_midi_lib.BASS_MIDI_StreamSetFonts(m_musicStream, &m_sfont, 1);
341 
342     //如果文件是MIDI音乐,则打开时获取MIDI音乐的信息
343     if (m_is_midi && m_bass_midi_lib.IsSucceed())
344     {
345         //获取MIDI音乐信息
346         BASS_ChannelGetAttribute(m_musicStream, BASS_ATTRIB_MIDI_PPQN, &m_midi_info.ppqn); // get PPQN value
347         m_midi_info.midi_length = static_cast<int>(BASS_ChannelGetLength(m_musicStream, BASS_POS_MIDI_TICK) / m_midi_info.ppqn);
348         m_midi_info.tempo = m_bass_midi_lib.BASS_MIDI_StreamGetEvent(m_musicStream, 0, MIDI_EVENT_TEMPO);
349         m_midi_info.speed = 60000000 / m_midi_info.tempo;
350         //获取MIDI音乐内嵌歌词
351         BASS_MIDI_MARK mark;
352         m_midi_lyric.midi_lyric.clear();
353         if (m_bass_midi_lib.BASS_MIDI_StreamGetMark(m_musicStream, BASS_MIDI_MARK_LYRIC, 0, &mark)) // got lyrics
354             BASS_ChannelSetSync(m_musicStream, BASS_SYNC_MIDI_MARK, BASS_MIDI_MARK_LYRIC, MidiLyricSync, (void*)BASS_MIDI_MARK_LYRIC);
355         else if (m_bass_midi_lib.BASS_MIDI_StreamGetMark(m_musicStream, BASS_MIDI_MARK_TEXT, 20, &mark)) // got text instead (over 20 of them)
356             BASS_ChannelSetSync(m_musicStream, BASS_SYNC_MIDI_MARK, BASS_MIDI_MARK_TEXT, MidiLyricSync, (void*)BASS_MIDI_MARK_TEXT);
357         BASS_ChannelSetSync(m_musicStream, BASS_SYNC_END, 0, MidiEndSync, 0);
358         m_midi_lyric.midi_no_lyric = true;
359     }
360     SetFXHandle();
361     m_musicStream = BASS_FX_TempoCreate(m_musicStream, BASS_FX_FREESOURCE);
362 }
363 
Close()364 void CBassCore::Close()
365 {
366     CSingleLock sync(&m_critical, TRUE);
367     if (m_musicStream != 0)
368     {
369         if (KillTimer(theApp.m_pMainWnd->GetSafeHwnd(), FADE_TIMER_ID))
370             BASS_ChannelStop(m_musicStream);
371         RemoveFXHandle();
372         BASS_StreamFree(m_musicStream);
373         m_musicStream = 0;
374     }
375 }
376 
Play()377 void CBassCore::Play()
378 {
379     m_playing_state = PS_PLAYING;
380     if (theApp.m_play_setting_data.fade_effect && theApp.m_play_setting_data.fade_time > 0)     //如果设置了播放时音量淡入淡出
381     {
382         KillTimer(theApp.m_pMainWnd->GetSafeHwnd(), FADE_TIMER_ID);
383         int pos = GetCurPosition();
384         pos -= (theApp.m_play_setting_data.fade_time / 2);
385         if (pos < 0)
386             pos = 0;
387         SetCurPosition(pos);
388         BASS_ChannelSetAttribute(m_musicStream, BASS_ATTRIB_VOL, 0);        //先将音量设为0
389         BASS_ChannelPlay(m_musicStream, FALSE);
390         float volume = static_cast<float>(m_volume) / 100;
391         BASS_ChannelSlideAttribute(m_musicStream, BASS_ATTRIB_VOL, volume, theApp.m_play_setting_data.fade_time);   //音量渐变到原来的音量
392     }
393     else
394     {
395         BASS_ChannelPlay(m_musicStream, FALSE);
396     }
397 }
398 
Pause()399 void CBassCore::Pause()
400 {
401     m_playing_state = PS_PAUSED;
402     if (theApp.m_play_setting_data.fade_effect && theApp.m_play_setting_data.fade_time > 0)     //如果设置了播放时音量淡入淡出
403     {
404         BASS_ChannelSlideAttribute(m_musicStream, BASS_ATTRIB_VOL, 0, theApp.m_play_setting_data.fade_time);        //音量渐变到0
405         KillTimer(theApp.m_pMainWnd->GetSafeHwnd(), FADE_TIMER_ID);
406         m_fading = true;
407         //设置一个淡出时间的定时器
408         SetTimer(theApp.m_pMainWnd->GetSafeHwnd(), FADE_TIMER_ID, theApp.m_play_setting_data.fade_time, [](HWND Arg1, UINT Arg2, UINT_PTR Arg3, DWORD Arg4)
409         {
410             KillTimer(theApp.m_pMainWnd->GetSafeHwnd(), FADE_TIMER_ID);
411             BASS_ChannelPause(CPlayer::GetInstance().GetBassHandle());     //当定时器器触发时,即音量已经渐变到0,执行暂停操作
412             m_fading = false;
413         });
414     }
415     else
416     {
417         BASS_ChannelPause(m_musicStream);
418     }
419 }
420 
Stop()421 void CBassCore::Stop()
422 {
423     m_playing_state = PS_STOPED;
424     DWORD playing_state = BASS_ChannelIsActive(m_musicStream);
425     if (theApp.m_play_setting_data.fade_effect && theApp.m_play_setting_data.fade_time > 0 && playing_state == BASS_ACTIVE_PLAYING)
426     {
427         BASS_ChannelSlideAttribute(m_musicStream, BASS_ATTRIB_VOL, 0, theApp.m_play_setting_data.fade_time);
428         KillTimer(theApp.m_pMainWnd->GetSafeHwnd(), FADE_TIMER_ID);
429         m_fading = true;
430         SetTimer(theApp.m_pMainWnd->GetSafeHwnd(), FADE_TIMER_ID, theApp.m_play_setting_data.fade_time, [](HWND Arg1, UINT Arg2, UINT_PTR Arg3, DWORD Arg4)
431         {
432             KillTimer(theApp.m_pMainWnd->GetSafeHwnd(), FADE_TIMER_ID);
433             BASS_ChannelStop(CPlayer::GetInstance().GetBassHandle());
434             BASS_ChannelSetPosition(CPlayer::GetInstance().GetBassHandle(), 0, BASS_POS_BYTE);
435             m_fading = false;
436         });
437     }
438     else
439     {
440         BASS_ChannelStop(m_musicStream);
441         SetCurPosition(0);
442     }
443 }
444 
SetVolume(int vol)445 void CBassCore::SetVolume(int vol)
446 {
447     if (!m_fading)
448     {
449         m_volume = vol;
450         float volume = static_cast<float>(vol) / 100.0f;
451         BASS_ChannelSetAttribute(m_musicStream, BASS_ATTRIB_VOL, volume);
452     }
453 }
454 
SetSpeed(float speed)455 void CBassCore::SetSpeed(float speed)
456 {
457     if (std::fabs(speed) < 0.01 || std::fabs(speed - 1) < 0.01 || speed < MIN_PLAY_SPEED || speed > MAX_PLAY_SPEED)
458         speed = 1;
459     float tempo = (speed - 1) * 100;
460     BASS_ChannelSetAttribute(m_musicStream, BASS_ATTRIB_TEMPO, tempo);
461 }
462 
SetPitch(int pitch)463 void CBassCore::SetPitch(int pitch)
464 {
465     if (pitch < MIN_PLAY_PITCH || pitch > MAX_PLAY_PITCH)
466     {
467         pitch = 0;
468     }
469     BASS_ChannelSetAttribute(m_musicStream, BASS_ATTRIB_TEMPO_PITCH, pitch);
470 }
471 
SongIsOver()472 bool CBassCore::SongIsOver()
473 {
474     DWORD state = BASS_ChannelIsActive(m_musicStream);
475     bool is_over{ (m_last_playing_state == BASS_ACTIVE_PLAYING && state == BASS_ACTIVE_STOPPED)
476         || m_error_code == BASS_ERROR_ENDED };
477     m_last_playing_state = state;
478     return is_over && m_playing_state == PS_PLAYING && m_musicStream != 0;
479 }
480 
GetCurPosition()481 int CBassCore::GetCurPosition()
482 {
483     CSingleLock sync(&m_critical, TRUE);
484     if (m_musicStream == 0)
485         return 0;
486     QWORD pos_bytes;
487     pos_bytes = BASS_ChannelGetPosition(m_musicStream, BASS_POS_BYTE);
488     double pos_sec;
489     pos_sec = BASS_ChannelBytes2Seconds(m_musicStream, pos_bytes);
490     int current_position = static_cast<int>(pos_sec * 1000);
491     if (current_position == -1000) current_position = 0;
492 
493     GetMidiPosition();
494     return current_position;
495 }
496 
GetSongLength()497 int CBassCore::GetSongLength()
498 {
499     CSingleLock sync(&m_critical, TRUE);
500     QWORD lenght_bytes;
501     lenght_bytes = BASS_ChannelGetLength(m_musicStream, BASS_POS_BYTE);
502     double length_sec;
503     length_sec = BASS_ChannelBytes2Seconds(m_musicStream, lenght_bytes);
504     int song_length = static_cast<int>(length_sec * 1000);
505     if (song_length <= -1000) song_length = 0;
506     return song_length;
507 }
508 
SetCurPosition(int position)509 void CBassCore::SetCurPosition(int position)
510 {
511     CSingleLock sync(&m_critical, TRUE);
512     double pos_sec = static_cast<double>(position) / 1000.0;
513     QWORD pos_bytes;
514     pos_bytes = BASS_ChannelSeconds2Bytes(m_musicStream, pos_sec);
515     BASS_ChannelSetPosition(m_musicStream, pos_bytes, BASS_POS_BYTE);
516     m_midi_lyric.midi_lyric.clear();
517     GetMidiPosition();
518 }
519 
GetAudioInfo(SongInfo & song_info,int flag)520 void CBassCore::GetAudioInfo(SongInfo & song_info, int flag)
521 {
522     CSingleLock sync(&m_critical, TRUE);
523     GetBASSAudioInfo(m_musicStream, song_info, flag);
524 }
525 
GetAudioInfo(const wchar_t * file_path,SongInfo & song_info,int flag)526 void CBassCore::GetAudioInfo(const wchar_t * file_path, SongInfo & song_info, int flag)
527 {
528     HSTREAM hStream;
529     if (song_info.file_path.empty())
530         song_info.file_path = file_path;
531     hStream = BASS_StreamCreateFile(FALSE, file_path, 0, 0, BASS_SAMPLE_FLOAT);
532     GetBASSAudioInfo(hStream, song_info, flag);
533     BASS_StreamFree(hStream);
534 }
535 
EncodeAudio(SongInfo song_info,const wstring & dest_file_path,EncodeFormat encode_format,void * encode_para,int dest_freq,EncodeAudioProc proc)536 bool CBassCore::EncodeAudio(SongInfo song_info, const wstring& dest_file_path, EncodeFormat encode_format, void* encode_para, int dest_freq, EncodeAudioProc proc)
537 {
538     wstring out_file_path_temp = CCommon::GetTemplatePath() + CONVERTING_TEMP_FILE_NAME;     //转换时的临时文件名
539 
540     //创建解码通道
541     const int BUFF_SIZE{ 20000 };
542     // char buff[BUFF_SIZE];
543     std::unique_ptr<char[]> buff(new char[BUFF_SIZE]);
544     HSTREAM hStream = BASS_StreamCreateFile(FALSE, song_info.file_path.c_str(), 0, 0, BASS_STREAM_DECODE/* | BASS_SAMPLE_FLOAT*/);
545     if (hStream == 0)
546     {
547         proc(CONVERT_ERROR_FILE_CANNOT_OPEN);
548         return false;
549     }
550 
551     //获取通道信息
552     BASS_CHANNELINFO channel_info;
553     BASS_ChannelGetInfo(hStream, &channel_info);
554     bool is_midi = (CAudioCommon::GetAudioTypeByBassChannel(channel_info.ctype) == AU_MIDI);
555     if (is_midi)
556     {
557         bool sf2_loaded = (CBassCore::m_bass_midi_lib.IsSucceed() && CBassCore::m_sfont.font != 0);
558         if (sf2_loaded)
559         {
560             CBassCore::m_bass_midi_lib.BASS_MIDI_StreamSetFonts(hStream, &CBassCore::m_sfont, 1);
561         }
562         else
563         {
564             BASS_StreamFree(hStream);
565             //::PostMessage(pthis->GetSafeHwnd(), WM_CONVERT_PROGRESS, file_index, CONVERT_ERROR_MIDI_NO_SF2);
566             proc(CONVERT_ERROR_MIDI_NO_SF2);
567             return false;
568         }
569     }
570 
571     //转换采样频率
572     HSTREAM hStreamOld = 0;
573     if (dest_freq > 0 && dest_freq != channel_info.freq)
574     {
575         hStreamOld = hStream;
576         hStream = m_bass_mix_lib.BASS_Mixer_StreamCreate(dest_freq, channel_info.chans, BASS_MIXER_END | BASS_STREAM_DECODE);
577         if (hStream != 0)
578         {
579             m_bass_mix_lib.BASS_Mixer_StreamAddChannel(hStream, hStreamOld, 0);
580         }
581         else
582         {
583             hStream = hStreamOld;
584             hStreamOld = 0;
585         }
586     }
587 
588     //获取流的长度
589     QWORD length = BASS_ChannelGetLength(hStreamOld != 0 ? hStreamOld : hStream, 0);
590     if (hStreamOld != 0 && channel_info.freq != 0)     //如果开启了转换采样频率
591         length = length * dest_freq / channel_info.freq;
592     if (length == 0) length = 1;	//防止length作为除数时为0
593 
594     //如果转换的音频是cue音轨,则先定位到曲目开始处
595     if (song_info.is_cue)
596     {
597         CBassCore::SetCurrentPosition(hStreamOld != 0 ? hStreamOld : hStream, song_info.start_pos.toInt());
598     }
599 
600     HENCODE hEncode;
601     if (encode_format != EncodeFormat::WMA)
602     {
603         //设置解码器命令行参数
604         wstring cmdline;
605         switch (encode_format)
606         {
607         case EncodeFormat::MP3:
608         {
609             if (encode_para == nullptr)
610                 break;
611             MP3EncodePara* mp3_para = (MP3EncodePara*)encode_para;
612             //设置lame命令行参数
613             cmdline = m_encode_dir;
614             cmdline += L"lame.exe ";
615             cmdline += mp3_para->cmd_para;
616             cmdline += L" - \"";
617             cmdline += out_file_path_temp;
618             cmdline.push_back(L'\"');
619         }
620         break;
621         case EncodeFormat::OGG:
622         {
623             if (encode_para == nullptr)
624                 break;
625             OggEncodePara* ogg_para = (OggEncodePara*)encode_para;
626 
627             cmdline = m_encode_dir;
628             CString str;
629             str.Format(_T("oggenc -q %d"), ogg_para->encode_quality);
630             cmdline += str;
631             cmdline += L" -o \"";
632             cmdline += dest_file_path;
633             cmdline += L"\" -";
634         }
635         break;
636         case EncodeFormat::FLAC:
637         {
638             if (encode_para == nullptr)
639                 break;
640             FlacEncodePara* flac_para = (FlacEncodePara*)encode_para;
641             cmdline = m_encode_dir;
642             cmdline += _T("flac.exe ");
643             cmdline += flac_para->cmd_para.c_str();
644             cmdline += L" -o \"";
645             cmdline += dest_file_path;
646             cmdline += L"\" -";
647         }
648         break;
649         default:
650             cmdline = dest_file_path;
651         }
652         //开始解码
653         hEncode = m_bass_encode_lib.BASS_Encode_StartW(hStream, cmdline.c_str(), BASS_ENCODE_AUTOFREE | (encode_format == EncodeFormat::WAV ? BASS_ENCODE_PCM : 0), NULL, 0);
654         if (hEncode == 0)
655         {
656             BASS_StreamFree(hStream);
657             if (hStreamOld != 0)
658                 BASS_StreamFree(hStreamOld);
659             proc(CONVERT_ERROR_ENCODE_CHANNEL_FAILED);
660             return false;
661         }
662     }
663     else		//输出为WMA时使用专用的编码器
664     {
665         if (!m_bass_wma_lib.IsSucceed())
666         {
667             BASS_StreamFree(hStream);
668             if (hStreamOld != 0)
669                 BASS_StreamFree(hStreamOld);
670             return false;
671         }
672         //开始解码
673         if (encode_para == nullptr)
674             return false;
675         WmaEncodePara* wma_para = (WmaEncodePara*)encode_para;
676         int wma_bitrate;
677         if (wma_para->cbr)
678             wma_bitrate = wma_para->cbr_bitrate * 1000;
679         else
680             wma_bitrate = wma_para->vbr_quality;
681         hEncode = m_bass_wma_lib.BASS_WMA_EncodeOpenFileW(hStream, NULL, BASS_WMA_ENCODE_STANDARD | BASS_WMA_ENCODE_SOURCE, wma_bitrate, dest_file_path.c_str());
682         int error = BASS_ErrorGetCode();
683         int error_code{ CONVERT_ERROR_ENCODE_CHANNEL_FAILED };
684         //如果出现了错误,则写入错误日志
685         if (error != 0)
686         {
687             wstring log_str = theApp.m_str_table.LoadTextFormat(L"LOG_BASS_FORMAT_CONVERT_ERROR", { song_info.file_path, L"WMA", error });
688             theApp.WriteLog(log_str);
689             switch (error)
690             {
691             case BASS_ERROR_WMA:
692                 error_code = CONVERT_ERROR_WMA_NO_WMP9_OR_LATER;
693                 break;
694             case BASS_ERROR_NOTAVAIL:
695                 error_code = CONVERT_ERROR_WMA_NO_SUPPORTED_ENCODER;
696                 break;
697             default:
698                 break;
699             }
700         }
701         if (hEncode == 0)
702         {
703             BASS_StreamFree(hStream);
704             if (hStreamOld != 0)
705                 BASS_StreamFree(hStreamOld);
706             proc(error_code);
707             return false;
708         }
709     }
710 
711     while (BASS_ChannelIsActive(hStream) != BASS_ACTIVE_STOPPED)
712     {
713         if (theApp.m_format_convert_dialog_exit)
714         {
715             m_bass_encode_lib.BASS_Encode_Stop(hEncode);
716             BASS_StreamFree(hStream);
717             if (hStreamOld != 0)
718                 BASS_StreamFree(hStreamOld);
719             return false;
720         }
721         // BASS_ChannelGetData(hStream, buff, BUFF_SIZE);
722         BASS_ChannelGetData(hStream, buff.get(), BUFF_SIZE);
723         //if (m_bass_encode_lib.BASS_Encode_IsActive(hStream) == 0)
724         //{
725         //	m_bass_encode_lib.BASS_Encode_Stop(hEncode);
726         //	BASS_StreamFree(hStream);
727         //}
728 
729         //获取转换百分比
730         int percent;
731         static int last_percent{ -1 };
732         if (!song_info.is_cue)
733         {
734             QWORD position = BASS_ChannelGetPosition(hStream, 0);
735             percent = static_cast<int>(position * 100 / length);
736         }
737         else
738         {
739             int cue_position = CBassCore::GetBASSCurrentPosition(hStreamOld != 0 ? hStreamOld : hStream) - song_info.start_pos.toInt();
740             int cue_length = song_info.length().toInt();
741             percent = static_cast<int>(cue_position * 100 / cue_length);
742             if (percent == 100)
743                 break;
744         }
745         if (percent < 0 || percent>100)
746         {
747             proc(CONVERT_ERROR_ENCODE_PARA_ERROR);
748             BASS_StreamFree(hStream);
749             if (hStreamOld != 0)
750                 BASS_StreamFree(hStreamOld);
751             return false;
752         }
753         if (last_percent != percent)
754         {
755             proc(percent);
756         }
757         last_percent = percent;
758     }
759 
760     BASS_StreamFree(hStream);
761     if (hStreamOld != 0)
762         BASS_StreamFree(hStreamOld);
763 
764     //由于在转换mp3文件时将目标文件输出到了临时目录,因此转换完成后将此文件移动到输出目录
765     if (encode_format == EncodeFormat::MP3)
766     {
767         CFilePathHelper out_file_path_helper{ dest_file_path };
768         CommonDialogMgr::MoveAFile(AfxGetMainWnd()->GetSafeHwnd(), out_file_path_temp, out_file_path_helper.GetDir());
769         if (CCommon::FileExist(out_file_path_helper.GetFilePath()))
770             CommonDialogMgr::DeleteAFile(AfxGetMainWnd()->GetSafeHwnd(), out_file_path_helper.GetFilePath());
771         CCommon::FileRename(out_file_path_helper.GetDir() + CONVERTING_TEMP_FILE_NAME, out_file_path_helper.GetFileName());
772     }
773     return true;
774 }
775 
InitEncoder()776 bool CBassCore::InitEncoder()
777 {
778     wstring encode_dll_path;
779     wstring wma_dll_path;
780     wstring plugin_dir;
781     m_encode_dir = theApp.m_local_dir + L"Encoder\\";
782     plugin_dir = theApp.m_local_dir + L"Plugins\\";
783     encode_dll_path = m_encode_dir + L"bassenc.dll";
784     m_bass_encode_lib.Init(encode_dll_path);
785 
786     wma_dll_path = plugin_dir + L"basswma.dll";
787     m_bass_wma_lib.Init(wma_dll_path);
788 
789     m_bass_mix_lib.Init(m_encode_dir + L"bassmix.dll");
790 
791     return m_bass_encode_lib.IsSucceed();
792 }
793 
UnInitEncoder()794 void CBassCore::UnInitEncoder()
795 {
796     m_bass_encode_lib.UnInit();
797     m_bass_wma_lib.UnInit();
798     m_bass_mix_lib.UnInit();
799 }
800 
IsFreqConvertAvailable()801 bool CBassCore::IsFreqConvertAvailable()
802 {
803     return m_bass_mix_lib.IsSucceed();
804 }
805 
IsMidi()806 bool CBassCore::IsMidi()
807 {
808     return m_is_midi;
809 }
810 
IsMidiConnotPlay()811 bool CBassCore::IsMidiConnotPlay()
812 {
813     return (m_is_midi && m_sfont.font == 0);
814 }
815 
GetMidiInnerLyric()816 std::wstring CBassCore::GetMidiInnerLyric()
817 {
818     return m_midi_lyric.midi_lyric;
819 }
820 
GetMidiInfo()821 MidiInfo CBassCore::GetMidiInfo()
822 {
823     return m_midi_info;
824 }
825 
MidiNoLyric()826 bool CBassCore::MidiNoLyric()
827 {
828     return m_midi_lyric.midi_no_lyric;
829 }
830 
GetPlayingState()831 PlayingState CBassCore::GetPlayingState()
832 {
833     DWORD state = BASS_ChannelIsActive(m_musicStream);
834     if (state == BASS_ACTIVE_PLAYING)
835         return PS_PLAYING;
836     else if (state == BASS_ACTIVE_PAUSED)
837         return PS_PAUSED;
838     else
839         return PS_STOPED;
840 }
841 
ApplyEqualizer(int channel,int gain)842 void CBassCore::ApplyEqualizer(int channel, int gain)
843 {
844     if (channel < 0 || channel >= EQU_CH_NUM) return;
845     //if (!m_equ_enable) return;
846     if (gain < -15) gain = -15;
847     if (gain > 15) gain = 15;
848     BASS_DX8_PARAMEQ parameq;
849     parameq.fBandwidth = 30;
850     parameq.fCenter = FREQ_TABLE[channel];
851     parameq.fGain = static_cast<float>(gain);
852     BASS_FXSetParameters(m_equ_handle[channel], &parameq);
853 
854 }
855 
SetReverb(int mix,int time)856 void CBassCore::SetReverb(int mix, int time)
857 {
858     BASS_DX8_REVERB parareverb;
859     parareverb.fInGain = 0;
860     //parareverb.fReverbMix = static_cast<float>(mix) / 100 * 96 - 96;
861     parareverb.fReverbMix = static_cast<float>(std::pow(static_cast<double>(mix) / 100, 0.1) * 96 - 96);
862     parareverb.fReverbTime = static_cast<float>(time * 10);
863     parareverb.fHighFreqRTRatio = 0.001f;
864     BASS_FXSetParameters(m_reverb_handle, &parareverb);
865 }
866 
ClearReverb()867 void CBassCore::ClearReverb()
868 {
869     BASS_DX8_REVERB parareverb;
870     parareverb.fInGain = 0;
871     parareverb.fReverbMix = -96;
872     parareverb.fReverbTime = 0.001f;
873     parareverb.fHighFreqRTRatio = 0.001f;
874     BASS_FXSetParameters(m_reverb_handle, &parareverb);
875 }
876 
GetFFTData(float fft_data[FFT_SAMPLE])877 void CBassCore::GetFFTData(float fft_data[FFT_SAMPLE])
878 {
879     BASS_ChannelGetData(m_musicStream, fft_data, BASS_DATA_FFT512);
880 }
881 
GetErrorCode()882 int CBassCore::GetErrorCode()
883 {
884     m_error_code = BASS_ErrorGetCode();
885     return m_error_code;
886 }
887 
GetErrorInfo(int error_code)888 std::wstring CBassCore::GetErrorInfo(int error_code)
889 {
890     // 这个方法获取写入错误日志的字符串,后面会附加CPlayer的方法名
891     wstring info = theApp.m_str_table.LoadTextFormat(L"LOG_BASS_ERROR", { error_code, m_file_path });
892     return info;
893 }
894 
GetErrorInfo()895 std::wstring CBassCore::GetErrorInfo()
896 {
897     // 这个方法获取错误时状态栏显示的错误字符串“播放出错: <%1%>”
898     int error_code = BASS_ErrorGetCode();
899     wstring info = theApp.m_str_table.LoadTextFormat(L"UI_TXT_PLAYSTATUS_ERROR_CORE_BASS", { error_code });
900     return info;
901 }
902 
903 //int CBassCore::GetDeviceCount()
904 //{
905 //    BASS_DEVICEINFO info;
906 //    int count{};
907 //    for (int i = 0; BASS_GetDeviceInfo(i, &info); i++)
908 //        if (info.flags&BASS_DEVICE_ENABLED) // device is enabled
909 //            count++; // count it
910 //    return count;
911 //}
912 
IsVolumeFadingOut()913 bool CBassCore::IsVolumeFadingOut()
914 {
915     return m_fading;
916 }
917 
GetBASSCurrentPosition(HSTREAM hStream)918 int CBassCore::GetBASSCurrentPosition(HSTREAM hStream)
919 {
920     QWORD pos_bytes;
921     pos_bytes = BASS_ChannelGetPosition(hStream, BASS_POS_BYTE);
922     double pos_sec;
923     pos_sec = BASS_ChannelBytes2Seconds(hStream, pos_bytes);
924     return static_cast<int>(pos_sec * 1000);
925 }
926 
GetBASSSongLength(HSTREAM hStream)927 Time CBassCore::GetBASSSongLength(HSTREAM hStream)
928 {
929     QWORD lenght_bytes;
930     lenght_bytes = BASS_ChannelGetLength(hStream, BASS_POS_BYTE);
931     double length_sec;
932     length_sec = BASS_ChannelBytes2Seconds(hStream, lenght_bytes);
933     int song_length_int = static_cast<int>(length_sec * 1000);
934     if (song_length_int <= -1000) song_length_int = 0;
935     return Time(song_length_int);		//将长度转换成Time结构
936 }
937 
SetCurrentPosition(HSTREAM hStream,int position)938 void CBassCore::SetCurrentPosition(HSTREAM hStream, int position)
939 {
940     double pos_sec = static_cast<double>(position) / 1000.0;
941     QWORD pos_bytes;
942     pos_bytes = BASS_ChannelSeconds2Bytes(hStream, pos_sec);
943     BASS_ChannelSetPosition(hStream, pos_bytes, BASS_POS_BYTE);
944 }
945