View Javadoc
1   /*
2    * Copyright (c) 2002-2014, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.captcha.modules.jcaptcha.service.sound.filter;
35  
36  import fr.paris.lutece.plugins.captcha.modules.jcaptcha.service.sound.LuteceWordToSound;
37  
38  
39  /**
40   *
41   */
42  public class EchoFilter extends SoundFilter
43  {
44      private short[] _delayBuffer;
45      private int _delayBufferPos;
46      private float _decay;
47  
48      /**
49       * Creates an EchoFilter with the specified number of delay samples and the
50       * specified decay rate.
51       * <p>
52       * The number of delay samples specifies how long before the echo is
53       * initially heard. For a 1 second echo with mono, 44100Hz sound, use 44100
54       * delay samples.
55       * <p>
56       * The decay value is how much the echo has decayed from the source. A decay
57       * value of .5 means the echo heard is half as loud as the source.
58       *
59       * @param echoDelay the echoDelay
60       * @param decay the decay
61       */
62      public EchoFilter( float echoDelay, float decay )
63      {
64          int numSampleDelay = ( Math.round( LuteceWordToSound.getSoundsSampleRate( ) * ( echoDelay / 1000 ) ) +
65              1 );
66          _delayBuffer = new short[numSampleDelay];
67          _decay = decay / 100;
68      }
69  
70      /**
71       * Gets the remaining size, in bytes, of samples that this filter can echo
72       * after the sound is done playing. Ensures that the sound will have decayed
73       * to below 1% of maximum volume (amplitude).
74       *
75       * @return the Remaining Size
76       */
77      public int getRemainingSize(  )
78      {
79          float finalDecay = 0.01f;
80  
81          // derived from Math.pow(decay,x) <= finalDecay
82          int numRemainingBuffers = (int) Math.ceil( Math.log( finalDecay ) / Math.log( _decay ) );
83          int bufferSize = _delayBuffer.length;
84  
85          return bufferSize * numRemainingBuffers;
86      }
87  
88      /**
89       * Clears this EchoFilter's internal delay buffer.
90       */
91      public void reset(  )
92      {
93          for ( int i = 0; i < _delayBuffer.length; i++ )
94          {
95              _delayBuffer[i] = 0;
96          }
97  
98          _delayBufferPos = 0;
99      }
100 
101     /**
102      * Filters the sound samples to add an echo. The samples played are added to
103      * the sound in the delay buffer multipied by the decay rate. The result is
104      * then stored in the delay buffer, so multiple echoes are heard.
105      *
106      * @param samples the samples
107      * @param offset the offset
108      * @param length the length
109      * @param sampleSizeInBits the sample size in bits
110      */
111     public void filter( byte[] samples, int offset, int length, int sampleSizeInBits )
112     {
113         if ( sampleSizeInBits == SAMPLE_SIZE_8_BIT )
114         {
115             for ( int i = offset; i < ( offset + length ); i++ )
116             {
117                 // update the sample
118                 short oldSample = get8bitSample( samples, i );
119                 short newSample = (short) ( oldSample + ( _decay * _delayBuffer[_delayBufferPos] ) );
120                 set8bitSample( samples, i, newSample );
121                 // update the delay buffer
122                 _delayBuffer[_delayBufferPos] = newSample;
123                 _delayBufferPos++;
124 
125                 if ( _delayBufferPos == _delayBuffer.length )
126                 {
127                     _delayBufferPos = 0;
128                 }
129             }
130         }
131         else if ( sampleSizeInBits == SAMPLE_SIZE_16_BIT )
132         {
133             for ( int i = offset; i < ( offset + length ); i += 2 )
134             {
135                 // update the sample
136                 short oldSample = get16bitSample( samples, i );
137                 short newSample = (short) ( oldSample + ( _decay * _delayBuffer[_delayBufferPos] ) );
138                 set16bitSample( samples, i, newSample );
139                 // update the delay buffer
140                 _delayBuffer[_delayBufferPos] = newSample;
141                 _delayBufferPos++;
142 
143                 if ( _delayBufferPos == _delayBuffer.length )
144                 {
145                     _delayBufferPos = 0;
146                 }
147             }
148         }
149     }
150 }