View Javadoc
1   /*
2    * Copyright (c) 2002-2021, City of 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  public class EchoFilter extends SoundFilter
42  {
43      private short [ ] _delayBuffer;
44      private int _delayBufferPos;
45      private float _decay;
46  
47      /**
48       * Creates an EchoFilter with the specified number of delay samples and the specified decay rate.
49       * <p>
50       * The number of delay samples specifies how long before the echo is initially heard. For a 1 second echo with mono, 44100Hz sound, use 44100 delay samples.
51       * <p>
52       * The decay value is how much the echo has decayed from the source. A decay value of .5 means the echo heard is half as loud as the source.
53       *
54       * @param echoDelay
55       *            the echoDelay
56       * @param decay
57       *            the decay
58       */
59      public EchoFilter( float echoDelay, float decay )
60      {
61          int numSampleDelay = ( Math.round( LuteceWordToSound.getSoundsSampleRate( ) * ( echoDelay / 1000 ) ) + 1 );
62          _delayBuffer = new short [ numSampleDelay];
63          _decay = decay / 100;
64      }
65  
66      /**
67       * Gets the remaining size, in bytes, of samples that this filter can echo after the sound is done playing. Ensures that the sound will have decayed to
68       * below 1% of maximum volume (amplitude).
69       *
70       * @return the Remaining Size
71       */
72      public int getRemainingSize( )
73      {
74          float finalDecay = 0.01f;
75  
76          // derived from Math.pow(decay,x) <= finalDecay
77          int numRemainingBuffers = (int) Math.ceil( Math.log( finalDecay ) / Math.log( _decay ) );
78          int bufferSize = _delayBuffer.length;
79  
80          return bufferSize * numRemainingBuffers;
81      }
82  
83      /**
84       * Clears this EchoFilter's internal delay buffer.
85       */
86      public void reset( )
87      {
88          for ( int i = 0; i < _delayBuffer.length; i++ )
89          {
90              _delayBuffer [i] = 0;
91          }
92  
93          _delayBufferPos = 0;
94      }
95  
96      /**
97       * Filters the sound samples to add an echo. The samples played are added to the sound in the delay buffer multipied by the decay rate. The result is then
98       * stored in the delay buffer, so multiple echoes are heard.
99       *
100      * @param samples
101      *            the samples
102      * @param offset
103      *            the offset
104      * @param length
105      *            the length
106      * @param sampleSizeInBits
107      *            the sample size in bits
108      */
109     public void filter( byte [ ] samples, int offset, int length, int sampleSizeInBits )
110     {
111         if ( sampleSizeInBits == SAMPLE_SIZE_8_BIT )
112         {
113             for ( int i = offset; i < ( offset + length ); i++ )
114             {
115                 // update the sample
116                 short oldSample = get8bitSample( samples, i );
117                 short newSample = (short) ( oldSample + ( _decay * _delayBuffer [_delayBufferPos] ) );
118                 set8bitSample( samples, i, newSample );
119                 // update the delay buffer
120                 _delayBuffer [_delayBufferPos] = newSample;
121                 _delayBufferPos++;
122 
123                 if ( _delayBufferPos == _delayBuffer.length )
124                 {
125                     _delayBufferPos = 0;
126                 }
127             }
128         }
129         else
130             if ( sampleSizeInBits == SAMPLE_SIZE_16_BIT )
131             {
132                 for ( int i = offset; i < ( offset + length ); i += 2 )
133                 {
134                     // update the sample
135                     short oldSample = get16bitSample( samples, i );
136                     short newSample = (short) ( oldSample + ( _decay * _delayBuffer [_delayBufferPos] ) );
137                     set16bitSample( samples, i, newSample );
138                     // update the delay buffer
139                     _delayBuffer [_delayBufferPos] = newSample;
140                     _delayBufferPos++;
141 
142                     if ( _delayBufferPos == _delayBuffer.length )
143                     {
144                         _delayBufferPos = 0;
145                     }
146                 }
147             }
148     }
149 }