no message
This commit is contained in:
181
cocos/audio/common/utils/format.cpp
Normal file
181
cocos/audio/common/utils/format.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
77
cocos/audio/common/utils/include/format.h
Normal file
77
cocos/audio/common/utils/include/format.h
Normal 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
|
||||
78
cocos/audio/common/utils/include/minifloat.h
Normal file
78
cocos/audio/common/utils/include/minifloat.h
Normal 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
|
||||
936
cocos/audio/common/utils/include/primitives.h
Normal file
936
cocos/audio/common/utils/include/primitives.h
Normal 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
|
||||
}
|
||||
92
cocos/audio/common/utils/include/tinysndfile.h
Normal file
92
cocos/audio/common/utils/include/tinysndfile.h
Normal 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
|
||||
59
cocos/audio/common/utils/minifloat.cpp
Normal file
59
cocos/audio/common/utils/minifloat.cpp
Normal 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);
|
||||
}
|
||||
500
cocos/audio/common/utils/primitives.cpp
Normal file
500
cocos/audio/common/utils/primitives.cpp
Normal 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
|
||||
38
cocos/audio/common/utils/private/private.h
Normal file
38
cocos/audio/common/utils/private/private.h
Normal 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*/
|
||||
522
cocos/audio/common/utils/tinysndfile.cpp
Normal file
522
cocos/audio/common/utils/tinysndfile.cpp
Normal 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
|
||||
Reference in New Issue
Block a user