1 /*
   2  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /* @test
  25  @summary Test RealTime-tunings using SoftReciver.send method */
  26 
  27 import java.io.IOException;
  28 
  29 import javax.sound.midi.*;
  30 import javax.sound.sampled.*;
  31 
  32 import com.sun.media.sound.*;
  33 
  34 public class RealTimeTuning {
  35 
  36     private static class PitchSpy {
  37         public float pitch = 0;
  38 
  39         public Soundbank getSoundBank() {
  40             ModelOscillator osc = new ModelOscillator() {
  41                 public float getAttenuation() {
  42                     return 0;
  43                 }
  44 
  45                 public int getChannels() {
  46                     return 0;
  47                 }
  48 
  49                 public ModelOscillatorStream open(float samplerate) {
  50                     return new ModelOscillatorStream() {
  51                         public void close() throws IOException {
  52                             pitch = 0;
  53                         }
  54 
  55                         public void noteOff(int velocity) {
  56                             pitch = 0;
  57                         }
  58 
  59                         public void noteOn(MidiChannel channel,
  60                                 VoiceStatus voice, int noteNumber, int velocity) {
  61                             pitch = noteNumber * 100;
  62                         }
  63 
  64                         public int read(float[][] buffer, int offset, int len)
  65                                 throws IOException {
  66                             return len;
  67                         }
  68 
  69                         public void setPitch(float ipitch) {
  70                             pitch = ipitch;
  71                         }
  72                     };
  73                 }
  74             };
  75             ModelPerformer performer = new ModelPerformer();
  76             performer.getOscillators().add(osc);
  77             SimpleInstrument testinstrument = new SimpleInstrument();
  78             testinstrument.setPatch(new Patch(0, 0));
  79             testinstrument.add(performer);
  80             SimpleSoundbank testsoundbank = new SimpleSoundbank();
  81             testsoundbank.addInstrument(testinstrument);
  82             return testsoundbank;
  83         }
  84     }
  85 
  86     public static void sendTuningChange(Receiver recv, int channel,
  87             int tuningpreset, int tuningbank) throws InvalidMidiDataException {
  88         // Data Entry
  89         ShortMessage sm1 = new ShortMessage();
  90         sm1.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x64, 04);
  91         ShortMessage sm2 = new ShortMessage();
  92         sm2.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x65, 00);
  93 
  94         // Tuning Bank
  95         ShortMessage sm3 = new ShortMessage();
  96         sm3.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x06, tuningbank);
  97         // Data Increment
  98         ShortMessage sm4 = new ShortMessage();
  99         sm4.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x60, 0x7F);
 100         // Data Decrement
 101         ShortMessage sm5 = new ShortMessage();
 102         sm5.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x61, 0x7F);
 103 
 104         // Data Entry
 105         ShortMessage sm6 = new ShortMessage();
 106         sm6.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x64, 03);
 107         ShortMessage sm7 = new ShortMessage();
 108         sm7.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x65, 00);
 109 
 110         // Tuning program
 111         ShortMessage sm8 = new ShortMessage();
 112         sm8
 113                 .setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x06,
 114                         tuningpreset);
 115         // Data Increment
 116         ShortMessage sm9 = new ShortMessage();
 117         sm9.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x60, 0x7F);
 118         // Data Decrement
 119         ShortMessage sm10 = new ShortMessage();
 120         sm10.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x61, 0x7F);
 121 
 122         recv.send(sm1, -1);
 123         recv.send(sm2, -1);
 124         recv.send(sm3, -1);
 125         recv.send(sm4, -1);
 126         recv.send(sm5, -1);
 127         recv.send(sm6, -1);
 128         recv.send(sm7, -1);
 129         recv.send(sm8, -1);
 130         recv.send(sm9, -1);
 131         recv.send(sm10, -1);
 132 
 133     }
 134 
 135     private static void assertTrue(boolean value) throws Exception {
 136         if (!value)
 137             throw new RuntimeException("assertTrue fails!");
 138     }
 139 
 140     public static void testTunings(int[] msg, int tuningProgram,
 141             int tuningBank, int targetNote, float targetPitch, boolean realtime)
 142             throws Exception {
 143         AudioSynthesizer synth = new SoftSynthesizer();
 144         AudioInputStream stream = synth.openStream(null, null);
 145         Receiver recv = synth.getReceiver();
 146         MidiChannel channel = synth.getChannels()[0];
 147         byte[] buff = new byte[2048];
 148 
 149         // Create test instrument which we can use to monitor pitch changes
 150         PitchSpy pitchspy = new PitchSpy();
 151 
 152         synth.unloadAllInstruments(synth.getDefaultSoundbank());
 153         synth.loadAllInstruments(pitchspy.getSoundBank());
 154 
 155         SysexMessage sysex = null;
 156 
 157         // Send tuning changes
 158         if (msg != null) {
 159             byte[] bmsg = new byte[msg.length];
 160             for (int i = 0; i < bmsg.length; i++)
 161                 bmsg[i] = (byte) msg[i];
 162             sysex = new SysexMessage();
 163             sysex.setMessage(bmsg, bmsg.length);
 164             if (targetPitch == 0) {
 165                 targetPitch = (float) new SoftTuning(bmsg)
 166                         .getTuning(targetNote);
 167                 // Check if targetPitch != targetNote * 100
 168                 assertTrue(Math.abs(targetPitch - targetNote * 100.0) > 0.001);
 169             }
 170         }
 171 
 172         if (tuningProgram != -1)
 173             sendTuningChange(recv, 0, tuningProgram, tuningBank);
 174 
 175         // First test without tunings
 176         channel.noteOn(targetNote, 64);
 177         stream.read(buff, 0, buff.length);
 178         assertTrue(Math.abs(pitchspy.pitch - (targetNote * 100.0)) < 0.001);
 179 
 180         // Test if realtime/non-realtime works
 181         if (sysex != null)
 182             recv.send(sysex, -1);
 183         stream.read(buff, 0, buff.length);
 184         if (realtime)
 185             assertTrue(Math.abs(pitchspy.pitch - targetPitch) < 0.001);
 186         else
 187             assertTrue(Math.abs(pitchspy.pitch - (targetNote * 100.0)) < 0.001);
 188 
 189         // Test if tunings works
 190         channel.noteOn(targetNote, 0);
 191         stream.read(buff, 0, buff.length);
 192         assertTrue(Math.abs(pitchspy.pitch - 0.0) < 0.001);
 193 
 194         channel.noteOn(targetNote, 64);
 195         stream.read(buff, 0, buff.length);
 196         assertTrue(Math.abs(pitchspy.pitch - targetPitch) < 0.001);
 197 
 198         channel.noteOn(targetNote, 0);
 199         stream.read(buff, 0, buff.length);
 200         assertTrue(Math.abs(pitchspy.pitch - 0.0) < 0.001);
 201 
 202         stream.close();
 203     }
 204 
 205     public static void main(String[] args) throws Exception {
 206         // Test with no-tunings
 207         testTunings(null, -1, -1, 60, 6000, false);
 208 
 209         int[] msg;
 210         // 0x02 SINGLE NOTE TUNING CHANGE (REAL-TIME)
 211         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x02, 0x10, 0x02, 36, 36, 64,
 212                 0, 60, 70, 0, 0, 0xf7 };
 213         testTunings(msg, 0x10, 0, 60, 7000, true);
 214 
 215         // 0x07 SINGLE NOTE TUNING CHANGE (NON REAL-TIME) (BANK)
 216         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x07, 0x05, 0x07, 0x02, 36,
 217                 36, 64, 0, 60, 80, 0, 0, 0xf7 };
 218         testTunings(msg, 0x07, 0x05, 60, 8000, false);
 219 
 220         // 0x07 SINGLE NOTE TUNING CHANGE (REAL-TIME) (BANK)
 221         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x07, 0x05, 0x07, 0x02, 36,
 222                 36, 64, 0, 60, 80, 0, 0, 0xf7 };
 223         testTunings(msg, 0x07, 0x05, 60, 8000, true);
 224 
 225         // 0x08 scale/octave tuning 1-byte form (Non Real-Time)
 226         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x08, 0x03, 0x7f, 0x7f, 5,
 227                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 0xf7 };
 228         testTunings(msg, -1, -1, 60, 0, false);
 229 
 230         // 0x08 scale/octave tuning 1-byte form (REAL-TIME)
 231         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x08, 0x03, 0x7f, 0x7f, 5,
 232                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 0xf7 };
 233         testTunings(msg, -1, -1, 60, 0, true);
 234 
 235         // 0x09 scale/octave tuning 2-byte form (Non Real-Time)
 236         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x09, 0x03, 0x7f, 0x7f, 5,
 237                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 5, 10, 15, 20, 25,
 238                 30, 35, 40, 45, 50, 51, 52, 0xf7 };
 239         testTunings(msg, -1, -1, 60, 0, false);
 240 
 241         // 0x09 scale/octave tuning 2-byte form (REAL-TIME)
 242         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x09, 0x03, 0x7f, 0x7f, 5,
 243                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 5, 10, 15, 20, 25,
 244                 30, 35, 40, 45, 50, 51, 52, 0xf7 };
 245         testTunings(msg, -1, -1, 60, 0, true);
 246 
 247     }
 248 }