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