changeset 2714:3fa6114faa54

6943053: Gervill: failures on invalid ranges and 14-bit banks Summary: ModelStandardIndexedDirector fails on invalid ranges. Program changes with 14-bit banks where handled incorectly as 7-bit banks. Reviewed-by: amenkov
author kalli
date Mon, 13 Sep 2010 15:34:24 +0400
parents 903f44341e34
children c610f475558d
files src/share/classes/com/sun/media/sound/ModelStandardIndexedDirector.java src/share/classes/com/sun/media/sound/SoftChannel.java test/javax/sound/midi/Gervill/ModelStandardIndexedDirector/ModelStandardIndexedDirectorTest.java test/javax/sound/midi/Gervill/SoftChannel/ProgramAndBankChange.java
diffstat 4 files changed, 382 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/media/sound/ModelStandardIndexedDirector.java	Mon Sep 13 15:12:31 2010 +0400
+++ b/src/share/classes/com/sun/media/sound/ModelStandardIndexedDirector.java	Mon Sep 13 15:34:24 2010 +0400
@@ -57,8 +57,7 @@
         buildindex();
     }
 
-    private int[] lookupIndex(int x, int y)
-    {
+    private int[] lookupIndex(int x, int y) {
         if ((x >= 0) && (x < 128) && (y >= 0) && (y < 128)) {
             int xt = trantables[0][x];
             int yt = trantables[1][y];
@@ -69,14 +68,30 @@
         return null;
     }
 
+    private int restrict(int value) {
+        if(value < 0) return 0;
+        if(value > 127) return 127;
+        return value;
+    }
+
     private void buildindex() {
         trantables = new byte[2][129];
         counters = new int[trantables.length];
         for (ModelPerformer performer : performers) {
-            trantables[0][performer.getKeyFrom()] = 1;
-            trantables[0][performer.getKeyTo() + 1] = 1;
-            trantables[1][performer.getVelFrom()] = 1;
-            trantables[1][performer.getVelTo() + 1] = 1;
+            int keyFrom = performer.getKeyFrom();
+            int keyTo = performer.getKeyTo();
+            int velFrom = performer.getVelFrom();
+            int velTo = performer.getVelTo();
+            if (keyFrom > keyTo) continue;
+            if (velFrom > velTo) continue;
+            keyFrom = restrict(keyFrom);
+            keyTo = restrict(keyTo);
+            velFrom = restrict(velFrom);
+            velTo = restrict(velTo);
+            trantables[0][keyFrom] = 1;
+            trantables[0][keyTo + 1] = 1;
+            trantables[1][velFrom] = 1;
+            trantables[1][velTo + 1] = 1;
         }
         for (int d = 0; d < trantables.length; d++) {
             byte[] trantable = trantables[d];
@@ -102,10 +117,20 @@
         mat = new int[counters[0] * counters[1]][];
         int ix = 0;
         for (ModelPerformer performer : performers) {
-            int x_from = trantables[0][performer.getKeyFrom()];
-            int x_to = trantables[0][performer.getKeyTo() + 1];
-            int y_from = trantables[1][performer.getVelFrom()];
-            int y_to = trantables[1][performer.getVelTo() + 1];
+            int keyFrom = performer.getKeyFrom();
+            int keyTo = performer.getKeyTo();
+            int velFrom = performer.getVelFrom();
+            int velTo = performer.getVelTo();
+            if (keyFrom > keyTo) continue;
+            if (velFrom > velTo) continue;
+            keyFrom = restrict(keyFrom);
+            keyTo = restrict(keyTo);
+            velFrom = restrict(velFrom);
+            velTo = restrict(velTo);
+            int x_from = trantables[0][keyFrom];
+            int x_to = trantables[0][keyTo + 1];
+            int y_from = trantables[1][velFrom];
+            int y_to = trantables[1][velTo + 1];
             if (x_to == -1)
                 x_to = counters[0];
             if (y_to == -1)
--- a/src/share/classes/com/sun/media/sound/SoftChannel.java	Mon Sep 13 15:12:31 2010 +0400
+++ b/src/share/classes/com/sun/media/sound/SoftChannel.java	Mon Sep 13 15:34:24 2010 +0400
@@ -1264,7 +1264,7 @@
     }
 
     public void programChange(int bank, int program) {
-        bank = restrict7Bit(bank);
+        bank = restrict14Bit(bank);
         program = restrict7Bit(program);
         synchronized (control_mutex) {
             mainmixer.activity();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/sound/midi/Gervill/ModelStandardIndexedDirector/ModelStandardIndexedDirectorTest.java	Mon Sep 13 15:34:24 2010 +0400
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+   @summary Test ModelStandardIndexedDirector class */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeSet;
+
+import com.sun.media.sound.ModelConnectionBlock;
+import com.sun.media.sound.ModelDirectedPlayer;
+import com.sun.media.sound.ModelPerformer;
+import com.sun.media.sound.ModelStandardDirector;
+import com.sun.media.sound.ModelStandardIndexedDirector;
+
+public class ModelStandardIndexedDirectorTest {
+
+    private static String treeToString(TreeSet<Integer> set)
+    {
+        StringBuffer buff = new StringBuffer();
+        boolean first = true;
+        for(Integer s : set)
+        {
+            if(!first)
+                buff.append(";");
+            buff.append(s);
+            first = false;
+        }
+        return buff.toString();
+    }
+
+    private static void testDirector(ModelPerformer[] performers) throws Exception
+    {
+        final TreeSet<Integer> played = new TreeSet<Integer>();
+        ModelDirectedPlayer player = new ModelDirectedPlayer()
+        {
+            public void play(int performerIndex,
+                    ModelConnectionBlock[] connectionBlocks) {
+                played.add(performerIndex);
+            }
+        };
+        ModelStandardIndexedDirector idirector =
+            new ModelStandardIndexedDirector(performers, player);
+        ModelStandardDirector director =
+            new ModelStandardDirector(performers, player);
+
+        for (int n = 0; n < 128; n++)
+        {
+            for (int v = 0; v < 128; v++)
+            {
+                director.noteOn(n, v);
+                String p1 = treeToString(played);
+                played.clear();
+                idirector.noteOn(n, v);
+                String p2 = treeToString(played);
+                played.clear();
+                if(!p1.equals(p2))
+                    throw new Exception(
+                            "Note = " + n + ", Vel = " + v + " failed");
+            }
+        }
+    }
+
+    private static void testDirectorCombinations(
+            ModelPerformer[] performers) throws Exception
+    {
+        for (int i = 0; i < performers.length; i++) {
+            ModelPerformer[] performers2 = new ModelPerformer[i];
+            for (int j = 0; j < performers2.length; j++) {
+                performers2[j] = performers[i];
+            }
+            testDirector(performers2);
+        }
+    }
+
+    private static void addPerformer(
+            List<ModelPerformer> performers,
+            int keyfrom,
+            int keyto,
+            int velfrom,
+            int velto)
+    {
+        ModelPerformer performer = new ModelPerformer();
+        performer.setKeyFrom(keyfrom);
+        performer.setKeyTo(keyto);
+        performer.setVelFrom(velfrom);
+        performer.setVelTo(velto);
+        performers.add(performer);
+    }
+
+    public static void main(String[] args) throws Exception
+    {
+        // Test collection of normal values
+        List<ModelPerformer> performers = new ArrayList<ModelPerformer>();
+        addPerformer(performers, 0, 0, 0, 127);
+        addPerformer(performers, 0, 50, 0, 127);
+        addPerformer(performers, 0, 127, 0, 127);
+        addPerformer(performers, 21, 21, 0, 127);
+        addPerformer(performers, 21, 60, 0, 127);
+        addPerformer(performers, 21, 127, 0, 127);
+        addPerformer(performers, 50, 50, 0, 127);
+        addPerformer(performers, 50, 60, 0, 127);
+        addPerformer(performers, 50, 127, 0, 127);
+        addPerformer(performers, 73, 73, 0, 127);
+        addPerformer(performers, 73, 80, 0, 127);
+        addPerformer(performers, 73, 127, 0, 127);
+        addPerformer(performers, 127, 127, 0, 127);
+        addPerformer(performers, 0, 0, 60, 127);
+        addPerformer(performers, 0, 50, 60, 127);
+        addPerformer(performers, 0, 127, 60, 127);
+        addPerformer(performers, 21, 21, 60, 127);
+        addPerformer(performers, 21, 60, 60, 127);
+        addPerformer(performers, 21, 127, 60, 127);
+        addPerformer(performers, 50, 50, 60, 127);
+        addPerformer(performers, 50, 60, 60, 127);
+        addPerformer(performers, 50, 127, 60, 127);
+        addPerformer(performers, 73, 73, 60, 127);
+        addPerformer(performers, 73, 80, 60, 127);
+        addPerformer(performers, 73, 127, 60, 127);
+        addPerformer(performers, 127, 127, 60, 127);
+        addPerformer(performers, 0, 0, 80, 83);
+        addPerformer(performers, 0, 50, 80, 83);
+        addPerformer(performers, 0, 127, 80, 83);
+        addPerformer(performers, 21, 21, 80, 83);
+        addPerformer(performers, 21, 60, 80, 83);
+        addPerformer(performers, 21, 127, 80, 83);
+        addPerformer(performers, 50, 50, 80, 83);
+        addPerformer(performers, 50, 60, 80, 83);
+        addPerformer(performers, 50, 127, 80, 83);
+        addPerformer(performers, 73, 73, 80, 83);
+        addPerformer(performers, 73, 80, 80, 83);
+        addPerformer(performers, 73, 127, 80, 83);
+        addPerformer(performers, 127, 127, 80, 83);
+
+
+        testDirectorCombinations(
+                performers.toArray(
+                        new ModelPerformer[performers.size()])
+                );
+
+        // Test reversed values
+        performers.clear();
+        addPerformer(performers, 50, 30, 80, 83);
+        addPerformer(performers, 30, 30, 50, 30);
+        addPerformer(performers, 37, 30, 50, 30);
+        testDirector(
+                performers.toArray(
+                        new ModelPerformer[performers.size()])
+                );
+
+        // Test out-of-range values
+        performers.clear();
+        addPerformer(performers, -20, 6, 0, 127);
+        addPerformer(performers, 0, 300, 0, 300);
+        addPerformer(performers, -2, -8, -5, -9);
+
+        testDirector(
+                performers.toArray(
+                        new ModelPerformer[performers.size()])
+                );
+
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/sound/midi/Gervill/SoftChannel/ProgramAndBankChange.java	Mon Sep 13 15:34:24 2010 +0400
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+   @summary Test SoftChannel program and bank change */
+
+import java.io.IOException;
+
+import javax.sound.midi.*;
+import javax.sound.sampled.*;
+
+import com.sun.media.sound.*;
+
+public class ProgramAndBankChange {
+
+    private static SimpleInstrument generateTestInstrument(Patch patch) {
+        ModelOscillator osc = new ModelOscillator() {
+            public float getAttenuation() {
+                return 0;
+            }
+
+            public int getChannels() {
+                return 1;
+            }
+
+            public ModelOscillatorStream open(float samplerate) {
+                return new ModelOscillatorStream() {
+                    public void close() throws IOException {
+                    }
+
+                    public void noteOff(int velocity) {
+                    }
+
+                    public void noteOn(MidiChannel channel, VoiceStatus voice,
+                            int noteNumber, int velocity) {
+                    }
+
+                    public int read(float[][] buffer, int offset, int len)
+                            throws IOException {
+                        return len;
+                    }
+
+                    public void setPitch(float ipitch) {
+                    }
+                };
+            }
+        };
+        ModelPerformer performer = new ModelPerformer();
+        performer.getOscillators().add(osc);
+        SimpleInstrument testinstrument = new SimpleInstrument();
+        testinstrument.setPatch(patch);
+        testinstrument.add(performer);
+        return testinstrument;
+    }
+
+    private static void assertTrue(boolean value) throws Exception {
+        if (!value)
+            throw new RuntimeException("assertTrue fails!");
+    }
+
+    private static void testProgramAndBank(SoftSynthesizer soft,
+            AudioInputStream stream, Patch patch) throws Exception {
+
+        int program = patch.getProgram();
+        int bank = patch.getBank();
+
+        MidiChannel channel = soft.getChannels()[0];
+        byte[] buff = new byte[2048];
+
+        channel.programChange(bank, program);
+        channel.noteOn(64, 64);
+        stream.read(buff, 0, buff.length);
+
+        int foundprogram = -1;
+        int foundbank = -1;
+        VoiceStatus[] vstatus = soft.getVoiceStatus();
+        for (int i = 0; i < vstatus.length; i++) {
+            if (vstatus[i].active) {
+                foundprogram = vstatus[i].program;
+                foundbank = vstatus[i].bank;
+                break;
+            }
+        }
+
+        assertTrue(foundprogram == program);
+        assertTrue(foundbank == bank);
+
+        channel.noteOn(64, 0);
+        stream.read(buff, 0, buff.length);
+
+        channel = soft.getChannels()[1];
+        // Send MSB Bank
+        channel.controlChange(0x00, bank / 128);
+        // Send LSB Bank
+        channel.controlChange(0x20, bank % 128);
+        // Send Program Change
+        channel.programChange(program);
+        channel.noteOn(64, 64);
+        stream.read(buff, 0, buff.length);
+
+        foundprogram = -1;
+        foundbank = -1;
+        vstatus = soft.getVoiceStatus();
+        for (int i = 0; i < vstatus.length; i++) {
+            if (vstatus[i].active) {
+                foundprogram = vstatus[i].program;
+                foundbank = vstatus[i].bank;
+                break;
+            }
+        }
+        assertTrue(foundprogram == program);
+        assertTrue(foundbank == bank);
+        channel.noteOn(64, 0);
+        stream.read(buff, 0, buff.length);
+    }
+
+    public static void main(String[] args) throws Exception {
+        SoftSynthesizer soft = new SoftSynthesizer();
+        AudioInputStream stream = soft.openStream(null, null);
+        soft.unloadAllInstruments(soft.getDefaultSoundbank());
+
+        soft.loadInstrument(generateTestInstrument(new Patch(0, 0)));
+        soft.loadInstrument(generateTestInstrument(new Patch(7, 0)));
+        soft.loadInstrument(generateTestInstrument(new Patch(20, 10)));
+        soft.loadInstrument(generateTestInstrument(new Patch(3678, 15)));
+        soft.loadInstrument(generateTestInstrument(new Patch(4678, 15)));
+
+        testProgramAndBank(soft, stream, new Patch(0, 0));
+        testProgramAndBank(soft, stream, new Patch(7, 0));
+        testProgramAndBank(soft, stream, new Patch(20, 10));
+        testProgramAndBank(soft, stream, new Patch(3678, 15));
+        testProgramAndBank(soft, stream, new Patch(4678, 15));
+
+        soft.close();
+    }
+}