no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "audio/common/decoder/AudioDecoder.h"
#include <cstdint>
#include <cstring>
#include "audio/include/AudioMacros.h"
#include "platform/FileUtils.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "AudioDecoder"
namespace cc {
AudioDecoder::AudioDecoder()
: _isOpened(false) {}
AudioDecoder::~AudioDecoder() = default;
bool AudioDecoder::isOpened() const {
return _isOpened;
}
uint32_t AudioDecoder::readFixedFrames(uint32_t framesToRead, char *pcmBuf) {
uint32_t framesRead = 0;
uint32_t framesReadOnce = 0;
do {
framesReadOnce = read(framesToRead - framesRead, pcmBuf + framesRead * _pcmHeader.bytesPerFrame);
framesRead += framesReadOnce;
} while (framesReadOnce != 0 && framesRead < framesToRead);
if (framesRead < framesToRead) {
memset(pcmBuf + framesRead * _pcmHeader.bytesPerFrame, 0x00, (framesToRead - framesRead) * _pcmHeader.bytesPerFrame);
}
return framesRead;
}
uint32_t AudioDecoder::getTotalFrames() const {
return _pcmHeader.totalFrames;
}
uint32_t AudioDecoder::getBytesPerFrame() const {
return _pcmHeader.bytesPerFrame;
}
uint32_t AudioDecoder::getSampleRate() const {
return _pcmHeader.sampleRate;
}
uint32_t AudioDecoder::getChannelCount() const {
return _pcmHeader.channelCount;
}
AudioDataFormat AudioDecoder::getDataFormat() const {
return _pcmHeader.dataFormat;
}
PCMHeader AudioDecoder::getPCMHeader() const {
return _pcmHeader;
}
} // namespace cc

View File

@@ -0,0 +1,130 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include <cstdint>
#include <functional>
#include "audio/include/AudioDef.h"
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
#include "vorbis/vorbisfile.h"
#elif CC_PLATFORM == CC_PLATFORM_LINUX || CC_PLATFORM == CC_PLATFORM_QNX
#include "vorbis/vorbisfile.h"
#elif CC_PLATFORM == CC_PLATFORM_OHOS
#include "ivorbisfile.h"
#endif
namespace cc {
/**
* @brief The class for decoding compressed audio file to PCM buffer.
*/
class AudioDecoder {
public:
static const uint32_t INVALID_FRAME_INDEX = UINT32_MAX;
/**
* @brief Opens an audio file specified by a file path.
* @return true if succeed, otherwise false.
*/
virtual bool open(const char *path) = 0;
/**
* @brief Checks whether decoder has opened file successfully.
* @return true if succeed, otherwise false.
*/
virtual bool isOpened() const;
/**
* @brief Closes opened audio file.
* @note The method will also be automatically invoked in the destructor.
*/
virtual void close() = 0;
/**
* @brief Reads audio frames of PCM format.
* @param framesToRead The number of frames excepted to be read.
* @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame.
* @return The number of frames actually read, it's probably less than 'framesToRead'. Returns 0 means reach the end of file.
*/
virtual uint32_t read(uint32_t framesToRead, char *pcmBuf) = 0;
/**
* @brief Reads fixed audio frames of PCM format.
* @param framesToRead The number of frames excepted to be read.
* @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame.
* @return The number of frames actually read, it's probably less than |framesToRead|. Returns 0 means reach the end of file.
* @note The different between |read| and |readFixedFrames| is |readFixedFrames| will do multiple reading operations if |framesToRead| frames
* isn't filled entirely, while |read| just does reading operation once whatever |framesToRead| is or isn't filled entirely.
* If current position reaches the end of frames, the return value may smaller than |framesToRead| and the remaining
* buffer in |pcmBuf| will be set with silence data (0x00).
*/
virtual uint32_t readFixedFrames(uint32_t framesToRead, char *pcmBuf);
/**
* @brief Sets frame offest to be read.
* @param frameOffset The frame offest to be set.
* @return true if succeed, otherwise false
*/
virtual bool seek(uint32_t frameOffset) = 0;
/**
* @brief Tells the current frame offset.
* @return The current frame offset.
*/
virtual uint32_t tell() const = 0;
/** Gets total frames of current audio.*/
virtual uint32_t getTotalFrames() const;
/** Gets bytes per frame of current audio.*/
virtual uint32_t getBytesPerFrame() const;
/** Gets sample rate of current audio.*/
virtual uint32_t getSampleRate() const;
/** Gets the channel count of current audio.
* @note Currently we only support 1 or 2 channels.
*/
virtual uint32_t getChannelCount() const;
virtual AudioDataFormat getDataFormat() const;
virtual PCMHeader getPCMHeader() const;
protected:
AudioDecoder();
virtual ~AudioDecoder();
bool _isOpened;
PCMHeader _pcmHeader;
void *_fsHooks = nullptr;
friend class AudioDecoderManager;
};
} // namespace cc

View File

@@ -0,0 +1,68 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#define LOG_TAG "AudioDecoderManager"
#include "audio/common/decoder/AudioDecoderManager.h"
#include "audio/common/decoder/AudioDecoderMp3.h"
#include "audio/common/decoder/AudioDecoderOgg.h"
#include "audio/common/decoder/AudioDecoderWav.h"
#include "audio/include/AudioMacros.h"
#include "base/memory/Memory.h"
#include "platform/FileUtils.h"
namespace cc {
bool AudioDecoderManager::init() {
return true;
}
void AudioDecoderManager::destroy() {
AudioDecoderMp3::destroy();
}
AudioDecoder *AudioDecoderManager::createDecoder(const char *path) {
ccstd::string suffix = FileUtils::getInstance()->getFileExtension(path);
if (suffix == ".ogg") {
return ccnew AudioDecoderOgg();
}
if (suffix == ".mp3") {
return ccnew AudioDecoderMp3();
}
#if CC_PLATFORM == CC_PLATFORM_OHOS || CC_PLATFORM == CC_PLATFORM_WINDOWS
if (suffix == ".wav") {
return ccnew AudioDecoderWav();
}
#endif
return nullptr;
}
void AudioDecoderManager::destroyDecoder(AudioDecoder *decoder) {
delete decoder;
}
} // namespace cc

View File

@@ -0,0 +1,40 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
namespace cc {
class AudioDecoder;
class AudioDecoderManager {
public:
static bool init();
static void destroy();
static AudioDecoder *createDecoder(const char *path);
static void destroyDecoder(AudioDecoder *decoder);
};
} // namespace cc

View File

@@ -0,0 +1,179 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "audio/common/decoder/AudioDecoderMp3.h"
#include <malloc.h>
#include <cstdint>
#include "audio/include/AudioMacros.h"
#include "platform/FileUtils.h"
#if CC_PLATFORM == CC_PLATFORM_WINDOWS || CC_PLATFORM == CC_PLATFORM_LINUX || CC_PLATFORM == CC_PLATFORM_QNX
#include "mpg123/mpg123.h"
#elif CC_PLATFORM == CC_PLATFORM_OHOS
#include <unistd.h>
#include "cocos/platform/ohos/FileUtils-ohos.h"
#include "mpg123.h"
#endif
#include <sys/stat.h>
#include <cstdlib>
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "AudioDecoderMp3"
namespace cc {
static bool sMp3Inited = false;
bool AudioDecoderMp3::lazyInit() {
bool ret = true;
if (!sMp3Inited) {
int error = mpg123_init();
if (error == MPG123_OK) {
sMp3Inited = true;
} else {
ALOGE("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
ret = false;
}
}
return ret;
}
void AudioDecoderMp3::destroy() {
if (sMp3Inited) {
mpg123_exit();
sMp3Inited = false;
}
}
AudioDecoderMp3::AudioDecoderMp3() {
lazyInit();
}
AudioDecoderMp3::~AudioDecoderMp3() {
close();
}
bool AudioDecoderMp3::open(const char *path) {
ccstd::string fullPath = FileUtils::getInstance()->fullPathForFilename(path);
long rate = 0; //NOLINT(google-runtime-int)
int error = MPG123_OK;
int mp3Encoding = 0;
int channel = 0;
do {
_mpg123handle = mpg123_new(nullptr, &error);
if (nullptr == _mpg123handle) {
ALOGE("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
break;
}
#if CC_PLATFORM_OHOS == CC_PLATFORM
auto *fu = static_cast<FileUtilsOHOS *>(FileUtils::getInstance());
_fdAndDeleter = fu->getFd(fullPath);
if (mpg123_open_fd(_mpg123handle, _fdAndDeleter.first) != MPG123_OK || mpg123_getformat(_mpg123handle, &rate, &channel, &mp3Encoding) != MPG123_OK) {
#else
if (mpg123_open(_mpg123handle, FileUtils::getInstance()->getSuitableFOpen(fullPath).c_str()) != MPG123_OK || mpg123_getformat(_mpg123handle, &rate, &channel, &mp3Encoding) != MPG123_OK) {
#endif
ALOGE("Trouble with mpg123: %s\n", mpg123_strerror(_mpg123handle));
break;
}
_pcmHeader.channelCount = channel;
_pcmHeader.sampleRate = rate;
if (mp3Encoding == MPG123_ENC_SIGNED_16) {
_pcmHeader.bytesPerFrame = 2 * _pcmHeader.channelCount;
_pcmHeader.dataFormat = AudioDataFormat::SIGNED_16;
} else if (mp3Encoding == MPG123_ENC_FLOAT_32) {
_pcmHeader.bytesPerFrame = 4 * _pcmHeader.channelCount;
_pcmHeader.dataFormat = AudioDataFormat::FLOAT_32;
} else {
ALOGE("Bad encoding: 0x%x!\n", mp3Encoding);
break;
}
/* Ensure that this output format will not change (it could, when we allow it). */
mpg123_format_none(_mpg123handle);
mpg123_format(_mpg123handle, rate, channel, mp3Encoding);
/* Ensure that we can get accurate length by call mpg123_length */
mpg123_scan(_mpg123handle);
_pcmHeader.totalFrames = mpg123_length(_mpg123handle);
_isOpened = true;
return true;
} while (false);
if (_mpg123handle != nullptr) {
mpg123_close(_mpg123handle);
mpg123_delete(_mpg123handle);
_mpg123handle = nullptr;
}
return false;
}
void AudioDecoderMp3::close() {
if (isOpened()) {
if (_mpg123handle != nullptr) {
mpg123_close(_mpg123handle);
mpg123_delete(_mpg123handle);
_mpg123handle = nullptr;
}
_isOpened = false;
}
#if CC_PLATFORM_OHOS == CC_PLATFORM
if (_fdAndDeleter.second) {
_fdAndDeleter.second();
_fdAndDeleter.second = nullptr;
}
#endif
}
uint32_t AudioDecoderMp3::read(uint32_t framesToRead, char *pcmBuf) {
size_t bytesToRead = framesToRead * _pcmHeader.bytesPerFrame;
size_t bytesRead = 0;
int err = mpg123_read(_mpg123handle, reinterpret_cast<unsigned char *>(pcmBuf), bytesToRead, &bytesRead);
if (err == MPG123_ERR) {
ALOGE("Trouble with mpg123: %s\n", mpg123_strerror(_mpg123handle));
return 0;
}
return static_cast<uint32_t>(bytesRead / _pcmHeader.bytesPerFrame);
}
bool AudioDecoderMp3::seek(uint32_t frameOffset) {
off_t offset = mpg123_seek(_mpg123handle, frameOffset, SEEK_SET);
//ALOGD("mpg123_seek return: %d", (int)offset);
return offset >= 0 && offset == frameOffset;
}
uint32_t AudioDecoderMp3::tell() const {
return static_cast<uint32_t>(mpg123_tell(_mpg123handle));
}
} // namespace cc

View File

@@ -0,0 +1,90 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "audio/common/decoder/AudioDecoder.h"
#include <functional>
struct mpg123_handle_struct;
namespace cc {
/**
* @brief The class for decoding compressed audio file to PCM buffer.
*/
class AudioDecoderMp3 : public AudioDecoder {
public:
/**
* @brief Opens an audio file specified by a file path.
* @return true if succeed, otherwise false.
*/
bool open(const char *path) override;
/**
* @brief Closes opened audio file.
* @note The method will also be automatically invoked in the destructor.
*/
void close() override;
/**
* @brief Reads audio frames of PCM format.
* @param framesToRead The number of frames excepted to be read.
* @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame.
* @return The number of frames actually read, it's probably less than 'framesToRead'. Returns 0 means reach the end of file.
*/
uint32_t read(uint32_t framesToRead, char *pcmBuf) override;
/**
* @brief Sets frame offest to be read.
* @param frameOffset The frame offest to be set.
* @return true if succeed, otherwise false
*/
bool seek(uint32_t frameOffset) override;
/**
* @brief Tells the current frame offset.
* @return The current frame offset.
*/
uint32_t tell() const override;
protected:
AudioDecoderMp3();
~AudioDecoderMp3() override;
static bool lazyInit();
static void destroy();
struct mpg123_handle_struct *_mpg123handle = nullptr;
#if CC_PLATFORM_OHOS == CC_PLATFORM
std::pair<int, std::function<void()>> _fdAndDeleter;
#endif
friend class AudioDecoderManager;
};
} // namespace cc

View File

@@ -0,0 +1,112 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "audio/common/decoder/AudioDecoderOgg.h"
#include <cstdint>
#include "audio/include/AudioMacros.h"
#include "platform/FileUtils.h"
#if CC_PLATFORM == CC_PLATFORM_OHOS
#include "audio/ohos/FsCallback.h"
namespace {
int ohosSeek_wrap(void *source, ogg_int64_t offset, int whence) { //NOLINT
return cc::ohosSeek(source, static_cast<long>(offset), whence); //NOLINT
}
ov_callbacks ogg_callbacks = { //NOLINT
static_cast<size_t (*)(void *, size_t, size_t, void *)>(cc::ohosRead),
static_cast<int (*)(void *, ogg_int64_t, int)>(ohosSeek_wrap),
static_cast<int (*)(void *)>(cc::ohosClose),
static_cast<long (*)(void *)>(cc::ohosTell)}; //NOLINT
} // namespace
#endif
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "AudioDecoderOgg"
namespace cc {
AudioDecoderOgg::AudioDecoderOgg() = default;
AudioDecoderOgg::~AudioDecoderOgg() {
close();
}
bool AudioDecoderOgg::open(const char *path) {
ccstd::string fullPath = FileUtils::getInstance()->fullPathForFilename(path);
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
if (0 == ov_fopen(FileUtils::getInstance()->getSuitableFOpen(fullPath).c_str(), &_vf)) {
#elif CC_PLATFORM == CC_PLATFORM_LINUX || CC_PLATFORM == CC_PLATFORM_QNX
if (0 == ov_fopen(FileUtils::getInstance()->getSuitableFOpen(fullPath).c_str(), &_vf)) {
#elif CC_PLATFORM == CC_PLATFORM_OHOS
auto *fp = cc::ohosOpen(FileUtils::getInstance()->getSuitableFOpen(fullPath).c_str(), this);
if (0 == ov_open_callbacks(fp, &_vf, nullptr, 0, ogg_callbacks)) {
#endif
// header
vorbis_info *vi = ov_info(&_vf, -1);
_pcmHeader.sampleRate = static_cast<uint32_t>(vi->rate);
_pcmHeader.channelCount = vi->channels;
_pcmHeader.bytesPerFrame = vi->channels * sizeof(int16_t);
_pcmHeader.dataFormat = AudioDataFormat::SIGNED_16;
_pcmHeader.totalFrames = static_cast<uint32_t>(ov_pcm_total(&_vf, -1));
_isOpened = true;
return true;
}
return false;
}
void AudioDecoderOgg::close() {
if (isOpened()) {
ov_clear(&_vf);
_isOpened = false;
}
}
uint32_t AudioDecoderOgg::read(uint32_t framesToRead, char *pcmBuf) {
int currentSection = 0;
auto bytesToRead = framesToRead * _pcmHeader.bytesPerFrame;
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
auto bytesRead = ov_read(&_vf, pcmBuf, bytesToRead, 0, 2, 1, &currentSection);
#elif CC_PLATFORM == CC_PLATFORM_OHOS
int bitstream = 0;
auto bytesRead = ov_read(&_vf, pcmBuf, bytesToRead, &bitstream);
#elif CC_PLATFORM == CC_PLATFORM_LINUX || CC_PLATFORM == CC_PLATFORM_QNX
auto bytesRead = ov_read(&_vf, pcmBuf, bytesToRead, 0, 2, 1, &currentSection);
#endif
return static_cast<uint32_t>(bytesRead / _pcmHeader.bytesPerFrame);
}
bool AudioDecoderOgg::seek(uint32_t frameOffset) {
return 0 == ov_pcm_seek(&_vf, frameOffset);
}
uint32_t AudioDecoderOgg::tell() const {
return static_cast<uint32_t>(ov_pcm_tell(const_cast<OggVorbis_File *>(&_vf)));
}
} // namespace cc

View File

@@ -0,0 +1,85 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "audio/common/decoder/AudioDecoder.h"
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
#include "vorbis/vorbisfile.h"
#elif CC_PLATFORM == CC_PLATFORM_OHOS
#include "ivorbisfile.h"
#endif
namespace cc {
/**
* @brief The class for decoding compressed audio file to PCM buffer.
*/
class AudioDecoderOgg : public AudioDecoder {
public:
/**
* @brief Opens an audio file specified by a file path.
* @return true if succeed, otherwise false.
*/
bool open(const char *path) override;
/**
* @brief Closes opened audio file.
* @note The method will also be automatically invoked in the destructor.
*/
void close() override;
/**
* @brief Reads audio frames of PCM format.
* @param framesToRead The number of frames excepted to be read.
* @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame.
* @return The number of frames actually read, it's probably less than 'framesToRead'. Returns 0 means reach the end of file.
*/
uint32_t read(uint32_t framesToRead, char *pcmBuf) override;
/**
* @brief Sets frame offest to be read.
* @param frameOffset The frame offest to be set.
* @return true if succeed, otherwise false
*/
bool seek(uint32_t frameOffset) override;
/**
* @brief Tells the current frame offset.
* @return The current frame offset.
*/
uint32_t tell() const override;
protected:
AudioDecoderOgg();
~AudioDecoderOgg() override;
OggVorbis_File _vf;
friend class AudioDecoderManager;
};
} // namespace cc

View File

@@ -0,0 +1,94 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#define LOG_TAG "AudioDecoderWav"
#include "audio/common/decoder/AudioDecoderWav.h"
#include "base/Log.h"
#include "platform/FileUtils.h"
namespace cc {
AudioDecoderWav::AudioDecoderWav() {
CC_LOG_DEBUG("Create AudioDecoderWav");
}
AudioDecoderWav::~AudioDecoderWav() {
close();
};
bool AudioDecoderWav::open(const char *path) {
bool ret{false};
auto fullPath = FileUtils::getInstance()->fullPathForFilename(path);
if (fullPath.empty()) {
CC_LOG_DEBUG("File does not exist %s", fullPath.c_str());
return false;
}
do {
sf::SF_INFO info;
_sf_handle = sf::sf_open_read(fullPath.c_str(), &info, nullptr, nullptr);
if (_sf_handle == nullptr) {
CC_LOG_ERROR("file %s open failed, it might be invalid", fullPath.c_str());
break;
}
if (info.frames == 0) {
CC_LOG_ERROR("file %s has no frame, is it an invalid wav file?", fullPath.c_str());
break;
}
CC_LOG_DEBUG("wav info: frames: %d, samplerate: %d, channels: %d, format: %d", info.frames, info.samplerate, info.channels, info.format);
_pcmHeader.channelCount = info.channels;
_pcmHeader.bytesPerFrame = 2 * info.channels; // FIXED_16
_pcmHeader.dataFormat = AudioDataFormat::SIGNED_16; //FIXED,
_pcmHeader.sampleRate = info.samplerate;
_pcmHeader.totalFrames = info.frames;
_isOpened = true;
ret = true;
} while (false);
return ret;
}
uint32_t AudioDecoderWav::read(uint32_t framesToRead, char *pcmBuf) {
//size_t bytesToRead = framesToRead * _pcmHeader.bytesPerFrame;
size_t framesRead = sf::sf_readf_short(_sf_handle, reinterpret_cast<int16_t *>(pcmBuf), framesToRead);
return framesRead;
}
bool AudioDecoderWav::seek(uint32_t frameOffset) {
auto offset = sf::sf_seek(_sf_handle, frameOffset, SEEK_SET);
return offset >= 0 && offset == frameOffset;
}
uint32_t AudioDecoderWav::tell() const {
return static_cast<uint32_t>(sf::sf_tell(_sf_handle));
}
void AudioDecoderWav::close() {
if (_isOpened) {
if (_sf_handle) {
sf::sf_close(_sf_handle);
}
_isOpened = false;
}
}
} // namespace cc

View File

@@ -0,0 +1,76 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "audio/common/decoder/AudioDecoder.h"
#include "audio/common/utils/include/tinysndfile.h"
namespace cc {
class AudioDecoderWav : public AudioDecoder {
public:
/**
* @brief Opens an audio file specified by a file path.
* @return true if succeed, otherwise false.
*/
bool open(const char *path) override;
/**
* @brief Closes opened audio file.
* @note The method will also be automatically invoked in the destructor.
*/
void close() override;
/**
* @brief Reads audio frames of PCM format.
* @param framesToRead The number of frames excepted to be read.
* @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame.
* @return The number of frames actually read, it's probably less than 'framesToRead'. Returns 0 means reach the end of file.
*/
uint32_t read(uint32_t framesToRead, char *pcmBuf) override;
/**
* @brief Sets frame offest to be read.
* @param frameOffset The frame offest to be set.
* @return true if succeed, otherwise false
*/
bool seek(uint32_t frameOffset) override;
/**
* @brief Tells the current frame offset.
* @return The current frame offset.
*/
uint32_t tell() const override;
protected:
AudioDecoderWav();
~AudioDecoderWav() override;
sf::SNDFILE *_sf_handle{nullptr};
friend class AudioDecoderManager;
};
} // namespace cc

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* #define LOG_NDEBUG 0 */
#define LOG_TAG "audio_utils_format"
#include "audio/common/utils/include/format.h"
#include "audio/android/audio.h"
#include "audio/android/cutils/log.h"
#include "audio/common/utils/include/primitives.h"
void memcpy_by_audio_format(void *dst, audio_format_t dst_format,
const void *src, audio_format_t src_format, size_t count) {
/* default cases for error falls through to fatal log below. */
if (dst_format == src_format) {
switch (dst_format) {
case AUDIO_FORMAT_PCM_16_BIT:
case AUDIO_FORMAT_PCM_FLOAT:
case AUDIO_FORMAT_PCM_8_BIT:
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
case AUDIO_FORMAT_PCM_32_BIT:
case AUDIO_FORMAT_PCM_8_24_BIT:
memcpy(dst, src, count * audio_bytes_per_sample(dst_format));
return;
default:
break;
}
}
switch (dst_format) {
case AUDIO_FORMAT_PCM_16_BIT:
switch (src_format) {
case AUDIO_FORMAT_PCM_FLOAT:
memcpy_to_i16_from_float((int16_t *)dst, (float *)src, count);
return;
case AUDIO_FORMAT_PCM_8_BIT:
memcpy_to_i16_from_u8((int16_t *)dst, (uint8_t *)src, count);
return;
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
memcpy_to_i16_from_p24((int16_t *)dst, (uint8_t *)src, count);
return;
case AUDIO_FORMAT_PCM_32_BIT:
memcpy_to_i16_from_i32((int16_t *)dst, (int32_t *)src, count);
return;
case AUDIO_FORMAT_PCM_8_24_BIT:
memcpy_to_i16_from_q8_23((int16_t *)dst, (int32_t *)src, count);
return;
default:
break;
}
break;
case AUDIO_FORMAT_PCM_FLOAT:
switch (src_format) {
case AUDIO_FORMAT_PCM_16_BIT:
memcpy_to_float_from_i16((float *)dst, (int16_t *)src, count);
return;
case AUDIO_FORMAT_PCM_8_BIT:
memcpy_to_float_from_u8((float *)dst, (uint8_t *)src, count);
return;
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
memcpy_to_float_from_p24((float *)dst, (uint8_t *)src, count);
return;
case AUDIO_FORMAT_PCM_32_BIT:
memcpy_to_float_from_i32((float *)dst, (int32_t *)src, count);
return;
case AUDIO_FORMAT_PCM_8_24_BIT:
memcpy_to_float_from_q8_23((float *)dst, (int32_t *)src, count);
return;
default:
break;
}
break;
case AUDIO_FORMAT_PCM_8_BIT:
switch (src_format) {
case AUDIO_FORMAT_PCM_16_BIT:
memcpy_to_u8_from_i16((uint8_t *)dst, (int16_t *)src, count);
return;
case AUDIO_FORMAT_PCM_FLOAT:
memcpy_to_u8_from_float((uint8_t *)dst, (float *)src, count);
return;
default:
break;
}
break;
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
switch (src_format) {
case AUDIO_FORMAT_PCM_16_BIT:
memcpy_to_p24_from_i16((uint8_t *)dst, (int16_t *)src, count);
return;
case AUDIO_FORMAT_PCM_FLOAT:
memcpy_to_p24_from_float((uint8_t *)dst, (float *)src, count);
return;
default:
break;
}
break;
case AUDIO_FORMAT_PCM_32_BIT:
switch (src_format) {
case AUDIO_FORMAT_PCM_16_BIT:
memcpy_to_i32_from_i16((int32_t *)dst, (int16_t *)src, count);
return;
case AUDIO_FORMAT_PCM_FLOAT:
memcpy_to_i32_from_float((int32_t *)dst, (float *)src, count);
return;
default:
break;
}
break;
case AUDIO_FORMAT_PCM_8_24_BIT:
switch (src_format) {
case AUDIO_FORMAT_PCM_16_BIT:
memcpy_to_q8_23_from_i16((int32_t *)dst, (int16_t *)src, count);
return;
case AUDIO_FORMAT_PCM_FLOAT:
memcpy_to_q8_23_from_float_with_clamp((int32_t *)dst, (float *)src, count);
return;
case AUDIO_FORMAT_PCM_24_BIT_PACKED: {
memcpy_to_q8_23_from_p24((int32_t *)dst, (uint8_t *)src, count);
return;
}
default:
break;
}
break;
default:
break;
}
LOG_ALWAYS_FATAL("invalid src format %#x for dst format %#x",
src_format, dst_format);
}
size_t memcpy_by_index_array_initialization_from_channel_mask(int8_t *idxary, size_t arysize,
audio_channel_mask_t dst_channel_mask, audio_channel_mask_t src_channel_mask) {
const audio_channel_representation_t src_representation =
audio_channel_mask_get_representation(src_channel_mask);
const audio_channel_representation_t dst_representation =
audio_channel_mask_get_representation(dst_channel_mask);
const uint32_t src_bits = audio_channel_mask_get_bits(src_channel_mask);
const uint32_t dst_bits = audio_channel_mask_get_bits(dst_channel_mask);
switch (src_representation) {
case AUDIO_CHANNEL_REPRESENTATION_POSITION:
switch (dst_representation) {
case AUDIO_CHANNEL_REPRESENTATION_POSITION:
return memcpy_by_index_array_initialization(idxary, arysize,
dst_bits, src_bits);
case AUDIO_CHANNEL_REPRESENTATION_INDEX:
return memcpy_by_index_array_initialization_dst_index(idxary, arysize,
dst_bits, src_bits);
default:
return 0;
}
break;
case AUDIO_CHANNEL_REPRESENTATION_INDEX:
switch (dst_representation) {
case AUDIO_CHANNEL_REPRESENTATION_POSITION:
return memcpy_by_index_array_initialization_src_index(idxary, arysize,
dst_bits, src_bits);
case AUDIO_CHANNEL_REPRESENTATION_INDEX:
return memcpy_by_index_array_initialization(idxary, arysize,
dst_bits, src_bits);
default:
return 0;
}
break;
default:
return 0;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef COCOS_AUDIO_FORMAT_H
#define COCOS_AUDIO_FORMAT_H
#include <stdint.h>
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#include <sys/cdefs.h>
#endif
#include "audio/android/audio.h"
/* Copy buffers with conversion between buffer sample formats.
*
* dst Destination buffer
* dst_format Destination buffer format
* src Source buffer
* src_format Source buffer format
* count Number of samples to copy
*
* Allowed format conversions are given by either case 1 or 2 below:
*
* 1) One of src_format or dst_format is AUDIO_FORMAT_PCM_16_BIT or
* AUDIO_FORMAT_PCM_FLOAT, and the other format type is one of:
*
* AUDIO_FORMAT_PCM_16_BIT
* AUDIO_FORMAT_PCM_FLOAT
* AUDIO_FORMAT_PCM_8_BIT
* AUDIO_FORMAT_PCM_24_BIT_PACKED
* AUDIO_FORMAT_PCM_32_BIT
* AUDIO_FORMAT_PCM_8_24_BIT
*
* 2) Both dst_format and src_format are identical and of the list given
* in (1). This is a straight copy.
*
* The destination and source buffers must be completely separate if the destination
* format size is larger than the source format size. These routines call functions
* in primitives.h, so descriptions of detailed behavior can be reviewed there.
*
* Logs a fatal error if dst or src format is not allowed by the conversion rules above.
*/
void memcpy_by_audio_format(void *dst, audio_format_t dst_format,
const void *src, audio_format_t src_format, size_t count);
/* This function creates an index array for converting audio data with different
* channel position and index masks, used by memcpy_by_index_array().
* Returns the number of array elements required.
* This may be greater than idxcount, so the return value should be checked
* if idxary size is less than 32. Returns zero if the input masks are unrecognized.
*
* Note that idxary is a caller allocated array
* of at least as many channels as present in the dst_mask.
*
* Parameters:
* idxary Updated array of indices of channels in the src frame for the dst frame
* idxcount Number of caller allocated elements in idxary
* dst_mask Bit mask corresponding to destination channels present
* src_mask Bit mask corresponding to source channels present
*/
size_t memcpy_by_index_array_initialization_from_channel_mask(int8_t *idxary, size_t arysize,
audio_channel_mask_t dst_channel_mask, audio_channel_mask_t src_channel_mask);
#endif // COCOS_AUDIO_FORMAT_H

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef COCOS_AUDIO_MINIFLOAT_H
#define COCOS_AUDIO_MINIFLOAT_H
#include <cstdint>
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#include <sys/cdefs.h>
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
#include <sys/types.h>
#endif
/* A single gain expressed as minifloat */
typedef uint16_t gain_minifloat_t;
/* A pair of gain_minifloat_t packed into a single word */
typedef uint32_t gain_minifloat_packed_t;
/* The nominal range of a gain, expressed as a float */
#define GAIN_FLOAT_ZERO 0.0f
#define GAIN_FLOAT_UNITY 1.0f
/* Unity gain expressed as a minifloat */
#define GAIN_MINIFLOAT_UNITY 0xE000
/* Pack a pair of gain_mini_float_t into a combined gain_minifloat_packed_t */
static inline gain_minifloat_packed_t gain_minifloat_pack(gain_minifloat_t left,
gain_minifloat_t right) {
return (right << 16) | left;
}
/* Unpack a gain_minifloat_packed_t into the two gain_minifloat_t components */
static inline gain_minifloat_t gain_minifloat_unpack_left(gain_minifloat_packed_t packed) {
return packed & 0xFFFF;
}
static inline gain_minifloat_t gain_minifloat_unpack_right(gain_minifloat_packed_t packed) {
return packed >> 16;
}
/* A pair of unity gains expressed as a gain_minifloat_packed_t */
#define GAIN_MINIFLOAT_PACKED_UNITY gain_minifloat_pack(GAIN_MINIFLOAT_UNITY, GAIN_MINIFLOAT_UNITY)
/* Convert a float to the internal representation used for gains.
* The nominal range [0.0, 1.0], but the hard range is [0.0, 2.0).
* Negative and underflow values are converted to 0.0,
* and values larger than the hard maximum are truncated to the hard maximum.
*
* Minifloats are ordered, and standard comparisons may be used between them
* in the gain_minifloat_t representation.
*
* Details on internal representation of gains, based on mini-floats:
* The nominal maximum is 1.0 and the hard maximum is 1 ULP less than 2.0, or +6 dB.
* The minimum non-zero value is approximately 1.9e-6 or -114 dB.
* Negative numbers, infinity, and NaN are not supported.
* There are 13 significand bits specified, 1 implied hidden bit, 3 exponent bits,
* and no sign bit. Denormals are supported.
*/
gain_minifloat_t gain_from_float(float v);
/* Convert the internal representation used for gains to float */
float float_from_gain(gain_minifloat_t a);
#endif // COCOS_AUDIO_MINIFLOAT_H

View File

@@ -0,0 +1,936 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#include <sys/cdefs.h>
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
#include <sys/types.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#include <sys/cdefs.h>
#endif
/* The memcpy_* conversion routines are designed to work in-place on same dst as src
* buffers only if the types shrink on copy, with the exception of memcpy_to_i16_from_u8().
* This allows the loops to go upwards for faster cache access (and may be more flexible
* for future optimization later).
*/
/**
* Dither and clamp pairs of 32-bit input samples (sums) to 16-bit output samples (out).
* Each 32-bit input sample can be viewed as a signed fixed-point Q19.12 of which the
* .12 fraction bits are dithered and the 19 integer bits are clamped to signed 16 bits.
* Alternatively the input can be viewed as Q4.27, of which the lowest .12 of the fraction
* is dithered and the remaining fraction is converted to the output Q.15, with clamping
* on the 4 integer guard bits.
*
* For interleaved stereo, c is the number of sample pairs,
* and out is an array of interleaved pairs of 16-bit samples per channel.
* For mono, c is the number of samples / 2, and out is an array of 16-bit samples.
* The name "dither" is a misnomer; the current implementation does not actually dither
* but uses truncation. This may change.
* The out and sums buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void ditherAndClamp(int32_t *out, const int32_t *sums, size_t c);
/* Expand and copy samples from unsigned 8-bit offset by 0x80 to signed 16-bit.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_i16_from_u8(int16_t *dst, const uint8_t *src, size_t count);
/* Shrink and copy samples from signed 16-bit to unsigned 8-bit offset by 0x80.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
* The conversion is done by truncation, without dithering, so it loses resolution.
*/
void memcpy_to_u8_from_i16(uint8_t *dst, const int16_t *src, size_t count);
/* Copy samples from float to unsigned 8-bit offset by 0x80.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
* The conversion is done by truncation, without dithering, so it loses resolution.
*/
void memcpy_to_u8_from_float(uint8_t *dst, const float *src, size_t count);
/* Shrink and copy samples from signed 32-bit fixed-point Q0.31 to signed 16-bit Q0.15.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
* The conversion is done by truncation, without dithering, so it loses resolution.
*/
void memcpy_to_i16_from_i32(int16_t *dst, const int32_t *src, size_t count);
/* Shrink and copy samples from single-precision floating-point to signed 16-bit.
* Each float should be in the range -1.0 to 1.0. Values outside that range are clamped,
* refer to clamp16_from_float().
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
* The conversion is done by truncation, without dithering, so it loses resolution.
*/
void memcpy_to_i16_from_float(int16_t *dst, const float *src, size_t count);
/* Copy samples from signed fixed-point 32-bit Q4.27 to single-precision floating-point.
* The nominal output float range is [-1.0, 1.0] if the fixed-point range is
* [0xf8000000, 0x07ffffff]. The full float range is [-16.0, 16.0]. Note the closed range
* at 1.0 and 16.0 is due to rounding on conversion to float. See float_from_q4_27() for details.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t count);
/* Copy samples from signed fixed-point 16 bit Q0.15 to single-precision floating-point.
* The output float range is [-1.0, 1.0) for the fixed-point range [0x8000, 0x7fff].
* No rounding is needed as the representation is exact.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_float_from_i16(float *dst, const int16_t *src, size_t count);
/* Copy samples from unsigned fixed-point 8 bit to single-precision floating-point.
* The output float range is [-1.0, 1.0) for the fixed-point range [0x00, 0xFF].
* No rounding is needed as the representation is exact.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_float_from_u8(float *dst, const uint8_t *src, size_t count);
/* Copy samples from signed fixed-point packed 24 bit Q0.23 to single-precision floating-point.
* The packed 24 bit input is stored in native endian format in a uint8_t byte array.
* The output float range is [-1.0, 1.0) for the fixed-point range [0x800000, 0x7fffff].
* No rounding is needed as the representation is exact.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_float_from_p24(float *dst, const uint8_t *src, size_t count);
/* Copy samples from signed fixed-point packed 24 bit Q0.23 to signed fixed point 16 bit Q0.15.
* The packed 24 bit output is stored in native endian format in a uint8_t byte array.
* The data is truncated without rounding.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_i16_from_p24(int16_t *dst, const uint8_t *src, size_t count);
/* Copy samples from signed fixed-point packed 24 bit Q0.23 to signed fixed-point 32-bit Q0.31.
* The packed 24 bit input is stored in native endian format in a uint8_t byte array.
* The output data range is [0x80000000, 0x7fffff00] at intervals of 0x100.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_i32_from_p24(int32_t *dst, const uint8_t *src, size_t count);
/* Copy samples from signed fixed point 16 bit Q0.15 to signed fixed-point packed 24 bit Q0.23.
* The packed 24 bit output is assumed to be a native-endian uint8_t byte array.
* The output data range is [0x800000, 0x7fff00] (not full).
* Nevertheless there is no DC offset on the output, if the input has no DC offset.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_p24_from_i16(uint8_t *dst, const int16_t *src, size_t count);
/* Copy samples from single-precision floating-point to signed fixed-point packed 24 bit Q0.23.
* The packed 24 bit output is assumed to be a native-endian uint8_t byte array.
* The data is clamped and rounded to nearest, ties away from zero. See clamp24_from_float()
* for details.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_p24_from_float(uint8_t *dst, const float *src, size_t count);
/* Copy samples from signed fixed-point 32-bit Q8.23 to signed fixed-point packed 24 bit Q0.23.
* The packed 24 bit output is assumed to be a native-endian uint8_t byte array.
* The data is clamped to the range is [0x800000, 0x7fffff].
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_p24_from_q8_23(uint8_t *dst, const int32_t *src, size_t count);
/* Shrink and copy samples from signed 32-bit fixed-point Q0.31
* to signed fixed-point packed 24 bit Q0.23.
* The packed 24 bit output is assumed to be a native-endian uint8_t byte array.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
* The conversion is done by truncation, without dithering, so it loses resolution.
*/
void memcpy_to_p24_from_i32(uint8_t *dst, const int32_t *src, size_t count);
/* Copy samples from signed fixed point 16-bit Q0.15 to signed fixed-point 32-bit Q8.23.
* The output data range is [0xff800000, 0x007fff00] at intervals of 0x100.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_q8_23_from_i16(int32_t *dst, const int16_t *src, size_t count);
/* Copy samples from single-precision floating-point to signed fixed-point 32-bit Q8.23.
* This copy will clamp the Q8.23 representation to [0xff800000, 0x007fffff] even though there
* are guard bits available. Fractional lsb is rounded to nearest, ties away from zero.
* See clamp24_from_float() for details.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_q8_23_from_float_with_clamp(int32_t *dst, const float *src, size_t count);
/* Copy samples from signed fixed point packed 24-bit Q0.23 to signed fixed-point 32-bit Q8.23.
* The output data range is [0xff800000, 0x007fffff].
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_q8_23_from_p24(int32_t *dst, const uint8_t *src, size_t count);
/* Copy samples from single-precision floating-point to signed fixed-point 32-bit Q4.27.
* The conversion will use the full available Q4.27 range, including guard bits.
* Fractional lsb is rounded to nearest, ties away from zero.
* See clampq4_27_from_float() for details.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_q4_27_from_float(int32_t *dst, const float *src, size_t count);
/* Copy samples from signed fixed-point 32-bit Q8.23 to signed fixed point 16-bit Q0.15.
* The data is clamped, and truncated without rounding.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_i16_from_q8_23(int16_t *dst, const int32_t *src, size_t count);
/* Copy samples from signed fixed-point 32-bit Q8.23 to single-precision floating-point.
* The nominal output float range is [-1.0, 1.0) for the fixed-point
* range [0xff800000, 0x007fffff]. The maximum output float range is [-256.0, 256.0).
* No rounding is needed as the representation is exact for nominal values.
* Rounding for overflow values is to nearest, ties to even.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_float_from_q8_23(float *dst, const int32_t *src, size_t count);
/* Copy samples from signed fixed point 16-bit Q0.15 to signed fixed-point 32-bit Q0.31.
* The output data range is [0x80000000, 0x7fff0000] at intervals of 0x10000.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must be completely separate.
*/
void memcpy_to_i32_from_i16(int32_t *dst, const int16_t *src, size_t count);
/* Copy samples from single-precision floating-point to signed fixed-point 32-bit Q0.31.
* If rounding is needed on truncation, the fractional lsb is rounded to nearest,
* ties away from zero. See clamp32_from_float() for details.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_i32_from_float(int32_t *dst, const float *src, size_t count);
/* Copy samples from signed fixed-point 32-bit Q0.31 to single-precision floating-point.
* The float range is [-1.0, 1.0] for the fixed-point range [0x80000000, 0x7fffffff].
* Rounding is done according to float_from_i32().
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of samples to copy
* The destination and source buffers must either be completely separate (non-overlapping), or
* they must both start at the same address. Partially overlapping buffers are not supported.
*/
void memcpy_to_float_from_i32(float *dst, const int32_t *src, size_t count);
/* Downmix pairs of interleaved stereo input 16-bit samples to mono output 16-bit samples.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of stereo frames to downmix
* The destination and source buffers must be completely separate (non-overlapping).
* The current implementation truncates the mean rather than dither, but this may change.
*/
void downmix_to_mono_i16_from_stereo_i16(int16_t *dst, const int16_t *src, size_t count);
/* Upmix mono input 16-bit samples to pairs of interleaved stereo output 16-bit samples by
* duplicating.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of mono samples to upmix
* The destination and source buffers must be completely separate (non-overlapping).
*/
void upmix_to_stereo_i16_from_mono_i16(int16_t *dst, const int16_t *src, size_t count);
/* Downmix pairs of interleaved stereo input float samples to mono output float samples
* by averaging the stereo pair together.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of stereo frames to downmix
* The destination and source buffers must be completely separate (non-overlapping),
* or they must both start at the same address.
*/
void downmix_to_mono_float_from_stereo_float(float *dst, const float *src, size_t count);
/* Upmix mono input float samples to pairs of interleaved stereo output float samples by
* duplicating.
* Parameters:
* dst Destination buffer
* src Source buffer
* count Number of mono samples to upmix
* The destination and source buffers must be completely separate (non-overlapping).
*/
void upmix_to_stereo_float_from_mono_float(float *dst, const float *src, size_t count);
/* Return the total number of non-zero 32-bit samples */
size_t nonZeroMono32(const int32_t *samples, size_t count);
/* Return the total number of non-zero 16-bit samples */
size_t nonZeroMono16(const int16_t *samples, size_t count);
/* Return the total number of non-zero stereo frames, where a frame is considered non-zero
* if either of its constituent 32-bit samples is non-zero
*/
size_t nonZeroStereo32(const int32_t *frames, size_t count);
/* Return the total number of non-zero stereo frames, where a frame is considered non-zero
* if either of its constituent 16-bit samples is non-zero
*/
size_t nonZeroStereo16(const int16_t *frames, size_t count);
/* Copy frames, selecting source samples based on a source channel mask to fit
* the destination channel mask. Unmatched channels in the destination channel mask
* are zero filled. Unmatched channels in the source channel mask are dropped.
* Channels present in the channel mask are represented by set bits in the
* uint32_t value and are matched without further interpretation.
* Parameters:
* dst Destination buffer
* dst_mask Bit mask corresponding to destination channels present
* src Source buffer
* src_mask Bit mask corresponding to source channels present
* sample_size Size of each sample in bytes. Must be 1, 2, 3, or 4.
* count Number of frames to copy
* The destination and source buffers must be completely separate (non-overlapping).
* If the sample size is not in range, the function will abort.
*/
void memcpy_by_channel_mask(void *dst, uint32_t dst_mask,
const void *src, uint32_t src_mask, size_t sample_size, size_t count);
/* Copy frames, selecting source samples based on an index array (idxary).
* The idxary[] consists of dst_channels number of elements.
* The ith element if idxary[] corresponds the ith destination channel.
* A non-negative value is the channel index in the source frame.
* A negative index (-1) represents filling with 0.
*
* Example: Swapping L and R channels for stereo streams
* idxary[0] = 1;
* idxary[1] = 0;
*
* Example: Copying a mono source to the front center 5.1 channel
* idxary[0] = -1;
* idxary[1] = -1;
* idxary[2] = 0;
* idxary[3] = -1;
* idxary[4] = -1;
* idxary[5] = -1;
*
* This copy allows swizzling of channels or replication of channels.
*
* Parameters:
* dst Destination buffer
* dst_channels Number of destination channels per frame
* src Source buffer
* src_channels Number of source channels per frame
* idxary Array of indices representing channels in the source frame
* sample_size Size of each sample in bytes. Must be 1, 2, 3, or 4.
* count Number of frames to copy
* The destination and source buffers must be completely separate (non-overlapping).
* If the sample size is not in range, the function will abort.
*/
void memcpy_by_index_array(void *dst, uint32_t dst_channels,
const void *src, uint32_t src_channels,
const int8_t *idxary, size_t sample_size, size_t count);
/* Prepares an index array (idxary) from channel masks, which can be later
* used by memcpy_by_index_array(). Returns the number of array elements required.
* This may be greater than idxcount, so the return value should be checked
* if idxary size is less than 32. Note that idxary is a caller allocated array
* of at least as many channels as present in the dst_mask.
* Channels present in the channel mask are represented by set bits in the
* uint32_t value and are matched without further interpretation.
*
* This function is typically used for converting audio data with different
* channel position masks.
*
* Parameters:
* idxary Updated array of indices of channels in the src frame for the dst frame
* idxcount Number of caller allocated elements in idxary
* dst_mask Bit mask corresponding to destination channels present
* src_mask Bit mask corresponding to source channels present
*/
size_t memcpy_by_index_array_initialization(int8_t *idxary, size_t idxcount,
uint32_t dst_mask, uint32_t src_mask);
/* Prepares an index array (idxary) from channel masks, which can be later
* used by memcpy_by_index_array(). Returns the number of array elements required.
*
* For a source channel index mask, the source channels will map to the destination
* channels as if counting the set bits in dst_mask in order from lsb to msb
* (zero bits are ignored). The ith bit of the src_mask corresponds to the
* ith SET bit of dst_mask and the ith destination channel. Hence, a zero ith
* bit of the src_mask indicates that the ith destination channel plays silence.
*
* Parameters:
* idxary Updated array of indices of channels in the src frame for the dst frame
* idxcount Number of caller allocated elements in idxary
* dst_mask Bit mask corresponding to destination channels present
* src_mask Bit mask corresponding to source channels present
*/
size_t memcpy_by_index_array_initialization_src_index(int8_t *idxary, size_t idxcount,
uint32_t dst_mask, uint32_t src_mask);
/* Prepares an index array (idxary) from channel mask bits, which can be later
* used by memcpy_by_index_array(). Returns the number of array elements required.
*
* This initialization is for a destination channel index mask from a positional
* source mask.
*
* For an destination channel index mask, the input channels will map
* to the destination channels, with the ith SET bit in the source bits corresponding
* to the ith bit in the destination bits. If there is a zero bit in the middle
* of set destination bits (unlikely), the corresponding source channel will
* be dropped.
*
* Parameters:
* idxary Updated array of indices of channels in the src frame for the dst frame
* idxcount Number of caller allocated elements in idxary
* dst_mask Bit mask corresponding to destination channels present
* src_mask Bit mask corresponding to source channels present
*/
size_t memcpy_by_index_array_initialization_dst_index(int8_t *idxary, size_t idxcount,
uint32_t dst_mask, uint32_t src_mask);
/**
* Clamp (aka hard limit or clip) a signed 32-bit sample to 16-bit range.
*/
static inline int16_t clamp16(int32_t sample) {
if ((sample >> 15) ^ (sample >> 31))
sample = 0x7FFF ^ (sample >> 31);
return sample;
}
/*
* Convert a IEEE 754 single precision float [-1.0, 1.0) to int16_t [-32768, 32767]
* with clamping. Note the open bound at 1.0, values within 1/65536 of 1.0 map
* to 32767 instead of 32768 (early clamping due to the smaller positive integer subrange).
*
* Values outside the range [-1.0, 1.0) are properly clamped to -32768 and 32767,
* including -Inf and +Inf. NaN will generally be treated either as -32768 or 32767,
* depending on the sign bit inside NaN (whose representation is not unique).
* Nevertheless, strictly speaking, NaN behavior should be considered undefined.
*
* Rounding of 0.5 lsb is to even (default for IEEE 754).
*/
static inline int16_t clamp16_from_float(float f) {
/* Offset is used to expand the valid range of [-1.0, 1.0) into the 16 lsbs of the
* floating point significand. The normal shift is 3<<22, but the -15 offset
* is used to multiply by 32768.
*/
static const float offset = (float)(3 << (22 - 15));
/* zero = (0x10f << 22) = 0x43c00000 (not directly used) */
static const int32_t limneg = (0x10f << 22) /*zero*/ - 32768; /* 0x43bf8000 */
static const int32_t limpos = (0x10f << 22) /*zero*/ + 32767; /* 0x43c07fff */
union {
float f;
int32_t i;
} u;
u.f = f + offset; /* recenter valid range */
/* Now the valid range is represented as integers between [limneg, limpos].
* Clamp using the fact that float representation (as an integer) is an ordered set.
*/
if (u.i < limneg)
u.i = -32768;
else if (u.i > limpos)
u.i = 32767;
return u.i; /* Return lower 16 bits, the part of interest in the significand. */
}
/*
* Convert a IEEE 754 single precision float [-1.0, 1.0) to uint8_t [0, 0xff]
* with clamping. Note the open bound at 1.0, values within 1/128 of 1.0 map
* to 255 instead of 256 (early clamping due to the smaller positive integer subrange).
*
* Values outside the range [-1.0, 1.0) are properly clamped to 0 and 255,
* including -Inf and +Inf. NaN will generally be treated either as 0 or 255,
* depending on the sign bit inside NaN (whose representation is not unique).
* Nevertheless, strictly speaking, NaN behavior should be considered undefined.
*
* Rounding of 0.5 lsb is to even (default for IEEE 754).
*/
static inline uint8_t clamp8_from_float(float f) {
/* Offset is used to expand the valid range of [-1.0, 1.0) into the 16 lsbs of the
* floating point significand. The normal shift is 3<<22, but the -7 offset
* is used to multiply by 128.
*/
static const float offset = (float)((3 << (22 - 7)) + 1 /* to cancel -1.0 */);
/* zero = (0x11f << 22) = 0x47c00000 */
static const int32_t limneg = (0x11f << 22) /*zero*/;
static const int32_t limpos = (0x11f << 22) /*zero*/ + 255; /* 0x47c000ff */
union {
float f;
int32_t i;
} u;
u.f = f + offset; /* recenter valid range */
/* Now the valid range is represented as integers between [limneg, limpos].
* Clamp using the fact that float representation (as an integer) is an ordered set.
*/
if (u.i < limneg)
return 0;
if (u.i > limpos)
return 255;
return u.i; /* Return lower 8 bits, the part of interest in the significand. */
}
/* Convert a single-precision floating point value to a Q0.23 integer value, stored in a
* 32 bit signed integer (technically stored as Q8.23, but clamped to Q0.23).
*
* Rounds to nearest, ties away from 0.
*
* Values outside the range [-1.0, 1.0) are properly clamped to -8388608 and 8388607,
* including -Inf and +Inf. NaN values are considered undefined, and behavior may change
* depending on hardware and future implementation of this function.
*/
static inline int32_t clamp24_from_float(float f) {
static const float scale = (float)(1 << 23);
static const float limpos = 0x7fffff / (float)(1 << 23);
static const float limneg = -0x800000 / (float)(1 << 23);
if (f <= limneg) {
return -0x800000;
} else if (f >= limpos) {
return 0x7fffff;
}
f *= scale;
/* integer conversion is through truncation (though int to float is not).
* ensure that we round to nearest, ties away from 0.
*/
return f > 0 ? f + 0.5 : f - 0.5;
}
/* Convert a signed fixed-point 32-bit Q8.23 value to a Q0.23 integer value,
* stored in a 32-bit signed integer (technically stored as Q8.23, but clamped to Q0.23).
*
* Values outside the range [-0x800000, 0x7fffff] are clamped to that range.
*/
static inline int32_t clamp24_from_q8_23(int32_t ival) {
static const int32_t limpos = 0x7fffff;
static const int32_t limneg = -0x800000;
if (ival < limneg) {
return limneg;
} else if (ival > limpos) {
return limpos;
} else {
return ival;
}
}
/* Convert a single-precision floating point value to a Q4.27 integer value.
* Rounds to nearest, ties away from 0.
*
* Values outside the range [-16.0, 16.0) are properly clamped to -2147483648 and 2147483647,
* including -Inf and +Inf. NaN values are considered undefined, and behavior may change
* depending on hardware and future implementation of this function.
*/
static inline int32_t clampq4_27_from_float(float f) {
static const float scale = (float)(1UL << 27);
static const float limpos = 16.;
static const float limneg = -16.;
if (f <= limneg) {
return INT32_MIN; /* or 0x80000000 */
} else if (f >= limpos) {
return INT32_MAX;
}
f *= scale;
/* integer conversion is through truncation (though int to float is not).
* ensure that we round to nearest, ties away from 0.
*/
return f > 0 ? f + 0.5 : f - 0.5;
}
/* Convert a single-precision floating point value to a Q0.31 integer value.
* Rounds to nearest, ties away from 0.
*
* Values outside the range [-1.0, 1.0) are properly clamped to -2147483648 and 2147483647,
* including -Inf and +Inf. NaN values are considered undefined, and behavior may change
* depending on hardware and future implementation of this function.
*/
static inline int32_t clamp32_from_float(float f) {
static const float scale = (float)(1UL << 31);
static const float limpos = 1.;
static const float limneg = -1.;
if (f <= limneg) {
return INT32_MIN; /* or 0x80000000 */
} else if (f >= limpos) {
return INT32_MAX;
}
f *= scale;
/* integer conversion is through truncation (though int to float is not).
* ensure that we round to nearest, ties away from 0.
*/
return f > 0 ? f + 0.5 : f - 0.5;
}
/* Convert a signed fixed-point 32-bit Q4.27 value to single-precision floating-point.
* The nominal output float range is [-1.0, 1.0] if the fixed-point range is
* [0xf8000000, 0x07ffffff]. The full float range is [-16.0, 16.0].
*
* Note the closed range at 1.0 and 16.0 is due to rounding on conversion to float.
* In more detail: if the fixed-point integer exceeds 24 bit significand of single
* precision floating point, the 0.5 lsb in the significand conversion will round
* towards even, as per IEEE 754 default.
*/
static inline float float_from_q4_27(int32_t ival) {
/* The scale factor is the reciprocal of the fractional bits.
*
* Since the scale factor is a power of 2, the scaling is exact, and there
* is no rounding due to the multiplication - the bit pattern is preserved.
* However, there may be rounding due to the fixed-point to float conversion,
* as described above.
*/
static const float scale = 1. / (float)(1UL << 27);
return ival * scale;
}
/* Convert an unsigned fixed-point 32-bit U4.28 value to single-precision floating-point.
* The nominal output float range is [0.0, 1.0] if the fixed-point range is
* [0x00000000, 0x10000000]. The full float range is [0.0, 16.0].
*
* Note the closed range at 1.0 and 16.0 is due to rounding on conversion to float.
* In more detail: if the fixed-point integer exceeds 24 bit significand of single
* precision floating point, the 0.5 lsb in the significand conversion will round
* towards even, as per IEEE 754 default.
*/
static inline float float_from_u4_28(uint32_t uval) {
static const float scale = 1. / (float)(1UL << 28);
return uval * scale;
}
/* Convert an unsigned fixed-point 16-bit U4.12 value to single-precision floating-point.
* The nominal output float range is [0.0, 1.0] if the fixed-point range is
* [0x0000, 0x1000]. The full float range is [0.0, 16.0).
*/
static inline float float_from_u4_12(uint16_t uval) {
static const float scale = 1. / (float)(1UL << 12);
return uval * scale;
}
/* Convert a single-precision floating point value to a U4.28 integer value.
* Rounds to nearest, ties away from 0.
*
* Values outside the range [0, 16.0] are properly clamped to [0, 4294967295]
* including -Inf and +Inf. NaN values are considered undefined, and behavior may change
* depending on hardware and future implementation of this function.
*/
static inline uint32_t u4_28_from_float(float f) {
static const float scale = (float)(1 << 28);
static const float limpos = 16.0f;
if (f <= 0.) {
return 0;
} else if (f >= limpos) {
// return 0xffffffff;
return UINT32_MAX;
}
/* integer conversion is through truncation (though int to float is not).
* ensure that we round to nearest, ties away from 0.
*/
return f * scale + 0.5;
}
/* Convert a single-precision floating point value to a U4.12 integer value.
* Rounds to nearest, ties away from 0.
*
* Values outside the range [0, 16.0) are properly clamped to [0, 65535]
* including -Inf and +Inf. NaN values are considered undefined, and behavior may change
* depending on hardware and future implementation of this function.
*/
static inline uint16_t u4_12_from_float(float f) {
static const float scale = (float)(1 << 12);
static const float limpos = 0xffff / (float)(1 << 12);
if (f <= 0.) {
return 0;
} else if (f >= limpos) {
// return 0xffff;
return UINT16_MAX;
}
/* integer conversion is through truncation (though int to float is not).
* ensure that we round to nearest, ties away from 0.
*/
return f * scale + 0.5;
}
/* Convert a signed fixed-point 16-bit Q0.15 value to single-precision floating-point.
* The output float range is [-1.0, 1.0) for the fixed-point range
* [0x8000, 0x7fff].
*
* There is no rounding, the conversion and representation is exact.
*/
static inline float float_from_i16(int16_t ival) {
/* The scale factor is the reciprocal of the nominal 16 bit integer
* half-sided range (32768).
*
* Since the scale factor is a power of 2, the scaling is exact, and there
* is no rounding due to the multiplication - the bit pattern is preserved.
*/
static const float scale = 1. / (float)(1UL << 15);
return ival * scale;
}
/* Convert an unsigned fixed-point 8-bit U0.8 value to single-precision floating-point.
* The nominal output float range is [-1.0, 1.0) if the fixed-point range is
* [0x00, 0xff].
*/
static inline float float_from_u8(uint8_t uval) {
static const float scale = 1. / (float)(1UL << 7);
return ((int)uval - 128) * scale;
}
/* Convert a packed 24bit Q0.23 value stored native-endian in a uint8_t ptr
* to a signed fixed-point 32 bit integer Q0.31 value. The output Q0.31 range
* is [0x80000000, 0x7fffff00] for the fixed-point range [0x800000, 0x7fffff].
* Even though the output range is limited on the positive side, there is no
* DC offset on the output, if the input has no DC offset.
*
* Avoid relying on the limited output range, as future implementations may go
* to full range.
*/
static inline int32_t i32_from_p24(const uint8_t *packed24) {
/* convert to 32b */
return (packed24[0] << 8) | (packed24[1] << 16) | (packed24[2] << 24);
}
/* Convert a 32-bit Q0.31 value to single-precision floating-point.
* The output float range is [-1.0, 1.0] for the fixed-point range
* [0x80000000, 0x7fffffff].
*
* Rounding may occur in the least significant 8 bits for large fixed point
* values due to storage into the 24-bit floating-point significand.
* Rounding will be to nearest, ties to even.
*/
static inline float float_from_i32(int32_t ival) {
static const float scale = 1. / (float)(1UL << 31);
return ival * scale;
}
/* Convert a packed 24bit Q0.23 value stored native endian in a uint8_t ptr
* to single-precision floating-point. The output float range is [-1.0, 1.0)
* for the fixed-point range [0x800000, 0x7fffff].
*
* There is no rounding, the conversion and representation is exact.
*/
static inline float float_from_p24(const uint8_t *packed24) {
return float_from_i32(i32_from_p24(packed24));
}
/* Convert a 24-bit Q8.23 value to single-precision floating-point.
* The nominal output float range is [-1.0, 1.0) for the fixed-point
* range [0xff800000, 0x007fffff]. The maximum float range is [-256.0, 256.0).
*
* There is no rounding in the nominal range, the conversion and representation
* is exact. For values outside the nominal range, rounding is to nearest, ties to even.
*/
static inline float float_from_q8_23(int32_t ival) {
static const float scale = 1. / (float)(1UL << 23);
return ival * scale;
}
/**
* Multiply-accumulate 16-bit terms with 32-bit result: return a + in*v.
*/
static inline int32_t mulAdd(int16_t in, int16_t v, int32_t a) {
#if defined(__arm__) && !defined(__thumb__)
int32_t out;
asm("smlabb %[out], %[in], %[v], %[a] \n"
: [out] "=r"(out)
: [in] "%r"(in), [v] "r"(v), [a] "r"(a)
:);
return out;
#else
return a + in * (int32_t)v;
#endif
}
/**
* Multiply 16-bit terms with 32-bit result: return in*v.
*/
static inline int32_t mul(int16_t in, int16_t v) {
#if defined(__arm__) && !defined(__thumb__)
int32_t out;
asm("smulbb %[out], %[in], %[v] \n"
: [out] "=r"(out)
: [in] "%r"(in), [v] "r"(v)
:);
return out;
#else
return in * (int32_t)v;
#endif
}
/**
* Similar to mulAdd, but the 16-bit terms are extracted from a 32-bit interleaved stereo pair.
*/
static inline int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a) {
#if defined(__arm__) && !defined(__thumb__)
int32_t out;
if (left) {
asm("smlabb %[out], %[inRL], %[vRL], %[a] \n"
: [out] "=r"(out)
: [inRL] "%r"(inRL), [vRL] "r"(vRL), [a] "r"(a)
:);
} else {
asm("smlatt %[out], %[inRL], %[vRL], %[a] \n"
: [out] "=r"(out)
: [inRL] "%r"(inRL), [vRL] "r"(vRL), [a] "r"(a)
:);
}
return out;
#else
if (left) {
return a + (int16_t)(inRL & 0xFFFF) * (int16_t)(vRL & 0xFFFF);
}
return a + (int16_t)(inRL >> 16) * (int16_t)(vRL >> 16);
#endif
}
/**
* Similar to mul, but the 16-bit terms are extracted from a 32-bit interleaved stereo pair.
*/
static inline int32_t mulRL(int left, uint32_t inRL, uint32_t vRL) {
#if defined(__arm__) && !defined(__thumb__)
int32_t out;
if (left) {
asm("smulbb %[out], %[inRL], %[vRL] \n"
: [out] "=r"(out)
: [inRL] "%r"(inRL), [vRL] "r"(vRL)
:);
} else {
asm("smultt %[out], %[inRL], %[vRL] \n"
: [out] "=r"(out)
: [inRL] "%r"(inRL), [vRL] "r"(vRL)
:);
}
return out;
#else
if (left) {
return (int16_t)(inRL & 0xFFFF) * (int16_t)(vRL & 0xFFFF);
}
return (int16_t)(inRL >> 16) * (int16_t)(vRL >> 16);
#endif
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
// This is a C library for reading and writing PCM .wav files. It is
// influenced by other libraries such as libsndfile and audiofile, except is
// much smaller and has an Apache 2.0 license.
// The API should be familiar to clients of similar libraries, but there is
// no guarantee that it will stay exactly source-code compatible with other libraries.
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#include <sys/cdefs.h>
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
#include <sys/types.h>
#elif CC_PLATFORM == CC_PLATFORM_OPENHARMONY
#include <cstdint>
#endif
#include <cstdint>
#include <cstdio>
namespace sf {
// visible to clients
using sf_count_t = int;
struct SF_INFO {
sf_count_t frames;
int samplerate;
int channels;
int format;
};
// opaque to clients
using SNDFILE = struct SNDFILE_;
// Format
#define SF_FORMAT_TYPEMASK 1
#define SF_FORMAT_WAV 1
#define SF_FORMAT_SUBMASK 14
#define SF_FORMAT_PCM_16 2
#define SF_FORMAT_PCM_U8 4
#define SF_FORMAT_FLOAT 6
#define SF_FORMAT_PCM_32 8
#define SF_FORMAT_PCM_24 10
struct snd_callbacks {
void *(*open)(const char *path, void *user);
size_t (*read)(void *ptr, size_t size, size_t nmemb, void *datasource);
int (*seek)(void *datasource, long offset, int whence); //NOLINT(google-runtime-int)
int (*close)(void *datasource);
long (*tell)(void *datasource); //NOLINT(google-runtime-int)
};
// Open stream
SNDFILE *sf_open_read(const char *path, SF_INFO *info, snd_callbacks *cb, void *user); //NOLINT(readability-identifier-naming)
// Close stream
void sf_close(SNDFILE *handle); //NOLINT(readability-identifier-naming)
// Read interleaved frames and return actual number of frames read
sf_count_t sf_readf_short(SNDFILE *handle, int16_t *ptr, sf_count_t desired); //NOLINT(readability-identifier-naming)
/*
sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desired);
sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desired);
*/
off_t sf_seek(SNDFILE *handle, int offset, int whence); //NOLINT(readability-identifier-naming)
off_t sf_tell(SNDFILE *handle); //NOLINT(readability-identifier-naming)
static int sInited = 0;
static void sf_lazy_init(); //NOLINT(readability-identifier-naming)
struct SNDFILE_ {
uint8_t *temp; // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
void *stream;
size_t bytesPerFrame;
size_t remaining; // frames unread for SFM_READ, frames written for SFM_WRITE
SF_INFO info;
snd_callbacks callback;
};
} // namespace sf

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "audio/common/utils/include/minifloat.h"
#include <cmath>
#define EXPONENT_BITS 3
#define EXPONENT_MAX ((1 << EXPONENT_BITS) - 1)
#define EXCESS ((1 << EXPONENT_BITS) - 2)
#define MANTISSA_BITS 13
#define MANTISSA_MAX ((1 << MANTISSA_BITS) - 1)
#define HIDDEN_BIT (1 << MANTISSA_BITS)
#define ONE_FLOAT ((float)(1 << (MANTISSA_BITS + 1)))
#define MINIFLOAT_MAX ((EXPONENT_MAX << MANTISSA_BITS) | MANTISSA_MAX)
#if EXPONENT_BITS + MANTISSA_BITS != 16
#error EXPONENT_BITS and MANTISSA_BITS must sum to 16
#endif
gain_minifloat_t gain_from_float(float v) {
if (std::isnan(v) || v <= 0.0f) {
return 0;
}
if (v >= 2.0f) {
return MINIFLOAT_MAX;
}
int exp;
float r = frexpf(v, &exp);
if ((exp += EXCESS) > EXPONENT_MAX) {
return MINIFLOAT_MAX;
}
if (-exp >= MANTISSA_BITS) {
return 0;
}
int mantissa = (int)(r * ONE_FLOAT);
return exp > 0 ? (exp << MANTISSA_BITS) | (mantissa & ~HIDDEN_BIT) : (mantissa >> (1 - exp)) & MANTISSA_MAX;
}
float float_from_gain(gain_minifloat_t a) {
int mantissa = a & MANTISSA_MAX;
int exponent = (a >> MANTISSA_BITS) & EXPONENT_MAX;
return ldexpf((exponent > 0 ? HIDDEN_BIT | mantissa : mantissa << 1) / ONE_FLOAT,
exponent - EXCESS);
}

View File

@@ -0,0 +1,500 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "audio/common/utils/include/primitives.h"
#include "audio/common/utils/private/private.h"
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#include "audio/android/cutils/bitops.h" /* for popcount() */
#else
#include "base/Utils.h"
using namespace cc::utils;
#endif
// namespace {
void ditherAndClamp(int32_t *out, const int32_t *sums, size_t c) {
size_t i;
for (i = 0; i < c; i++) {
int32_t l = *sums++;
int32_t r = *sums++;
int32_t nl = l >> 12;
int32_t nr = r >> 12;
l = clamp16(nl);
r = clamp16(nr);
*out++ = (r << 16) | (l & 0xFFFF);
}
}
void memcpy_to_i16_from_u8(int16_t *dst, const uint8_t *src, size_t count) {
dst += count;
src += count;
while (count--) {
*--dst = static_cast<int16_t>(*--src - 0x80) << 8;
}
}
void memcpy_to_u8_from_i16(uint8_t *dst, const int16_t *src, size_t count) {
while (count--) {
*dst++ = (*src++ >> 8) + 0x80;
}
}
void memcpy_to_u8_from_float(uint8_t *dst, const float *src, size_t count) {
while (count--) {
*dst++ = clamp8_from_float(*src++);
}
}
void memcpy_to_i16_from_i32(int16_t *dst, const int32_t *src, size_t count) {
while (count--) {
*dst++ = *src++ >> 16;
}
}
void memcpy_to_i16_from_float(int16_t *dst, const float *src, size_t count) {
while (count--) {
*dst++ = clamp16_from_float(*src++);
}
}
void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t count) {
while (count--) {
*dst++ = float_from_q4_27(*src++);
}
}
void memcpy_to_float_from_i16(float *dst, const int16_t *src, size_t count) {
while (count--) {
*dst++ = float_from_i16(*src++);
}
}
void memcpy_to_float_from_u8(float *dst, const uint8_t *src, size_t count) {
while (count--) {
*dst++ = float_from_u8(*src++);
}
}
void memcpy_to_float_from_p24(float *dst, const uint8_t *src, size_t count) {
while (count--) {
*dst++ = float_from_p24(src);
src += 3;
}
}
void memcpy_to_i16_from_p24(int16_t *dst, const uint8_t *src, size_t count) {
while (count--) {
#ifdef HAVE_BIG_ENDIAN
*dst++ = src[1] | (src[0] << 8);
#else
*dst++ = src[1] | (src[2] << 8);
#endif
src += 3;
}
}
void memcpy_to_i32_from_p24(int32_t *dst, const uint8_t *src, size_t count) {
while (count--) {
#ifdef HAVE_BIG_ENDIAN
*dst++ = (src[2] << 8) | (src[1] << 16) | (src[0] << 24);
#else
*dst++ = (src[0] << 8) | (src[1] << 16) | (src[2] << 24);
#endif
src += 3;
}
}
void memcpy_to_p24_from_i16(uint8_t *dst, const int16_t *src, size_t count) {
while (count--) {
#ifdef HAVE_BIG_ENDIAN
*dst++ = *src >> 8;
*dst++ = *src++;
*dst++ = 0;
#else
*dst++ = 0;
*dst++ = *src;
*dst++ = *src++ >> 8;
#endif
}
}
void memcpy_to_p24_from_float(uint8_t *dst, const float *src, size_t count) {
while (count--) {
int32_t ival = clamp24_from_float(*src++);
#ifdef HAVE_BIG_ENDIAN
*dst++ = ival >> 16;
*dst++ = ival >> 8;
*dst++ = ival;
#else
*dst++ = ival;
*dst++ = ival >> 8;
*dst++ = ival >> 16;
#endif
}
}
void memcpy_to_p24_from_q8_23(uint8_t *dst, const int32_t *src, size_t count) {
while (count--) {
int32_t ival = clamp24_from_q8_23(*src++);
#ifdef HAVE_BIG_ENDIAN
*dst++ = ival >> 16;
*dst++ = ival >> 8;
*dst++ = ival;
#else
*dst++ = ival;
*dst++ = ival >> 8;
*dst++ = ival >> 16;
#endif
}
}
void memcpy_to_p24_from_i32(uint8_t *dst, const int32_t *src, size_t count) {
while (count--) {
int32_t ival = *src++ >> 8;
#ifdef HAVE_BIG_ENDIAN
*dst++ = ival >> 16;
*dst++ = ival >> 8;
*dst++ = ival;
#else
*dst++ = ival;
*dst++ = ival >> 8;
*dst++ = ival >> 16;
#endif
}
}
void memcpy_to_q8_23_from_i16(int32_t *dst, const int16_t *src, size_t count) {
while (count--) {
*dst++ = static_cast<int32_t>(*src++) << 8;
}
}
void memcpy_to_q8_23_from_float_with_clamp(int32_t *dst, const float *src, size_t count) {
while (count--) {
*dst++ = clamp24_from_float(*src++);
}
}
void memcpy_to_q8_23_from_p24(int32_t *dst, const uint8_t *src, size_t count) {
while (count--) {
#ifdef HAVE_BIG_ENDIAN
*dst++ = (int8_t)src[0] << 16 | src[1] << 8 | src[2];
#else
*dst++ = static_cast<int8_t>(src[2]) << 16 | src[1] << 8 | src[0];
#endif
src += 3;
}
}
void memcpy_to_q4_27_from_float(int32_t *dst, const float *src, size_t count) {
while (count--) {
*dst++ = clampq4_27_from_float(*src++);
}
}
void memcpy_to_i16_from_q8_23(int16_t *dst, const int32_t *src, size_t count) {
while (count--) {
*dst++ = clamp16(*src++ >> 8);
}
}
void memcpy_to_float_from_q8_23(float *dst, const int32_t *src, size_t count) {
while (count--) {
*dst++ = float_from_q8_23(*src++);
}
}
void memcpy_to_i32_from_i16(int32_t *dst, const int16_t *src, size_t count) {
while (count--) {
*dst++ = static_cast<int32_t>(*src++) << 16;
}
}
void memcpy_to_i32_from_float(int32_t *dst, const float *src, size_t count) {
while (count--) {
*dst++ = clamp32_from_float(*src++);
}
}
void memcpy_to_float_from_i32(float *dst, const int32_t *src, size_t count) {
while (count--) {
*dst++ = float_from_i32(*src++);
}
}
void downmix_to_mono_i16_from_stereo_i16(int16_t *dst, const int16_t *src, size_t count) {
while (count--) {
*dst++ = static_cast<int16_t>((static_cast<int32_t>(src[0]) + static_cast<int32_t>(src[1])) >> 1);
src += 2;
}
}
void upmix_to_stereo_i16_from_mono_i16(int16_t *dst, const int16_t *src, size_t count) {
while (count--) {
int32_t temp = *src++;
dst[0] = temp;
dst[1] = temp;
dst += 2;
}
}
void downmix_to_mono_float_from_stereo_float(float *dst, const float *src, size_t frames) {
while (frames--) {
*dst++ = (src[0] + src[1]) * 0.5;
src += 2;
}
}
void upmix_to_stereo_float_from_mono_float(float *dst, const float *src, size_t frames) {
while (frames--) {
float temp = *src++;
dst[0] = temp;
dst[1] = temp;
dst += 2;
}
}
size_t nonZeroMono32(const int32_t *samples, size_t count) {
size_t nonZero = 0;
while (count-- > 0) {
if (*samples++ != 0) {
nonZero++;
}
}
return nonZero;
}
size_t nonZeroMono16(const int16_t *samples, size_t count) {
size_t nonZero = 0;
while (count-- > 0) {
if (*samples++ != 0) {
nonZero++;
}
}
return nonZero;
}
size_t nonZeroStereo32(const int32_t *frames, size_t count) {
size_t nonZero = 0;
while (count-- > 0) {
if (frames[0] != 0 || frames[1] != 0) {
nonZero++;
}
frames += 2;
}
return nonZero;
}
size_t nonZeroStereo16(const int16_t *frames, size_t count) {
size_t nonZero = 0;
while (count-- > 0) {
if (frames[0] != 0 || frames[1] != 0) {
nonZero++;
}
frames += 2;
}
return nonZero;
}
/*
* C macro to do channel mask copying independent of dst/src sample type.
* Don't pass in any expressions for the macro arguments here.
*/
#define COPY_FRAME_BY_MASK(dst, dmask, src, smask, count, zero) \
{ \
int32_t bit, ormask; \
while ((count)--) { \
ormask = (dmask) | (smask); \
while (ormask) { \
bit = ormask & -ormask; /* get lowest bit */ \
ormask ^= bit; /* remove lowest bit */ \
if ((dmask)&bit) { \
*(dst)++ = (smask)&bit ? *(src)++ : (zero); \
} else { /* source channel only */ \
++(src); \
} \
} \
} \
}
void memcpy_by_channel_mask(void *dst, uint32_t dstMask,
const void *src, uint32_t srcMask, size_t sampleSize, size_t count) {
#if 0
/* alternate way of handling memcpy_by_channel_mask by using the idxary */
int8_t idxary[32];
uint32_t src_channels = popcount(src_mask);
uint32_t dst_channels =
memcpy_by_index_array_initialization(idxary, 32, dst_mask, src_mask);
memcpy_by_idxary(dst, dst_channels, src, src_channels, idxary, sample_size, count);
#else
if (dstMask == srcMask) {
memcpy(dst, src, sampleSize * popcount(dstMask) * count);
return;
}
switch (sampleSize) {
case 1: {
auto *udst = static_cast<uint8_t *>(dst);
const auto *usrc = static_cast<const uint8_t *>(src);
COPY_FRAME_BY_MASK(udst, dstMask, usrc, srcMask, count, 0);
} break;
case 2: {
auto *udst = static_cast<uint16_t *>(dst);
const auto *usrc = static_cast<const uint16_t *>(src);
COPY_FRAME_BY_MASK(udst, dstMask, usrc, srcMask, count, 0);
} break;
case 3: { /* could be slow. use a struct to represent 3 bytes of data. */
auto *udst = static_cast<uint8x3_t *>(dst);
const auto *usrc = static_cast<const uint8x3_t *>(src);
static const uint8x3_t ZERO{0, 0, 0}; /* tricky - we use this to zero out a sample */
COPY_FRAME_BY_MASK(udst, dstMask, usrc, srcMask, count, ZERO);
} break;
case 4: {
auto *udst = static_cast<uint32_t *>(dst);
const auto *usrc = static_cast<const uint32_t *>(src);
COPY_FRAME_BY_MASK(udst, dstMask, usrc, srcMask, count, 0);
} break;
default:
abort(); /* illegal value */
break;
}
#endif
}
/*
* C macro to do copying by index array, to rearrange samples
* within a frame. This is independent of src/dst sample type.
* Don't pass in any expressions for the macro arguments here.
*/
#define COPY_FRAME_BY_IDX(dst, dst_channels, src, src_channels, idxary, count, zero) \
{ \
unsigned i; \
int index; \
while ((count)--) { \
for (i = 0; i < (dst_channels); ++i) { \
index = (idxary)[i]; \
*(dst)++ = index < 0 ? (zero) : (src)[index]; \
} \
(src) += (src_channels); \
} \
}
void memcpy_by_index_array(void *dst, uint32_t dstChannels,
const void *src, uint32_t srcChannels,
const int8_t *idxary, size_t sampleSize, size_t count) {
switch (sampleSize) {
case 1: {
auto *udst = static_cast<uint8_t *>(dst);
const auto *usrc = static_cast<const uint8_t *>(src);
COPY_FRAME_BY_IDX(udst, dstChannels, usrc, srcChannels, idxary, count, 0); // NOLINT
} break;
case 2: {
auto *udst = static_cast<uint16_t *>(dst);
const auto *usrc = static_cast<const uint16_t *>(src);
COPY_FRAME_BY_IDX(udst, dstChannels, usrc, srcChannels, idxary, count, 0); // NOLINT
} break;
case 3: { /* could be slow. use a struct to represent 3 bytes of data. */
auto *udst = static_cast<uint8x3_t *>(dst);
const auto *usrc = static_cast<const uint8x3_t *>(src);
static const uint8x3_t ZERO{0, 0, 0};
COPY_FRAME_BY_IDX(udst, dstChannels, usrc, srcChannels, idxary, count, ZERO);
} break;
case 4: {
auto *udst = static_cast<uint32_t *>(dst);
const auto *usrc = static_cast<const uint32_t *>(src);
COPY_FRAME_BY_IDX(udst, dstChannels, usrc, srcChannels, idxary, count, 0);
} break;
default:
abort(); /* illegal value */
break;
}
}
size_t memcpy_by_index_array_initialization(int8_t *idxary, size_t idxcount,
uint32_t dstMask, uint32_t srcMask) {
size_t n = 0;
int srcidx = 0;
int32_t bit;
int32_t ormask = srcMask | dstMask;
while (ormask && n < idxcount) {
bit = ormask & -ormask; /* get lowest bit */
ormask ^= bit; /* remove lowest bit */
if (srcMask & dstMask & bit) { /* matching channel */
idxary[n++] = srcidx++;
} else if (srcMask & bit) { /* source channel only */
++srcidx;
} else { /* destination channel only */
idxary[n++] = -1;
}
}
return n + popcount(ormask & dstMask);
}
size_t memcpy_by_index_array_initialization_src_index(int8_t *idxary, size_t idxcount,
uint32_t dstMask, uint32_t srcMask) {
size_t dstCount = popcount(dstMask);
if (idxcount == 0) {
return dstCount;
}
if (dstCount > idxcount) {
dstCount = idxcount;
}
size_t srcIdx;
size_t dstIdx;
for (srcIdx = 0, dstIdx = 0; dstIdx < dstCount; ++dstIdx) {
if (srcMask & 1) {
idxary[dstIdx] = srcIdx++;
} else {
idxary[dstIdx] = -1;
}
srcMask >>= 1;
}
return dstIdx;
}
size_t memcpy_by_index_array_initialization_dst_index(int8_t *idxary, size_t idxcount,
uint32_t dstMask, uint32_t srcMask) {
size_t srcIdx;
size_t dstIdx;
size_t dstCount = popcount(dstMask);
size_t srcCount = popcount(srcMask);
if (idxcount == 0) {
return dstCount;
}
if (dstCount > idxcount) {
dstCount = idxcount;
}
for (srcIdx = 0, dstIdx = 0; dstIdx < dstCount; ++srcIdx) {
if (dstMask & 1) {
idxary[dstIdx++] = srcIdx < srcCount ? static_cast<signed>(srcIdx) : -1;
}
dstMask >>= 1;
}
return dstIdx;
}
//} // namespace

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_AUDIO_PRIVATE_H
#define ANDROID_AUDIO_PRIVATE_H
#if CC_PLATFORM == CC_PLATFORM_ANDROID
#include <sys/cdefs.h>
#elif CC_PLATFORM == CC_PLATFORM_WINDOWS
#include <sys/types.h>
#define __attribute__(x)
#endif
#include <stdint.h>
/* Defines not necessary for external use but kept here to be common
* to the audio_utils library.
*/
/* struct representation of 3 bytes for packed PCM 24 bit data.
* The naming follows the ARM NEON convention.
*/
extern "C" {
typedef struct __attribute__((__packed__)) {
uint8_t c[3];
} uint8x3_t;
}
#endif /*ANDROID_AUDIO_PRIVATE_H*/

View File

@@ -0,0 +1,522 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "tinysndfile"
#include "audio/common/utils/include/tinysndfile.h"
#include "audio/common/utils/include/primitives.h"
#include "base/Log.h"
// #ifdef HAVE_STDERR
// #include <stdio.h>
// #endif
#include <cassert>
#include <cerrno>
#include <cstring>
#ifndef HAVE_STDERR
#define HAVE_STDERR
#endif
#define WAVE_FORMAT_PCM 1
#define WAVE_FORMAT_IEEE_FLOAT 3
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
namespace sf {
static snd_callbacks sDefaultCallback;
static unsigned little2u(unsigned char *ptr) {
return (ptr[1] << 8) + ptr[0];
}
static unsigned little4u(unsigned char *ptr) {
return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
}
static int isLittleEndian() {
static const uint16_t ONE = 1;
return *(reinterpret_cast<const char *>(&ONE)) == 1;
}
// "swab" conflicts with OS X <string.h>
static void my_swab(int16_t *ptr, size_t numToSwap) { //NOLINT(readability-identifier-naming)
while (numToSwap > 0) {
*ptr = static_cast<int16_t>(little2u(reinterpret_cast<unsigned char *>(ptr)));
--numToSwap;
++ptr;
}
}
static void *open_func(const char *path, void * /*user*/) { //NOLINT(readability-identifier-naming)
return fopen(path, "rb");
}
static size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { //NOLINT(readability-identifier-naming)
return fread(ptr, size, nmemb, static_cast<FILE *>(datasource));
}
static int seek_func(void *datasource, long offset, int whence) { //NOLINT(google-runtime-int,readability-identifier-naming)
return fseek(static_cast<FILE *>(datasource), offset, whence);
}
static int close_func(void *datasource) { //NOLINT(readability-identifier-naming)
return fclose(static_cast<FILE *>(datasource));
}
static long tell_func(void *datasource) { //NOLINT(google-runtime-int,readability-identifier-naming)
return ftell(static_cast<FILE *>(datasource));
}
static void sf_lazy_init() { //NOLINT(readability-identifier-naming)
if (sInited == 0) {
sDefaultCallback.open = open_func;
sDefaultCallback.read = read_func;
sDefaultCallback.seek = seek_func;
sDefaultCallback.close = close_func;
sDefaultCallback.tell = tell_func;
sInited = 1;
}
}
SNDFILE *sf_open_read(const char *path, SF_INFO *info, snd_callbacks *cb, void *user) { //NOLINT(readability-identifier-naming)
sf_lazy_init();
if (path == nullptr || info == nullptr) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("path=%p info=%p\n", path, info);
#endif
return nullptr;
}
auto *handle = static_cast<SNDFILE *>(malloc(sizeof(SNDFILE)));
handle->temp = nullptr;
handle->info.format = SF_FORMAT_WAV;
if (cb != nullptr) {
handle->callback = *cb;
} else {
handle->callback = sDefaultCallback;
}
void *stream = handle->callback.open(path, user);
if (stream == nullptr) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("fopen %s failed errno %d\n", path, errno);
#endif
free(handle);
return nullptr;
}
handle->stream = stream;
// don't attempt to parse all valid forms, just the most common ones
unsigned char wav[12];
size_t actual;
unsigned riffSize;
size_t remaining;
int hadFmt = 0;
int hadData = 0;
long dataTell = 0L; //NOLINT(google-runtime-int)
actual = handle->callback.read(wav, sizeof(char), sizeof(wav), stream);
if (actual < 12) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("actual %zu < 44\n", actual);
#endif
goto close;
}
if (memcmp(wav, "RIFF", 4)) { //NOLINT(bugprone-suspicious-string-compare)
#ifdef HAVE_STDERR
CC_LOG_ERROR("wav != RIFF\n");
#endif
goto close;
}
riffSize = little4u(&wav[4]);
if (riffSize < 4) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("riffSize %u < 4\n", riffSize);
#endif
goto close;
}
if (memcmp(&wav[8], "WAVE", 4)) { //NOLINT(bugprone-suspicious-string-compare)
#ifdef HAVE_STDERR
CC_LOG_ERROR("missing WAVE\n");
#endif
goto close;
}
remaining = riffSize - 4;
while (remaining >= 8) {
unsigned char chunk[8];
actual = handle->callback.read(chunk, sizeof(char), sizeof(chunk), stream);
if (actual != sizeof(chunk)) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("actual %zu != %zu\n", actual, sizeof(chunk));
#endif
goto close;
}
remaining -= 8;
unsigned chunkSize = little4u(&chunk[4]);
if (chunkSize > remaining) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("chunkSize %u > remaining %zu\n", chunkSize, remaining);
#endif
goto close;
}
if (!memcmp(&chunk[0], "fmt ", 4)) {
if (hadFmt) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("multiple fmt\n");
#endif
goto close;
}
if (chunkSize < 2) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("chunkSize %u < 2\n", chunkSize);
#endif
goto close;
}
unsigned char fmt[40];
actual = handle->callback.read(fmt, sizeof(char), 2, stream);
if (actual != 2) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("actual %zu != 2\n", actual);
#endif
goto close;
}
unsigned format = little2u(&fmt[0]);
size_t minSize = 0;
switch (format) {
case WAVE_FORMAT_PCM:
case WAVE_FORMAT_IEEE_FLOAT:
minSize = 16;
break;
case WAVE_FORMAT_EXTENSIBLE:
minSize = 40;
break;
default:
#ifdef HAVE_STDERR
CC_LOG_ERROR("unsupported format %u\n", format);
#endif
goto close;
}
if (chunkSize < minSize) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("chunkSize %u < minSize %zu\n", chunkSize, minSize);
#endif
goto close;
}
actual = handle->callback.read(&fmt[2], sizeof(char), minSize - 2, stream);
if (actual != minSize - 2) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("actual %zu != %zu\n", actual, minSize - 16);
#endif
goto close;
}
if (chunkSize > minSize) {
handle->callback.seek(stream, static_cast<long>(chunkSize - minSize), SEEK_CUR); //NOLINT(google-runtime-int)
}
unsigned channels = little2u(&fmt[2]);
// IDEA: FCC_8
if (channels != 1 && channels != 2 && channels != 4 && channels != 6 && channels != 8) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("unsupported channels %u\n", channels);
#endif
goto close;
}
unsigned samplerate = little4u(&fmt[4]);
if (samplerate == 0) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("samplerate %u == 0\n", samplerate);
#endif
goto close;
}
// ignore byte rate
// ignore block alignment
unsigned bitsPerSample = little2u(&fmt[14]);
if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 &&
bitsPerSample != 32) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("bitsPerSample %u != 8 or 16 or 24 or 32\n", bitsPerSample);
#endif
goto close;
}
unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
handle->bytesPerFrame = bytesPerFrame;
handle->info.samplerate = static_cast<int>(samplerate);
handle->info.channels = static_cast<int>(channels);
switch (bitsPerSample) {
case 8:
handle->info.format |= SF_FORMAT_PCM_U8;
break;
case 16:
handle->info.format |= SF_FORMAT_PCM_16;
break;
case 24:
handle->info.format |= SF_FORMAT_PCM_24;
break;
case 32:
if (format == WAVE_FORMAT_IEEE_FLOAT) {
handle->info.format |= SF_FORMAT_FLOAT;
} else {
handle->info.format |= SF_FORMAT_PCM_32;
}
break;
}
hadFmt = 1;
} else if (!memcmp(&chunk[0], "data", 4)) {
if (!hadFmt) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("data not preceded by fmt\n");
#endif
goto close;
}
if (hadData) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("multiple data\n");
#endif
goto close;
}
handle->remaining = chunkSize / handle->bytesPerFrame;
handle->info.frames = handle->remaining;
dataTell = handle->callback.tell(stream);
if (chunkSize > 0) {
handle->callback.seek(stream, static_cast<long>(chunkSize), SEEK_CUR); //NOLINT(google-runtime-int)
}
hadData = 1;
} else if (!memcmp(&chunk[0], "fact", 4)) {
// ignore fact
if (chunkSize > 0) {
handle->callback.seek(stream, static_cast<long>(chunkSize), SEEK_CUR); //NOLINT(google-runtime-int)
}
} else {
// ignore unknown chunk
#ifdef HAVE_STDERR
CC_LOG_ERROR("ignoring unknown chunk %c%c%c%c\n",
chunk[0], chunk[1], chunk[2], chunk[3]);
#endif
if (chunkSize > 0) {
handle->callback.seek(stream, static_cast<long>(chunkSize), SEEK_CUR); //NOLINT(google-runtime-int)
}
}
remaining -= chunkSize;
}
if (remaining > 0) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("partial chunk at end of RIFF, remaining %zu\n", remaining);
#endif
goto close;
}
if (!hadData) {
#ifdef HAVE_STDERR
CC_LOG_ERROR("missing data\n");
#endif
goto close;
}
(void)handle->callback.seek(stream, dataTell, SEEK_SET);
*info = handle->info;
return handle;
close:
handle->callback.close(stream);
free(handle);
return nullptr;
}
void sf_close(SNDFILE *handle) { //NOLINT(readability-identifier-naming)
if (handle == nullptr) {
return;
}
free(handle->temp);
(void)handle->callback.close(handle->stream);
free(handle);
}
off_t sf_seek(SNDFILE *handle, int offset, int whence) { //NOLINT(readability-identifier-naming)
if (whence == SEEK_SET) {
assert(offset >= 0 && offset <= handle->info.frames);
} else if (whence == SEEK_CUR) {
offset += sf_tell(handle);
assert(offset >= 0 && offset <= handle->info.frames);
} else if (whence == SEEK_END) {
offset += handle->info.frames;
assert(offset >= 0 && offset <= handle->info.frames);
} else {
assert(false); // base whence value
}
handle->remaining = handle->info.frames - offset;
return offset;
}
off_t sf_tell(SNDFILE *handle) { //NOLINT(readability-identifier-naming)
return handle->info.frames - handle->remaining;
}
sf_count_t sf_readf_short(SNDFILE *handle, int16_t *ptr, sf_count_t desiredFrames) { //NOLINT(readability-identifier-naming)
if (handle == nullptr || ptr == nullptr || !handle->remaining ||
desiredFrames <= 0) {
return 0;
}
if (handle->remaining < static_cast<size_t>(desiredFrames)) {
desiredFrames = handle->remaining;
}
// does not check for numeric overflow
size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
size_t actualBytes;
void *temp = nullptr;
unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT || format == SF_FORMAT_PCM_24) {
temp = malloc(desiredBytes);
actualBytes = handle->callback.read(temp, sizeof(char), desiredBytes, handle->stream);
} else {
actualBytes = handle->callback.read(ptr, sizeof(char), desiredBytes, handle->stream);
}
size_t actualFrames = actualBytes / handle->bytesPerFrame;
handle->remaining -= actualFrames;
switch (format) {
case SF_FORMAT_PCM_U8:
memcpy_to_i16_from_u8(ptr, reinterpret_cast<unsigned char *>(ptr), actualFrames * handle->info.channels);
break;
case SF_FORMAT_PCM_16:
if (!isLittleEndian()) {
my_swab(ptr, actualFrames * handle->info.channels);
}
break;
case SF_FORMAT_PCM_32:
memcpy_to_i16_from_i32(ptr, static_cast<const int *>(temp), actualFrames * handle->info.channels);
free(temp);
break;
case SF_FORMAT_FLOAT:
memcpy_to_i16_from_float(ptr, static_cast<const float *>(temp), actualFrames * handle->info.channels);
free(temp);
break;
case SF_FORMAT_PCM_24:
memcpy_to_i16_from_p24(ptr, static_cast<const uint8_t *>(temp), actualFrames * handle->info.channels);
free(temp);
break;
default:
memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int16_t));
break;
}
return actualFrames;
}
/*
sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
{
if (handle == nullptr || ptr == nullptr || !handle->remaining ||
desiredFrames <= 0) {
return 0;
}
if (handle->remaining < (size_t) desiredFrames) {
desiredFrames = handle->remaining;
}
// does not check for numeric overflow
size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
size_t actualBytes;
void *temp = nullptr;
unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) {
temp = malloc(desiredBytes);
actualBytes = handle->callback.read(temp, sizeof(char), desiredBytes, handle->stream);
} else {
actualBytes = handle->callback.read(ptr, sizeof(char), desiredBytes, handle->stream);
}
size_t actualFrames = actualBytes / handle->bytesPerFrame;
handle->remaining -= actualFrames;
switch (format) {
case SF_FORMAT_PCM_U8:
#if 0
// REFINE: - implement
memcpy_to_float_from_u8(ptr, (const unsigned char *) temp,
actualFrames * handle->info.channels);
#endif
free(temp);
break;
case SF_FORMAT_PCM_16:
memcpy_to_float_from_i16(ptr, (const int16_t *) temp, actualFrames * handle->info.channels);
free(temp);
break;
case SF_FORMAT_PCM_32:
memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels);
break;
case SF_FORMAT_FLOAT:
break;
case SF_FORMAT_PCM_24:
memcpy_to_float_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
free(temp);
break;
default:
memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float));
break;
}
return actualFrames;
}
sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames)
{
if (handle == nullptr || ptr == nullptr || !handle->remaining ||
desiredFrames <= 0) {
return 0;
}
if (handle->remaining < (size_t) desiredFrames) {
desiredFrames = handle->remaining;
}
// does not check for numeric overflow
size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
void *temp = nullptr;
unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
size_t actualBytes;
if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) {
temp = malloc(desiredBytes);
actualBytes = handle->callback.read(temp, sizeof(char), desiredBytes, handle->stream);
} else {
actualBytes = handle->callback.read(ptr, sizeof(char), desiredBytes, handle->stream);
}
size_t actualFrames = actualBytes / handle->bytesPerFrame;
handle->remaining -= actualFrames;
switch (format) {
case SF_FORMAT_PCM_U8:
#if 0
// REFINE: - implement
memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp,
actualFrames * handle->info.channels);
#endif
free(temp);
break;
case SF_FORMAT_PCM_16:
memcpy_to_i32_from_i16(ptr, (const int16_t *) temp, actualFrames * handle->info.channels);
free(temp);
break;
case SF_FORMAT_PCM_32:
break;
case SF_FORMAT_FLOAT:
memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels);
break;
case SF_FORMAT_PCM_24:
memcpy_to_i32_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels);
free(temp);
break;
default:
memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int));
break;
}
return actualFrames;
}
*/
} // namespace sf