View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.audio;
31  
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  import org.openimaj.audio.analysis.FourierTransform;
36  import org.openimaj.audio.processor.AudioProcessor;
37  import org.openimaj.util.pair.IndependentPair;
38  import org.openimaj.util.pair.Pair;
39  
40  /**
41   * {@link AudioProcessor} that provides frequency information.
42   * 
43   * @author David Dupplaw (dpd@ecs.soton.ac.uk)
44   *
45   */
46  public class FrequencyAudioSource extends AudioProcessor implements Runnable {
47  	
48  	/**
49  	 * Interface for classes that listen to the frequency information
50  	 * extracted by the {@link FrequencyAudioSource}.
51  	 * 
52  	 * @author David Dupplaw (dpd@ecs.soton.ac.uk)
53  	 *
54  	 */
55  	public static interface Listener {
56  
57  		/**
58  		 * Called when new frequency data is available.
59  		 * 
60  		 * @param fftReal real fft values
61  		 * @param fftImag imaginary fft values
62  		 * @param low
63  		 * @param high
64  		 */
65  		public void consumeFrequency(float[] fftReal, float[] fftImag, int low, int high);
66  		
67  	}
68  
69  	private final FourierTransform fftProc;
70  	private final List<IndependentPair<Listener, Pair<Integer>>> listeners;
71  	private float[] fftReal;
72  	private float[] fftImag;
73  	
74  	
75  	/**
76  	 * Construct on top of given stream
77  	 * @param stream the stream
78  	 */
79  	public FrequencyAudioSource(final AudioStream stream) {
80  		super(stream);
81  		this.fftProc = new FourierTransform();
82  		this.listeners = new ArrayList<IndependentPair<Listener, Pair<Integer>>>();
83  		new Thread(this).start();
84  	}
85  	
86  	@Override
87  	public SampleChunk process(final SampleChunk sample) throws Exception {
88  		this.fftProc.process(sample);
89  		final float[] fft = this.fftProc.getLastFFT()[0];
90  		this.fireFrequencyEvent(fft,sample);
91  		return sample;
92  	}
93  
94  	private void fireFrequencyEvent(final float[] fft,final SampleChunk sample) {
95  		final double binSize = (sample.getFormat().getSampleRateKHz()*1000) / (fft.length/2);
96  		if(this.fftReal == null || fft.length/4 != this.fftReal.length){
97  			this.fftReal = new float[fft.length/4];
98  			this.fftImag = new float[fft.length/4];
99  		}
100 		// Extract the spectra
101 		for( int i = 0; i < fft.length/4; i++ )
102 		{
103 			final float re = fft[i*2];
104 			final float im = fft[i*2+1];
105 			this.fftReal[i] = re;
106 			this.fftImag[i] = im;
107 		}
108 		for(final IndependentPair<Listener,Pair<Integer>> l : this.listeners){
109 			final Pair<Integer> range = l.secondObject();
110 			final int low = (int) (range.firstObject()/binSize);
111 			final int high = (int) (range.secondObject()/binSize);
112 			l.firstObject().consumeFrequency(this.fftReal,this.fftImag,low,high);
113 		}
114 	}
115 
116 	@Override
117 	public void run() {
118 //		while(true){
119 			try
120 	        {
121 		        Thread.sleep( 500 );
122 		        SampleChunk s = null;
123 		        while( (s = this.nextSampleChunk()) != null ) {
124 		        	this.process( s );
125 		        }
126 	        }
127 	        catch( final InterruptedException e )
128 	        {
129 	        	e.printStackTrace();
130 	        } 
131 	        catch (final Exception e) 
132 	        {
133 	        	e.printStackTrace();
134 	        }
135 	        
136 //		}
137 	}
138 
139 	/**
140 	 * Add a listener
141 	 * @param l the listener
142 	 */
143 	public void addFrequencyListener(final Listener l) {
144 		final Pair<Integer> range = null;
145 		this.listeners.add(IndependentPair.pair(l,range));
146 	}
147 	
148 	/**
149 	 * Add a listener
150 	 * @param l the listener
151 	 * @param requestFrequencyRange the range
152 	 */
153 	public void addFrequencyListener(final Listener l, final Pair<Integer> requestFrequencyRange) {
154 		this.listeners.add(IndependentPair.pair(l,requestFrequencyRange));
155 	}
156 }