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.util;
034
035import java.util.Arrays;
036
037/**
038 *      Represents a note in a standard equal-tempered Western scale. There
039 *      are various static methods for creating notes from names, note numbers,
040 *      and frequencies.
041 *
042 *      @author David Dupplaw (dpd@ecs.soton.ac.uk)
043 *      @see "http://www.musicdsp.org/showone.php?id=125"
044 *      @created 27 Nov 2011
045 */
046public class WesternScaleNote
047{
048        /** The MIDI note number of the note */
049        public int noteNumber;
050        
051        /** The note name; e.g. C, D, F#, etc. */
052        public String noteName;
053        
054        /** The octave number. Note C4 is middle C */
055        public int octaveNumber;
056        
057        /** The frequency of the note */
058        public float frequency;
059        
060        /**
061         *      {@inheritDoc}
062         *      @see java.lang.Object#toString()
063         */
064        @Override
065        public String toString()
066        {
067                return this.noteName + this.octaveNumber + 
068                                " ("+this.frequency+"Hz, "+this.noteNumber+")";
069//              return this.noteName+this.octaveNumber;
070        }
071        
072        // ---------------------- Static stuff below here ------------------------ //
073        /** The standing tuning is A=440Hz. Can change that here. */
074        public static float tuningOfA = 440.0f;
075        
076        /** The twelfth root of 2 */
077        public static final double twelfthRootOfTwo = 1.059463094359;
078        
079        /** The names of the notes in the Western scale */
080        public static final String[] noteNames = new String[]
081                  {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B" };
082        
083        /**
084         *      Returns the number of half-steps between the two given notes.
085         * 
086         *      @param note1 The first note of the interval
087         *      @param note2 The second note of the interval
088         *      @return The number of half-steps between the two notes.
089         */
090        public final static int nStepsBetween( final WesternScaleNote note1, 
091                        final WesternScaleNote note2 )
092        {
093                return note1.noteNumber - note2.noteNumber;
094        }
095        
096        /**
097         *      Converts a MIDI note number to a frequency.
098         *      @param noteNumber The note number
099         *      @return The frequency
100         */
101        public final static float noteToFrequency( final int noteNumber )
102        {
103                return (float)(WesternScaleNote.tuningOfA * 
104                                Math.pow( 2, (noteNumber-69)/12d ));
105        }
106        
107        /**
108         *      Converts a frequency to the nearest note number
109         *      @param frequency The frequency
110         *      @return The note number
111         */
112        public final static int frequencyToNote( final float frequency )
113        {
114                return (int)Math.round( 12 * Math.log( 
115                                frequency/WesternScaleNote.tuningOfA )/Math.log(2) )
116                                +69;
117        }
118        
119        /**
120         *      Given a note string, returns a new {@link WesternScaleNote} from
121         *      which other information can be garnered. A note string is the note name
122         *      followed by the octave; e.g. "D3" or "A#5".
123         *      @param noteString The note string
124         *      @return A {@link WesternScaleNote} or null if the note string is invalid
125         */
126        public final static WesternScaleNote createNote( final String noteString )
127        {
128                // Options we have are nO, nAO, nOO, nAOO.
129
130                // Find the longest matching start to the string
131                String startNoteName = null;
132                int length = 0;
133                for( String noteName: noteNames )
134                {
135                        if( noteString.startsWith( noteName ) && noteName.length() > length )
136                        {
137                                startNoteName = noteName;
138                                length = noteName.length();
139                        }
140                }
141                
142                if( startNoteName == null ) return null;
143                
144                // Parse the octave value
145                int octave = Integer.parseInt( noteString.substring( 
146                                startNoteName.length() ) );
147                
148                // Create a new note
149                return WesternScaleNote.createNote( startNoteName, octave ); 
150        }
151        
152        /**
153         *      Given a note name and octave, returns a {@link WesternScaleNote} from
154         *      which other information can be garnered.
155         * 
156         *      @param noteName The name of the note
157         *      @param octaveNumber The octave of the note.
158         *      @return A new WesternScaleNote
159         */
160        public final static WesternScaleNote createNote( final String noteName, 
161                        final int octaveNumber )
162        {
163                final WesternScaleNote n = new WesternScaleNote();
164                n.noteName = noteName;
165                n.octaveNumber = octaveNumber;
166                n.noteNumber = Arrays.asList( WesternScaleNote.noteNames ).indexOf( noteName ) + 
167                        (octaveNumber+1)*12;
168                n.frequency = WesternScaleNote.noteToFrequency( n.noteNumber );
169                                          
170                return n;
171        }
172        
173        /**
174         *      Create a {@link WesternScaleNote} given a frequency. It does this by
175         *      converting the frequency in to a MIDI note number and using
176         *      {@link WesternScaleNote#createNote(int)} which returns the note. For
177         *      this reason, the value of the member {@link #frequency} in the returned
178         *      note may not be the same as the given frequency. 
179         * 
180         *      @param frequency The frequency to convert to a note.
181         *      @return A {@link WesternScaleNote}
182         */
183        public final static WesternScaleNote createNote( final float frequency )
184        {
185                final int noteNum = WesternScaleNote.frequencyToNote( frequency );
186                return WesternScaleNote.createNote( noteNum );
187        }
188
189        /**
190         *      Given a note number, returns a {@link WesternScaleNote} from which other
191         *      information can be garnered.
192         * 
193         *      @param noteNumber A note number
194         *      @return A {@link WesternScaleNote}
195         */
196        public final static WesternScaleNote createNote( final int noteNumber )
197        {
198                final WesternScaleNote n = new WesternScaleNote();
199                n.noteNumber = noteNumber;
200                n.octaveNumber = noteNumber/12 -1;
201                n.noteName = WesternScaleNote.noteNames[ noteNumber%12 ];
202                n.frequency = WesternScaleNote.noteToFrequency( noteNumber );
203                
204                return n;
205        }
206
207}