/* ** out_asio - ASIO output for WINAMP5 ** ** 2006/10/4 Written by Otachan ** http://otachan.com/ */ #define STRICT #include #include #include #include "pcmasio.h" extern AsioDrivers* asioDrivers; CRITICAL_SECTION CriticalSection; PcmAsio* pPcmAsio; Resampler_base* SSRC_Class; HANDLE hRealTimeMsgThread; HANDLE hSSRC_Thread; HANDLE EventReadyRealTimeMsgThread; HANDLE EventDestroyRealTimeMsgThread; HANDLE EventReadySSRC_Thread; HANDLE EventDestroySSRC_Thread; int* CommonBuff; bool PostOutput; bool ValidBufferSwitchTime; __int64 BufferSwitchTime; int PreferredSize; int BuffPreferredSize; int BuffStart; int BuffEnd; int WriteSamples; int GapWriteSamples; __int64 TotalOutputSamples; __int64 TotalWriteSize; ASIOCallbacks Callbacks; _FormatInfo FormatInfo; _ChannelInfo* ChannelInfo; ASIOBufferInfo* BufferInfo; int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpszCmdLine*/, int /*nCmdShow*/) { MSG Msg; ::PeekMessage(&Msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); EventDestroySSRC_Thread = ::CreateEvent(NULL, false, false, NULL); EventReadySSRC_Thread = ::CreateEvent(NULL, false, false, NULL); unsigned int dwSSRC_Thread; hSSRC_Thread = reinterpret_cast(_beginthreadex(NULL, 0, SSRC_ThreadProc, NULL, 0, &dwSSRC_Thread)); _WaitForSingleObject(EventReadySSRC_Thread); ::CloseHandle(EventReadySSRC_Thread); asioDrivers = new AsioDrivers; pPcmAsio = new PcmAsio; HANDLE hCommonBuff = ::CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, COMMON_BUFFER_SIZE, FILE_MAPPING_COMMON_BUFFER); CommonBuff = reinterpret_cast(::MapViewOfFileEx(hCommonBuff, FILE_MAP_WRITE, 0, 0, 0, NULL)); HANDLE EventServerMsg = ::CreateEvent(NULL, false, false, EVENT_SERVER_MSG); HANDLE EventServerRetMsg = ::CreateEvent(NULL, false, false, EVENT_SERVER_RETMSG); EventReadyRealTimeMsgThread = ::CreateEvent(NULL, false, false, NULL); EventDestroyRealTimeMsgThread = ::CreateEvent(NULL, false, false, NULL); unsigned int dwRealTimeMsgThread; hRealTimeMsgThread = reinterpret_cast(_beginthreadex( NULL, 0, RealTimeMsgThreadProc, NULL, 0, &dwRealTimeMsgThread)); _WaitForSingleObject(EventReadyRealTimeMsgThread); ::CloseHandle(EventReadyRealTimeMsgThread); HANDLE EventClientReadyServer = ::OpenEvent(EVENT_MODIFY_STATE, false, EVENT_CLIENT_READY_SERVER); ::SetEvent(EventClientReadyServer); ::CloseHandle(EventClientReadyServer); bool Loop = true; while(Loop) { _WaitForSingleObject(EventServerMsg); int RetMsg; switch(*(CommonBuff + 1 + SERVER_INDEX)) { case MSG_QUIT: pPcmAsio->CloseDriver(); Loop = false; break; case MSG_OPEN: RetMsg = pPcmAsio->MsgOpen( *(CommonBuff + 1 + SERVER_INDEX + 1), *(CommonBuff + 1 + SERVER_INDEX + 2), *(CommonBuff + 1 + SERVER_INDEX + 3)); break; case MSG_CLOSE: pPcmAsio->MsgClose(); break; case MSG_CAN_WRITE: RetMsg = pPcmAsio->MsgCanWrite(); break; case MSG_WRITE: RetMsg = pPcmAsio->MsgWrite( *(CommonBuff + 1 + SERVER_INDEX + 1), reinterpret_cast (CommonBuff + 1 + SERVER_INDEX + 2)); break; case MSG_IS_PLAYING: RetMsg = pPcmAsio->MsgIsPlaying(); break; case MSG_PAUSE: RetMsg = pPcmAsio->MsgPause(*(CommonBuff + 1 + SERVER_INDEX + 1)); break; case MSG_FLUSH: pPcmAsio->MsgFlush(*(CommonBuff + 1 + SERVER_INDEX + 1)); break; // case MSG_GET_OUTPUT_TIME: // RetMsg = pPcmAsio->MsgGetOutputTime(); // break; // case MSG_GET_WRITTEN_TIME: // RetMsg = pPcmAsio->MsgGetWrittenTime(); // break; } *(CommonBuff + 1 + SERVER_INDEX) = RetMsg; ::SetEvent(EventServerRetMsg); } ::SetEvent(EventDestroyRealTimeMsgThread); if(_WaitForSingleObject(hRealTimeMsgThread, 5000) != WAIT_OBJECT_0) { if(::TerminateThread(hRealTimeMsgThread, 0)) { _WaitForSingleObject(hRealTimeMsgThread, 3000); } } ::CloseHandle(hRealTimeMsgThread); ::CloseHandle(EventDestroyRealTimeMsgThread); ::CloseHandle(EventServerMsg); ::CloseHandle(EventServerRetMsg); ::UnmapViewOfFile(CommonBuff); ::CloseHandle(hCommonBuff); delete pPcmAsio; delete asioDrivers; ::SetEvent(EventDestroySSRC_Thread); if(_WaitForSingleObject(hSSRC_Thread, 5000) != WAIT_OBJECT_0) { if(::TerminateThread(hSSRC_Thread, 0)) { _WaitForSingleObject(hSSRC_Thread, 3000); } } ::CloseHandle(hSSRC_Thread); ::CloseHandle(EventDestroySSRC_Thread); return 0; } unsigned int __stdcall RealTimeMsgThreadProc(void* /*Param*/) { HANDLE EventServerRealTimeMsg = ::CreateEvent(NULL, false, false, EVENT_SERVER_REALTIME_MSG); HANDLE EventServerRealTimeRetMsg = ::CreateEvent(NULL, false, false, EVENT_SERVER_REALTIME_RETMSG); HANDLE HandleEvent[2]; *(HandleEvent + 0) = EventDestroyRealTimeMsgThread; *(HandleEvent + 1) = EventServerRealTimeMsg; ::SetEvent(EventReadyRealTimeMsgThread); while(true) { if(::WaitForMultipleObjects(2, HandleEvent, false, INFINITE) == (WAIT_OBJECT_0 + 1)) { switch(*(CommonBuff + 1)) { case MSG_GET_NUM_DEVICE: *(CommonBuff + 1) = asioDrivers->asioGetNumDev(); break; case MSG_GET_DRIVER_NAME: asioDrivers->asioGetDriverName( *(CommonBuff + 2), reinterpret_cast(CommonBuff + 1), SERVER_INDEX); break; case MSG_SET_OPTION: pPcmAsio->SetOption( *reinterpret_cast(CommonBuff + 2), *(CommonBuff + 4), *(CommonBuff + 5), *(CommonBuff + 6), *(CommonBuff + 7), *(CommonBuff + 8), *(CommonBuff + 9), *(CommonBuff + 10), *(CommonBuff + 11), *(CommonBuff + 12), *(CommonBuff + 13), *(CommonBuff + 14), *(CommonBuff + 15)); break; case MSG_SET_RE_OPEN: pPcmAsio->SetReOpen(); break; case MSG_GET_OUTPUT_TIME: *(CommonBuff + 1) = pPcmAsio->MsgGetOutputTime(); break; case MSG_GET_WRITTEN_TIME: *(CommonBuff + 1) = pPcmAsio->MsgGetWrittenTime(); break; } ::SetEvent(EventServerRealTimeRetMsg); } else { break; } } ::CloseHandle(EventServerRealTimeMsg); ::CloseHandle(EventServerRealTimeRetMsg); _endthreadex(0); return 0; } unsigned int __stdcall SSRC_ThreadProc(void* /*Param*/) { SSRC_Class = NULL; ::SetEvent(EventReadySSRC_Thread); while(::WaitForSingleObjectEx(EventDestroySSRC_Thread, INFINITE, true) != WAIT_OBJECT_0); if(SSRC_Class) delete SSRC_Class; _endthreadex(0); return 0; } void CALLBACK SSRC_ApcProc(ULONG_PTR dwParam) { SSRC_Msg* const Param = reinterpret_cast(dwParam); switch(Param->Msg) { case SSRC_CREATE: if(SSRC_Class) { delete SSRC_Class; SSRC_Class = NULL; } int nPriority; switch(Param->Param1) { case 1: nPriority = THREAD_PRIORITY_ABOVE_NORMAL; break; case 2: nPriority = THREAD_PRIORITY_HIGHEST; break; case 3: nPriority = THREAD_PRIORITY_TIME_CRITICAL; break; default: nPriority = THREAD_PRIORITY_NORMAL; break; } ::SetThreadPriority(::GetCurrentThread(), nPriority); SSRC_Class = SSRC_create( Param->Param2, Param->Param3, Param->Param4, Param->Param5, Param->Param6, Param->Param7, Param->Param8, Param->Param9); break; case SSRC_DELETE: if(SSRC_Class) { delete SSRC_Class; SSRC_Class = NULL; ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_NORMAL); } break; case SSRC_WRITE: SSRC_Class->Write(Param->Buff, Param->Param1); break; case SSRC_FINISH: SSRC_Class->Finish(); break; case SSRC_GET_BUFFER: Param->RetBuff = reinterpret_cast(SSRC_Class->GetBuffer(&Param->RetMsg)); Param->RetMsg = Min(Param->RetMsg, Param->Param1); break; case SSRC_READ: SSRC_Class->Read(Param->Param1); break; case SSRC_FLUSH: SSRC_Class->Flush(); break; case SSRC_GET_DATA_IN_OUT_BUF: Param->RetMsg = SSRC_Class->GetDataInOutbuf(); break; } Param->UnPause(); } SSRC_Msg::SSRC_Msg( const UINT ThreadPriority, const UINT sfrq, const UINT dfrq, const UINT sformat, const UINT dformat, const UINT bps, const UINT dbps, const UINT nch, const UINT quality) { Msg = SSRC_CREATE; Param1 = ThreadPriority; Param2 = sfrq; Param3 = dfrq; Param4 = sformat; Param5 = dformat; Param6 = bps; Param7 = dbps; Param8 = nch; Param9 = quality; Call(); } SSRC_Msg::~SSRC_Msg(void) { Msg = SSRC_DELETE; Call(); } void SSRC_Msg::UnPause(void) { ::SetEvent(EventWaitThread); } void SSRC_Msg::Write(void* input, const UINT size) { Msg = SSRC_WRITE; Param1 = size; Buff = reinterpret_cast(input); Call(); } void SSRC_Msg::Finish(void) { Msg = SSRC_FINISH; Call(); } void* SSRC_Msg::GetBuffer(UINT* s, const UINT NeedSize) { Msg = SSRC_GET_BUFFER; Param1 = NeedSize; *s = Call(); return RetBuff; } void SSRC_Msg::Read(const UINT s) { Msg = SSRC_READ; Param1 = s; Call(); } void SSRC_Msg::Flush(void) { Msg = SSRC_FLUSH; Call(); } UINT SSRC_Msg::GetDataInOutbuf(void) { Msg = SSRC_GET_DATA_IN_OUT_BUF; return Call(); } UINT SSRC_Msg::Call(void) { EventWaitThread = ::CreateEvent(NULL, false, false, NULL); ::QueueUserAPC(&SSRC_ApcProc, hSSRC_Thread, reinterpret_cast(this)); _WaitForSingleObject(EventWaitThread); ::CloseHandle(EventWaitThread); return RetMsg; } ASIOError _ASIOStop(void) { ::EnterCriticalSection(&CriticalSection); const ASIOError RetCode = ASIOStop(); ResetAsioBuff(0, 0); ResetAsioBuff(1, 0); ::LeaveCriticalSection(&CriticalSection); return RetCode; } void BufferSwitch(long index, ASIOBool directProcess) { ::EnterCriticalSection(&CriticalSection); int CopySamples; if(WriteSamples < PreferredSize) { CopySamples = WriteSamples; ResetAsioBuff(index, CopySamples); } else { CopySamples = PreferredSize; } if(CopySamples) { const int MaxCopySamples = BuffStart + CopySamples; if(MaxCopySamples <= BuffPreferredSize) { ToAsioBuff(index, CopySamples); } else { ToAsioBuffOverRun(index, MaxCopySamples); } BuffStart = (MaxCopySamples < BuffPreferredSize) ? MaxCopySamples : MaxCopySamples - BuffPreferredSize; WriteSamples -= CopySamples; if(GapWriteSamples > 0) { if((GapWriteSamples -= CopySamples) < 0) { TotalOutputSamples = -GapWriteSamples; } } else { TotalOutputSamples += CopySamples; } BufferSwitchTime = Timer::Get(); ValidBufferSwitchTime = true; } ::LeaveCriticalSection(&CriticalSection); if(PostOutput) ASIOOutputReady(); } inline void ResetAsioBuff(const int index, const int CopySamples) { const int SetSamples = PreferredSize - CopySamples; for(UINT Idx = 0; Idx < FormatInfo.Nch; Idx++) { const int Bps_b = ChannelInfo[Idx].Bps_b; memset( reinterpret_cast(BufferInfo[Idx].buffers[index]) + CopySamples * Bps_b, 0, SetSamples * Bps_b); } } inline void ToAsioBuff(const int index, const int CopySamples) { for(UINT Idx = 0; Idx < FormatInfo.Nch; Idx++) { const int Bps_b = ChannelInfo[Idx].Bps_b; memcpy( BufferInfo[Idx].buffers[index], ChannelInfo[Idx].Buff + BuffStart * Bps_b, CopySamples * Bps_b); } } inline void ToAsioBuffOverRun(const int index, const int MaxCopySamples) { const int CopySamples1 = BuffPreferredSize - BuffStart; const int CopySamples2 = MaxCopySamples - BuffPreferredSize; for(UINT Idx = 0; Idx < FormatInfo.Nch; Idx++) { const int Bps_b = ChannelInfo[Idx].Bps_b; const int CopySize1 = CopySamples1 * Bps_b; unsigned char* Buff = ChannelInfo[Idx].Buff; unsigned char* AsioBuff = reinterpret_cast(BufferInfo[Idx].buffers[index]); memcpy(AsioBuff, Buff + BuffStart * Bps_b, CopySize1); memcpy(AsioBuff + CopySize1, Buff, CopySamples2 * Bps_b); } } void SampleRateChanged(ASIOSampleRate sRate) { } long ASIOMessages(long selector, long value, void* message, double* opt) { long RetCode; switch(selector) { case kAsioSelectorSupported: RetCode = (value == kAsioEngineVersion) || (value == kAsioResetRequest) || (value == kAsioBufferSizeChange) || (value == kAsioResyncRequest) || (value == kAsioLatenciesChanged) || (value == kAsioSupportsTimeInfo) || (value == kAsioSupportsTimeCode) || (value == kAsioSupportsInputMonitor); break; case kAsioEngineVersion: RetCode = 2; break; case kAsioResetRequest: case kAsioBufferSizeChange: case kAsioLatenciesChanged: pPcmAsio->SetReOpen(); RetCode = 1; break; case kAsioResyncRequest: case kAsioSupportsTimeInfo: case kAsioSupportsTimeCode: RetCode = 0; break; case kAsioSupportsInputMonitor: RetCode = 1; break; default: RetCode = 0; break; } return RetCode; } ASIOTime* BufferSwitchTimeInfo(ASIOTime* timeInfo, long index, ASIOBool directProcess) { return NULL; } PcmAsio::PcmAsio(void) { ::InitializeCriticalSection(&CriticalSection); Timer::Init(); Callbacks.bufferSwitch = &BufferSwitch; Callbacks.sampleRateDidChange = &SampleRateChanged; Callbacks.asioMessage = &ASIOMessages; Callbacks.bufferSwitchTimeInfo = &BufferSwitchTimeInfo; LoadDriver = false; InitDriver = false; InitOpen = false; NowOpen = false; } PcmAsio::~PcmAsio(void) { CloseDriver(); ::DeleteCriticalSection(&CriticalSection); } inline bool PcmAsio::OpenDriver(void) { const int MaxDriver = asioDrivers->asioGetNumDev(); if(MaxDriver && (cfg_device < MaxDriver)) { const int DriverNameLen = 64; char DriverName[DriverNameLen]; if(asioDrivers->asioGetDriverName(cfg_device, DriverName, DriverNameLen) == 0) { if(asioDrivers->loadDriver(DriverName)) { ASIODriverInfo DriverInfo; DriverInfo.asioVersion = 2; DriverInfo.sysRef = NULL; if(ASIOInit(&DriverInfo) == ASE_OK) Setup(); LoadDriver = true; } } } return InitDriver; } void PcmAsio::CloseDriver(const bool RemoveDriver) { if(InitDriver) { InitOpen = false; Stop(); ::SetPriorityClass(::GetCurrentProcess(), NORMAL_PRIORITY_CLASS); ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_NORMAL); ::SetThreadPriority(hRealTimeMsgThread, THREAD_PRIORITY_NORMAL); Close(); if(RemoveDriver) ASIOExit(); if(SSRC_MsgClass) delete SSRC_MsgClass; delete[] CutBuff; delete[] ChannelInfo; delete[] BufferInfo; InitDriver = false; } if(RemoveDriver && LoadDriver) { asioDrivers->removeCurrentDriver(); LoadDriver = false; } } void PcmAsio::SetOption( const HWND _hMainWindow, const int _cfg_device, const int _cfg_process_priority, const int _cfg_thread_priority, const int _cfg_buffer_size, const int _cfg_shift_channels, const int _cfg_gapless_mode, const int _cfg_convert_1ch_to_2ch, const int _cfg_direct_input_monitor, const int _cfg_resampling_enable, const int _cfg_resampling_thread_priority, const int _cfg_resampling_sample_rate, const int _cfg_resampling_quality) { if(_hMainWindow) hMainWindow = _hMainWindow; cfg_device = _cfg_device; cfg_process_priority = _cfg_process_priority; cfg_thread_priority = _cfg_thread_priority; cfg_buffer_size = _cfg_buffer_size; cfg_shift_channels = _cfg_shift_channels; cfg_gapless_mode = _cfg_gapless_mode != 0; cfg_convert_1ch_to_2ch = _cfg_convert_1ch_to_2ch != 0; cfg_direct_input_monitor = _cfg_direct_input_monitor != 0; cfg_resampling_enable = _cfg_resampling_enable != 0; cfg_resampling_thread_priority = _cfg_resampling_thread_priority; cfg_resampling_sample_rate = _cfg_resampling_sample_rate; cfg_resampling_quality = _cfg_resampling_quality; } inline void PcmAsio::Setup(void) { First = true; FormatInfo.Sr = 0; FormatInfo.Format = -1; FormatInfo.Bps = 0; FormatInfo.Nch = 0; ShiftChannels = cfg_shift_channels; GaplessMode = cfg_gapless_mode; Convert1chTo2ch = cfg_convert_1ch_to_2ch; EnableConvert1chTo2ch = false; long InputNch; long OutputNch; ASIOGetChannels(&InputNch, &OutputNch); DeviceNch = OutputNch; CutBuff = new unsigned char[(MAX_BPS >> 3) * DeviceNch]; long MinSize; long MaxSize; long _PreferredSize; long Granularity; ASIOGetBufferSize(&MinSize, &MaxSize, &_PreferredSize, &Granularity); PreferredSize = _PreferredSize; BuffPreferredSize = PreferredSize * BUFFER_SIZE * (cfg_buffer_size + 1); PostOutput = ASIOOutputReady() == ASE_OK; ASIOInputMonitor InputMonitor; InputMonitor.input = -1; InputMonitor.output = ShiftChannels; InputMonitor.gain = 0x20000000; InputMonitor.state = cfg_direct_input_monitor ? ASIOTrue : ASIOFalse; InputMonitor.pan = 0x3fffffff; ASIOFuture(kAsioSetInputMonitor, &InputMonitor); ChannelInfo = new _ChannelInfo[DeviceNch]; for(UINT Idx = 0; Idx < DeviceNch; Idx++) { ChannelInfo[Idx].Buff = NULL; } BufferInfo = new ASIOBufferInfo[DeviceNch]; InitBuff = false; SSRC_MsgClass = NULL; SSRC_SetCreate(); SSRC_Enable = cfg_resampling_enable; SSRC_Sr = cfg_resampling_sample_rate; SSRC_Quality = cfg_resampling_quality; DWORD dwPriorityClass; switch(cfg_process_priority) { case 1: dwPriorityClass = HIGH_PRIORITY_CLASS; break; case 2: dwPriorityClass = REALTIME_PRIORITY_CLASS; break; default: dwPriorityClass = NORMAL_PRIORITY_CLASS; break; } ::SetPriorityClass(::GetCurrentProcess(), dwPriorityClass); int nPriority; switch(cfg_thread_priority) { case 1: nPriority = THREAD_PRIORITY_ABOVE_NORMAL; break; case 2: nPriority = THREAD_PRIORITY_HIGHEST; break; case 3: nPriority = THREAD_PRIORITY_TIME_CRITICAL; break; default: nPriority = THREAD_PRIORITY_NORMAL; break; } ::SetThreadPriority(::GetCurrentThread(), nPriority); ::SetThreadPriority(hRealTimeMsgThread, nPriority); InitDriver = true; } void PcmAsio::SSRC_Create(UINT& sr, int& format, UINT& bps, const UINT nch) { if((SSRC_BeforeSr != sr) || (SSRC_BeforeFormat != format) || (SSRC_BeforeBps != bps) || (SSRC_BeforeNch != nch)) { FlushWrite(); if(SSRC_MsgClass) delete SSRC_MsgClass; if(sr == SSRC_Sr) { SSRC_MsgClass = NULL; } else { SSRC_MsgClass = new SSRC_Msg( cfg_resampling_thread_priority, sr, SSRC_Sr, format, DATA_FORMAT_IEEE_FLOAT, bps, 64, nch, SSRC_Quality); } SSRC_BeforeSr = sr; SSRC_BeforeFormat = format; SSRC_BeforeBps = bps; SSRC_BeforeNch = nch; } if(SSRC_MsgClass) { sr = SSRC_Sr; format = DATA_FORMAT_IEEE_FLOAT; bps = 64; } } void PcmAsio::SSRC_SetCreate(void) { SSRC_BeforeSr = 0; SSRC_BeforeFormat = -1; SSRC_BeforeBps = 0; SSRC_BeforeNch = 0; } void PcmAsio::SetReOpen(void) { if(GaplessMode && (NowOpen == false)) { CloseDriver(); } else { ReOpen = true; } } int PcmAsio::MsgOpen(UINT sr, int _bps, UINT nch) { if(InitDriver == false) { if(LoadDriver) { Setup(); } else if(OpenDriver() == false) { CloseDriver(); return -1; } } const UINT old_org_Sr = FormatInfo.org_Sr; const int old_org_Bps_b_Nch = FormatInfo.org_Bps_b_Nch; int format = (_bps >= 0) ? DATA_FORMAT_LINEAR_PCM : DATA_FORMAT_IEEE_FLOAT; UINT bps = (_bps >= 0) ? _bps : -_bps; FormatInfo.org_Sr = sr; FormatInfo.org_Bps = _bps; FormatInfo.org_Nch = nch; FormatInfo.org_Bps_b_Nch = (bps >> 3) * nch; if(SSRC_Enable) SSRC_Create(sr, format, bps, nch); const bool ChangeSr = sr != FormatInfo.Sr; const bool ChangeFormat = format != FormatInfo.Format; const bool ChangeBps = bps != FormatInfo.Bps; bool _EnableConvert1chTo2ch; if(Convert1chTo2ch && (nch == 1)) { nch = 2; _EnableConvert1chTo2ch = true; } else { _EnableConvert1chTo2ch = false; } const bool ChangeNch = nch != FormatInfo.Nch; if((ChangeSr == false) && (ChangeFormat == false) && (ChangeBps == false) && (ChangeNch == false) && (_EnableConvert1chTo2ch == EnableConvert1chTo2ch)) { ::EnterCriticalSection(&CriticalSection); GapWriteSamples = SSRC_MsgClass ? static_cast(TotalWriteSize / old_org_Bps_b_Nch * sr / old_org_Sr - TotalOutputSamples) : WriteSamples; SetParam(); ::LeaveCriticalSection(&CriticalSection); return GetMaxLatency(sr); } if(ChangeSr || ChangeNch) Stop(); if(ChangeSr) { if(ASIOCanSampleRate(sr) == ASE_OK) { ASIOSetSampleRate(sr); } else { CloseDriver(); // FormatInfo.Sr = 0; return -1; } FormatInfo.Sr = sr; } if(ChangeFormat || ChangeBps) { if(( ((format == DATA_FORMAT_LINEAR_PCM) && ((bps == 8) || (bps == 16) || (bps == 24) || (bps == 32))) || ((format == DATA_FORMAT_IEEE_FLOAT) && ((bps == 32) || (bps == 64)))) == false) { CloseDriver(); // FormatInfo.Format = -1; // FormatInfo.Bps = 0; return -1; } FormatInfo.Format = format; FormatInfo.Bps = bps; FormatInfo.Bps_b = bps >> 3; } FormatInfo.Bps_b_Nch = FormatInfo.Bps_b * FormatInfo.org_Nch; if(ChangeNch) { if((nch + ShiftChannels) > DeviceNch) { CloseDriver(); // FormatInfo.Nch = 0; return -1; } Close(); for(UINT Idx = 0; Idx < nch; Idx++) { const UINT ChannelNum = Idx + ShiftChannels; ASIOChannelInfo OneChannelInfo; OneChannelInfo.channel = ChannelNum; OneChannelInfo.isInput = ASIOFalse; ASIOGetChannelInfo(&OneChannelInfo); int Channel_Bps_b; switch(OneChannelInfo.type) { case ASIOSTInt16MSB: case ASIOSTInt16LSB: Channel_Bps_b = 2; break; case ASIOSTInt24MSB: case ASIOSTInt24LSB: Channel_Bps_b = 3; break; case ASIOSTInt32MSB: case ASIOSTFloat32MSB: case ASIOSTInt32MSB16: case ASIOSTInt32MSB24: case ASIOSTInt32LSB: case ASIOSTFloat32LSB: case ASIOSTInt32LSB16: case ASIOSTInt32LSB24: Channel_Bps_b = 4; break; case ASIOSTFloat64MSB: case ASIOSTFloat64LSB: Channel_Bps_b = 8; break; default: // Close(); CloseDriver(); // FormatInfo.Nch = 0; return -1; } ChannelInfo[Idx].Type = OneChannelInfo.type; ChannelInfo[Idx].Bps_b = Channel_Bps_b; ChannelInfo[Idx].Buff = new unsigned char[BuffPreferredSize * Channel_Bps_b]; BufferInfo[Idx].isInput = ASIOFalse; BufferInfo[Idx].channelNum = ChannelNum; BufferInfo[Idx].buffers[0] = NULL; BufferInfo[Idx].buffers[1] = NULL; } ASIOCreateBuffers(BufferInfo, nch, PreferredSize, &Callbacks); InitBuff = true; long InputLatency; long OutputLatency; ASIOGetLatencies(&InputLatency, &OutputLatency); DeviceLatency = OutputLatency * 2; FormatInfo.Nch = nch; ResetAsioBuff(0, 0); ResetAsioBuff(1, 0); } if(ChangeFormat || ChangeBps || ChangeNch) { for(UINT Idx = 0; Idx < nch; Idx++) { ChannelInfo[Idx].ToBuffFunc = SetToBuffFuc(ChannelInfo[Idx].Type, format, bps); } } EnableConvert1chTo2ch = _EnableConvert1chTo2ch; if(ChangeSr || ChangeNch) { SetFlush(); } else { GapWriteSamples = WriteSamples; } SetParam(); InitOpen = true; return GetMaxLatency(sr); } void PcmAsio::SetFlush(void) { BuffStart = 0; BuffEnd = 0; WriteSamples = 0; GapWriteSamples = 0; CutBuffSize = 0; EndThread = false; } void PcmAsio::SetParam(void) { ReOpen = false; NowOpen = true; NowPause = false; TotalOutputSamples = 0; TotalWriteSize = 0; } int PcmAsio::GetMaxLatency(const UINT sr) { return ::MulDiv(BuffPreferredSize + DeviceLatency, 1000, sr) + (SSRC_Enable ? SSRC_MAX_LATENCY : 0); } void PcmAsio::Close(void) { if(InitBuff) { ASIODisposeBuffers(); InitBuff = false; } for(UINT Idx = 0; Idx < DeviceNch; Idx++) { if(ChannelInfo[Idx].Buff) { delete[] ChannelInfo[Idx].Buff; ChannelInfo[Idx].Buff = NULL; } } } void PcmAsio::MsgClose(void) { if(GaplessMode) { if(*CommonBuff == 0) CloseDriver(false); } else { CloseDriver(); } NowOpen = false; } int PcmAsio::MsgCanWrite(void) { int RetCode; if(InitOpen) { if(ReOpen) { CloseDriver(); const __int64 _TotalWriteSize = TotalWriteSize; if(MsgOpen(FormatInfo.org_Sr, FormatInfo.org_Bps, FormatInfo.org_Nch) >= 0) { TotalOutputSamples = _TotalWriteSize / FormatInfo.org_Bps_b_Nch * 10000 / FormatInfo.org_Sr * FormatInfo.Sr / 10000; TotalWriteSize = _TotalWriteSize; } else { ReOpen = false; ::PostMessage(hMainWindow, WM_COMMAND, 40047, 0); return 0; } } RetCode = NowPause ? 0 : GetCanWriteSize(); } else { RetCode = 0; } return RetCode; // return InitOpen ? (NowPause ? 0 : GetCanWriteSize()) : 0; } int PcmAsio::GetCanWriteSize(void) { int CanWriteSize = BuffPreferredSize - WriteSamples; if(SSRC_MsgClass) { CanWriteSize = ::MulDiv(CanWriteSize, FormatInfo.org_Sr, FormatInfo.Sr) * FormatInfo.org_Bps_b_Nch; } else { CanWriteSize *= FormatInfo.org_Bps_b_Nch; CanWriteSize = (CanWriteSize > CutBuffSize) ? CanWriteSize - CutBuffSize : 0; } return CanWriteSize; } int PcmAsio::MsgWrite(const int size, unsigned char* data) { int RetCode; if(InitOpen) { if(size) { if(SSRC_MsgClass) SSRC_MsgClass->Write(data, size); Write(false, size, data); TotalWriteSize += size; } RetCode = 0; } else { RetCode = 1; } return RetCode; } void PcmAsio::FlushWrite(void) { if(InitOpen) { if(SSRC_MsgClass) { if(SSRC_BeforeSr) { SSRC_SetCreate(); SSRC_MsgClass->Finish(); while(EndThread == false) { Write(true, 0, NULL); if(SSRC_MsgClass->GetDataInOutbuf() == 0) break; Sleep(1); } } } else { Write(true, 0, NULL); } } } void PcmAsio::Write(const bool flush, int size, unsigned char* data) { const int org_size = size; int CutSamples; unsigned char* AddData; if(SSRC_MsgClass) { data = reinterpret_cast(SSRC_MsgClass->GetBuffer( reinterpret_cast(&size), (BuffPreferredSize - WriteSamples) * FormatInfo.Bps_b_Nch)); CutSamples = size / FormatInfo.Bps_b_Nch; } else { if(CutBuffSize) { if(size) { const int NewSize = CutBuffSize + size; AddData = new unsigned char[NewSize]; memcpy(AddData, CutBuff, CutBuffSize); memcpy(AddData + CutBuffSize, data, size); data = AddData; size = NewSize; } else { AddData = NULL; data = CutBuff; size = CutBuffSize; } } else { AddData = NULL; } CutSamples = size / FormatInfo.Bps_b_Nch; const int CutSize = CutSamples * FormatInfo.Bps_b_Nch; if((CutBuffSize = size - CutSize) != 0) { memcpy(CutBuff, data + CutSize, CutBuffSize); } } unsigned char* ReadPnt = data; while(EndThread == false) { ::EnterCriticalSection(&CriticalSection); const int ToBuffSamples = Min(CutSamples, BuffPreferredSize - WriteSamples); for(int Idx = 0; Idx < ToBuffSamples; Idx++) { ReadPnt = ToBuff(ReadPnt); BuffEnd = (++BuffEnd < BuffPreferredSize) ? BuffEnd : 0; } WriteSamples += ToBuffSamples; ::LeaveCriticalSection(&CriticalSection); if(First) { if(flush || (GetCanWriteSize() < (org_size * 2))) { Play(); First = false; } } if((CutSamples -= ToBuffSamples) == 0) break; Sleep(1); } if(SSRC_MsgClass) { SSRC_MsgClass->Read(size); } else { if(AddData) delete[] AddData; } } int PcmAsio::MsgIsPlaying(void) { int RetCode; if(InitOpen) { if(GaplessMode) { RetCode = 0; } else { FlushWrite(); RetCode = !!(WriteSamples + GetLatency()); } } else { RetCode = 0; } return RetCode; } void PcmAsio::Play(void) { ValidBufferSwitchTime = false; ASIOStart(); } void PcmAsio::Stop(void) { if(First == false) { _ASIOStop(); EndThread = true; First = true; } } int PcmAsio::MsgPause(const int pause) { int RetCode; if(InitOpen) { if(First == false) { if(pause) { _ASIOStop(); PauseTime = Timer::Get(); } else { Play(); } } RetCode = NowPause; NowPause = pause != 0; } else { RetCode = 0; } return RetCode; } void PcmAsio::MsgFlush(const int t) { if(InitOpen) { Stop(); SetFlush(); TotalOutputSamples = static_cast<__int64>(t) * FormatInfo.Sr / 1000; TotalWriteSize = static_cast<__int64>(t) * FormatInfo.org_Sr / 1000 * FormatInfo.org_Bps_b_Nch; if(SSRC_MsgClass) { UINT sr = SSRC_BeforeSr; int format = SSRC_BeforeFormat; UINT bps = SSRC_BeforeBps; UINT nch = SSRC_BeforeNch; SSRC_SetCreate(); SSRC_MsgClass->Flush(); SSRC_Create(sr, format, bps, nch); } } } inline int PcmAsio::GetLatency(void) { int RetCode; if((First == false) && ValidBufferSwitchTime) { const int Pos = ::MulDiv( Timer::Sub(NowPause ? PauseTime : Timer::Get(), BufferSwitchTime), FormatInfo.Sr, 1000); RetCode = (DeviceLatency > Pos) ? DeviceLatency - Pos : 0; } else { RetCode = 0; } return RetCode; } int PcmAsio::MsgGetOutputTime(void) { return InitOpen ? static_cast((TotalOutputSamples - GetLatency()) * 1000 / FormatInfo.Sr) : 0; } int PcmAsio::MsgGetWrittenTime(void) { return InitOpen ? static_cast(TotalWriteSize / FormatInfo.org_Bps_b_Nch * 1000 / FormatInfo.org_Sr) : 0; } inline unsigned char* PcmAsio::ToBuff(unsigned char* ReadPnt) { for(UINT Idx = 0; Idx < FormatInfo.Nch; Idx++) { ChannelInfo[Idx].ToBuffFunc(Idx, ReadPnt); if((EnableConvert1chTo2ch == false) || (Idx == 1)) ReadPnt += FormatInfo.Bps_b; } return ReadPnt; } inline TO_BUFF_FUNC PcmAsio::SetToBuffFuc(const ASIOSampleType Type, const int Format, const UINT Bps) { TO_BUFF_FUNC ToBuffFunc; switch(Bps) { case 8: switch(Type) { case ASIOSTInt16MSB: ToBuffFunc = ToBuff_Int8_Int16MSB; break; case ASIOSTInt24MSB: ToBuffFunc = ToBuff_Int8_Int24MSB; break; case ASIOSTInt32MSB: ToBuffFunc = ToBuff_Int8_Int32MSB; break; case ASIOSTFloat32MSB: ToBuffFunc = ToBuff_Int8_Float32MSB; break; case ASIOSTFloat64MSB: ToBuffFunc = ToBuff_Int8_Float64MSB; break; case ASIOSTInt32MSB16: ToBuffFunc = ToBuff_Int8_Int32MSB16; break; case ASIOSTInt32MSB24: ToBuffFunc = ToBuff_Int8_Int32MSB24; break; case ASIOSTInt16LSB: ToBuffFunc = ToBuff_Int8_Int16LSB; break; case ASIOSTInt24LSB: ToBuffFunc = ToBuff_Int8_Int24LSB; break; case ASIOSTInt32LSB: ToBuffFunc = ToBuff_Int8_Int32LSB; break; case ASIOSTFloat32LSB: ToBuffFunc = ToBuff_Int8_Float32LSB; break; case ASIOSTFloat64LSB: ToBuffFunc = ToBuff_Int8_Float64LSB; break; case ASIOSTInt32LSB16: ToBuffFunc = ToBuff_Int8_Int32LSB16; break; case ASIOSTInt32LSB24: ToBuffFunc = ToBuff_Int8_Int32LSB24; break; } break; case 16: switch(Type) { case ASIOSTInt16MSB: ToBuffFunc = ToBuff_Int16_Int16MSB; break; case ASIOSTInt24MSB: ToBuffFunc = ToBuff_Int16_Int24MSB; break; case ASIOSTInt32MSB: ToBuffFunc = ToBuff_Int16_Int32MSB; break; case ASIOSTFloat32MSB: ToBuffFunc = ToBuff_Int16_Float32MSB; break; case ASIOSTFloat64MSB: ToBuffFunc = ToBuff_Int16_Float64MSB; break; case ASIOSTInt32MSB16: ToBuffFunc = ToBuff_Int16_Int32MSB16; break; case ASIOSTInt32MSB24: ToBuffFunc = ToBuff_Int16_Int32MSB24; break; case ASIOSTInt16LSB: ToBuffFunc = ToBuff_Int16_Int16LSB; break; case ASIOSTInt24LSB: ToBuffFunc = ToBuff_Int16_Int24LSB; break; case ASIOSTInt32LSB: ToBuffFunc = ToBuff_Int16_Int32LSB; break; case ASIOSTFloat32LSB: ToBuffFunc = ToBuff_Int16_Float32LSB; break; case ASIOSTFloat64LSB: ToBuffFunc = ToBuff_Int16_Float64LSB; break; case ASIOSTInt32LSB16: ToBuffFunc = ToBuff_Int16_Int32LSB16; break; case ASIOSTInt32LSB24: ToBuffFunc = ToBuff_Int16_Int32LSB24; break; } break; case 24: switch(Type) { case ASIOSTInt16MSB: ToBuffFunc = ToBuff_Int24_Int16MSB; break; case ASIOSTInt24MSB: ToBuffFunc = ToBuff_Int24_Int24MSB; break; case ASIOSTInt32MSB: ToBuffFunc = ToBuff_Int24_Int32MSB; break; case ASIOSTFloat32MSB: ToBuffFunc = ToBuff_Int24_Float32MSB; break; case ASIOSTFloat64MSB: ToBuffFunc = ToBuff_Int24_Float64MSB; break; case ASIOSTInt32MSB16: ToBuffFunc = ToBuff_Int24_Int32MSB16; break; case ASIOSTInt32MSB24: ToBuffFunc = ToBuff_Int24_Int32MSB24; break; case ASIOSTInt16LSB: ToBuffFunc = ToBuff_Int24_Int16LSB; break; case ASIOSTInt24LSB: ToBuffFunc = ToBuff_Int24_Int24LSB; break; case ASIOSTInt32LSB: ToBuffFunc = ToBuff_Int24_Int32LSB; break; case ASIOSTFloat32LSB: ToBuffFunc = ToBuff_Int24_Float32LSB; break; case ASIOSTFloat64LSB: ToBuffFunc = ToBuff_Int24_Float64LSB; break; case ASIOSTInt32LSB16: ToBuffFunc = ToBuff_Int24_Int32LSB16; break; case ASIOSTInt32LSB24: ToBuffFunc = ToBuff_Int24_Int32LSB24; break; } break; case 32: switch(Format) { case DATA_FORMAT_LINEAR_PCM: switch(Type) { case ASIOSTInt16MSB: ToBuffFunc = ToBuff_Int32_Int16MSB; break; case ASIOSTInt24MSB: ToBuffFunc = ToBuff_Int32_Int24MSB; break; case ASIOSTInt32MSB: ToBuffFunc = ToBuff_Int32_Int32MSB; break; case ASIOSTFloat32MSB: ToBuffFunc = ToBuff_Int32_Float32MSB; break; case ASIOSTFloat64MSB: ToBuffFunc = ToBuff_Int32_Float64MSB; break; case ASIOSTInt32MSB16: ToBuffFunc = ToBuff_Int32_Int32MSB16; break; case ASIOSTInt32MSB24: ToBuffFunc = ToBuff_Int32_Int32MSB24; break; case ASIOSTInt16LSB: ToBuffFunc = ToBuff_Int32_Int16LSB; break; case ASIOSTInt24LSB: ToBuffFunc = ToBuff_Int32_Int24LSB; break; case ASIOSTInt32LSB: ToBuffFunc = ToBuff_Int32_Int32LSB; break; case ASIOSTFloat32LSB: ToBuffFunc = ToBuff_Int32_Float32LSB; break; case ASIOSTFloat64LSB: ToBuffFunc = ToBuff_Int32_Float64LSB; break; case ASIOSTInt32LSB16: ToBuffFunc = ToBuff_Int32_Int32LSB16; break; case ASIOSTInt32LSB24: ToBuffFunc = ToBuff_Int32_Int32LSB24; break; } break; case DATA_FORMAT_IEEE_FLOAT: switch(Type) { case ASIOSTInt16MSB: ToBuffFunc = ToBuff_Float32_Int16MSB; break; case ASIOSTInt24MSB: ToBuffFunc = ToBuff_Float32_Int24MSB; break; case ASIOSTInt32MSB: ToBuffFunc = ToBuff_Float32_Int32MSB; break; case ASIOSTFloat32MSB: ToBuffFunc = ToBuff_Float32_Float32MSB; break; case ASIOSTFloat64MSB: ToBuffFunc = ToBuff_Float32_Float64MSB; break; case ASIOSTInt32MSB16: ToBuffFunc = ToBuff_Float32_Int32MSB16; break; case ASIOSTInt32MSB24: ToBuffFunc = ToBuff_Float32_Int32MSB24; break; case ASIOSTInt16LSB: ToBuffFunc = ToBuff_Float32_Int16LSB; break; case ASIOSTInt24LSB: ToBuffFunc = ToBuff_Float32_Int24LSB; break; case ASIOSTInt32LSB: ToBuffFunc = ToBuff_Float32_Int32LSB; break; case ASIOSTFloat32LSB: ToBuffFunc = ToBuff_Float32_Float32LSB; break; case ASIOSTFloat64LSB: ToBuffFunc = ToBuff_Float32_Float64LSB; break; case ASIOSTInt32LSB16: ToBuffFunc = ToBuff_Float32_Int32LSB16; break; case ASIOSTInt32LSB24: ToBuffFunc = ToBuff_Float32_Int32LSB24; break; } break; } break; case 64: switch(Type) { case ASIOSTInt16MSB: ToBuffFunc = ToBuff_Float64_Int16MSB; break; case ASIOSTInt24MSB: ToBuffFunc = ToBuff_Float64_Int24MSB; break; case ASIOSTInt32MSB: ToBuffFunc = ToBuff_Float64_Int32MSB; break; case ASIOSTFloat32MSB: ToBuffFunc = ToBuff_Float64_Float32MSB; break; case ASIOSTFloat64MSB: ToBuffFunc = ToBuff_Float64_Float64MSB; break; case ASIOSTInt32MSB16: ToBuffFunc = ToBuff_Float64_Int32MSB16; break; case ASIOSTInt32MSB24: ToBuffFunc = ToBuff_Float64_Int32MSB24; break; case ASIOSTInt16LSB: ToBuffFunc = ToBuff_Float64_Int16LSB; break; case ASIOSTInt24LSB: ToBuffFunc = ToBuff_Float64_Int24LSB; break; case ASIOSTInt32LSB: ToBuffFunc = ToBuff_Float64_Int32LSB; break; case ASIOSTFloat32LSB: ToBuffFunc = ToBuff_Float64_Float32LSB; break; case ASIOSTFloat64LSB: ToBuffFunc = ToBuff_Float64_Float64LSB; break; case ASIOSTInt32LSB16: ToBuffFunc = ToBuff_Float64_Int32LSB16; break; case ASIOSTInt32LSB24: ToBuffFunc = ToBuff_Float64_Int32LSB24; break; } break; } return ToBuffFunc; }