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}