BlipBuffer.java :  » Port » gme-android » com » zeromaid » gme_android » Android Open Source

Android Open Source » Port » gme android 
gme android » com » zeromaid » gme_android » BlipBuffer.java
package com.zeromaid.gme_android;

// Band-limited sound synthesis buffer
// http://www.slack.net/~ant/

/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */

final class BlipBuffer
{
  static final boolean muchFaster = false; // speeds synthesis at a cost of quality
  
  BlipBuffer() { setVolume( 1.0 ); }
   
  // Sets sample rate of output and changes buffer length to msec
  public void setSampleRate( int rate, int msec )
  {
    sampleRate = rate;
    buf = new int [(int) ((long) msec * rate / 1000) + 1024];
  }
  
  // Sets input clock rate. Must be set after sample rate.
  public void setClockRate( int rate )
  {
    clockRate_ = rate;
    factor = (int) (sampleRate / (float) clockRate_ * (1 << timeBits) + 0.5);
  }
  
  // Current clock rate
  public int clockRate() { return clockRate_; }
  
  // Removes all samples from buffer
  public void clear()
  {
    offset = 0;
    accum  = 0;
    java.util.Arrays.fill( buf, 0, buf.length, 0 );
  }
  
  // Sets overall volume, where 1.0 is normal
  public void setVolume( double v )
  {
    final int shift = 15;
    final int round = 1 << (shift - 1);
    
    volume = (int) ((1 << shift) * v + 0.5) & ~1;
    
    if ( !muchFaster )
    {
      // build new set of kernels
      int [] [] nk = new int [phaseCount + 1] [];
      for ( int i = nk.length; --i >= 0; )
        nk [i] = new int [halfWidth];
      
      
      // must be even since center kernel uses same half twice
      final int mul = volume;
      
      final int pc = phaseCount;
      for ( int p = 17; --p >= 0; )
      {
        int remain = mul;
        for ( int i = 8; --i >= 0; )
        {
          remain -= (nk [     p] [i] = (baseKernel [      p  * halfWidth + i] * mul   + round) >> shift);
          remain -= (nk [pc - p] [i] = (baseKernel [(pc - p) * halfWidth + i] * mul   + round) >> shift);
        }
        nk [p] [7] += remain; // each pair of kernel halves must total mul
      }
      
      // replace kernel atomically
      kernel = nk;
    }
  }
  
  // Adds delta at given time
  public void addDelta( int time, int delta )
  {
    final int [] buf = this.buf;
    final int phase = (time = time * factor + offset) >>
        (timeBits - phaseBits) & (phaseCount - 1);
    
    if ( muchFaster )
    {
      final int right = ((delta *= volume) >> phaseBits) * phase;
      buf [time >>= timeBits] += delta - right;
      buf [time + 1         ] += right;
    }
    else
    {
      // TODO: use smaller kernel
      
      // left half
      int [] k = kernel [phase];
      buf [time >>= timeBits] += k [0] * delta;
      buf [time + 1] += k [1] * delta;
      buf [time + 2] += k [2] * delta;
      buf [time + 3] += k [3] * delta;
      buf [time + 4] += k [4] * delta;
      buf [time + 5] += k [5] * delta;
      buf [time + 6] += k [6] * delta;
      buf [time + 7] += k [7] * delta;
      
      // right half (mirrored version of a left half)
      k = kernel [phaseCount - phase];
      time += 8;
      buf [time    ] += k [7] * delta;
      buf [time + 1] += k [6] * delta;
      buf [time + 2] += k [5] * delta;
      buf [time + 3] += k [4] * delta;
      buf [time + 4] += k [3] * delta;
      buf [time + 5] += k [2] * delta;
      buf [time + 6] += k [1] * delta;
      buf [time + 7] += k [0] * delta;
    }
  }
  
  // Number of samples that would be available at time
  public int countSamples( int time )
  {
    int last_sample  = (time * factor + offset) >> timeBits;
    int first_sample = offset >> timeBits;
    return last_sample - first_sample;
  }
  
  // Ends current time frame and makes samples available for reading
  public void endFrame( int time )
  {
    offset += time * factor;
    assert samplesAvail() < buf.length;
  }
  
  // Number of samples available to be read
  public int samplesAvail() { return offset >> timeBits; }
  
  // Reads at most count samples into out at offset pos*2 (2 bytes per sample)
  // and returns number of samples actually read.
  public int readSamples( byte [] out, int pos, int count )
  {
    final int avail = samplesAvail();
    if ( count > avail )
      count = avail;
    
    if ( count > 0 )
    {
      // Integrate
      final int [] buf = this.buf;
      int accum = this.accum;
      pos <<= 1;
      int i = 0;
      do
      {
        int s = (accum += buf [i] - (accum >> 9)) >> 15;
        
        // clamp to 16 bits
        if ( (short) s != s )
          s = (s >> 24) ^ 0x7FFF;
        
        // write as little-endian
        out [pos    ] = (byte) s;
        out [pos + 1] = (byte) (s >> 8);
        pos += 2;
      }
      while ( ++i < count );
      this.accum = accum;
      
      removeSamples( count );
    }
    return count;
  }

// internal

  static final int timeBits = 16;
  static final int phaseBits = (muchFaster ? 8 : 5);
  static final int phaseCount = 1 << phaseBits;
  static final int halfWidth = 8;
  static final int stepWidth = halfWidth * 2;
  
  int factor;
  int offset;
  int [] [] kernel;
  int accum;
  int [] buf;
  int sampleRate;
  int clockRate_;
  int volume;
  
  void removeSilence( int count )
  {
    offset -= count << timeBits;
    assert samplesAvail() >= 0;
  }
  
  void removeSamples( int count )
  {
    int remain = samplesAvail() - count + stepWidth;
    System.arraycopy( buf, count, buf, 0, remain );
    java.util.Arrays.fill( buf, remain, remain + count, 0 );
    removeSilence( count );
  }
  
  // TODO: compute at run-time
  static final int [] baseKernel =
  {
    10,  -61,  284, -615, 1359,-1753, 5911,22498,
    14,  -71,  295, -616, 1314,-1615, 5259,22472,
    17,  -80,  304, -611, 1260,-1468, 4626,22402,
    21,  -88,  309, -603, 1200,-1313, 4015,22285,
    23,  -94,  313, -589, 1134,-1151, 3426,22122,
    26, -100,  313, -572, 1063, -986, 2861,21915,
    28, -104,  312, -550,  986, -818, 2322,21663,
    30, -108,  308, -525,  906, -648, 1810,21369,
    31, -110,  302, -497,  823, -478, 1326,21034,
    33, -112,  295, -466,  737, -309,  871,20657,
    34, -112,  285, -433,  649, -143,  446,20242,
    34, -111,  274, -397,  561,   19,   51,19790,
    34, -110,  261, -359,  472,  176, -313,19302,
    35, -108,  247, -320,  383,  327, -646,18781,
    34, -105,  232, -280,  296,  472, -948,18230,
    34, -101,  216, -240,  210,  608,-1219,17651,
    33,  -97,  199, -199,  126,  736,-1459,17045,
    32,  -92,  182, -158,   45,  855,-1668,16413,
    31,  -86,  164, -117,  -33,  964,-1847,15761,
    30,  -80,  145,  -77, -107, 1063,-1996,15091,
    28,  -74,  127,  -38, -177, 1151,-2117,14405,
    26,  -67,  108,    0, -243, 1228,-2211,13706,
    24,  -60,   90,   37, -304, 1294,-2277,12996,
    22,  -53,   72,   72, -360, 1349,-2318,12278,
    20,  -46,   54,  105, -410, 1392,-2334,11556,
    18,  -39,   37,  136, -455, 1425,-2327,10831,
    15,  -31,   21,  164, -495, 1446,-2298,10107,
    13,  -24,    5,  191, -529, 1456,-2249, 9385,
    10,  -17,  -10,  215, -557, 1456,-2182, 8669,
     8,  -10,  -24,  236, -580, 1446,-2096, 7962,
     5,   -3,  -37,  255, -597, 1426,-1996, 7265,
     3,    4,  -50,  271, -608, 1397,-1881, 6580,
     0,   10,  -61,  284, -615, 1359,-1753, 5911,
  };
}

// Stereo sound buffer with center channel

final class StereoBuffer
{
  private BlipBuffer [] bufs = new BlipBuffer [3];
  
  // Same behavior as in BlipBuffer unless noted
  
  public StereoBuffer()
  {
    for ( int i = bufs.length; --i >= 0; )
      bufs [i] = new BlipBuffer();
  }
  
  public void setSampleRate( int rate, int msec )
  {
    for ( int i = bufs.length; --i >= 0; )
      bufs [i].setSampleRate( rate, msec );
  }
  
  public void setClockRate( int rate )
  {
    for ( int i = bufs.length; --i >= 0; )
      bufs [i].setClockRate( rate );
  }
  
  public int clockRate() { return bufs [0].clockRate(); }
  
  public int countSamples( int time ) { return bufs [0].countSamples( time ); }
  
  public void clear()
  {
    for ( int i = bufs.length; --i >= 0; )
      bufs [i].clear();
  }
  
  public void setVolume( double v )
  {
    for ( int i = bufs.length; --i >= 0; )
      bufs [i].setVolume( v );
  }
  
  // The three channels that are mixed together
  // left output  = left  + center
  // right output = right + center
  public BlipBuffer center() { return bufs [2]; }
  public BlipBuffer left  () { return bufs [0]; }
  public BlipBuffer right () { return bufs [1]; }
  
  public void endFrame( int time )
  {
    for ( int i = bufs.length; --i >= 0; )
      bufs [i].endFrame( time );
  }
  
  public int samplesAvail() { return bufs [2].samplesAvail() << 1; }
  
  // Output is in stereo, so count must always be a multiple of 2
  public int readSamples( byte [] out, int start, int count )
  {
    assert (count & 1) == 0;
    
    final int avail = samplesAvail();
    if ( count > avail )
      count = avail;
    
    if ( (count >>= 1) > 0 )
    {
      // TODO: optimize for mono case
      
      // calculate center in place
      final int [] mono = bufs [2].buf;
      {
        int accum = bufs [2].accum;
        int i = 0;
        do
        {
          mono [i] = (accum += mono [i] - (accum >> 9));
        }
        while ( ++i < count );
        bufs [2].accum = accum;
      }
      
      // calculate left and right
      for ( int ch = 2; --ch >= 0; )
      {
        // add right and output
        final int [] buf = bufs [ch].buf;
        int accum = bufs [ch].accum;
        int pos = (start + ch) << 1;
        int i = 0;
        do
        {
          int s = ((accum += buf [i] - (accum >> 9)) + mono [i]) >> 15;
          
          // clamp to 16 bits
          if ( (short) s != s )
            s = (s >> 24) ^ 0x7FFF;
          
          // write as big endian
          out [pos    ] = (byte) s;
          out [pos + 1] = (byte) (s >> 8);
          pos += 4;
        }
        while ( ++i < count );
        bufs [ch].accum = accum;
      }
      
      for ( int i = bufs.length; --i >= 0; )
        bufs [i].removeSamples( count );
    }
    return count << 1;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.