Skip to content

Commit

Permalink
support float16 && float24 input (WAV only)
Browse files Browse the repository at this point in the history
  • Loading branch information
nu774 committed Oct 20, 2013
1 parent 1f0ed6c commit 69907b1
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 29 deletions.
28 changes: 27 additions & 1 deletion Quantizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ Quantizer::Quantizer(const std::shared_ptr<ISource> &source,
m_convert = &Quantizer::convertSamples_i2i_2;
else
m_convert = &Quantizer::convertSamples_i2i_1;
} else if (asbd.mBitsPerChannel <= 32)
}
else if (asbd.mBitsPerChannel == 16)
m_convert = dither ? &Quantizer::convertSamples_h2i_2
: &Quantizer::convertSamples_h2i_1;
else if (asbd.mBitsPerChannel <= 32)
m_convert = dither ? &Quantizer::convertSamples_f2i_2
: &Quantizer::convertSamples_f2i_1;
else
Expand Down Expand Up @@ -95,6 +99,28 @@ size_t Quantizer::convertSamples_i2i_2(void *buffer, size_t nsamples)
return nsamples;
}

size_t Quantizer::convertSamples_h2i_1(void *buffer, size_t nsamples)
{
nsamples = readSamplesAsFloat(source(), &m_pivot,
static_cast<float*>(buffer), nsamples);
ditherFloat1(static_cast<float *>(buffer),
static_cast<int32_t *>(buffer),
m_asbd.mChannelsPerFrame * nsamples,
m_asbd.mBitsPerChannel);
return nsamples;
}

size_t Quantizer::convertSamples_h2i_2(void *buffer, size_t nsamples)
{
nsamples = readSamplesAsFloat(source(), &m_pivot,
static_cast<float*>(buffer), nsamples);
ditherFloat2(static_cast<float *>(buffer),
static_cast<int32_t *>(buffer),
m_asbd.mChannelsPerFrame * nsamples,
m_asbd.mBitsPerChannel);
return nsamples;
}

size_t Quantizer::convertSamples_f2i_1(void *buffer, size_t nsamples)
{
nsamples = source()->readSamples(buffer, nsamples);
Expand Down
2 changes: 2 additions & 0 deletions Quantizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class Quantizer: public FilterBase {
size_t convertSamples_i2i_0(void *buffer, size_t nsamples);
size_t convertSamples_i2i_1(void *buffer, size_t nsamples);
size_t convertSamples_i2i_2(void *buffer, size_t nsamples);
size_t convertSamples_h2i_1(void *buffer, size_t nsamples);
size_t convertSamples_h2i_2(void *buffer, size_t nsamples);
size_t convertSamples_f2i_1(void *buffer, size_t nsamples);
size_t convertSamples_f2i_2(void *buffer, size_t nsamples);
size_t convertSamples_d2i_1(void *buffer, size_t nsamples);
Expand Down
99 changes: 76 additions & 23 deletions iointer.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,51 @@
#include <cstdio>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include "iointer.h"

inline float quantize(double v)
{
const float anti_denormal = 1.0e-30f;
float x = static_cast<float>(v);
x += anti_denormal;
x -= anti_denormal;
return x;
namespace {
static union uif_t {
uint32_t i;
float f;
} *h2s_table;

inline float quantize(double v)
{
const float anti_denormal = 1.0e-30f;
float x = static_cast<float>(v);
x += anti_denormal;
x -= anti_denormal;
return x;
}

uint32_t half2single_(uint16_t n)
{
unsigned sign = n >> 15;
unsigned exp = (n >> 10) & 0x1F;
unsigned mantissa = n & 0x3FF;

if (exp == 0 && mantissa == 0)
return sign << 31;
if (exp == 0x1F)
exp = 0x8F;
else if (exp == 0) {
for (; !(mantissa & 0x400); mantissa <<= 1, --exp)
;
++exp;
mantissa &= ~0x400;
}
return (sign << 31) | ((exp + 0x70) << 23) | (mantissa << 13);
}
void init_h2s_table()
{
if (!h2s_table) {
uif_t *p = new uif_t[1<<16];
for (unsigned n = 0; n < (1<<16); ++n)
p[n].i = half2single_(n);
InterlockedCompareExchangePointerRelease((void**)&h2s_table, p, 0);
}
}
}

size_t readSamplesAsFloat(ISource *src, std::vector<uint8_t> *pivot,
Expand All @@ -23,26 +61,33 @@ size_t readSamplesAsFloat(ISource *src, std::vector<uint8_t> *pivot,
float *floatBuffer, size_t nsamples)
{
const AudioStreamBasicDescription &sf = src->getSampleFormat();
uint32_t bpc = sf.mBytesPerFrame / sf.mChannelsPerFrame;

if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) &&
sf.mBytesPerFrame / sf.mChannelsPerFrame == 4)
{
if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) && bpc == 4)
return src->readSamples(floatBuffer, nsamples);
}

if (pivot->size() < nsamples * sf.mBytesPerFrame)
pivot->resize(nsamples * sf.mBytesPerFrame);

uint8_t *bp = &(*pivot)[0];
void *bp = &(*pivot)[0];
float *fp = floatBuffer;
nsamples = src->readSamples(bp, nsamples);
size_t blen = nsamples * sf.mBytesPerFrame;

if (sf.mFormatFlags & kAudioFormatFlagIsFloat) {
double *src = reinterpret_cast<double *>(bp);
std::transform(src, src + (blen / 8), fp, quantize);
if (bpc == 8) {
double *src = static_cast<double *>(bp);
std::transform(src, src + (blen / 8), fp, quantize);
} else if (bpc == 2) {
uint16_t *src = static_cast<uint16_t *>(bp);
init_h2s_table();
for (size_t i = 0; i < blen / 2; ++i)
*fp++ = h2s_table[src[i]].f / 65536.0;
} else {
throw std::runtime_error("readSamplesAsFloat(): BUG");
}
} else {
int *src = reinterpret_cast<int *>(bp);
int *src = static_cast<int *>(bp);
for (size_t i = 0; i < blen / 4; ++i)
*fp++ = src[i] / 2147483648.0f;
}
Expand All @@ -62,26 +107,34 @@ size_t readSamplesAsFloat(ISource *src, std::vector<uint8_t> *pivot,
double *doubleBuffer, size_t nsamples)
{
const AudioStreamBasicDescription &sf = src->getSampleFormat();
uint32_t bpc = sf.mBytesPerFrame / sf.mChannelsPerFrame;

if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) &&
sf.mBytesPerFrame / sf.mChannelsPerFrame == 8)
{
if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) && bpc == 8)
return src->readSamples(doubleBuffer, nsamples);
}

if (pivot->size() < nsamples * sf.mBytesPerFrame)
pivot->resize(nsamples * sf.mBytesPerFrame);

uint8_t *bp = &(*pivot)[0];
void *bp = &(*pivot)[0];
double *fp = doubleBuffer;
nsamples = src->readSamples(bp, nsamples);
size_t blen = nsamples * sf.mBytesPerFrame;

if (sf.mFormatFlags & kAudioFormatFlagIsFloat) {
float *src = reinterpret_cast<float*>(bp);
std::copy(src, src + (blen / 4), fp);
if (bpc == 4) {
float *src = static_cast<float*>(bp);
std::copy(src, src + (blen / 4), fp);
} else if (bpc == 2) {
uint16_t *src = static_cast<uint16_t *>(bp);
init_h2s_table();
for (size_t i = 0; i < blen / 2; ++i) {
*fp++ = h2s_table[src[i]].f / 65536.0;
}
} else {
throw std::runtime_error("readSamplesAsFloat(): BUG");
}
} else {
int *src = reinterpret_cast<int *>(bp);
int *src = static_cast<int *>(bp);
for (size_t i = 0; i < blen / 4; ++i)
*fp++ = src[i] / 2147483648.0;
}
Expand Down
26 changes: 23 additions & 3 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,10 +753,19 @@ void build_filter_chain_sub(std::shared_ptr<ISeekableSource> src,
opts.native_resampler_complexity >= 0 ||
(!opts.isAAC() && !opts.isALAC()))
{
AudioStreamBasicDescription
sfmt = chain.back()->getSampleFormat();
if ((sfmt.mFormatFlags & kAudioFormatFlagIsFloat) &&
sfmt.mBitsPerChannel < 32)
{
chain.push_back(std::make_shared<Quantizer>(chain.back(),
32, false, true));
}
int quality = opts.native_resampler_quality;
uint32_t complexity = opts.native_resampler_complexity;
if (quality == -1) quality = kAudioConverterQuality_Medium;
if (!complexity) complexity = 'norm';

CoreAudioResampler *resampler =
new CoreAudioResampler(chain.back(), oasbd.mSampleRate,
bound_quality(quality), complexity);
Expand Down Expand Up @@ -786,11 +795,14 @@ void build_filter_chain_sub(std::shared_ptr<ISeekableSource> src,
chain.push_back(scaler);
}
if (opts.bits_per_sample) {
bool is_float = (opts.bits_per_sample == 32 && !opts.isALAC());
unsigned sbits = chain.back()->getSampleFormat().mBitsPerChannel;
bool sflags = chain.back()->getSampleFormat().mFormatFlags;

if (opts.isAAC())
LOG(L"WARNING: --bits-per-sample has no effect for AAC\n");
else if (chain.back()->getSampleFormat().mBitsPerChannel
!= opts.bits_per_sample) {
bool is_float = (opts.bits_per_sample == 32 && !opts.isALAC());
else if (sbits != opts.bits_per_sample ||
!!(sflags & kAudioFormatFlagIsFloat) != is_float) {
std::shared_ptr<ISource>
isrc(new Quantizer(chain.back(), opts.bits_per_sample,
opts.no_dither, is_float));
Expand All @@ -813,6 +825,14 @@ void build_filter_chain_sub(std::shared_ptr<ISeekableSource> src,
// Don't automatically quantize float input
throw std::runtime_error("ALAC: input format is not supported");
}
} else if (opts.isAAC()) {
AudioStreamBasicDescription sfmt = chain.back()->getSampleFormat();
if ((sfmt.mFormatFlags & kAudioFormatFlagIsFloat) &&
sfmt.mBitsPerChannel < 32)
{
chain.push_back(std::make_shared<Quantizer>(chain.back(), 32,
false, true));
}
} else if (opts.isLPCM() || opts.isWaveOut()) {
/* output f64 sample only when input was already f64 */
if (sasbd.mBitsPerChannel <= 32 &&
Expand Down
17 changes: 15 additions & 2 deletions wavsource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,23 @@ void WaveSource::fmt(size_t size)
throw std::runtime_error("WaveSource: invalid wave fmt");
}

if (isfloat) {
if (wValidBitsPerSample != 16 && wValidBitsPerSample != 24 &&
wValidBitsPerSample != 32 && wValidBitsPerSample != 64)
throw std::runtime_error("WaveSource: not supported float format");
if (wBitsPerSample > 64)
throw std::runtime_error("WaveSource: not supported float format");
} else if (wBitsPerSample > 32)
throw std::runtime_error("WaveSource: not supported integer format");

m_block_align = nBlockAlign;

unsigned bits = 32;
if (isfloat && wValidBitsPerSample > 32) bits = 64;
else if (isfloat && wValidBitsPerSample <= 16) bits = 16;

m_asbd = cautil::buildASBDForPCM2(nSamplesPerSec, nChannels,
wValidBitsPerSample,
isfloat ? wBitsPerSample : 32,
wValidBitsPerSample, bits,
isfloat ? kAudioFormatFlagIsFloat
: kAudioFormatFlagIsSignedInteger);
}

0 comments on commit 69907b1

Please sign in to comment.