]> git.mxchange.org Git - simgear.git/commitdiff
Add IMA4 support
authorErik Hofman <erik@ehofman.com>
Mon, 30 May 2016 12:17:16 +0000 (14:17 +0200)
committerRoland Haeder <roland@mxchange.org>
Sat, 13 Aug 2016 08:21:16 +0000 (10:21 +0200)
simgear/sound/openal_test2.cxx
simgear/sound/readwav.cxx
simgear/sound/readwav.hxx
simgear/sound/sample.hxx
simgear/sound/soundmgr_openal.cxx
simgear/sound/soundmgr_openal.hxx

index dc81a9bfe7ac4edd86f287a52d770bee92990481..97680e5860e176ee6f28bb9b5cfdf68029658bca 100644 (file)
@@ -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();
index 1618449c997e16af796d50ec503779dab370805e..71ec14b44f09e77602f41a38c9637697c43c96ba 100644 (file)
@@ -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);
index 375b26aeda1067a294d968c5fe2c0953ced3a6fb..7957250f3f4d918bee3725ffe171f6c0627792a2 100644 (file)
@@ -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);
 }
index 408c5fccb34e3fad8201975a70690672622a74c2..1980eb71ad490c4e13f04c21decfc628c78fec9c 100644 (file)
@@ -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
index 337a237315e8250bfb868d23d1d782b9333e13f1..7e48da8c2df3ff0f0c61fecd6d0e900f0fb60e21 100644 (file)
@@ -28,6 +28,7 @@
 #ifdef HAVE_CONFIG_H
 #  include <simgear_config.h>
 #endif
+#include <stdio.h>
 
 #include <iostream>
 #include <algorithm>
@@ -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;
 
index 60f073678d0b2cc3a727ed4d7552859af15e3e51..8ecdd57bd6c721752c6d6d78c5e3a1b681e3ef3c 100644 (file)
@@ -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<SoundManagerPrivate> d;
 
+    bool _block_support;
     bool _active;
     bool _changed;
     float _volume;