Add IMA4 support

This commit is contained in:
Erik Hofman 2016-05-30 14:17:16 +02:00
parent 469097ed5b
commit 976c85ff57
6 changed files with 159 additions and 13 deletions

View File

@ -47,7 +47,7 @@ int main( int argc, char *argv[] ) {
printf("playing sample2\n"); printf("playing sample2\n");
sleep(1); sleep(1);
SGSoundSample *sample3 = new SGSoundSample("jet.wav", srcDir); SGSoundSample *sample3 = new SGSoundSample("jet_ima4.wav", srcDir);
sample3->set_volume(0.5); sample3->set_volume(0.5);
sample3->set_pitch(0.8); sample3->set_pitch(0.8);
sample3->play_looped(); sample3->play_looped();

View File

@ -38,6 +38,7 @@ namespace
public: public:
ALvoid* data; ALvoid* data;
unsigned int format; unsigned int format;
unsigned int block_align;
ALsizei length; ALsizei length;
ALfloat frequency; ALfloat frequency;
SGPath path; SGPath path;
@ -96,10 +97,10 @@ namespace
mantissa = mulawbyte & 0x0F; mantissa = mulawbyte & 0x0F;
sample = exp_lut[exponent] + (mantissa << (exponent + 3)); sample = exp_lut[exponent] + (mantissa << (exponent + 3));
return sign ? -sample : sample; return sign ? -sample : sample;
} }
void codecULaw (Buffer* b) void codecULaw (Buffer* b)
{ {
uint8_t *d = (uint8_t *) b->data; uint8_t *d = (uint8_t *) b->data;
size_t newLength = b->length * 2; size_t newLength = b->length * 2;
int16_t *buf = (int16_t *) malloc(newLength); int16_t *buf = (int16_t *) malloc(newLength);
@ -115,6 +116,98 @@ namespace
b->length = newLength; b->length = newLength;
} }
int16_t ima2linear (uint8_t nibble, int16_t *val, uint8_t *idx)
{
const int16_t _ima4_index_table[16] =
{
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};
const int16_t _ima4_step_table[89] =
{
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
int32_t predictor;
int16_t diff, step;
int8_t delta, sign;
int8_t index;
index = *idx;
if (index > 88) index = 88;
else if (index < 0) index = 0;
predictor = *val;
step = _ima4_step_table[index];
sign = nibble & 0x8;
delta = nibble & 0x7;
diff = 0;
if (delta & 4) diff += step;
if (delta & 2) diff += (step >> 1);
if (delta & 1) diff += (step >> 2);
diff += (step >> 3);
if (sign) predictor -= diff;
else predictor += diff;
index += _ima4_index_table[nibble];
if (index > 88) index = 88;
else if (index < 0) index = 0;
*idx = index;
if (predictor < -32768) predictor = -32768;
else if (predictor > 32767) predictor = 32767;
*val = predictor;
return *val;
}
void codecIMA4 (Buffer* b)
{
uint8_t *d = (uint8_t *) b->data;
unsigned int block_align = b->block_align;
size_t blocks = b->length/block_align;
size_t newLength = block_align * blocks * 4;
int16_t *buf = (int16_t *) malloc ( newLength );
if (buf == NULL)
throw sg_exception("malloc failed decoing IMA4 WAV file");
int16_t *ptr = buf;
for (size_t i = 0; i < blocks; i++)
{
int16_t predictor;
uint8_t index;
predictor = *d++;
predictor |= *d++ << 8;
index = *d++;
d++;
for (size_t j = 0; j < block_align; j += 4)
{
for (unsigned int q=0; q<4; q++)
{
uint8_t nibble = *d++;
*ptr++ = ima2linear(nibble & 0xF, &predictor, &index);
*ptr++ = ima2linear(nibble >> 4, &predictor, &index);
}
}
}
free(b->data);
b->data = buf;
b->length = newLength;
}
bool gzSkip(gzFile fd, int skipCount) bool gzSkip(gzFile fd, int skipCount)
{ {
int r = gzseek(fd, skipCount, SEEK_CUR); int r = gzseek(fd, skipCount, SEEK_CUR);
@ -219,10 +312,23 @@ namespace
codec = codecULaw; codec = codecULaw;
} }
break; break;
case 17: /* IMA4 ADPCM */
if (alIsExtensionPresent((ALchar *)"AL_EXT_ima4") &&
(alIsExtensionPresent((ALchar *)"AL_SOFT_block_alignment")
|| blockAlign == 65)) {
compressed = true;
codec = codecLinear;
} else {
bitsPerSample *= 4; /* uLaw is 16-bit packed into 8 bits */
codec = codecIMA4;
}
break;
default: default:
throw sg_io_exception("unsupported WAV encoding", b->path); throw sg_io_exception("unsupported WAV encoding", b->path);
} }
b->block_align = blockAlign;
b->frequency = samplesPerSecond; b->frequency = samplesPerSecond;
b->format = formatConstruct(numChannels, bitsPerSample, compressed); b->format = formatConstruct(numChannels, bitsPerSample, compressed);
} else if (magic == WAV_DATA_4CC) { } else if (magic == WAV_DATA_4CC) {
@ -256,7 +362,7 @@ namespace
namespace simgear namespace simgear
{ {
ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size, ALfloat& freqf) ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size, ALfloat& freqf, unsigned int& block_align)
{ {
if (!path.exists()) { if (!path.exists()) {
throw sg_io_exception("loadWAVFromFile: file not found", path); throw sg_io_exception("loadWAVFromFile: file not found", path);
@ -275,6 +381,7 @@ ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size,
ALvoid* data = b.data; ALvoid* data = b.data;
b.data = NULL; // don't free when Buffer does out of scope b.data = NULL; // don't free when Buffer does out of scope
format = b.format; format = b.format;
block_align = b.block_align;
size = b.length; size = b.length;
freqf = b.frequency; freqf = b.frequency;
@ -287,9 +394,10 @@ ALuint createBufferFromFile(const SGPath& path)
ALuint buffer = -1; ALuint buffer = -1;
#ifdef ENABLE_SOUND #ifdef ENABLE_SOUND
unsigned int format; unsigned int format;
unsigned int block_align;
ALsizei size; ALsizei size;
ALfloat sampleFrequency; ALfloat sampleFrequency;
ALvoid* data = loadWAVFromFile(path, format, size, sampleFrequency); ALvoid* data = loadWAVFromFile(path, format, size, sampleFrequency, block_alight);
assert(data); assert(data);
alGenBuffers(1, &buffer); alGenBuffers(1, &buffer);
@ -299,6 +407,7 @@ ALuint createBufferFromFile(const SGPath& path)
} }
alBufferData (buffer, format, data, size, (ALsizei) sampleFrequency); alBufferData (buffer, format, data, size, (ALsizei) sampleFrequency);
alBufferi (buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, block_align);
if (alGetError() != AL_NO_ERROR) { if (alGetError() != AL_NO_ERROR) {
alDeleteBuffers(1, &buffer); alDeleteBuffers(1, &buffer);
free(data); free(data);

View File

@ -16,7 +16,7 @@ class SGPath;
namespace simgear namespace simgear
{ {
ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size, ALfloat& freqf); ALvoid* loadWAVFromFile(const SGPath& path, unsigned int& format, ALsizei& size, ALfloat& freqf, unsigned int& block_align);
ALuint createBufferFromFile(const SGPath& path); ALuint createBufferFromFile(const SGPath& path);
} }

View File

@ -65,7 +65,17 @@ public:
* Returns the format of this audio sample. * Returns the format of this audio sample.
* @return SimGear format-id * @return SimGear format-id
*/ */
inline unsigned int get_format() { return (_tracks | _bits | _compressed*256); } inline unsigned int get_format() {
return (_tracks | _bits | _compressed*256);
}
/**
* Returns the block alignment of this audio sample.
* @return block alignment in bytes
*/
inline unsigned int get_block_align() {
return _block_align;
}
/** /**
* Get the reference name of this audio sample. * Get the reference name of this audio sample.
@ -170,6 +180,7 @@ protected:
unsigned int _tracks; unsigned int _tracks;
unsigned int _samples; unsigned int _samples;
unsigned int _frequency; unsigned int _frequency;
unsigned int _block_align;
bool _compressed; bool _compressed;
bool _loop; bool _loop;
@ -432,6 +443,14 @@ public:
_tracks = fmt & 0x3; _bits = fmt & 0x1C; _compressed = fmt & 0x100; _tracks = fmt & 0x3; _bits = fmt & 0x1C; _compressed = fmt & 0x100;
} }
/**
* Set the block alignament for compressed audio.
* @param block the block alignment in bytes
*/
inline void set_block_align( int block ) {
_block_align = block;
}
/** /**
* Set the frequency (in Herz) of this audio sample. * Set the frequency (in Herz) of this audio sample.
* @param freq Frequency * @param freq Frequency

View File

@ -28,6 +28,7 @@
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <simgear_config.h> # include <simgear_config.h>
#endif #endif
#include <stdio.h>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
@ -60,6 +61,9 @@ using std::vector;
#ifndef AL_FORMAT_MONO_IMA4 #ifndef AL_FORMAT_MONO_IMA4
# define AL_FORMAT_MONO_IMA4 0x1300 # define AL_FORMAT_MONO_IMA4 0x1300
#endif #endif
#ifndef AL_UNPACK_BLOCK_ALIGNMENT_SOFT
# define AL_UNPACK_BLOCK_ALIGNMENT_SOFT 0x200C
#endif
class SGSoundMgr::SoundManagerPrivate class SGSoundMgr::SoundManagerPrivate
{ {
@ -141,6 +145,8 @@ SGSoundMgr::SGSoundMgr() :
{ {
d.reset(new SoundManagerPrivate); d.reset(new SoundManagerPrivate);
d->_base_pos = SGVec3d::fromGeod(_geod_pos); d->_base_pos = SGVec3d::fromGeod(_geod_pos);
_block_support = alIsExtensionPresent((ALchar *)"AL_SOFT_block_alignment");
} }
// destructor // destructor
@ -542,11 +548,11 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
// sample name was not found in the buffer cache. // sample name was not found in the buffer cache.
if ( sample->is_file() ) { if ( sample->is_file() ) {
int freq, format; int freq, format, block;
size_t size; size_t size;
try { try {
bool res = load(sample_name, &sample_data, &format, &size, &freq); bool res = load(sample_name, &sample_data, &format, &size, &freq, &block);
if (res == false) return NO_BUFFER; if (res == false) return NO_BUFFER;
} catch (sg_exception& e) { } catch (sg_exception& e) {
SG_LOG(SG_SOUND, SG_ALERT, SG_LOG(SG_SOUND, SG_ALERT,
@ -555,6 +561,7 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
return FAILED_BUFFER; return FAILED_BUFFER;
} }
sample->set_block_align( block );
sample->set_frequency( freq ); sample->set_frequency( freq );
sample->set_format( format ); sample->set_format( format );
sample->set_size( size ); sample->set_size( size );
@ -573,11 +580,17 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
if (fmt == SG_SAMPLE_MONO16) format = AL_FORMAT_MONO16; if (fmt == SG_SAMPLE_MONO16) format = AL_FORMAT_MONO16;
else if (fmt == SG_SAMPLE_MONO8) format = AL_FORMAT_MONO8; else if (fmt == SG_SAMPLE_MONO8) format = AL_FORMAT_MONO8;
else if (fmt == SG_SAMPLE_MULAW) format = AL_FORMAT_MONO_MULAW_EXT; else if (fmt == SG_SAMPLE_MULAW) format = AL_FORMAT_MONO_MULAW_EXT;
else if (fmt == SG_SAMPLE_ADPCM) format = AL_FORMAT_MONO_IMA4;
ALsizei size = sample->get_size(); ALsizei size = sample->get_size();
ALsizei freq = sample->get_frequency(); ALsizei freq = sample->get_frequency();
alBufferData( buffer, format, sample_data, size, freq ); alBufferData( buffer, format, sample_data, size, freq );
if (_block_support) {
ALsizei block_align = sample->get_block_align();
alBufferi (buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, block_align);
}
if ( !testForError("buffer add data") ) { if ( !testForError("buffer add data") ) {
sample->set_buffer(buffer); sample->set_buffer(buffer);
d->_buffers[sample_name] = refUint(buffer); d->_buffers[sample_name] = refUint(buffer);
@ -763,19 +776,21 @@ bool SGSoundMgr::load( const std::string &samplepath,
void **dbuf, void **dbuf,
int *fmt, int *fmt,
size_t *sz, size_t *sz,
int *frq ) int *frq,
int *block )
{ {
if ( !is_working() ) if ( !is_working() )
return false; return false;
unsigned int format; unsigned int format;
unsigned int block_align;
ALsizei size; ALsizei size;
ALsizei freq; ALsizei freq;
ALvoid *data; ALvoid *data;
ALfloat freqf; ALfloat freqf;
data = simgear::loadWAVFromFile(samplepath, format, size, freqf ); data = simgear::loadWAVFromFile(samplepath, format, size, freqf, block_align );
freq = (ALsizei)freqf; freq = (ALsizei)freqf;
if (data == NULL) { if (data == NULL) {
throw sg_io_exception("Failed to load wav file", sg_location(samplepath)); throw sg_io_exception("Failed to load wav file", sg_location(samplepath));
@ -788,6 +803,7 @@ bool SGSoundMgr::load( const std::string &samplepath,
*dbuf = (void *)data; *dbuf = (void *)data;
*fmt = (int)format; *fmt = (int)format;
*block = (int)block_align;
*sz = (size_t)size; *sz = (size_t)size;
*frq = (int)freq; *frq = (int)freq;

View File

@ -312,7 +312,8 @@ public:
void **data, void **data,
int *format, int *format,
size_t *size, size_t *size,
int *freq ); int *freq,
int *block );
/** /**
* Get a list of available playback devices. * Get a list of available playback devices.
@ -333,6 +334,7 @@ private:
/// private implementation object /// private implementation object
std::auto_ptr<SoundManagerPrivate> d; std::auto_ptr<SoundManagerPrivate> d;
bool _block_support;
bool _active; bool _active;
bool _changed; bool _changed;
float _volume; float _volume;