001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030/**
031 * 
032 */
033package org.openimaj.audio.filters;
034
035import java.util.ArrayList;
036import java.util.List;
037
038import org.openimaj.audio.AudioFormat;
039import org.openimaj.audio.SampleChunk;
040import org.openimaj.audio.processor.AudioProcessor;
041import org.openimaj.audio.util.WesternScaleNote;
042
043/**
044 *      A filter bank for musical note detection. Uses a set of comb filters
045 *      tuned to the notes that are to be detected.
046 *
047 *  @author David Dupplaw <dpd@ecs.soton.ac.uk>
048 *      @version $Author$, $Revision$, $Date$
049 *      @created 30 Apr 2012
050 */
051public class MusicalNoteFilterBank extends AudioProcessor
052{
053        /** The start of the range as a MIDI note number */
054        private int startOfRange = 0;
055        
056        /** The end of the range as a MIDI note number */
057        private int endOfRange = 0;
058        
059        /** The filters we'll use */
060        private List<FeedForwardCombFilter> filters = null;
061        
062        /** The audio format that will be processed */
063        private AudioFormat format = null;
064        
065        /** The last calculated output power of the filter bank */
066        private double outputPower = 0;
067        
068        /**
069         *      Create a 12-deep filter bank for detecting notes
070         *      between C4 and C5. The audio format may be null
071         *      if not known in advance; in this case, the filter
072         *      bank will be setup lazily during the processing loop. 
073         * 
074         *      @param af The audio format of the filter bank
075         */
076        public MusicalNoteFilterBank( final AudioFormat af )
077    {
078                this( 60, 72, af );
079    }
080        
081        /**
082         *      Create a filter bank for detecting notes between the start
083         *      note given and the end note given. The audio format may be null
084         *      if not known in advance; in this case, the filter
085         *      bank will be setup lazily during the processing loop.
086         * 
087         *  @param startOfRange The start MIDI note number to detect
088         *  @param endOfRange The end MIDI note number to detect.
089         *  @param af The format
090         */
091        public MusicalNoteFilterBank( final int startOfRange, final int endOfRange, final AudioFormat af )
092    {
093                this.startOfRange = startOfRange;
094                this.endOfRange = endOfRange;
095                this.format = af;
096                
097                if( this.format != null )
098                        this.setupFilters();
099    }
100        
101        /**
102         *      Setup the filters based on the settings of this class.
103         */
104        private void setupFilters()
105        {
106                this.filters = new ArrayList<FeedForwardCombFilter>();
107                for( int i = this.startOfRange; i < this.endOfRange; i++ )
108                {
109                        // Get the frequency of the given note
110                        final double f = WesternScaleNote.createNote( i ).frequency;
111                        
112                        // Add a feed-forward comb filter for that frequency
113                        this.filters.add( new FeedForwardCombFilter( f, 
114                                        this.format.getSampleRateKHz()*1000d, 1f ) );
115                }
116    }
117
118        /**
119         *      {@inheritDoc}
120         *      @see org.openimaj.audio.processor.AudioProcessor#process(org.openimaj.audio.SampleChunk)
121         */
122        @Override
123        public SampleChunk process( final SampleChunk sample ) throws Exception
124        {
125                if( this.filters == null )
126                {
127                        this.format = sample.getFormat();
128                        this.setupFilters();
129                }
130                
131                this.outputPower = 0;
132                for( final FeedForwardCombFilter filter : this.filters )
133                {
134                        // Process the sample with each filter
135                        filter.process( sample );
136                        this.outputPower += filter.getOutputPower();
137                }
138                
139                return sample;
140        }
141        
142        /**
143         *      Get the last calculated output power.
144         *      @return The last calcualted output power
145         */
146        public double getOutputPower()
147        {
148                return this.outputPower;
149        }
150}