diff --git a/cocos/kr2/cocosstudio/ui/FileItem.csd b/cocos/kr2/cocosstudio/ui/FileItem.csd index f8d8dd65..23db9b8c 100644 --- a/cocos/kr2/cocosstudio/ui/FileItem.csd +++ b/cocos/kr2/cocosstudio/ui/FileItem.csd @@ -46,7 +46,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -76,7 +76,7 @@ - + diff --git a/cocos/kr2/cocosstudio/ui/FileManageMenu.csd b/cocos/kr2/cocosstudio/ui/FileManageMenu.csd index d9d98bcf..54f6697c 100644 --- a/cocos/kr2/cocosstudio/ui/FileManageMenu.csd +++ b/cocos/kr2/cocosstudio/ui/FileManageMenu.csd @@ -47,7 +47,7 @@ - + @@ -93,14 +93,14 @@ - + - + @@ -224,14 +224,14 @@ - + - + @@ -407,14 +407,14 @@ - + - + @@ -592,14 +592,14 @@ - + - + @@ -629,10 +629,10 @@ - + - + @@ -645,7 +645,7 @@ - + @@ -709,60 +709,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -911,17 +924,17 @@ - + - + - + @@ -1031,17 +1044,17 @@ - + - + - + @@ -1164,10 +1177,10 @@ - + - + diff --git a/cocos/kr2/cocosstudio/ui/KeySelect.csd b/cocos/kr2/cocosstudio/ui/KeySelect.csd new file mode 100644 index 00000000..be67f7a8 --- /dev/null +++ b/cocos/kr2/cocosstudio/ui/KeySelect.csd @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cocos/kr2/cocosstudio/ui/SelectList.csd b/cocos/kr2/cocosstudio/ui/SelectList.csd index febe7a91..f20dbb60 100644 --- a/cocos/kr2/cocosstudio/ui/SelectList.csd +++ b/cocos/kr2/cocosstudio/ui/SelectList.csd @@ -6,8 +6,22 @@ - + + + + + + + + + + + + + + + @@ -19,10 +33,10 @@ - + - + @@ -35,14 +49,14 @@ - + - + @@ -51,7 +65,7 @@ - + @@ -64,7 +78,7 @@ - + @@ -83,7 +97,7 @@ - + @@ -92,7 +106,7 @@ - + @@ -105,7 +119,7 @@ - + diff --git a/cocos/kr2/kr2.ccs b/cocos/kr2/kr2.ccs index 9b379e60..0e091883 100644 --- a/cocos/kr2/kr2.ccs +++ b/cocos/kr2/kr2.ccs @@ -61,6 +61,7 @@ + diff --git a/src/core/environ/ui/FileSelectorForm.cpp b/src/core/environ/ui/FileSelectorForm.cpp index ee92721b..2fab9562 100644 --- a/src/core/environ/ui/FileSelectorForm.cpp +++ b/src/core/environ/ui/FileSelectorForm.cpp @@ -223,6 +223,7 @@ void TVPBaseFileSelectorForm::onCellLongPress(int idx) LocaleConfigManager::GetInstance()->initText(reader.findController("titleUnpack")); LocaleConfigManager::GetInstance()->initText(reader.findController("titleDelete")); LocaleConfigManager::GetInstance()->initText(reader.findController("titleSendTo")); + LocaleConfigManager::GetInstance()->initText(reader.findController("titleRename")); _fileOperateMenulist = reader.findController("list"); _fileOperateCell_unselect = reader.findWidget("unselect"); _fileOperateCell_view = reader.findWidget("view", false); @@ -232,6 +233,7 @@ void TVPBaseFileSelectorForm::onCellLongPress(int idx) _fileOperateCell_delete = reader.findWidget("delete"); _fileOperateCell_unpack = reader.findWidget("unpack", false); _fileOperateCell_sendto = reader.findWidget("sendto"); + _fileOperateCell_rename = reader.findWidget("rename", false); Widget *btn;; if ((btn = reader.findWidget("btnUnselect"))) { btn->addClickEventListener(std::bind(&TVPBaseFileSelectorForm::onUnselectClicked, this, std::placeholders::_1)); @@ -257,7 +259,10 @@ void TVPBaseFileSelectorForm::onCellLongPress(int idx) if ((btn = reader.findWidget("btnSendTo"))) { btn->addClickEventListener(std::bind(&TVPBaseFileSelectorForm::onSendToClicked, this, std::placeholders::_1)); } - _fileOperateMenulist->removeAllItems(); + if ((btn = reader.findWidget("btnRename"))) { + btn->addClickEventListener(std::bind(&TVPBaseFileSelectorForm::onBtnRenameClicked, this, std::placeholders::_1)); + } + _fileOperateMenulist->removeAllChildrenWithCleanup(false); float scale = 1.5; _fileOperateMenu->setScale(1 / scale); _fileOperateMenu->setContentSize(_fileOperateMenuNode->getContentSize() * scale); @@ -493,9 +498,12 @@ void TVPBaseFileSelectorForm::onUnpackClicked(cocos2d::Ref *owner) class UnpackArchive { const int UpdateMS = 100; // update rate 10 fps tTVPUnpackArchive ArcUnpacker; + std::string ArcPath; + std::string PasswordBuffer; public: bool Init(const std::string &path, const std::string &outpath) { + ArcPath = path; if (ArcUnpacker.Prepare(path, outpath, &TotalSize) <= 0) { return false; } @@ -519,6 +527,7 @@ void TVPBaseFileSelectorForm::onUnpackClicked(cocos2d::Ref *owner) std::bind(&UnpackArchive::OnError, this, std::placeholders::_1, std::placeholders::_2), std::bind(&UnpackArchive::OnProgress, this, std::placeholders::_1, std::placeholders::_2), std::bind(&UnpackArchive::OnNewFile, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) + //std::bind(&UnpackArchive::OnPassword, this) ); return true; } @@ -584,6 +593,20 @@ void TVPBaseFileSelectorForm::onUnpackClicked(cocos2d::Ref *owner) ProgressForm->setContent(filename); }); } + const char *OnPassword() { + LocaleConfigManager *localeMgr = LocaleConfigManager::GetInstance(); + std::vector btns; + btns.emplace_back(localeMgr->GetText("ok")); + btns.emplace_back(localeMgr->GetText("cancel")); + ttstr text(ArcPath); + if (TVPShowSimpleInputBox(text, localeMgr->GetText("please_input_password"), "", btns) == 0) { + PasswordBuffer = text.AsStdString(); + return PasswordBuffer.c_str(); + } else { + ArcUnpacker.Stop(); + return ""; + } + } TVPSimpleProgressForm *ProgressForm = nullptr; tjs_uint32 LastUpdate = 0; @@ -649,6 +672,26 @@ void TVPBaseFileSelectorForm::onSendToClicked(cocos2d::Ref *owner) clearFileMenu(); } +void TVPBaseFileSelectorForm::onBtnRenameClicked(cocos2d::Ref *owner) +{ + if (_selectedFileIndex.size() != 1) return; + FileInfo &info = CurrentDirList[*_selectedFileIndex.begin()]; + ttstr name = info.NameForDisplay.c_str(); + std::vector btns; + btns.emplace_back("OK"); + btns.emplace_back("Cancel"); + if (TVPShowSimpleInputBox(name, "Input new name", "", btns) == 0) { + ttstr newname = TVPExtractStoragePath(info.FullPath); + newname += name; + std::string strNewName = newname.AsNarrowStdString(); + TVPRenameFile(info.FullPath, strNewName); + info.FullPath = strNewName; + info.NameForDisplay = name.AsNarrowStdString(); + ReloadTableViewAndKeepPos(FileList); + } + clearFileMenu(); +} + void TVPBaseFileSelectorForm::updateFileMenu() { ReloadTableViewAndKeepPos(FileList); @@ -659,7 +702,7 @@ void TVPBaseFileSelectorForm::updateFileMenu() } return; } - _fileOperateMenulist->removeAllItems(); + _fileOperateMenulist->removeAllChildrenWithCleanup(false); _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_unselect.get()); if (!_clipboardForFileManager.empty() && _clipboardPath != CurrentPath) { _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_paste.get()); @@ -667,6 +710,7 @@ void TVPBaseFileSelectorForm::updateFileMenu() if (_selectedFileIndex.size() == 1) { if (_fileOperateCell_view) _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_view.get()); _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_unpack.get()); + _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_rename.get()); // _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_sendto.get()); } if (!_selectedFileIndex.empty()) { @@ -873,7 +917,7 @@ void TVPBaseFileSelectorForm::FileItemCellImpl::initFromFile(const char * filena setContentSize(OrigCellModelSize); DirIcon = reader.findController(str_diricon); SelectBox = reader.findController(str_select); - SelectBox->setTouchEnabled(false); + //SelectBox->setTouchEnabled(false); FileNameNode = static_cast(reader.findController(str_filename)); if (DirIcon && FileNameNode) { CellTextAreaSize = DirIcon->getContentSize(); diff --git a/src/core/environ/ui/FileSelectorForm.h b/src/core/environ/ui/FileSelectorForm.h index e069fca2..9c13dcbe 100644 --- a/src/core/environ/ui/FileSelectorForm.h +++ b/src/core/environ/ui/FileSelectorForm.h @@ -61,6 +61,7 @@ class TVPBaseFileSelectorForm : public iTVPBaseForm, public cocos2d::extension:: _fileOperateCell_unpack, _fileOperateCell_repack, // TODO _fileOperateCell_delete, + _fileOperateCell_rename, _fileOperateCell_sendto; std::vector _clipboardForFileManager; @@ -75,6 +76,7 @@ class TVPBaseFileSelectorForm : public iTVPBaseForm, public cocos2d::extension:: void onUnpackClicked(cocos2d::Ref *owner); void onDeleteClicked(cocos2d::Ref *owner); void onSendToClicked(cocos2d::Ref *owner); + void onBtnRenameClicked(cocos2d::Ref *owner); void updateFileMenu(); void clearFileMenu(); @@ -152,7 +154,7 @@ class TVPBaseFileSelectorForm : public iTVPBaseForm, public cocos2d::extension:: if (ret) { _impl = nullptr; ret->retain(); - ret->removeFromParent(); + ret->removeFromParentAndCleanup(false); ret->reset(); } return ret; diff --git a/src/core/movie/ffmpeg/AEFactory.cpp b/src/core/movie/ffmpeg/AEFactory.cpp index 78b9e164..bef92130 100644 --- a/src/core/movie/ffmpeg/AEFactory.cpp +++ b/src/core/movie/ffmpeg/AEFactory.cpp @@ -35,7 +35,7 @@ static int64_t GetLayoutByChannels(int nChannel) { } class CAEStreamAL : public IAEStream { - TVPALSoundWrap *m_impl = nullptr; + iTVPSoundBuffer *m_impl = nullptr; AEAudioFormat m_format; IAEClockCallback *m_cbClock; double m_lastPts = 0; @@ -116,7 +116,7 @@ class CAEStreamAL : public IAEStream { format.SpeakerConfig = 0; format.IsFloat = false; format.Seekable = false; - m_impl = TVPALSoundWrap::Create(format); + m_impl = TVPCreateSoundBuffer(format, 8); } virtual ~CAEStreamAL() { @@ -182,7 +182,7 @@ class CAEStreamAL : public IAEStream { } virtual double GetDelay() override { - return (double)m_impl->GetUnprocessedSamples() / m_format.m_sampleRate; + return (double)m_impl->GetLatencySeconds(); } virtual CAESyncInfo GetSyncInfo() override { @@ -200,7 +200,8 @@ class CAEStreamAL : public IAEStream { } virtual double GetCacheTotal() override { - return std::max(GetDelay(), (double)TVPAL_BUFFER_COUNT); + // return std::max(GetDelay(), (double)TVPAL_BUFFER_COUNT); + return GetDelay(); } virtual void Pause() override { m_impl->Pause(); } @@ -211,7 +212,7 @@ class CAEStreamAL : public IAEStream { m_impl->Reset(); } - virtual void* GetNativeImpl() override { return m_impl; } + virtual iTVPSoundBuffer* GetNativeImpl() override { return m_impl; } }; diff --git a/src/core/movie/ffmpeg/AEStream.h b/src/core/movie/ffmpeg/AEStream.h index f8a244d4..4ad743dc 100644 --- a/src/core/movie/ffmpeg/AEStream.h +++ b/src/core/movie/ffmpeg/AEStream.h @@ -5,6 +5,7 @@ extern "C" { #include "libavcodec/avcodec.h" } +class iTVPSoundBuffer; NS_KRMOVIE_BEGIN /** @@ -252,7 +253,7 @@ class IAEStream */ virtual bool HasDSP() { return false; } - virtual void* GetNativeImpl() { return nullptr; } + virtual iTVPSoundBuffer* GetNativeImpl() { return nullptr; } }; NS_KRMOVIE_END \ No newline at end of file diff --git a/src/core/movie/ffmpeg/KRMoviePlayer.cpp b/src/core/movie/ffmpeg/KRMoviePlayer.cpp index 1178bcd0..f9ebda20 100644 --- a/src/core/movie/ffmpeg/KRMoviePlayer.cpp +++ b/src/core/movie/ffmpeg/KRMoviePlayer.cpp @@ -97,17 +97,17 @@ void TVPMoviePlayer::GetPlayRate(double *rate) { *rate = m_pPlayer->GetSpeed(); } -TVPALSoundWrap* TVPMoviePlayer::GetSoundDevice() { +iTVPSoundBuffer* TVPMoviePlayer::GetSoundDevice() { IDVDStreamPlayerAudio *audioplayer = m_pPlayer->GetAudioPlayer(); if (!audioplayer) return nullptr; IAEStream *audiostream = audioplayer->GetOutputDevice()->m_pAudioStream; if (!audiostream) return nullptr; - return (TVPALSoundWrap*)audiostream->GetNativeImpl(); + return audiostream->GetNativeImpl(); } void TVPMoviePlayer::GetAudioBalance(long *balance) { - TVPALSoundWrap* alsound = GetSoundDevice(); + iTVPSoundBuffer* alsound = GetSoundDevice(); if (alsound) { *balance = alsound->GetPan() * 100000; } @@ -115,7 +115,7 @@ void TVPMoviePlayer::GetAudioBalance(long *balance) void TVPMoviePlayer::SetAudioBalance(long balance) { - TVPALSoundWrap* alsound = GetSoundDevice(); + iTVPSoundBuffer* alsound = GetSoundDevice(); if (alsound) { alsound->SetPan(balance / 100000.0f); } @@ -123,13 +123,13 @@ void TVPMoviePlayer::SetAudioBalance(long balance) void TVPMoviePlayer::SetAudioVolume(long volume) { - TVPALSoundWrap* alsound = GetSoundDevice(); + iTVPSoundBuffer* alsound = GetSoundDevice(); if (alsound) alsound->SetVolume(volume / 100000.f); } void TVPMoviePlayer::GetAudioVolume(long *volume) { - TVPALSoundWrap* alsound = GetSoundDevice(); + iTVPSoundBuffer* alsound = GetSoundDevice(); if (alsound) *volume = alsound->GetVolume() * 100000; } diff --git a/src/core/movie/ffmpeg/KRMoviePlayer.h b/src/core/movie/ffmpeg/KRMoviePlayer.h index 6d5c97b4..3c5eb030 100644 --- a/src/core/movie/ffmpeg/KRMoviePlayer.h +++ b/src/core/movie/ffmpeg/KRMoviePlayer.h @@ -4,7 +4,7 @@ #include "krmovie.h" #include "ComplexRect.h" struct SwsContext; -class TVPALSoundWrap; +class iTVPSoundBuffer; class TVPYUVSprite; namespace cocos2d { @@ -119,7 +119,7 @@ class TVPMoviePlayer : public iTVPVideoOverlay, public CBaseRenderer { protected: TVPMoviePlayer(); - TVPALSoundWrap* GetSoundDevice(); + iTVPSoundBuffer* GetSoundDevice(); uint32_t RefCount = 1; bool Visible = false; diff --git a/src/core/movie/ffmpeg/VideoPlayer.cpp b/src/core/movie/ffmpeg/VideoPlayer.cpp index 3f0d901d..b8b40f69 100644 --- a/src/core/movie/ffmpeg/VideoPlayer.cpp +++ b/src/core/movie/ffmpeg/VideoPlayer.cpp @@ -22,7 +22,6 @@ #include "../omxplayer/OMXHelper.h" #endif -void TVPInitDirectSound(); void TVPInitLibAVCodec(); NS_KRMOVIE_BEGIN @@ -1868,16 +1867,18 @@ void BasePlayer::SeekTime(int64_t iTime) // return the time in milliseconds int64_t BasePlayer::GetTime() { +#if 0 // TODO delay tunning ? if (m_VideoPlayerAudio) { CDVDAudio *audioDev = m_VideoPlayerAudio->GetOutputDevice(); if (audioDev && audioDev->m_pAudioStream) { - TVPALSoundWrap *alsound = (TVPALSoundWrap*)audioDev->m_pAudioStream->GetNativeImpl(); + iTVPSoundBuffer *alsound = audioDev->m_pAudioStream->GetNativeImpl(); int remainSamples = alsound->GetUnprocessedSamples(); // double audio_clk = is->audio_clock - (double)remainSamples / is->audio_tgt.freq; // set_clock_at(&is->audclk, audio_clk, is->audio_clock_serial, av_gettime() / 1000000.0); // sync_clock_to_slave(&is->extclk, &is->audclk); } } +#endif CSingleLock lock(m_StateSection); double offset = 0; const double limit = DVD_MSEC_TO_TIME(500); diff --git a/src/core/movie/ffmpeg/krffmpeg.cpp b/src/core/movie/ffmpeg/krffmpeg.cpp index 95be4ab5..7c09953c 100644 --- a/src/core/movie/ffmpeg/krffmpeg.cpp +++ b/src/core/movie/ffmpeg/krffmpeg.cpp @@ -15,8 +15,6 @@ extern "C" { #include "KRMoviePlayer.h" #include "KRMovieLayer.h" -void TVPInitDirectSound(); - extern std::thread::id TVPMainThreadID; static int lockmgr(void **arg, enum AVLockOp op) diff --git a/src/core/sound/ARM/wavemix_arm.c b/src/core/sound/ARM/wavemix_arm.c index 9488f994..1e3c413f 100644 --- a/src/core/sound/ARM/wavemix_arm.c +++ b/src/core/sound/ARM/wavemix_arm.c @@ -1,16 +1,108 @@ #include #include "cpu_types.h" #include +#include "Protect.h" -#ifdef DEBUG_ARM_NEON +#ifdef __cplusplus extern "C" { #endif extern const int MAX_VOLUME; +typedef void(FAudioMix)(void *dst, const void *src, int samples, int16_t *volume); +static FAudioMix* _VolumeMix_CPP_S16_CH2; -void TVPWaveMixer_ASM_Init() +static void VolumeMix_NEON_S16_CH2(void *dst, const void *src, int len, int16_t *vol) { + int16_t *dst16 = (int16_t *)dst; + const int16_t *src16 = (const int16_t *)src; + + const int addr_mask = ~(8 - 1); + signed short* pEndDst = dst16 + len * 2; + // Pre Frag + { + int PreFragLen = ((int16_t*)((((intptr_t)dst16) + 7)&~7) - dst16) / 4; + if (PreFragLen > len) PreFragLen = len; + if (PreFragLen) { + _VolumeMix_CPP_S16_CH2(dst16, src16, PreFragLen, vol); + dst16 += PreFragLen * 2; + src16 += PreFragLen * 2; + } + } + + int16_t* pVecEndDst = (int16_t*)(((intptr_t)pEndDst)&addr_mask) - 7; + int16x4_t volume = vld1_s16(vol); + int16x4_t sign_mask = vdup_n_s16(0x8000); + + while (dst16 < pVecEndDst) { + int16x4_t src_sample = vld1_s16(src16); + int16x4_t dest_sample = vld1_s16(dst16); + int16x4_t src_sign = vreinterpret_s16_u16(vtst_s16(src_sample, sign_mask)); + int16x4_t dst_sign = vreinterpret_s16_u16(vtst_s16(dest_sample, sign_mask)); + src_sample = vshrn_n_s32(vmull_s16(src_sample, volume), 14); + int16x4_t error = vshrn_n_s32(vmull_s16(src_sample, dest_sample), 15); + int16x4_t same_sign = veor_s16(src_sign, dst_sign); // -1 = same sign + error = vand_s16(error, same_sign); + int16x4_t errneg = vneg_s16(error); + errneg = vand_s16(errneg, src_sign); + error = vand_s16(error, vmvn_s16(src_sign)); + int16x4_t result = vadd_s16(src_sample, dest_sample); + result = vadd_s16(result, error); + result = vadd_s16(result, errneg); + vst1_s16(dst16, result); + src16 += 4; + dst16 += 4; + } + + if (dst16 < pEndDst) { + _VolumeMix_CPP_S16_CH2(dst16, src16, (pEndDst - dst16) / 2, vol); + } +} + +static FAudioMix* _VolumeMix_CPP_F32_CH2; +static void VolumeMix_NEON_F32_CH2(void *dst, const void *src, int len, int16_t *vol) { + float *dst32 = (float *)dst; + const float *src32 = (const float *)src; + const int addr_mask = ~(16 - 1); + float* pEndDst = dst32 + len; + // Pre Frag + { + int PreFragLen = ((float*)((((intptr_t)dst32) + 15)&~15) - dst32) / 8; + if (PreFragLen > len) PreFragLen = len / 2; + if (PreFragLen) { + _VolumeMix_CPP_S16_CH2(dst32, src32, PreFragLen, vol); + dst32 += PreFragLen * 2; + src32 += PreFragLen * 2; + } + } + + float* pVecEndDst = (float*)(((intptr_t)pEndDst)&addr_mask) - 15; + float32x4_t volume = vcvtq_f32_s32(vmovl_s16(vld1_s16(vol))); + int32x4_t sign_mask = vdupq_n_s32((int32_t)0x80000000); + + while (dst32 < pVecEndDst) { + float32x4_t src_sample = vmulq_f32(vld1q_f32(src32), volume); + float32x4_t dest_sample = vld1q_f32(dst32); + int32x4_t src_sign = vreinterpretq_s32_u32(vtstq_s32(vreinterpretq_s32_f32(src_sample), sign_mask)); + int32x4_t dst_sign = vreinterpretq_s32_u32(vtstq_s32(vreinterpretq_s32_f32(dest_sample), sign_mask)); + float32x4_t error = vmulq_f32(src_sample, dest_sample); + float32x4_t result = vaddq_f32(src_sample, dest_sample); + result = vsubq_f32(result, error); + vst1q_f32(dst32, result); + src32 += 4; + dst32 += 4; + } + + if (dst32 < pEndDst) { + _VolumeMix_CPP_F32_CH2(dst32, src32, (pEndDst - dst32) / 2, vol); + } +} + +void TVPWaveMixer_ASM_Init(FAudioMix **func16, FAudioMix **func32) { + return; // currently not used if ((TVPCPUFeatures & TVP_CPU_FAMILY_MASK) == TVP_CPU_FAMILY_ARM && (TVPCPUFeatures & TVP_CPU_HAS_NEON)) { -#if defined( DEBUG_ARM_NEON) && 0 + _VolumeMix_CPP_S16_CH2 = func16[1]; + _VolumeMix_CPP_F32_CH2 = func32[1]; + func16[1] = VolumeMix_NEON_S16_CH2; +#if defined( DEBUG_ARM_NEON) && 1 static const signed short testsrc_s16[12] = { -1,0,0x7FFF,-0x8000, 0x7000, 0x7000, -0x7000, -0x7000, @@ -21,11 +113,12 @@ void TVPWaveMixer_ASM_Init() }; signed short testdest_s16_1[12]; + short vol16[] = { 16384 , 16384 , 16384 , 16384 }; memcpy(testdest_s16_1, testdest_s16_c, sizeof(testdest_s16_c)); - VolumeMix_c_s16(testdest_s16_1, testsrc_s16, 12, 16384); + _VolumeMix_CPP_S16_CH2(testdest_s16_1, testsrc_s16, 4, vol16); signed short testdest_s16_2[12]; memcpy(testdest_s16_2, testdest_s16_c, sizeof(testdest_s16_c)); - VolumeMix_NEON_s16(testdest_s16_2, testsrc_s16, 12, 16384); + VolumeMix_NEON_S16_CH2(testdest_s16_2, testsrc_s16, 4, vol16); assert(!memcmp(testdest_s16_1, testdest_s16_2, sizeof(testdest_s16_1))); static const float testsrc_f32[12] = { @@ -39,15 +132,15 @@ void TVPWaveMixer_ASM_Init() float testdest_f32_1[12]; memcpy(testdest_f32_1, testdest_f32_c, sizeof(testdest_f32_c)); - VolumeMix_c_f32(testdest_f32_1, testsrc_f32, 12, 0.9); + _VolumeMix_CPP_F32_CH2(testdest_f32_1, testsrc_f32, 4, vol16); float testdest_f32_2[12]; memcpy(testdest_f32_2, testdest_f32_c, sizeof(testdest_f32_c)); - VolumeMix_NEON_f32(testdest_f32_2, testsrc_f32, 12, 0.9); + VolumeMix_NEON_F32_CH2(testdest_f32_2, testsrc_f32, 4, vol16); assert(!memcmp(testdest_f32_1, testdest_f32_2, sizeof(testdest_f32_1))); #endif //VolumeMix = VolumeMix_NEON; } } -#ifdef DEBUG_ARM_NEON +#ifdef __cplusplus } #endif \ No newline at end of file diff --git a/src/core/sound/win32/WaveImpl.cpp b/src/core/sound/win32/WaveImpl.cpp index 6b2f6ed4..03741581 100644 --- a/src/core/sound/win32/WaveImpl.cpp +++ b/src/core/sound/win32/WaveImpl.cpp @@ -2065,7 +2065,7 @@ void tTJSNI_WaveSoundBuffer::TryCreateSoundBuffer(bool use3d) if(Level2Buffer) delete [] Level2Buffer, Level2Buffer = NULL; Level2Buffer = new tjs_uint8[Level2BufferSize]; - SoundBuffer = TVPALSoundWrap::Create(InputFormat); + SoundBuffer = TVPCreateSoundBuffer(InputFormat, L1BufferUnits); #if 0 // setup parameters DSBUFFERDESC dsbd; @@ -2181,7 +2181,7 @@ void tTJSNI_WaveSoundBuffer::CreateSoundBuffer() } - if(failed) + if(failed || !SoundBuffer) { failed = false; //TVPWaveFormatToWAVEFORMATEXTENSIBLE2(&InputFormat, &Format, use3d); @@ -2198,7 +2198,7 @@ void tTJSNI_WaveSoundBuffer::CreateSoundBuffer() } } - if(failed) + if(failed || !SoundBuffer) { try16bits: failed = false; @@ -2657,7 +2657,7 @@ bool tTJSNI_WaveSoundBuffer::FillBuffer(bool firstwrite, bool allowpause) // drift between main clock and clocks which come from other sources // is a good environ noise. #endif - SoundBufferWritePos = SoundBuffer->GetNextBufferIndex(); + // SoundBufferWritePos = SoundBuffer->GetNextBufferIndex(); // check position tTVPWaveSegmentQueue * segment; @@ -2668,12 +2668,12 @@ bool tTJSNI_WaveSoundBuffer::FillBuffer(bool firstwrite, bool allowpause) writepos = 0; segment = L1BufferSegmentQueues + 0; bufferdecodesamplepos = L1BufferDecodeSamplePos + 0; -#if 0 +#if 1 PlayStopPos = -1; SoundBufferWritePos = 1; SoundBufferPrevReadPos = 0; #endif - SoundBuffer->SetSampleOffset(0); + // SoundBuffer->SetSampleOffset(0); } else { @@ -2733,14 +2733,15 @@ bool tTJSNI_WaveSoundBuffer::FillBuffer(bool firstwrite, bool allowpause) } #endif writepos = SoundBufferWritePos * AccessUnitBytes; - if (!SoundBuffer->IsBufferValid()) - { - if (!SoundBuffer->IsPlaying()) { // run out of buffer - SoundBuffer->Play(); - // reset offset - SoundBuffer->SetSampleOffset(writepos / InputFormat.BytesPerSample / InputFormat.Channels); - } - else { + if (SoundBuffer->GetRemainBuffers() >= TVPAL_BUFFER_COUNT) + { +// if (!SoundBuffer->IsPlaying()) { // run out of buffer +// SoundBuffer->Play(); +// // reset offset +// SoundBuffer->SetSampleOffset(writepos / InputFormat.BytesPerSample / InputFormat.Channels); +// } +// else + { return true; } } @@ -2829,7 +2830,7 @@ void tTJSNI_WaveSoundBuffer::ResetLastCheckedDecodePos(DWORD pp) rblock = SoundBufferWritePos; offset = 0; } else { - offset = SoundBuffer->GetCurrentSampleOffset(); + offset = SoundBuffer->GetCurrentPlaySamples(); rblock = offset / AccessUnitSamples; offset %= AccessUnitSamples; rblock %= L1BufferUnits; @@ -3129,7 +3130,7 @@ tjs_uint64 tTJSNI_WaveSoundBuffer::GetSamplePosition() rblock = SoundBufferWritePos; offset = 0; } else { - offset = SoundBuffer->GetCurrentSampleOffset(); + offset = SoundBuffer->GetCurrentPlaySamples(); rblock = offset / AccessUnitSamples; offset %= AccessUnitSamples; rblock %= L1BufferUnits; @@ -3439,7 +3440,7 @@ tjs_int tTJSNI_WaveSoundBuffer::GetVisBuffer(tjs_int16 *dest, tjs_int numsamples // the critical section protects only here; // the rest is not important code (does anyone care about that the retrieved // visualization becomes wrong a little ?) - offset = SoundBuffer->GetCurrentSampleOffset() + aheadsamples; + offset = SoundBuffer->GetCurrentPlaySamples() + aheadsamples; int rblock = offset / AccessUnitSamples; offset %= buffersamples; if (L1BufferSegmentQueues[rblock % L1BufferUnits].GetFilteredLength() == 0) diff --git a/src/core/sound/win32/WaveImpl.h b/src/core/sound/win32/WaveImpl.h index ff5175ca..56debb6c 100644 --- a/src/core/sound/win32/WaveImpl.h +++ b/src/core/sound/win32/WaveImpl.h @@ -28,7 +28,7 @@ #ifndef __DSOUND_INCLUDED__ struct IDirectSound; #endif -class TVPALSoundWrap; +class iTVPSoundBuffer; /*]*/ @@ -76,7 +76,7 @@ class tTJSNI_WaveSoundBuffer : public tTJSNI_BaseWaveSoundBuffer //-- buffer management ------------------------------------------------ private: - TVPALSoundWrap* SoundBuffer; + iTVPSoundBuffer* SoundBuffer; #if 0 LPDIRECTSOUND3DBUFFER Sound3DBuffer; #endif diff --git a/src/core/sound/win32/WaveMixer.cpp b/src/core/sound/win32/WaveMixer.cpp index a0ac8052..5c117f7f 100644 --- a/src/core/sound/win32/WaveMixer.cpp +++ b/src/core/sound/win32/WaveMixer.cpp @@ -7,6 +7,9 @@ #include "AL/alc.h" #include "AL/alext.h" #endif +#ifdef __ANDROID__ +#include "oboe/Oboe.h" +#endif #include #include #include "WaveImpl.h" @@ -16,325 +19,739 @@ #include #include #include "DebugIntf.h" +#include "SDL.h" +#include +#include + +class iTVPAudioRenderer; +static iTVPAudioRenderer *TVPAudioRenderer; + +template +void MixAudioS16CPP(void *dst, const void *src, int samples, int16_t *volume) { + int16_t *dst16 = (int16_t *)dst; + const int16_t *src16 = (const int16_t *)src; + while (samples--) { + for (int i = 0; i < ch; ++i) { + int src_sample = *src16++; + src_sample = (src_sample * volume[i]) >> 14; + int dest_sample = *dst16; + if (src_sample > 0 && dest_sample > 0) { + dest_sample = src_sample + dest_sample - ((dest_sample * src_sample + 0x8000) >> 15); + } else if (src_sample < 0 && dest_sample < 0) { + dest_sample = src_sample + dest_sample + ((dest_sample * src_sample) >> 15); + } else { + dest_sample += src_sample; + } + *dst16++ = dest_sample; + } + } +} -static ALCdevice *TVPPrimarySoundHandle = nullptr; -static ALCcontext *s_ALContext = nullptr; +template +void MixAudioF32CPP(void *dst, const void *src, int samples, int16_t *volume) { + float *dst32 = (float *)dst; + const float *src32 = (const float *)src; + const float fmaxvolume = 1.0f / 16384/*tTVPSoundBuffer::MAX_VOLUME*/; + float fvolume[ch]; + for (int i = 0; i < ch; ++i) + fvolume[i] = volume[i] * fmaxvolume; + while (samples--) { + for (int i = 0; i < ch; ++i) { + float src_sample = SDL_SwapFloatLE(*src32++) * fvolume[i]; + float dest_sample = SDL_SwapFloatLE(*dst32); + if (src_sample > 0 && dest_sample > 0) { + dest_sample = src_sample + dest_sample - dest_sample * src_sample; + } else if (src_sample < 0 && dest_sample < 0) { + dest_sample = src_sample + dest_sample + dest_sample * src_sample; + } else { + dest_sample += src_sample; + } + *(dst32++) = SDL_SwapFloatLE(dest_sample); + } + } +} -extern "C" void TVPInitSoundASM(); -extern "C" void TVPInitSoundASM() {} +typedef void(FAudioMix)(void *dst, const void *src, int samples, int16_t *volume); +static FAudioMix *_AudioMixS16[8] = { // 7.1 max + &MixAudioS16CPP<1>, + &MixAudioS16CPP<2>, + &MixAudioS16CPP<3>, + &MixAudioS16CPP<4>, + &MixAudioS16CPP<5>, + &MixAudioS16CPP<6>, + &MixAudioS16CPP<7>, + &MixAudioS16CPP<8> +}; +static FAudioMix *_AudioMixF32[8] = { // 7.1 max + &MixAudioF32CPP<1>, + &MixAudioF32CPP<2>, + &MixAudioF32CPP<3>, + &MixAudioF32CPP<4>, + &MixAudioF32CPP<5>, + &MixAudioF32CPP<6>, + &MixAudioF32CPP<7>, + &MixAudioF32CPP<8> +}; -void TVPInitDirectSound() -{ - //TVPInitSoundOptions(); +extern "C" void TVPWaveMixer_ASM_Init(FAudioMix **func16, FAudioMix **func32); + +class tTVPSoundBuffer : public iTVPSoundBuffer { +public: + bool _playing = false; + float _volume = 1; + float _pan = 0; + const signed int MAX_VOLUME = 16384; // limit in signed 16bit + int16_t _volume_raw[8]; + SDL_AudioCVT *_cvt = nullptr; + std::vector _cvtbuf; + int _frame_size = 0; + + void RecalcVolume() { + if (_pan > 0) { + _volume_raw[0] = (1.0f - _pan) * _volume * MAX_VOLUME; + } else { + _volume_raw[0] = _volume * MAX_VOLUME; + } + if (_pan < 0) { + _volume_raw[1] = (_pan + 1.0f) * _volume * MAX_VOLUME; + } else { + _volume_raw[1] = _volume * MAX_VOLUME; + } + _volume_raw[2] = _volume_raw[0]; // for SIMD + _volume_raw[3] = _volume_raw[1]; + } + std::mutex _buffer_mtx; + std::deque > _buffers; + tjs_uint _sendedFrontBuffer = 0; + tjs_uint _sendedSamples = 0, _inCachedSamples = 0; + + tTVPSoundBuffer(int framesize, SDL_AudioCVT *cvt) : _frame_size(framesize), _cvt(cvt) { + RecalcVolume(); + if (cvt) { + _cvtbuf.resize(/*2352*/ 2400 * 2 * 4 * _cvt->len_mult); // IEEE f.32 stereo 48000kHz + _cvt->buf = &_cvtbuf.front(); + } + } + virtual ~tTVPSoundBuffer(); + virtual void Release() override { delete this; } + virtual void Play() override { + _playing = true; + } + virtual void Pause() override { + _playing = false; + } + virtual void Stop() override { + _playing = false; + Reset(); + } + virtual void Reset() override { + std::lock_guard lk(_buffer_mtx); + _buffers.clear(); + _inCachedSamples = 0; + _sendedFrontBuffer = 0; + _sendedSamples = 0; + } + virtual bool IsPlaying() override { + return _playing; + } + virtual void SetVolume(float v) override { + _volume = v; + RecalcVolume(); + } + virtual float GetVolume() override { return _volume; } + virtual void SetPan(float v) override { + _pan = v; + RecalcVolume(); + } + virtual float GetPan() override { return _pan; } + virtual void AppendBuffer(const void *_inbuf, unsigned int inlen/*, int tag = 0*/) override { + if (_cvt) { + std::vector buffer; + uint8_t* inbuf = (uint8_t*)_inbuf; + int buflen = _frame_size * 2352; + _cvt->len = buflen; + while (inlen > buflen) { // fill 2352 samples to fit 48k/44.1k + memcpy(_cvt->buf, inbuf, buflen); + SDL_ConvertAudio(_cvt); + buffer.insert(buffer.end(), _cvt->buf, _cvt->buf + _cvt->len_cvt); + inlen -= buflen; + inbuf += buflen; + } + if (inlen > 0) { + int buflen = inlen; + memcpy(_cvt->buf, inbuf, buflen); + _cvt->len = buflen; + SDL_ConvertAudio(_cvt); + buffer.insert(buffer.end(), _cvt->buf, _cvt->buf + _cvt->len_cvt); + } + std::lock_guard lk(_buffer_mtx); + _inCachedSamples += buffer.size() / _frame_size; + _buffers.emplace_back(); + _buffers.back().swap(buffer); + } else { + std::lock_guard lk(_buffer_mtx); + _buffers.emplace_back((uint8_t*)_inbuf, ((uint8_t*)_inbuf) + inlen); + _inCachedSamples += inlen / _frame_size; + } + } + virtual bool IsBufferValid() override { + return true; // unlimited buffer size + //return !_buffers.empty(); // thread safe if read only + } + virtual tjs_uint GetLatencySamples() override; +// virtual void SetSampleOffset(tjs_uint n) override { +// _sendedSamples = n; +// } + virtual int GetRemainBuffers() override { + return _buffers.size(); + } + virtual tjs_uint GetCurrentPlaySamples() override { + return _sendedSamples - GetLatencySamples(); + } + virtual float GetLatencySeconds() override; - // set primary buffer 's sound format - if (!TVPPrimarySoundHandle) - { - TVPInitSoundASM(); - ALboolean enumeration = alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"); - if (enumeration == AL_FALSE) { - // enumeration not supported - TVPPrimarySoundHandle = alcOpenDevice(NULL); + void FillBuffer(uint8_t *out, int len); +}; + +class iTVPAudioRenderer { +protected: + SDL_AudioSpec _spec; + std::mutex _streams_mtx; + std::unordered_set _streams; + int _frame_size = 0; + +public: + iTVPAudioRenderer() { + memset(&_spec, 0, sizeof(_spec)); + _spec.freq = 48000; + _spec.format = AUDIO_S16; + _spec.channels = 2; + _spec.callback = [](void *p, Uint8 *s, int l) { + memset(s, 0, l); + ((iTVPAudioRenderer*)p)->FillBuffer(s, l); + }; + _spec.userdata = this; + _spec.size = 4; + _frame_size = 4; + } + void InitMixer() { + if (SDL_Init(SDL_INIT_AUDIO) < 0) { // for format converter + SDL_Log("Fail to initialize audio."); + return; + } + } + + FAudioMix *DoMixAudio; + void SetupMixer() { + TVPWaveMixer_ASM_Init(_AudioMixS16, _AudioMixF32); + if (_spec.format == AUDIO_S16LSB) { + DoMixAudio = _AudioMixS16[_spec.channels - 1]; + } else if (_spec.format == AUDIO_F32LSB) { + DoMixAudio = _AudioMixF32[_spec.channels - 1]; } else { - // enumeration supported - const ALCchar *devices = alcGetString(NULL, ALC_DEVICE_SPECIFIER); - std::vector alldev; - ttstr log(TJS_W("(info) Sound Driver/Device found : ")); - while (*devices) { - TVPAddImportantLog(log + devices); - alldev.emplace_back(devices); - devices += alldev.back().length(); + DoMixAudio = [](void *dst, const void *src, int samples, int16_t *volume) {}; + } + } + + virtual bool Init() = 0; + + virtual tTVPSoundBuffer* CreateStream(tTVPWaveFormat &fmt, int bufcount) { + SDL_AudioSpec spec; + memset(&spec, 0, sizeof(spec)); + spec.freq = fmt.SamplesPerSec; + spec.channels = fmt.Channels; + if (fmt.IsFloat) { + spec.format = AUDIO_F32LSB; + } else { + switch (fmt.BitsPerSample) { + case 8: spec.format = AUDIO_S8; break; + case 16: spec.format = AUDIO_S16LSB; break; + case 32: spec.format = AUDIO_S32LSB; break; + default: return nullptr; + } + } + SDL_AudioCVT *cvt = nullptr; + if (spec.freq != _spec.freq || + spec.channels != _spec.channels || + spec.format != _spec.format) { + cvt = new SDL_AudioCVT; + int err = SDL_BuildAudioCVT(cvt, + spec.format, spec.channels, spec.freq, + _spec.format, _spec.channels, _spec.freq); + if (err != 1) { + delete cvt; return nullptr; } - TVPPrimarySoundHandle = alcOpenDevice(alldev[0].c_str()); } - s_ALContext = alcCreateContext(TVPPrimarySoundHandle, NULL); - alcMakeContextCurrent(s_ALContext); + tTVPSoundBuffer* s = new tTVPSoundBuffer(fmt.BytesPerSample * fmt.Channels, cvt); + std::lock_guard lk(_streams_mtx); + _streams.emplace(s); + return s; } -} -void TVPUninitDirectSound() -{ - if (TVPPrimarySoundHandle) { - if (s_ALContext) { - //alDeleteSources(TVP_MAX_AUDIO_COUNT, _alSources); + void ReleaseStream(tTVPSoundBuffer* s) { + std::lock_guard lk(_streams_mtx); + _streams.erase(s); + } - alcMakeContextCurrent(nullptr); - alcDestroyContext(s_ALContext); - s_ALContext = nullptr; + void FillBuffer(Uint8 *buf, int len) { + // memset(buf, 0, len); + std::lock_guard lk(_streams_mtx); + for (tTVPSoundBuffer* s : _streams) { + s->FillBuffer(buf, len); } - alcCloseDevice(TVPPrimarySoundHandle); - TVPPrimarySoundHandle = nullptr; } -} -struct ALSoundImpl { - ALSoundImpl() { - alGenSources(1, &_alSource); - alGenBuffers(TVPAL_BUFFER_COUNT, _bufferIds); + int MixAudio(uint8_t *dst, uint8_t *src, int len, int16_t *vol) { + int samples = len / _frame_size; + DoMixAudio(dst, src, samples, vol); + return samples; } - ~ALSoundImpl() { - alDeleteBuffers(TVPAL_BUFFER_COUNT, _bufferIds); - alDeleteSources(1, &_alSource); + + const SDL_AudioSpec& GetSpec() { + return _spec; } - ALuint _alSource; - ALenum _alFormat; - ALuint _bufferIds[TVPAL_BUFFER_COUNT]; + + virtual int32_t GetUnprocessedSamples() { return 0; } }; -TVPALSoundWrap::TVPALSoundWrap() { - if (!TVPPrimarySoundHandle) TVPInitDirectSound(); - _impl = new ALSoundImpl; - _inPlaying = false; - _bufferIdx = -1; - _samplesProcessed = 0; +tTVPSoundBuffer::~tTVPSoundBuffer() +{ + Stop(); + TVPAudioRenderer->ReleaseStream(this); + if (_cvt) delete _cvt; } -TVPALSoundWrap::~TVPALSoundWrap() { - Stop(); - delete _impl; +tjs_uint tTVPSoundBuffer::GetLatencySamples() +{ + int32_t samples = TVPAudioRenderer->GetUnprocessedSamples(); + return samples + _inCachedSamples; } -TVPALSoundWrap * TVPALSoundWrap::Create(const tTVPWaveFormat &desired) { - TVPALSoundWrap* ret = new TVPALSoundWrap(); - ret->Init(desired); -// TVPAddSoundMixer(ret); +float tTVPSoundBuffer::GetLatencySeconds() +{ + return GetLatencySamples() / TVPAudioRenderer->GetSpec().freq; +} - return ret; +void tTVPSoundBuffer::FillBuffer(uint8_t *out, int len) +{ + if (!_playing) return; + std::lock_guard lk(_buffer_mtx); + while (len > 0 && !_buffers.empty()) { + std::vector &buf = _buffers.front(); + if (buf.size() > _sendedFrontBuffer) { + int n = std::min((size_t)len, buf.size() - _sendedFrontBuffer); + int samples = TVPAudioRenderer->MixAudio(out, &buf.front() + _sendedFrontBuffer, n, _volume_raw); + _sendedSamples += samples; + _inCachedSamples -= samples; + _sendedFrontBuffer += n; + out += n; + len -= n; + } + if (_sendedFrontBuffer >= buf.size()) { + _sendedFrontBuffer = 0; + _buffers.pop_front(); + } + } } -void TVPALSoundWrap::Init(const tTVPWaveFormat &desired) { - SoundFormat = desired; - alSourcef(_impl->_alSource, AL_GAIN, 1.0f); - //InitSoundMixer(desired); -// assert(desired.BigEndian == false); -// assert(desired.Signed == true); - //alSourceQueueBuffers(_impl->_alSource, TVPAL_BUFFER_COUNT, _bufferIds); - if (desired.Channels == 1) { - switch (desired.BitsPerSample) { - case 8: - _impl->_alFormat = AL_FORMAT_MONO8; - break; - case 16: - _impl->_alFormat = AL_FORMAT_MONO16; - break; - default: - assert(false); +class tTVPAudioRendererSDL : public iTVPAudioRenderer { + SDL_AudioDeviceID _playback_id; + +public: + bool Init() override { + InitMixer(); + _playback_id = SDL_OpenAudioDevice(nullptr, false, &_spec, &_spec, SDL_AUDIO_ALLOW_ANY_CHANGE); + if (_playback_id <= 0) { + SDL_Log("Fail to open audio @%dHz.", _spec.freq); + return false; + } + _frame_size = SDL_AUDIO_BITSIZE(_spec.format) / 8 * _spec.channels; + SDL_Log("Audio Device: %s", SDL_GetCurrentAudioDriver()); + SDL_PauseAudioDevice(_playback_id, false); + SetupMixer(); + return true; + } +}; + +#ifdef __ANDROID__ +class tTVPAudioRendererOboe : public iTVPAudioRenderer, public oboe::AudioStreamCallback { + oboe::AudioStream *_oboeAudioStream = nullptr; + +public: + virtual ~tTVPAudioRendererOboe() { + if (_oboeAudioStream) delete _oboeAudioStream; + } + + bool Init() override { + InitMixer(); + // Create a builder + oboe::AudioStreamBuilder builder; + //builder.setFormat(oboe::AudioFormat::I16); + builder.setChannelCount(2); + //builder.setSampleRate(oboe::kUnspecified); + builder.setCallback(this); + // builder.setPerformanceMode(PerformanceMode::None); + // builder.setSharingMode(SharingMode::Shared); + oboe::Result result = builder.openStream(&_oboeAudioStream); +// if (result != oboe::Result::OK) { +// // try down sample rate +// _spec.freq = 44100; +// builder.setSampleRate(_spec.freq); +// result = builder.openStream(&_oboeAudioStream); +// } + if (result == oboe::Result::OK) { + _spec.freq = _oboeAudioStream->getSampleRate(); + switch (_oboeAudioStream->getFormat()) { + case oboe::AudioFormat::I16: _spec.format = AUDIO_S16LSB; break; + case oboe::AudioFormat::Float: _spec.format = AUDIO_F32LSB; break; + } + _frame_size = SDL_AUDIO_BITSIZE(_spec.format) / 8 * _spec.channels; + _oboeAudioStream->requestStart(); + SDL_Log("Audio Device: Oboe @%dHz", _spec.freq); + SetupMixer(); + return true; + } + SDL_Log("Fail to open Oboe audio"); + // SetupSDL(); + return false; + } + + virtual oboe::DataCallbackResult onAudioReady( + oboe::AudioStream *oboeStream, + void *audioData, + int32_t numFrames) override { + int len = _frame_size * numFrames; + memset(audioData, 0, _frame_size * numFrames); + //if (oboeStream == _oboeAudioStream) + FillBuffer((uint8_t*)audioData, len); +// else +// fillCaptureBuffer((uint8_t*)audioData, /*Mono*/2 * numFrames); + return oboe::DataCallbackResult::Continue; + } + + virtual int32_t GetUnprocessedSamples() { + int64_t hardwareFrameIndex; + int64_t timeNanoseconds; + oboe::Result result = _oboeAudioStream->getTimestamp(CLOCK_MONOTONIC, &hardwareFrameIndex, &timeNanoseconds); + if (result != oboe::Result::OK) { // OpenSL TODO accumulate calc + return 0; } - } else if (desired.Channels == 2) { - switch (desired.BitsPerSample) { - case 8: - _impl->_alFormat = AL_FORMAT_STEREO8; - break; - case 16: - _impl->_alFormat = AL_FORMAT_STEREO16; - break; - default: + int64_t appFrameIndex = _oboeAudioStream->getFramesWritten(); + return appFrameIndex - hardwareFrameIndex; + } +}; +#endif + +class tTVPSoundBufferAL : public tTVPSoundBuffer { + typedef tTVPSoundBuffer inherit; + + ALuint _alSource; + ALenum _alFormat; + ALuint *_bufferIds, *_bufferIds2; + tjs_uint *_bufferSize; + tjs_uint _bufferCount; + int _bufferIdx = -1; + tTVPWaveFormat _format; +public: + tTVPSoundBufferAL(tTVPWaveFormat &desired, int bufcount) + : tTVPSoundBuffer(desired.BytesPerSample * desired.Channels, nullptr), _bufferCount(bufcount) + { + _bufferIds = new ALuint[bufcount]; + _bufferIds2 = new ALuint[bufcount]; + _bufferSize = new tjs_uint[bufcount]; + _format = desired; + alGenSources(1, &_alSource); + alGenBuffers(_bufferCount, _bufferIds); + alSourcef(_alSource, AL_GAIN, 1.0f); + if (desired.Channels == 1) { + switch (desired.BitsPerSample) { + case 8: + _alFormat = AL_FORMAT_MONO8; + break; + case 16: + _alFormat = AL_FORMAT_MONO16; + break; + default: + assert(false); + } + } else if (desired.Channels == 2) { + switch (desired.BitsPerSample) { + case 8: + _alFormat = AL_FORMAT_STEREO8; + break; + case 16: + _alFormat = AL_FORMAT_STEREO16; + break; + default: + assert(false); + } + } else { assert(false); } - } else { - assert(false); } - _bufferIdx = -1; - //_tagPoint = 0; -} -bool TVPALSoundWrap::IsBufferValid() { - ALint processed = 0; - alGetSourcei(_impl->_alSource, AL_BUFFERS_PROCESSED, &processed); - if (processed > 0) return true; - ALint queued = 0; - alGetSourcei(_impl->_alSource, AL_BUFFERS_QUEUED, &queued); - return queued < TVPAL_BUFFER_COUNT; -} + virtual ~tTVPSoundBufferAL() { + alDeleteBuffers(_bufferCount, _bufferIds); + alDeleteSources(1, &_alSource); + delete[]_bufferIds; + delete[]_bufferIds2; + delete[]_bufferSize; + } + + bool IsBufferValid() override { + ALint processed = 0; + alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &processed); + if (processed > 0) return true; + ALint queued = 0; + alGetSourcei(_alSource, AL_BUFFERS_QUEUED, &queued); + return queued < _bufferCount; + } -void TVPALSoundWrap::AppendBuffer(const void *buf, unsigned int len/*, int tag*/) { - if (len <= 0) return; - std::lock_guard lk(_mutex); - - /* First remove any processed buffers. */ - ALint processed = 0; - alGetSourcei(_impl->_alSource, AL_BUFFERS_PROCESSED, &processed); - if (processed > 0) { - ALuint ids[TVPAL_BUFFER_COUNT]; - alSourceUnqueueBuffers(_impl->_alSource, processed, ids); - for (int i = 0; i < processed; ++i) { - for (int j = 0; j < TVPAL_BUFFER_COUNT; ++j) { - if (_impl->_bufferIds[j] == ids[i]) - { - _samplesProcessed += _bufferSize[j] / SoundFormat.BytesPerSample / SoundFormat.Channels; - break; + virtual void AppendBuffer(const void *buf, unsigned int len/*, int tag = 0*/) override { + if (len <= 0) return; + std::lock_guard lk(_buffer_mtx); + + /* First remove any processed buffers. */ + ALint processed = 0; + alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &processed); + if (processed > 0) { + alSourceUnqueueBuffers(_alSource, processed, _bufferIds2); + checkerr("alSourceUnqueueBuffers"); + for (int i = 0; i < processed; ++i) { + for (int j = 0; j < _bufferCount; ++j) { + if (_bufferIds[j] == _bufferIds2[i]) + { + _sendedSamples += _bufferSize[j] / _frame_size; + break; + } } } } + + /* Refill the buffer queue. */ + ALint queued = 0; + alGetSourcei(_alSource, AL_BUFFERS_QUEUED, &queued); + + if (queued >= _bufferCount) + return; + ++_bufferIdx; + if (_bufferIdx >= _bufferCount) _bufferIdx = 0; + ALuint bufid = _bufferIds[_bufferIdx]; + alBufferData(bufid, _alFormat, buf, len, _format.SamplesPerSec); + checkerr("alBufferData"); + alSourceQueueBuffers(_alSource, 1, &bufid); + checkerr("alSourceQueueBuffers"); + //_tags[_bufferIdx] = tag; + _bufferSize[_bufferIdx] = len; } - /* Refill the buffer queue. */ - ALint queued = 0; - alGetSourcei(_impl->_alSource, AL_BUFFERS_QUEUED, &queued); -#if 0 - tjs_uint32 tick = TVPGetTickTime(); - int ms = tick % 1000; tick /= 1000; - int sec = tick % 60; tick /= 60; - int min = tick % 60; tick /= 60; - int hour = tick; - std::stringstream timestr; - if (hour) timestr << std::setw(2) << std::setfill('0') << hour << ":"; - if (hour || min) timestr << std::setw(2) << std::setfill('0') << min << ":"; - timestr << std::setw(2) << std::setfill('0') << sec << "."; - timestr << std::setw(3) << std::setfill('0') << ms; - std::string stime = timestr.str(); - - ALint state; - alGetSourcei(_impl->_alSource, AL_SOURCE_STATE, &state); - printf("[oal]%s id=%d stat=%d queue=%d proced=%f[s]\n", stime.c_str(), _impl->_alSource, state, - queued, (float)_samplesProcessed / SoundFormat.BytesPerSample / SoundFormat.SamplesPerSec / SoundFormat.Channels); -#endif - if (queued >= TVPAL_BUFFER_COUNT) - return; - ++_bufferIdx; - if (_bufferIdx >= TVPAL_BUFFER_COUNT) _bufferIdx = 0; - ALuint bufid = _impl->_bufferIds[_bufferIdx]; - alBufferData(bufid, _impl->_alFormat, buf, len, SoundFormat.SamplesPerSec); - alSourceQueueBuffers(_impl->_alSource, 1, &bufid); - //_tags[_bufferIdx] = tag; - _bufferSize[_bufferIdx] = len; -} + void Reset() override { + inherit::Reset(); + std::lock_guard lk(_buffer_mtx); + alSourceRewind(_alSource); + alSourcei(_alSource, AL_BUFFER, 0); + } -void TVPALSoundWrap::Reset() { - Rewind(); -} + void Pause() override { + alSourcePause(_alSource); + checkerr("Pause"); + _playing = false; + } + + static void checkerr(const char *funcname); + + void Play() override { + ALenum state; + alGetSourcei(_alSource, AL_SOURCE_STATE, &state); + checkerr("Play"); + if (state != AL_PLAYING) { + alSourcePlay(_alSource); + checkerr("Play"); + } -void TVPALSoundWrap::Pause() { - alSourcePause(_impl->_alSource); - _inPlaying = false; -} + _playing = true; + } -void TVPALSoundWrap::Play() { - ALenum state; - alGetSourcei(_impl->_alSource, AL_SOURCE_STATE, &state); - if (state != AL_PLAYING) { -// ALint processed = 0, queued; -// alGetSourcei(_impl->_alSource, AL_BUFFERS_PROCESSED, &processed); -// alGetSourcei(_impl->_alSource, AL_BUFFERS_QUEUED, &queued); -// if (queued + processed != _attachedBuffers) { -// alSourceStop(_impl->_alSource); -// _attachedBuffers = 0; -// } -// std::vector silentbuff; -// silentbuff.resize(); - //_tagPoint = 0; - alSourcePlay(_impl->_alSource); + void Stop() override { + alSourceStop(_alSource); + checkerr("Stop"); + Reset(); + _bufferIdx = -1; + _playing = false; } - _inPlaying = true; -} + void SetVolume(float volume) override { + alSourcef(_alSource, AL_GAIN, volume); + checkerr("SetVolume"); + } -void TVPALSoundWrap::Stop() { - alSourceStop(_impl->_alSource); - alSourcei(_impl->_alSource, AL_BUFFER, 0); - _bufferIdx = -1; - //_tagPoint = 0; - _inPlaying = false; - _samplesProcessed = 0; -} + float GetVolume() override { + float volume = 0; + alGetSourcef(_alSource, AL_GAIN, &volume); + return volume; + } -void TVPALSoundWrap::SetVolume(float volume) { - alSourcef(_impl->_alSource, AL_GAIN, volume); -} + void SetPan(float pan) override { + float sourcePosAL[] = { pan, 0.0f, 0.0f }; + alSourcefv(_alSource, AL_POSITION, sourcePosAL); + } -float TVPALSoundWrap::GetVolume() { - float volume = 0; - alGetSourcef(_impl->_alSource, AL_GAIN, &volume); - return volume; -} + float GetPan() override { + float sourcePosAL[3]; + alGetSourcefv(_alSource, AL_POSITION, sourcePosAL); + return sourcePosAL[0]; + } -void TVPALSoundWrap::SetPan(float pan) { - float sourcePosAL[] = { pan, 0.0f, 0.0f }; - alSourcefv(_impl->_alSource, AL_POSITION, sourcePosAL); -} + bool IsPlaying() override { + ALenum state; + alGetSourcei(_alSource, AL_SOURCE_STATE, &state); + return state == AL_PLAYING; + } -float TVPALSoundWrap::GetPan() { - float sourcePosAL[3]; - alGetSourcefv(_impl->_alSource, AL_POSITION, sourcePosAL); - return sourcePosAL[0]; -} + void SetPosition(float x, float y, float z) override { + float sourcePosAL[] = { x, y, z }; + alSourcefv(_alSource, AL_POSITION, sourcePosAL); + checkerr("SetPosition"); + } -tjs_uint TVPALSoundWrap::GetCurrentSampleOffset() { - ALint offset = 0; - alGetSourcei(_impl->_alSource, AL_SAMPLE_OFFSET, &offset); + int GetRemainBuffers() override { + ALint processed, queued = 0; + alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(_alSource, AL_BUFFERS_QUEUED, &queued); + return queued - processed; + } -// printf("offset = %f(%d)\n", (float)(_samplesProcessed + offset) / SoundFormat.SamplesPerSec, -// _samplesProcessed + offset); - return _samplesProcessed + offset; -} + tjs_uint GetLatencySamples() override { + std::lock_guard lk(_buffer_mtx); + ALint offset = 0, queued = 0; + alGetSourcei(_alSource, AL_BYTE_OFFSET, &offset); + alGetSourcei(_alSource, AL_BUFFERS_QUEUED, &queued); + int remainBuffers = queued; + if (remainBuffers == 0) return 0; + tjs_int total = -offset; + for (int i = 0; i < remainBuffers; ++i) { + int idx = _bufferIdx + 1 - remainBuffers + i; + if (idx >= _bufferCount) idx -= _bufferCount; + else if (idx < 0) idx += _bufferCount; + total += _bufferSize[idx]; + } + return total / _frame_size; + } + + virtual float GetLatencySeconds() override { + return (float)GetLatencySamples() / _format.SamplesPerSec; + } -bool TVPALSoundWrap::IsPlaying() { - ALenum state; - alGetSourcei(_impl->_alSource, AL_SOURCE_STATE, &state); - return state == AL_PLAYING; + virtual tjs_uint GetCurrentPlaySamples() override { + ALint offset = 0; + alGetSourcei(_alSource, AL_SAMPLE_OFFSET, &offset); + return _sendedSamples + offset; + } +}; + +class tTVPAudioRendererAL : public iTVPAudioRenderer { + ALCdevice *_device = nullptr; + ALCcontext *_context = nullptr; +public: + virtual ~tTVPAudioRendererAL() { + if (_context) { + //alDeleteSources(TVP_MAX_AUDIO_COUNT, _alSources); + alcMakeContextCurrent(nullptr); + alcDestroyContext(_context); + } + if (_device) + alcCloseDevice(_device); + } + bool Init() override { + ALboolean enumeration = alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"); + if (enumeration == AL_FALSE) { + // enumeration not supported + _device = alcOpenDevice(NULL); + } else { + // enumeration supported + const ALCchar *devices = alcGetString(NULL, ALC_DEVICE_SPECIFIER); + std::vector alldev; + ttstr log(TJS_W("(info) Sound Driver/Device found : ")); + while (*devices) { + TVPAddImportantLog(log + devices); + alldev.emplace_back(devices); + devices += alldev.back().length(); + } + _device = alcOpenDevice(alldev[0].c_str()); + } + if (!_device) return false; + + _context = alcCreateContext(_device, NULL); + alcMakeContextCurrent(_context); + + return true; + } + + virtual tTVPSoundBuffer* CreateStream(tTVPWaveFormat &fmt, int bufcount) override { + tTVPSoundBuffer* s = new tTVPSoundBufferAL(fmt, bufcount); + _streams.emplace(s); + return s; + } + + ALCcontext *GetContext() { + return _context; + } +}; + +void tTVPSoundBufferAL::checkerr(const char *funcname) { +#if _DEBUG + ALCcontext *ctx = static_cast (TVPAudioRenderer)->GetContext(); + if (alcGetCurrentContext() != ctx) { + alcMakeContextCurrent(ctx); + } + ALenum err = alGetError(); + if (AL_NO_ERROR == err) return; + SDL_Log("%s OpenAL Error %X", funcname, err); +#endif } -void TVPALSoundWrap::Update() { - while (IsBufferValid() && DoFillBuffer()); - if (_inPlaying && !IsPlaying()) Play(); // out of buffer +static iTVPAudioRenderer *CreateAudioRenderer() { + iTVPAudioRenderer *renderer = nullptr; +#ifdef __ANDROID__ + renderer = new tTVPAudioRendererOboe; + if (renderer->Init()) return renderer; + delete renderer; +#elif defined(_MSC_VER) && 0 + renderer = new tTVPAudioRendererSDL; + renderer->Init(); + return renderer; +#endif + renderer = new tTVPAudioRendererAL; + renderer->Init(); + return renderer; } -bool TVPALSoundWrap::DoFillBuffer() +void TVPInitDirectSound(int freq) { - return false; + if (!TVPAudioRenderer) { + TVPAudioRenderer = CreateAudioRenderer(); + } + //TVPInitSoundOptions(); } -void TVPALSoundWrap::SetPosition(float x, float y, float z) { - float sourcePosAL[] = { x, y, z }; - alSourcefv(_impl->_alSource, AL_POSITION, sourcePosAL); +void TVPUninitDirectSound() +{ + // nothing to do } +iTVPSoundBuffer* TVPCreateSoundBuffer(tTVPWaveFormat &fmt, int bufcount) +{ + return TVPAudioRenderer->CreateStream(fmt, bufcount); +} +#if 0 int TVPALSoundWrap::GetNextBufferIndex() { int n = _bufferIdx + 1; if (n >= TVPAL_BUFFER_COUNT) n = 0; return n; } -int TVPALSoundWrap::GetRemainBuffers() { - ALint processed, queued = 0; - alGetSourcei(_impl->_alSource, AL_BUFFERS_PROCESSED, &processed); - alGetSourcei(_impl->_alSource, AL_BUFFERS_QUEUED, &queued); - return queued - processed; -} - -int TVPALSoundWrap::GetValidBuffers() { - return TVPAL_BUFFER_COUNT - GetRemainBuffers(); -} - void TVPALSoundWrap::SetSampleOffset(tjs_uint n /*= 0*/) { _samplesProcessed = n; } - -tjs_uint TVPALSoundWrap::GetUnprocessedSamples() { - std::lock_guard lk(_mutex); - ALint offset = 0, queued = 0; - alGetSourcei(_impl->_alSource, AL_BYTE_OFFSET, &offset); - alGetSourcei(_impl->_alSource, AL_BUFFERS_QUEUED, &queued); - int remainBuffers = queued; - if (remainBuffers == 0) return 0; - tjs_int total = -offset; - for (int i = 0; i < remainBuffers; ++i) { - int idx = _bufferIdx + 1 - remainBuffers + i; - if (idx >= TVPAL_BUFFER_COUNT) idx -= TVPAL_BUFFER_COUNT; - else if (idx < 0) idx += TVPAL_BUFFER_COUNT; - total += _bufferSize[idx]; - } - return total / SoundFormat.BytesPerSample / SoundFormat.Channels; -} - -void TVPALSoundWrap::Rewind() { - std::lock_guard lk(_mutex); - alSourceRewind(_impl->_alSource); - alSourcei(_impl->_alSource, AL_BUFFER, 0); - _samplesProcessed = 0; -} - -void TVPALSoundWrap::Release() { - delete this; -} +#endif diff --git a/src/core/sound/win32/WaveMixer.h b/src/core/sound/win32/WaveMixer.h index 0f4e28d3..fc258e52 100644 --- a/src/core/sound/win32/WaveMixer.h +++ b/src/core/sound/win32/WaveMixer.h @@ -6,10 +6,37 @@ #define TVPAL_BUFFER_COUNT 4 struct ALSoundImpl; -void TVPInitDirectSound(); +void TVPInitDirectSound(int freq = 48000); void TVPUninitDirectSound(); -class TVPALSoundWrap /*: public iSoundMixer*/ { +class iTVPSoundBuffer { +public: + virtual ~iTVPSoundBuffer() {} + virtual void Release() = 0; + virtual void Play() = 0; + virtual void Pause() = 0; + virtual void Stop() = 0; + virtual void Reset() = 0; + virtual bool IsPlaying() = 0; + virtual void SetVolume(float v) = 0; + virtual float GetVolume() = 0; + virtual void SetPan(float v) = 0; + virtual float GetPan() = 0; + virtual void AppendBuffer(const void *buf, unsigned int len/*, int tag = 0*/) = 0; + virtual bool IsBufferValid() = 0; + virtual tjs_uint GetCurrentPlaySamples() = 0; + virtual tjs_uint GetLatencySamples() = 0; + virtual float GetLatencySeconds() { return 0; } +// virtual void SetSampleOffset(tjs_uint n) = 0; + virtual int GetRemainBuffers() = 0; +// virtual int GetNextBufferIndex() = 0; + virtual void SetPosition(float x, float y, float z) { + // not implemented + } +}; +iTVPSoundBuffer* TVPCreateSoundBuffer(tTVPWaveFormat &fmt, int bufcount); +#if 0 +class TVPALSoundWrap : public iTVPSoundBuffer { private: ALSoundImpl *_impl; tjs_uint _bufferSize[TVPAL_BUFFER_COUNT]; @@ -52,4 +79,5 @@ class TVPALSoundWrap /*: public iSoundMixer*/ { tjs_uint GetUnprocessedSamples(); bool IsPlaying(); void SetPosition(float x, float y, float z); -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/src/core/utils/threadpool11/pool.cpp b/src/core/utils/threadpool11/pool.cpp deleted file mode 100644 index 6dd6cabb..00000000 --- a/src/core/utils/threadpool11/pool.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/*! -Copyright (c) 2013, 2014, 2015 Tolga HOŞGÖR -All rights reserved. - -This file is part of threadpool11. - - threadpool11 is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - threadpool11 is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with threadpool11. If not, see . -*/ - -#include "threadpool11/pool.hpp" - -#include - -#include -#include -#include - -namespace threadpool11 { - -Pool::Pool(std::size_t worker_count) - : worker_count_(0) - , active_worker_count_(0) - , are_all_really_finished_{true} - , work_queue_(new boost::lockfree::queue(0)) - , work_queue_size_(0) { - spawnWorkers(worker_count); -} - -Pool::~Pool() { joinAll(); } - -void Pool::waitAll() { - std::unique_lock notify_all_finished_lock(notify_all_finished_mutex_); - - notify_all_finished_signal_.wait(notify_all_finished_lock, [this]() { return are_all_really_finished_.load(); }); -} - -void Pool::joinAll() { decWorkerCountBy(std::numeric_limits::max(), Method::SYNC); } - -size_t Pool::getWorkerCount() const { return worker_count_.load(); } - -void Pool::setWorkerCount(std::size_t n, Method method) { - if (getWorkerCount() < n) - incWorkerCountBy(n - getWorkerCount()); - else - decWorkerCountBy(getWorkerCount() - n, method); -} - -size_t Pool::getWorkQueueSize() const { return work_queue_size_.load(); } - -size_t Pool::getActiveWorkerCount() const { return active_worker_count_.load(); } - -size_t Pool::getInactiveWorkerCount() const { return worker_count_.load() - active_worker_count_.load(); } - -void Pool::incWorkerCountBy(std::size_t n) { spawnWorkers(n); } - -void Pool::decWorkerCountBy(size_t n, Method method) { - n = std::min(n, getWorkerCount()); - if (method == Method::SYNC) { - std::vector> futures; - futures.reserve(n); - while (n-- > 0) - futures.emplace_back(postWork([]() {}, Work::Type::TERMINAL)); - for (auto& it : futures) - it.get(); - } else { - while (n-- > 0) - postWork([]() {}, Work::Type::TERMINAL); - } -} - -void Pool::spawnWorkers(std::size_t n) { - //'OR' makes sure the case where one of the expressions is zero, is valid. - assert(static_cast(worker_count_ + n) > n || - static_cast(worker_count_ + n) > worker_count_); - while (n-- > 0) { - new Worker(*this); //! Worker class takes care of its de-allocation itself after here - ++worker_count_; - } -} - -void Pool::push(Work::Callable* workFunc) { - are_all_really_finished_ = false; - - std::unique_lock work_signal_lock(work_signal_mutex_); - ++work_queue_size_; - work_queue_->push(workFunc); - work_signal_.notify_one(); -} -} diff --git a/src/core/utils/threadpool11/pool.hpp b/src/core/utils/threadpool11/pool.hpp deleted file mode 100644 index f09e2e12..00000000 --- a/src/core/utils/threadpool11/pool.hpp +++ /dev/null @@ -1,271 +0,0 @@ -/*! -Copyright (c) 2013, 2014, 2015 Tolga HOŞGÖR -All rights reserved. - -This file is part of threadpool11. - - threadpool11 is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - threadpool11 is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with threadpool11. If not, see . -*/ - -#pragma once - -#include "worker.hpp" -#include "utility.hpp" - -#include -#include -#include -#include -#include -#include - -#if defined(WIN32) && defined(threadpool11_DLL) -#ifdef threadpool11_EXPORTING -#define threadpool11_EXPORT __declspec(dllexport) -#else -#define threadpool11_EXPORT __declspec(dllimport) -#endif -#else -#define threadpool11_EXPORT -#endif - -namespace boost { -namespace parameter { -struct void_; -} -namespace lockfree { -template -class queue; -} -} - -namespace threadpool11 { - -class Pool { - friend class Worker; - -public: - enum class Method { SYNC, ASYNC }; - -public: - threadpool11_EXPORT Pool(std::size_t worker_count = std::thread::hardware_concurrency()); - ~Pool(); - - /*! - * \brief postWork Posts a work to the pool for getting processed. - * - * If there are no threads left (i.e. you called Pool::joinAll(); prior to - * this function) all the works you post gets enqueued. If you spawn new threads in - * future, they will be executed then. - * - * Properties: thread-safe. - */ - template - threadpool11_EXPORT std::future postWork(std::function callable, Work::Type type = Work::Type::STD); - // TODO: convert 'type' above to const& when MSVC fixes that bug. - - /** - * \brief waitAll Blocks the calling thread until all posted works are finished. - * - * This function suspends the calling thread until all posted works are finished and, therefore, all worker - * threads are free. It guarantees you that before the function returns, all queued works are finished. - */ - threadpool11_EXPORT void waitAll(); - - /*! - * \brief joinAll Joins the worker threads. - * - * This function joins all the threads in the thread pool as fast as possible. - * All the posted works are NOT GUARANTEED to be finished before the worker threads - * are destroyed and this function returns. - * - * However, ongoing works in the threads in the pool are guaranteed - * to finish before that threads are terminated. - * - * Properties: NOT thread-safe. - */ - threadpool11_EXPORT void joinAll(); - - /*! - * \brief getWorkerCount - * - * Properties: thread-safe. - * - * \return The number of worker threads. - */ - threadpool11_EXPORT size_t getWorkerCount() const; - - /*! - * \brief setWorkerCount - * \param n - * - * WARNING: This function behaves different based on second parameter. (Only if decreasing) - * - * Method::ASYNC: It will return before the threads are joined. It will just post - * 'n' requests for termination. This means that if you call this function multiple times, - * worker termination requests will pile up. It can even kill the newly - * created workers if all workers are removed before all requests are processed. - * - * Method::SYNC: It won't return until the specified number of workers are actually destroyed. - * There still may be a few milliseconds delay before value returned by Pool::getWorkerCount is updated. - * But it will be more accurate compared to ASYNC one. - */ - threadpool11_EXPORT void setWorkerCount(std::size_t n, Method method = Method::ASYNC); - - /*! - * \brief getWorkQueueSize - * - * Properties: thread-safe. - * - * \return The number of work items that has not been acquired by workers. - */ - threadpool11_EXPORT size_t getWorkQueueSize() const; - - /*! - * \brief getActiveWorkerCount Gets the number of active workers when the function is called. - * - * The information this function returns does not really mean much. The worker may be starting to execute a work from queue, - * it may be executing a work or it may have just executed a work. - * - * \return The number of active workers. - */ - threadpool11_EXPORT size_t getActiveWorkerCount() const; - - /** - * \brief getInactiveWorkerCount Gets the number of the inactive worker count. - * - * The information this function returns does not really mean much. The worker may be starting to execute a work from queue, - * it may be executing a work or it may have just executed a work. - * - * \return The number of active workers. - */ - threadpool11_EXPORT size_t getInactiveWorkerCount() const; - - /*! - * \brief incWorkerCountBy Increases the number of threads in the pool by n. - * - * Properties: thread-safe. - */ - threadpool11_EXPORT void incWorkerCountBy(std::size_t n); - - /*! - * \brief decWorkerCountBy Tries to decrease the number of threads in the pool by n. - * - * Setting 'n' higher than the number of workers has no effect. - * Calling without arguments asynchronously terminates all workers. - * - * \warning This function behaves different based on second parameter. - * - * Method::ASYNC: It will return before the threads are joined. It will just post - * 'n' requests for termination. This means that if you call this function multiple times, - * worker termination requests will pile up. It can even kill the newly - * created workers if all workers are removed before all requests are processed. - * - * Method::SYNC: It won't return until the specified number of workers are actually destroyed. - * There still may be a few milliseconds delay before value returned by Pool::getWorkerCount is updated. - * But it will be more accurate compared to ASYNC one. - * - * Properties: thread-safe. - */ - threadpool11_EXPORT void decWorkerCountBy(std::size_t n = std::numeric_limits::max(), - Method method = Method::ASYNC); - -private: - Pool(Pool&&) = delete; - Pool(Pool const&) = delete; - Pool& operator=(Pool&&) = delete; - Pool& operator=(Pool const&) = delete; - - void spawnWorkers(std::size_t n); - - /*! - * \brief executor - * This is run by different threads to do necessary operations for queue processing. - */ - void executor(std::unique_ptr self); - - /** - * @brief push Internal usage. - * @param workFunc - */ - void push(Work::Callable* workFunc); - -private: - std::atomic worker_count_; - std::atomic active_worker_count_; - - mutable std::mutex notify_all_finished_mutex_; - std::condition_variable notify_all_finished_signal_; - std::atomic are_all_really_finished_; - - mutable std::mutex work_signal_mutex_; - // bool work_signal_prot; //! wake up protection // <- work_queue_size is used instead of this - std::condition_variable work_signal_; - - std::unique_ptr< - boost::lockfree:: - queue> - work_queue_; - std::atomic work_queue_size_; -}; - -template -threadpool11_EXPORT inline std::future Pool::postWork(std::function callable, Work::Type type) { - std::promise promise; - auto future = promise.get_future(); - - /* TODO: how to avoid copy of callable into this lambda and the ones below? In a decent way... */ - /* evil move hack */ - auto move_callable = make_move_on_copy(std::move(callable)); - /* evil move hack */ - auto move_promise = make_move_on_copy(std::move(promise)); - - auto workFunc = new Work::Callable([move_callable, move_promise, type]() mutable { - move_promise.value().set_value((move_callable.value())()); - return type; - }); - - push(workFunc); - - return future; -} - -template <> -threadpool11_EXPORT inline std::future Pool::postWork(std::function callable, - Work::Type const type) { - std::promise promise; - auto future = promise.get_future(); - - /* evil move hack */ - auto move_callable = make_move_on_copy(std::move(callable)); - /* evil move hack */ - auto move_promise = make_move_on_copy(std::move(promise)); - - auto workFunc = new Work::Callable([move_callable, move_promise, type]() mutable { - (move_callable.value())(); - move_promise.value().set_value(); - return type; - }); - - push(workFunc); - - return future; -} - -#undef threadpool11_EXPORT -#undef threadpool11_EXPORTING -} diff --git a/src/core/utils/threadpool11/threadpool11.hpp b/src/core/utils/threadpool11/threadpool11.hpp deleted file mode 100644 index ebc99eca..00000000 --- a/src/core/utils/threadpool11/threadpool11.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright (c) 2013, Tolga HOŞGÖR -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of the FreeBSD Project. -*/ - -#pragma once - -#include "pool.hpp" -#include "worker.hpp" diff --git a/src/core/utils/threadpool11/utility.hpp b/src/core/utils/threadpool11/utility.hpp deleted file mode 100644 index e4291a00..00000000 --- a/src/core/utils/threadpool11/utility.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/*! -Copyright (c) 2013, 2014, 2015 Tolga HOŞGÖR -All rights reserved. - -This file is part of threadpool11. - - threadpool11 is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - threadpool11 is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with threadpool11. If not, see . -*/ - -#pragma once - -#include - -namespace threadpool11 { - -namespace Work { -enum class Type { STD, TERMINAL }; -enum class Prio { DEFERRED, IMMIDIATE }; - -typedef std::function Callable; -} - -template -class move_on_copy { -public: - move_on_copy(T&& aValue) - : value_(std::move(aValue)) {} - move_on_copy(const move_on_copy& other) - : value_(std::move(other.value_)) {} - - move_on_copy& operator=(move_on_copy&& aValue) = delete; // not needed here - move_on_copy& operator=(const move_on_copy& aValue) = delete; // not needed here - - T& value() { return value_; } - const T& value() const { return value_; } - -private: - mutable T value_; -}; - -template -inline move_on_copy make_move_on_copy(T&& aValue) { - return move_on_copy(std::move(aValue)); -} -} diff --git a/src/core/utils/threadpool11/worker.cpp b/src/core/utils/threadpool11/worker.cpp deleted file mode 100644 index a220f3ab..00000000 --- a/src/core/utils/threadpool11/worker.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/*! -Copyright (c) 2013, 2014, 2015 Tolga HOŞGÖR -All rights reserved. - -This file is part of threadpool11. - - threadpool11 is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - threadpool11 is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with threadpool11. If not, see . -*/ - -#include "threadpool11/pool.hpp" -#include "threadpool11/worker.hpp" - -#include - -namespace threadpool11 { - -Worker::Worker(Pool& pool) - : thread_(std::bind(&Worker::execute, this, std::ref(pool))) { - // std::cout << std::this_thread::get_id() << " Worker created" << std::endl; - thread_.detach(); -} - -void Worker::execute(Pool& pool) { - const std::unique_ptr self(this); //! auto de-allocation when thread is terminated - - while (true) { - Work::Callable* work_ptr; - Work::Type work_type = Work::Type::STD; - - ++pool.active_worker_count_; - - while (work_type != Work::Type::TERMINAL - && pool.work_queue_->pop(work_ptr)) { - --pool.work_queue_size_; - - const std::unique_ptr work(work_ptr); - - work_type = (*work)(); - } - - std::unique_lock work_signal_lock(pool.work_signal_mutex_); - - // work_queue_size is increased when work_signal_mutex locked - // active_worker_count_ is only decreased here so it is also when mutex is locked - - // as far as I can see when there is a work, it is certain that either work_queue_size or active_worker counter - // is greater than zero so I don't see any race condition - if (--pool.active_worker_count_ == 0 && pool.work_queue_size_ == 0) { - std::unique_lock notify_all_finished_lock(pool.notify_all_finished_mutex_); - - pool.are_all_really_finished_ = true; - pool.notify_all_finished_signal_.notify_all(); - } - - if (work_type == Work::Type::TERMINAL) { - --pool.worker_count_; - - return; - } - - // block here until signalled - pool.work_signal_.wait(work_signal_lock, [&pool]() { return (pool.work_queue_size_ > 0); }); - } -} - -} diff --git a/src/core/utils/threadpool11/worker.hpp b/src/core/utils/threadpool11/worker.hpp deleted file mode 100644 index 7f83281e..00000000 --- a/src/core/utils/threadpool11/worker.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/*! -Copyright (c) 2013, 2014, 2015 Tolga HOŞGÖR -All rights reserved. - -This file is part of threadpool11. - - threadpool11 is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - threadpool11 is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with threadpool11. If not, see . -*/ - -#pragma once - -#include "utility.hpp" - -#include -#include -#include -#include -#include - -namespace threadpool11 { - -class Pool; - -class Worker { - friend class Pool; - -public: - Worker(Pool& pool); - ~Worker() = default; - -private: - Worker(Worker&&) = delete; - Worker(Worker const&) = delete; - Worker& operator=(Worker&&) = delete; - Worker& operator=(Worker const&) = delete; - - void execute(Pool& pool); - -private: - /*! - * This should always stay at bottom so that it is called at the most end. - */ - std::thread thread_; -}; -}