JDK-5083572 : Re-editing of ShortMessage Data2 fails and Data1 is substituted!
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.sound
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2004-08-06
  • Updated: 2004-08-09
  • Resolved: 2004-08-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 rcFixed
Description

Name: rmT116609			Date: 08/05/2004


FULL PRODUCT VERSION :
java version "1.4.2_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.
Java HotSpot(TM) Client VM (build 1.4.2_01-b06, mixed mode)

java version "1.4.2_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b05)
Java HotSpot(TM) Client VM (build 1.4.2_04-b05, mixed mode)

FULL OS VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
When you read in Midi Data from a file using MidiSystem.

And then go through the events.  If there is a Channel Volume Change
message (or probably most Short Messages with a data2)
and edit the value in data2 than the value in data2 is replaced
with the value in data1.

  In the case of channel volume it makes the volume close to
inaudible.

I tried both ways to do this.  There are two methods in ShortMessage called
setMessage() and both replace the value of data2 with the value of data1
(but the other quantities command, channel and data1 go in properly).

  As a result 2 byte messages like a program change work fine!

   If you create a Sequence from scratch, you can use setMessage() as many times
as you like and it works fine.  

   My guess is that the internal byte length stored in ShortMessage is getting messed up somehow.  The code in ShortMessage looks buggy.  Instead of using an int to store length, you can just use the length of the byte array.  But of course the downside to that is that then you need to change the size of the byte array when you change the type of ShortMessage which is cumbersome.  I have my own fix, when I do similar stuff but its long to explain.  

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.  Read in a Midi File from any source and get its bytes
2.  Pass the bytes to the MidiSystem via an input stream to get the Sequence
3.  Go through the tracks and events in the sequence looking for Short Messages
that use data2  (I have just tested with Channel Volume).
4.  Try to max the Channel Volume to 127 out of 127.
5.  Print out before adn after results.
6.  Either play the midi file from java or write it to the disk and play it so
you can hear the results.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I expected the volume to change to 127, but it was changed to 7 instead!
(127 is not a magic number, all numbers 0-127 change to 7)
7 is the number that goes in data1 and means CHANNEL VOLUME (previously MASTER VOLUME in the old version of midi).

ShortMessage.setMessage(x,x,x,x)
and
ShortMessage.setMessage(x,x,x)
do not always put in data2 right with a sequence gotten from the MidiSystem
ACTUAL -
data2 is replaced with data1

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * Used to help debug javax.sound.midi
 * Input a midi file with Volume changes
 * Output a midi file with Volume Changes all at 7 (instead of max)
 */
import java.io.*;
import java.util.*;
import javax.sound.midi.*;
 
public class MidiProblem
{
  
  /**
   * A method that instead of maxing the volume of a file makes it almost inaudible in JDK 1.4.2 only
   *  Previous JDKS it maxes the volume
   *  (Provided that there are channel volume changes on all channels)
   * @param args[0]:The input midi file
   * @param args[1] The output midi file
   */
  public static void main(String args [])
  {
    try
    {
      int VOLUME_CONTROLLER = 7;
      
      //Get the Midi data from a file
      File midiFile = new File(args[0]);
      DataInputStream midiIn = new DataInputStream(new FileInputStream(midiFile));
      byte[] midiData = new byte[(int)midiFile.length()];
      midiIn.read(midiData);
      midiIn.close();
      
      //Get the Midi Sequence
      Sequence seq = MidiSystem.getSequence(new ByteArrayInputStream(midiData));
      
      //Go through looking for Channel Volume Messages and try to max them
      
      Track tracks[] = seq.getTracks();

      for(int trkNo=0; trkNo < tracks.length;trkNo++){
        for(int i=0; i<tracks[trkNo].size(); i++){
          MidiEvent midievent=tracks[trkNo].get(i);
          if(midievent.getMessage() instanceof ShortMessage){
            ShortMessage shortChunk = (ShortMessage)midievent.getMessage();

            int channelNo = shortChunk.getChannel();

            if((midievent.getMessage().getStatus()==ShortMessage.CONTROL_CHANGE+channelNo)
               &&(shortChunk.getData1()==VOLUME_CONTROLLER))
            {
              System.err.println("BEFORE:"+shortChunk.getCommand()+" "+shortChunk.getChannel()+" "+
                                 shortChunk.getData1()+" "+shortChunk.getData2());
                //HACK to fix BUG where data2 is set to data1 in jdk 1.4.2
                //shortChunk = new ShortMessage();
                //End ugly HACK
              shortChunk.setMessage(ShortMessage.CONTROL_CHANGE + channelNo,VOLUME_CONTROLLER,127);
              //Alternate method that also fails
              //shortChunk.setMessage(ShortMessage.CONTROL_CHANGE,channelNo,VOLUME_CONTROLLER,127);
              
              System.err.println("AFTER:"+shortChunk.getCommand()+" "+shortChunk.getChannel()+" "+
                                 shortChunk.getData1()+" "+shortChunk.getData2());
            }
          }
        }
      }
      
      //Write the file out to disk to confirm
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      javax.sound.midi.MidiSystem.write(seq, 1, baos);
      File out = new File(args[1]);
      FileWriter fwed = new FileWriter(out);
      FileOutputStream foutstream = new FileOutputStream(out);
      foutstream.write(baos.toByteArray());
      foutstream.close();
      fwed.close();

    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
When you read in a ShortMessage from the midi system
and wish to change it (if it has a data2) then one must
read the data out.

Create a new short message.

Then put the data in.

So you just need to uncomment the hack I put in the test case
and you can avoid this problem.


(Incident Review ID: 200982) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-rc FIXED IN: tiger-rc INTEGRATED IN: tiger-rc
10-08-2004

EVALUATION ###@###.### 2004-08-06 This bug is reported against JDK 1.4.2. Significant changes have been made to the midi architecture in 1.5. Please reconfirm bug on JDK 1.5 beta3 build 64. -- Problem has been duplicated (with 1.4.2), but does not appear in Tiger (1.5). ###@###.### 2004-08-09 --
09-08-2004