source: trunk/src/org/apollo/meldex/McNabMongeauSankoffAlgorithm.java

Last change on this file was 315, checked in by bjn8, 16 years ago

Apollo spin-off added

  • Property svn:executable set to *
File size: 7.8 KB
Line 
1package org.apollo.meldex;
2
3/**
4 * An implementation of an approximate string matching algorithm described by Marcel Mongeau
5 * and David Sankoff ("Comparison of Musical Sequences", Computers and the Humanities, Vol 24
6 * (1990), p161 - 175) and modified by Rodger McNab for music matching (REF).
7 * <p>To do: Test fragmentation and consolidation.
8 */
9public class McNabMongeauSankoffAlgorithm extends DynamicProgrammingAlgorithm
10{
11 private static final boolean doFragmentationConsolidation = false;
12
13 /** Constant values used by this algorithm */
14 private final double K1_FACTOR = 112.0;
15
16
17 /**
18 * Creates a new McNabMongeauSankoffAlgorithm and initialises it for use.
19 */
20 public McNabMongeauSankoffAlgorithm()
21 {
22 }
23
24
25 /**
26 * Returns whether this algorithm has special functionality that is enabled.
27 *
28 * @return whether this algorithm has special functionality that is enabled.
29 */
30 protected boolean hasSpecialFunctionality()
31 {
32 return doFragmentationConsolidation;
33 }
34
35
36 /**
37 * Lets the algorithm perform its special functionality (fragmentation and consolidation).
38 *
39 * @param D The distance matrix at the current point in time.
40 * @param i The current x position in the distance matrix.
41 * @param j The current y position in the distance matrix.
42 * @param T The melody that is being matched to the query pattern.
43 * @param queryEvent The melody event at position j-1 in the query pattern.
44 * @param matchEvent The melody event at position i-1 in the match pattern.
45 */
46 protected void doAlgorithmSpecifics(int[][] D, int i, int j, Melody P, Melody T,
47 MelodyEvent queryEvent, MelodyEvent matchEvent)
48 {
49 if (doFragmentationConsolidation) {
50 int pitchType = T.getPitchType();
51
52 // Do fragmentations (one query event matches many match events)
53 int fragPitch = matchEvent.getPitch();
54 double queryDuration = queryEvent.getDuration();
55 double totalDuration = matchEvent.getDuration();
56
57 // Subsequent pitches must be the same
58 int requiredPitch = 0;
59 if (pitchType == Melody.ABSOLUTE_PITCH)
60 requiredPitch = fragPitch;
61
62 // Sum the fragmented weights of the previous k match events
63 int k = 2;
64 while ((k <= i) && (fragPitch == requiredPitch)) {
65 // Total fragmentation length should not exceed query event length
66 if (totalDuration > queryDuration)
67 break;
68
69 // All fragments must have the same pitch
70 MelodyEvent fragEvent = T.getEvent(i-k);
71 fragPitch = fragEvent.getPitch();
72 if (pitchType == Melody.ABSOLUTE_PITCH && fragPitch != requiredPitch)
73 break;
74
75 // Calculate the new pitch and duration cost for this fragmentation
76 double wPitch = weightPitch(queryEvent.getPitch(), fragPitch);
77 totalDuration += fragEvent.getDuration();
78 double wDuration = weightDuration(queryDuration, totalDuration);
79 double shortestDuration = (queryDuration < totalDuration ? queryDuration : totalDuration);
80
81 // Calculate the total cost for this fragmentation
82 int fragWeight = (int) ((wPitch * shortestDuration) + (K1_FACTOR * wDuration));
83
84 if (debugOutput) {
85 System.out.println("Frag: k = " + k + "\tDi-k,j-1 = " + D[i-k][j-1] + "\tPitch weight: " + wPitch + "\tDuration weight: " + wDuration + "\tTotal weight: " + fragWeight);
86 }
87
88 // Case 4: Many events are equivalent to one (fragmentation)
89 D[i][j] = Math.min(D[i][j], (D[i-k][j-1] + fragWeight));
90 k++;
91 }
92
93 // Do consolidations (one match event matches many query events)
94 int consPitch = queryEvent.getPitch();
95 double matchDuration = matchEvent.getDuration();
96 totalDuration = queryEvent.getDuration();
97
98 // Subsequent pitches must be the same
99 requiredPitch = 0;
100 if (pitchType == Melody.ABSOLUTE_PITCH)
101 requiredPitch = consPitch;
102
103 // Sum the fragmented weights of the previous k query events
104 k = 2;
105 while ((k <= j) && (consPitch == requiredPitch)) {
106 // Total consolidation length should not exceed match event length
107 if (totalDuration > matchDuration)
108 break;
109
110 // All fragments must have the same pitch
111 MelodyEvent consEvent = P.getEvent(j-k);
112 consPitch = consEvent.getPitch();
113 if (pitchType == Melody.ABSOLUTE_PITCH && consPitch != requiredPitch)
114 break;
115
116 // Calculate the new pitch and duration cost for this consolidation
117 double wPitch = weightPitch(matchEvent.getPitch(), consPitch);
118 totalDuration += consEvent.getDuration();
119 double wDuration = weightDuration(matchDuration, totalDuration);
120 double shortestDuration = (matchDuration < totalDuration ? matchDuration : totalDuration);
121
122 // Calculate the total cost for this consolidation
123 int consWeight = (int) ((wPitch * shortestDuration) + (K1_FACTOR * wDuration));
124
125 if (debugOutput) {
126 System.out.println("Cons: k = " + k + "\tDi-1,j-k = " + D[i-1][j-k] + "\tPitch weight: " + wPitch + "\tDuration weight: " + wDuration + "\tTotal weight: " + consWeight);
127 }
128
129 // Case 5: Many events are equivalent to one (consolidation)
130 D[i][j] = Math.min(D[i][j], (D[i-1][j-k] + consWeight));
131 k++;
132 }
133 }
134 }
135
136
137 /**
138 * Returns a distance measure signifying the distance between the query and match events.
139 *
140 * @param queryEvent The event to compare from the query pattern.
141 * @param matchEvent The event to compare from the match pattern.
142 *
143 * @return a distance measure signifying the distance between the query and match events.
144 */
145 protected int weightFunction(MelodyEvent queryEvent, MelodyEvent matchEvent)
146 {
147 // Calculate the pitch weighting
148 int queryPitch = ((queryEvent == null) ? 0 : queryEvent.getPitch());
149 int matchPitch = ((matchEvent == null) ? 0 : matchEvent.getPitch());
150 double wPitch = weightPitch(queryPitch, matchPitch);
151
152 return (int)wPitch;
153
154 /*
155
156 // Calculate the duration weighting
157 double queryLength = ((queryEvent == null) ? 0.0 : queryEvent.getDuration());
158 double matchLength = ((matchEvent == null) ? 0.0 : matchEvent.getDuration());
159 double wDuration = weightDuration(queryLength, matchLength);
160 double shortestLength = (queryLength < matchLength ? queryLength : matchLength);
161
162 // Calculate the final weighting (linear combination of pitch and duration)
163 return (int) ((wPitch * shortestLength) + (K1_FACTOR * wDuration));
164 */
165
166 }
167
168
169 /**
170 * Returns a measure signifying the distance between the query and match pitches.
171 *
172 * @param queryPitch The pitch of the query event.
173 * @param matchPitch The pitch of the match event.
174 *
175 * @return a measure signifying the distance between the query and match pitches.
176 */
177 private double weightPitch(int queryPitch, int matchPitch)
178 {
179 // If either of the events is a wild then the pitch weight is 0
180 if ((queryPitch == MelodyEvent.WILD_EVENT) || (matchPitch == MelodyEvent.WILD_EVENT))
181 return 0.0;
182
183 // If one or other of the events is a rest, use a special pitch factor of 80.0
184 if ((queryPitch == MelodyEvent.REST_EVENT) ^ (matchPitch == MelodyEvent.REST_EVENT))
185 return 80.0;
186
187 // Otherwise calculate a pitch factor based on the pitch difference
188 int pitchDifference = Math.abs((queryPitch - matchPitch));
189
190 // Special case absolute pitch
191 /* if (P.getPitchType() == Melody.ABSOLUTE_PITCH) {
192 pitchDifference = pitchDifference % 12;
193 if (pitchDifference > 6)
194 pitchDifference = 12 - pitchDifference;
195 } */
196
197 return (pitchDifference * 32.0);
198 }
199
200
201 /**
202 * Returns a measure signifying the distance between the query and match durations.
203 *
204 * @param queryLength The duration of the query event.
205 * @param matchLength The duration of the match event.
206 *
207 * @return a measure signifying the distance between the query and match durations.
208 */
209 private double weightDuration(double queryLength, double matchLength)
210 {
211 // The duration weight is directly proportional to the distance between the two lengths
212 return Math.abs((queryLength - matchLength));
213 }
214}
Note: See TracBrowser for help on using the repository browser.