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.ArrayList; 036import java.util.Arrays; 037import java.util.List; 038 039import org.openimaj.util.pair.IndependentPair; 040 041/** 042 * Static utility methods that are related to music (more semantic 043 * than audio utilities). 044 * <p> 045 * For working with notes, see {@link WesternScaleNote}. 046 * 047 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 048 * 049 * @created 27 Nov 2011 050 */ 051public class MusicUtils 052{ 053 /** 054 * Given a beats-per-minute rate, returns the number of milliseconds 055 * in a single beat. 056 * 057 * @param bpm Beats per minute 058 * @return Number of milliseconds per beat. 059 */ 060 public static int millisPerBeat( final float bpm ) 061 { 062 return (int)(60000f/bpm); 063 } 064 065 /** 066 * Parses the music notation from ABC files (without the header). Note number 067 * -1 signifies a rest. 068 * 069 * @param abc The abc note string 070 * @return An list of note numbers and lengths 071 */ 072 public static List<IndependentPair<Integer,Double>> 073 parseABCNotes( final String abc ) 074 { 075 final List<IndependentPair<Integer, Double>> ret = 076 new ArrayList<IndependentPair<Integer,Double>>(); 077 078 // Start with middle-C and alter from there 079 int nn = 60; 080 int length = 1; 081 IndependentPair<Integer,Double> lastNote = null; 082 083 for( int i = 0; i < abc.length(); i++ ) 084 { 085 char c = abc.charAt( i ); 086 087 switch( c ) 088 { 089 case '_': nn -= 1; break; 090 case '=': nn -= 0; break; 091 case '^': nn += 1; break; 092 case ',': nn -= 12; break; 093 case '\'': nn += 12; break; 094 case '/': 095 if( lastNote != null ) 096 lastNote.setSecondObject( lastNote.getSecondObject()/2d ); 097 break; 098 case '-': 099 case 'z': 100 nn = -1; break; 101 default: 102 break; 103 } 104 105 if( Character.isDigit( c ) && lastNote != null ) 106 lastNote.setSecondObject( lastNote.getSecondObject() * (c-48) ); 107 108 // If it's a note name... 109 if( Character.isLetter( c ) && "abcdefgABCDEFG".contains( ""+c ) ) 110 { 111 // If it's lower case it's the upper octave 112 if( Character.isLowerCase( c ) ) 113 nn += 12; 114 115 // Convert to upper case and get the note offset 116 c = Character.toUpperCase( c ); 117 nn += Arrays.asList( WesternScaleNote.noteNames ).indexOf( ""+c ); 118 119 // Add it to the list 120 ret.add( lastNote = new IndependentPair<Integer, Double>( nn, 121 (double)length ) ); 122 123 // Reset our variables for the next note 124 nn = 60; 125 length = 1; 126 } 127 else 128 if( nn == -1 ) 129 ret.add( new IndependentPair<Integer, Double>( -1, (double)length ) ); 130 } 131 132 return ret; 133 } 134}