heavy
heavy copied to clipboard
Easier way to work with 16 bit audio using the C API?
Hello!
I've just started a cross-platform game using Heavy and SDL2 for audio. SDL2 gives me 32-bit float audio on Windows, but on Android, it gives me 16-bit int audio.
Here's a simple example of what I'm working with. As you can see, working with floats is easy. But with 16 bit ints, I have to process using a separate float buffer and then cast back into 16 bit ints.
I was wondering if I'm doing something wrong here, or if there's any way to work with other audio formats using Heavy?
void Process_Audio_Heavy(void* userdata, Uint8* stream, int len)
{
Audio_Heavy* heavy = (Audio_Heavy*)userdata;
#if GAME_AUDIO_FORMAT == AUDIO_F32
hv_processInlineInterleaved(heavy->osc, nullptr, (float*)stream, GAME_AUDIO_SAMPLE_SIZE);
#elif GAME_AUDIO_FORMAT == AUDIO_S16
static float floatBuffer[GAME_AUDIO_BLOCK_SIZE];
hv_processInlineInterleaved(heavy->osc, nullptr, floatBuffer, GAME_AUDIO_SAMPLE_SIZE);
Sint16* intStream = (Sint16*)stream;
for (int i = 0; i < GAME_AUDIO_BLOCK_SIZE; ++i)
{
intStream[i] = (Sint16)(floatBuffer[i] * INT16_MAX);
}
#endif
}
Thanks! -Travis
Hi @travisfoo. Just so that I understand, you are using SDL2 on Android? I didn't know that was a thing. Otherwise of course there is OpenSLES and the new (recommended) AAudio.
Either way, what you are doing in your code snippet is correct. It's what I would do. I'm not sure what INT16_MAX
resolves to, but I would use 2^15-1 == 32767.0f
, so that you have no chance of overflow (under normal operation).
If your sample format conversion is happening only in Android (i.e. ARM), then you might try something like the snippet below to use hardware (totally untested).
#include <arm_neon.h>
assert(len % 8 == 0); // len must be a multiple of 8
int i = 0;
for (int i = 0; i < len, i += 8) {
float32x4 a = vld1q_f32(floatBuffer+i);
a = vmulq_n_f32(a, 32767.0f);
int32x4_t b = vcvtq_s32_f32(a);
int16x4_t c = vqmovn_s32(b);
float32x4 d = vld1q_f32(floatBuffer+i+4);
d = vmulq_n_f32(d, 32767.0f);
int32x4_t e = vcvtq_s32_f32(d);
int16x4_t f = vqmovn_s32(e);
vst1q_s16(intStream+i, vcombine_s16(f, c));
}
Thanks for the quick response @mhroth ! Indeed, SDL2 does support Android (and iOS). I actually wasn't aware of those other audio options on Android, but I'm going to stick with SDL2 for now since it makes cross platform development so much easier.
INT16_MAX does indeed resolve to 32767. The code I posted didn't have any functional issues, I guess I was just wondering if there was the ability to call hv_process with a 16 bit int buffer instead of a float buffer. I'll definitely look into SIMD processing once I get to the optimization stage of the game.
Keep up the good work on heavy! :)
Go ahead and close the issue unless you have any more questions for me.
@travisfoo we did have a 16bit int interface but it got removed ages ago, it basically did the same thing as what you're doing already.