RFR: 8074211: javax.sound.midi: Error with send System Exclusive messages of different length [v3]
Phil Race
prr at openjdk.org
Mon Oct 30 21:32:31 UTC 2023
On Sun, 29 Oct 2023 14:29:45 GMT, Alec Su <duke at openjdk.org> wrote:
>> JVM attempts to reuse the buffer for sending MIDI out data when the buffer size is enough. It use `dwBytesRecorded` in `MIDIHDR` structure to indicate the actual size of the data. However, `midiOutLongMsg()` ignores `dwBytesRecorded`, although it did not mentioned in the documentation. I've tested on Windows 7, 10 and 11. All of them have the same behavior.
>>
>> The bug cannot be easily reproduced because some MIDI drivers filter out any malformed MIDI data. The example code below create a special case to make sure all MIDI data are legally when the bug is triggered.
>>
>>
>> import javax.sound.midi.*;
>>
>> public class MidiTest {
>> public static class RawMidiMessage extends MidiMessage {
>> public RawMidiMessage(byte[] data) {
>> super(data);
>> }
>>
>> @Override
>> public Object clone() {
>> return new RawMidiMessage(this.getMessage());
>> }
>> }
>>
>> public static void main(String[] args) {
>> var deviceInfos = MidiSystem.getMidiDeviceInfo();
>> for (var info : deviceInfos) {
>> try (MidiDevice device = MidiSystem.getMidiDevice(info)) {
>> if (device.getMaxReceivers() != 0) {
>> System.out.println("Open MIDI port: " + info.getName());
>> device.open();
>> Receiver receiver = device.getReceiver();
>> // Send two sysex messages at once
>> receiver.send(new RawMidiMessage(new byte[]{
>> (byte) 0xF0, 0x7D, 0x01, (byte) 0xF7,
>> (byte) 0xF0, 0x7D, 0x02, (byte) 0xF7
>> }), -1);
>> // Send another sysex message
>> receiver.send(new RawMidiMessage(new byte[]{(byte) 0xF0, 0x7D, 0x03, (byte) 0xF7}), -1);
>> }
>> } catch (MidiUnavailableException e) {
>> e.printStackTrace();
>> }
>> }
>> }
>> }
>>
>>
>> The expected messages received should be the following three messages
>>
>> F0 7D 01 F7
>> F0 7D 02 F7
>> F0 7D 03 F7
>>
>>
>> But acually four messages was received with the second message repeated twice.
>>
>> F0 7D 01 F7
>> F0 7D 02 F7
>> F0 7D 03 F7
>> F0 7D 02 F7
>>
>>
>> To resolve the issue, I add a new variable to backup the actual buffer size and set `dwBufferLength` of `MIDIHDR` structure to the size of MIDI data. After calling `midiOutLongMsg()`, I restore the original buffer size if the buf...
>
> Alec Su has updated the pull request incrementally with one additional commit since the last revision:
>
> Code cleanup
> It use `dwBytesRecorded` in `MIDIHDR` structure to indicate the actual size of the data
> However, `midiOutLongMsg()` ignores `dwBytesRecorded`, although it did not mentioned in the documentation.
This seems to be documented by Microsoft only in the most obscure fashion.
If you look at the doc for midiOutPrepareHeader
https://learn.microsoft.com/en-us/previous-versions/dd798477(v=vs.85)
it says "Before calling the function, set the lpData, dwBufferLength, and dwFlags members of the [MIDIHDR](https://learn.microsoft.com/en-us/previous-versions/dd798449(v=vs.85)) structure"
So no mention of dwBytesRecorded.
WAVEHDR is a very similar struct to MIDIHDR and if you look at this WAVEHDR doc it says
https://learn.microsoft.com/en-us/previous-versions/dd743837(v=vs.85)
dwBytesRecorded
When the header is used in input, specifies how much data is in the buffer.
So I infer the same is true for MIDI
Therefore dwBytesRecorded is only used for input and dwBufferLength is what's important for output.
-------------
PR Comment: https://git.openjdk.org/jdk/pull/16399#issuecomment-1786077861
More information about the client-libs-dev
mailing list