From 976c85ff5773a991a85467e60298b9af690258c2 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Mon, 30 May 2016 14:17:16 +0200 Subject: [PATCH] Add IMA4 support --- simgear/sound/openal_test2.cxx | 2 +- simgear/sound/readwav.cxx | 119 ++++++++++++++++++++++++++++-- simgear/sound/readwav.hxx | 2 +- simgear/sound/sample.hxx | 21 +++++- simgear/sound/soundmgr_openal.cxx | 24 +++++- simgear/sound/soundmgr_openal.hxx | 4 +- 6 files changed, 159 insertions(+), 13 deletions(-) diff --git a/simgear/sound/openal_test2.cxx b/simgear/sound/openal_test2.cxx index dc81a9bf..97680e58 100644 --- a/simgear/sound/openal_test2.cxx +++ b/simgear/sound/openal_test2.cxx @@ -47,7 +47,7 @@ int main( int argc, char *argv[] ) { printf("playing sample2\n"); sleep(1); - SGSoundSample *sample3 = new SGSoundSample("jet.wav", srcDir); + SGSoundSample *sample3 = new SGSoundSample("jet_ima4.wav", srcDir); sample3->set_volume(0.5); sample3->set_pitch(0.8); sample3->play_looped(); diff --git a/simgear/sound/readwav.cxx b/simgear/sound/readwav.cxx index 1618449c..71ec14b4 100644 --- a/simgear/sound/readwav.cxx +++ b/simgear/sound/readwav.cxx @@ -38,6 +38,7 @@ namespace public: ALvoid* data; unsigned int format; + unsigned int block_align; ALsizei length; ALfloat frequency; SGPath path; @@ -96,10 +97,10 @@ namespace mantissa = mulawbyte & 0x0F; sample = exp_lut[exponent] + (mantissa << (exponent + 3)); return sign ? -sample : sample; - } + } - void codecULaw (Buffer* b) - { + void codecULaw (Buffer* b) + { uint8_t *d = (uint8_t *) b->data; size_t newLength = b->length * 2; int16_t *buf = (int16_t *) malloc(newLength); @@ -115,6 +116,98 @@ namespace 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) { int r = gzseek(fd, skipCount, SEEK_CUR); @@ -219,10 +312,23 @@ namespace codec = codecULaw; } 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: throw sg_io_exception("unsupported WAV encoding", b->path); } + b->block_align = blockAlign; b->frequency = samplesPerSecond; b->format = formatConstruct(numChannels, bitsPerSample, compressed); } else if (magic == WAV_DATA_4CC) { @@ -256,7 +362,7 @@ namespace 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()) { 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; b.data = NULL; // don't free when Buffer does out of scope format = b.format; + block_align = b.block_align; size = b.length; freqf = b.frequency; @@ -287,9 +394,10 @@ ALuint createBufferFromFile(const SGPath& path) ALuint buffer = -1; #ifdef ENABLE_SOUND unsigned int format; + unsigned int block_align; ALsizei size; ALfloat sampleFrequency; - ALvoid* data = loadWAVFromFile(path, format, size, sampleFrequency); + ALvoid* data = loadWAVFromFile(path, format, size, sampleFrequency, block_alight); assert(data); alGenBuffers(1, &buffer); @@ -299,6 +407,7 @@ ALuint createBufferFromFile(const SGPath& path) } alBufferData (buffer, format, data, size, (ALsizei) sampleFrequency); + alBufferi (buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, block_align); if (alGetError() != AL_NO_ERROR) { alDeleteBuffers(1, &buffer); free(data); diff --git a/simgear/sound/readwav.hxx b/simgear/sound/readwav.hxx index 375b26ae..7957250f 100644 --- a/simgear/sound/readwav.hxx +++ b/simgear/sound/readwav.hxx @@ -16,7 +16,7 @@ class SGPath; 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); } diff --git a/simgear/sound/sample.hxx b/simgear/sound/sample.hxx index 408c5fcc..1980eb71 100644 --- a/simgear/sound/sample.hxx +++ b/simgear/sound/sample.hxx @@ -65,7 +65,17 @@ public: * Returns the format of this audio sample. * @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. @@ -170,6 +180,7 @@ protected: unsigned int _tracks; unsigned int _samples; unsigned int _frequency; + unsigned int _block_align; bool _compressed; bool _loop; @@ -432,6 +443,14 @@ public: _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. * @param freq Frequency diff --git a/simgear/sound/soundmgr_openal.cxx b/simgear/sound/soundmgr_openal.cxx index 337a2373..7e48da8c 100644 --- a/simgear/sound/soundmgr_openal.cxx +++ b/simgear/sound/soundmgr_openal.cxx @@ -28,6 +28,7 @@ #ifdef HAVE_CONFIG_H # include #endif +#include #include #include @@ -60,6 +61,9 @@ using std::vector; #ifndef AL_FORMAT_MONO_IMA4 # define AL_FORMAT_MONO_IMA4 0x1300 #endif +#ifndef AL_UNPACK_BLOCK_ALIGNMENT_SOFT +# define AL_UNPACK_BLOCK_ALIGNMENT_SOFT 0x200C +#endif class SGSoundMgr::SoundManagerPrivate { @@ -141,6 +145,8 @@ SGSoundMgr::SGSoundMgr() : { d.reset(new SoundManagerPrivate); d->_base_pos = SGVec3d::fromGeod(_geod_pos); + + _block_support = alIsExtensionPresent((ALchar *)"AL_SOFT_block_alignment"); } // destructor @@ -542,11 +548,11 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample) // sample name was not found in the buffer cache. if ( sample->is_file() ) { - int freq, format; + int freq, format, block; size_t size; 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; } catch (sg_exception& e) { SG_LOG(SG_SOUND, SG_ALERT, @@ -555,6 +561,7 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample) return FAILED_BUFFER; } + sample->set_block_align( block ); sample->set_frequency( freq ); sample->set_format( format ); sample->set_size( size ); @@ -573,11 +580,17 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample) if (fmt == SG_SAMPLE_MONO16) format = AL_FORMAT_MONO16; 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_ADPCM) format = AL_FORMAT_MONO_IMA4; ALsizei size = sample->get_size(); ALsizei freq = sample->get_frequency(); 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") ) { sample->set_buffer(buffer); d->_buffers[sample_name] = refUint(buffer); @@ -763,19 +776,21 @@ bool SGSoundMgr::load( const std::string &samplepath, void **dbuf, int *fmt, size_t *sz, - int *frq ) + int *frq, + int *block ) { if ( !is_working() ) return false; unsigned int format; + unsigned int block_align; ALsizei size; ALsizei freq; ALvoid *data; ALfloat freqf; - data = simgear::loadWAVFromFile(samplepath, format, size, freqf ); + data = simgear::loadWAVFromFile(samplepath, format, size, freqf, block_align ); freq = (ALsizei)freqf; if (data == NULL) { 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; *fmt = (int)format; + *block = (int)block_align; *sz = (size_t)size; *frq = (int)freq; diff --git a/simgear/sound/soundmgr_openal.hxx b/simgear/sound/soundmgr_openal.hxx index 60f07367..8ecdd57b 100644 --- a/simgear/sound/soundmgr_openal.hxx +++ b/simgear/sound/soundmgr_openal.hxx @@ -312,7 +312,8 @@ public: void **data, int *format, size_t *size, - int *freq ); + int *freq, + int *block ); /** * Get a list of available playback devices. @@ -333,6 +334,7 @@ private: /// private implementation object std::auto_ptr d; + bool _block_support; bool _active; bool _changed; float _volume;