1 /*
   2  * Copyright (c) 2010, 2015, 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  @modules java.desktop/com.sun.media.sound
  27 */
  28 
  29 import java.io.IOException;
  30 
  31 import javax.sound.midi.*;
  32 import javax.sound.sampled.*;
  33 
  34 import com.sun.media.sound.*;
  35 
  36 public class RealTimeTuning {
  37 
  38     private static class PitchSpy {
  39         public float pitch = 0;
  40 
  41         public Soundbank getSoundBank() {
  42             ModelOscillator osc = new ModelOscillator() {
  43                 public float getAttenuation() {
  44                     return 0;
  45                 }
  46 
  47                 public int getChannels() {
  48                     return 0;
  49                 }
  50 
  51                 public ModelOscillatorStream open(float samplerate) {
  52                     return new ModelOscillatorStream() {
  53                         public void close() throws IOException {
  54                             pitch = 0;
  55                         }
  56 
  57                         public void noteOff(int velocity) {
  58                             pitch = 0;
  59                         }
  60 
  61                         public void noteOn(MidiChannel channel,
  62                                 VoiceStatus voice, int noteNumber, int velocity) {
  63                             pitch = noteNumber * 100;
  64                         }
  65 
  66                         public int read(float[][] buffer, int offset, int len)
  67                                 throws IOException {
  68                             return len;
  69                         }
  70 
  71                         public void setPitch(float ipitch) {
  72                             pitch = ipitch;
  73                         }
  74                     };
  75                 }
  76             };
  77             ModelPerformer performer = new ModelPerformer();
  78             performer.getOscillators().add(osc);
  79             SimpleInstrument testinstrument = new SimpleInstrument();
  80             testinstrument.setPatch(new Patch(0, 0));
  81             testinstrument.add(performer);
  82             SimpleSoundbank testsoundbank = new SimpleSoundbank();
  83             testsoundbank.addInstrument(testinstrument);
  84             return testsoundbank;
  85         }
  86     }
  87 
  88     public static void sendTuningChange(Receiver recv, int channel,
  89             int tuningpreset, int tuningbank) throws InvalidMidiDataException {
  90         // Data Entry
  91         ShortMessage sm1 = new ShortMessage();
  92         sm1.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x64, 04);
  93         ShortMessage sm2 = new ShortMessage();
  94         sm2.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x65, 00);
  95 
  96         // Tuning Bank
  97         ShortMessage sm3 = new ShortMessage();
  98         sm3.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x06, tuningbank);
  99         // Data Increment
 100         ShortMessage sm4 = new ShortMessage();
 101         sm4.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x60, 0x7F);
 102         // Data Decrement
 103         ShortMessage sm5 = new ShortMessage();
 104         sm5.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x61, 0x7F);
 105 
 106         // Data Entry
 107         ShortMessage sm6 = new ShortMessage();
 108         sm6.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x64, 03);
 109         ShortMessage sm7 = new ShortMessage();
 110         sm7.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x65, 00);
 111 
 112         // Tuning program
 113         ShortMessage sm8 = new ShortMessage();
 114         sm8
 115                 .setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x06,
 116                         tuningpreset);
 117         // Data Increment
 118         ShortMessage sm9 = new ShortMessage();
 119         sm9.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x60, 0x7F);
 120         // Data Decrement
 121         ShortMessage sm10 = new ShortMessage();
 122         sm10.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x61, 0x7F);
 123 
 124         recv.send(sm1, -1);
 125         recv.send(sm2, -1);
 126         recv.send(sm3, -1);
 127         recv.send(sm4, -1);
 128         recv.send(sm5, -1);
 129         recv.send(sm6, -1);
 130         recv.send(sm7, -1);
 131         recv.send(sm8, -1);
 132         recv.send(sm9, -1);
 133         recv.send(sm10, -1);
 134 
 135     }
 136 
 137     private static void assertTrue(boolean value) throws Exception {
 138         if (!value)
 139             throw new RuntimeException("assertTrue fails!");
 140     }
 141 
 142     public static void testTunings(int[] msg, int tuningProgram,
 143             int tuningBank, int targetNote, float targetPitch, boolean realtime)
 144             throws Exception {
 145         AudioSynthesizer synth = new SoftSynthesizer();
 146         AudioInputStream stream = synth.openStream(null, null);
 147         Receiver recv = synth.getReceiver();
 148         MidiChannel channel = synth.getChannels()[0];
 149         byte[] buff = new byte[2048];
 150 
 151         // Create test instrument which we can use to monitor pitch changes
 152         PitchSpy pitchspy = new PitchSpy();
 153 
 154         synth.unloadAllInstruments(synth.getDefaultSoundbank());
 155         synth.loadAllInstruments(pitchspy.getSoundBank());
 156 
 157         SysexMessage sysex = null;
 158 
 159         // Send tuning changes
 160         if (msg != null) {
 161             byte[] bmsg = new byte[msg.length];
 162             for (int i = 0; i < bmsg.length; i++)
 163                 bmsg[i] = (byte) msg[i];
 164             sysex = new SysexMessage();
 165             sysex.setMessage(bmsg, bmsg.length);
 166             if (targetPitch == 0) {
 167                 targetPitch = (float) new SoftTuning(bmsg)
 168                         .getTuning(targetNote);
 169                 // Check if targetPitch != targetNote * 100
 170                 assertTrue(Math.abs(targetPitch - targetNote * 100.0) > 0.001);
 171             }
 172         }
 173 
 174         if (tuningProgram != -1)
 175             sendTuningChange(recv, 0, tuningProgram, tuningBank);
 176 
 177         // First test without tunings
 178         channel.noteOn(targetNote, 64);
 179         stream.read(buff, 0, buff.length);
 180         assertTrue(Math.abs(pitchspy.pitch - (targetNote * 100.0)) < 0.001);
 181 
 182         // Test if realtime/non-realtime works
 183         if (sysex != null)
 184             recv.send(sysex, -1);
 185         stream.read(buff, 0, buff.length);
 186         if (realtime)
 187             assertTrue(Math.abs(pitchspy.pitch - targetPitch) < 0.001);
 188         else
 189             assertTrue(Math.abs(pitchspy.pitch - (targetNote * 100.0)) < 0.001);
 190 
 191         // Test if tunings works
 192         channel.noteOn(targetNote, 0);
 193         stream.read(buff, 0, buff.length);
 194         assertTrue(Math.abs(pitchspy.pitch - 0.0) < 0.001);
 195 
 196         channel.noteOn(targetNote, 64);
 197         stream.read(buff, 0, buff.length);
 198         assertTrue(Math.abs(pitchspy.pitch - targetPitch) < 0.001);
 199 
 200         channel.noteOn(targetNote, 0);
 201         stream.read(buff, 0, buff.length);
 202         assertTrue(Math.abs(pitchspy.pitch - 0.0) < 0.001);
 203 
 204         stream.close();
 205     }
 206 
 207     public static void main(String[] args) throws Exception {
 208         // Test with no-tunings
 209         testTunings(null, -1, -1, 60, 6000, false);
 210 
 211         int[] msg;
 212         // 0x02 SINGLE NOTE TUNING CHANGE (REAL-TIME)
 213         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x02, 0x10, 0x02, 36, 36, 64,
 214                 0, 60, 70, 0, 0, 0xf7 };
 215         testTunings(msg, 0x10, 0, 60, 7000, true);
 216 
 217         // 0x07 SINGLE NOTE TUNING CHANGE (NON REAL-TIME) (BANK)
 218         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x07, 0x05, 0x07, 0x02, 36,
 219                 36, 64, 0, 60, 80, 0, 0, 0xf7 };
 220         testTunings(msg, 0x07, 0x05, 60, 8000, false);
 221 
 222         // 0x07 SINGLE NOTE TUNING CHANGE (REAL-TIME) (BANK)
 223         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x07, 0x05, 0x07, 0x02, 36,
 224                 36, 64, 0, 60, 80, 0, 0, 0xf7 };
 225         testTunings(msg, 0x07, 0x05, 60, 8000, true);
 226 
 227         // 0x08 scale/octave tuning 1-byte form (Non Real-Time)
 228         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x08, 0x03, 0x7f, 0x7f, 5,
 229                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 0xf7 };
 230         testTunings(msg, -1, -1, 60, 0, false);
 231 
 232         // 0x08 scale/octave tuning 1-byte form (REAL-TIME)
 233         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x08, 0x03, 0x7f, 0x7f, 5,
 234                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 0xf7 };
 235         testTunings(msg, -1, -1, 60, 0, true);
 236 
 237         // 0x09 scale/octave tuning 2-byte form (Non Real-Time)
 238         msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x09, 0x03, 0x7f, 0x7f, 5,
 239                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 5, 10, 15, 20, 25,
 240                 30, 35, 40, 45, 50, 51, 52, 0xf7 };
 241         testTunings(msg, -1, -1, 60, 0, false);
 242 
 243         // 0x09 scale/octave tuning 2-byte form (REAL-TIME)
 244         msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x09, 0x03, 0x7f, 0x7f, 5,
 245                 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 5, 10, 15, 20, 25,
 246                 30, 35, 40, 45, 50, 51, 52, 0xf7 };
 247         testTunings(msg, -1, -1, 60, 0, true);
 248 
 249     }
 250 }