1
0
mirror of https://github.com/wine-mirror/wine synced 2024-06-29 06:14:34 +00:00

dmime: Handle MIDI control events in MIDI files.

Adding them to the seqtrack, and also implementing playing them from the seqtrack.
This commit is contained in:
Yuxuan Shui 2024-02-22 18:08:17 +00:00 committed by Alexandre Julliard
parent 5ff94358a0
commit 28b94c4d42
3 changed files with 89 additions and 30 deletions

View File

@ -187,22 +187,12 @@ static HRESULT read_midi_event(IStream *stream, struct midi_event *event, BYTE *
status_type = event->status & 0xf0;
event->data[0] = byte;
if (status_type == MIDI_PROGRAM_CHANGE)
{
TRACE("MIDI program change event status %#02x, data: %#02x, time +%lu\n", event->status,
event->data[0], delta_time);
}
else
{
if (status_type != MIDI_CHANNEL_PRESSURE && (hr = stream_read_at_most(stream, &byte, 1, bytes_left)) != S_OK)
return hr;
event->data[1] = byte;
if (status_type == MIDI_NOTE_ON || status_type == MIDI_NOTE_OFF)
TRACE("MIDI note event status %#02x, data: %#02x, %#02x, time +%lu\n", event->status,
event->data[0], event->data[1], delta_time);
else
FIXME("MIDI event status %#02x, time +%lu, not supported\n", event->status, delta_time);
}
if (status_type != MIDI_PROGRAM_CHANGE && status_type != MIDI_CHANNEL_PRESSURE &&
(hr = stream_read_at_most(stream, &byte, 1, bytes_left)) != S_OK)
return hr;
event->data[1] = byte;
TRACE("MIDI event status %#02x, data: %#02x, %#02x, time +%lu\n", event->status, event->data[0],
event->data[1], delta_time);
return S_OK;
}
@ -303,6 +293,27 @@ static HRESULT midi_parser_handle_note_on_off(struct midi_parser *parser, struct
return S_OK;
}
static HRESULT midi_parser_handle_control(struct midi_parser *parser, struct midi_event *event)
{
struct midi_seqtrack_item *item;
DMUS_IO_SEQ_ITEM *seq_item;
MUSIC_TIME dmusic_time = (ULONGLONG)parser->time * DMUS_PPQ / parser->division;
if ((item = calloc(1, sizeof(struct midi_seqtrack_item))) == NULL) return E_OUTOFMEMORY;
seq_item = &item->item;
seq_item->mtTime = dmusic_time;
seq_item->mtDuration = 0;
seq_item->dwPChannel = event->status & 0xf;
seq_item->bStatus = event->status & 0xf0;
seq_item->bByte1 = event->data[0];
seq_item->bByte2 = event->data[1];
list_add_tail(&parser->seqtrack_items, &item->entry);
parser->seqtrack_items_count++;
return S_OK;
}
static int midi_seqtrack_item_compare(const void *a, const void *b)
{
const DMUS_IO_SEQ_ITEM *item_a = a, *item_b = b;
@ -351,6 +362,11 @@ static HRESULT midi_parser_parse(struct midi_parser *parser, IDirectMusicSegment
case MIDI_NOTE_OFF:
hr = midi_parser_handle_note_on_off(parser, &event);
break;
case MIDI_CHANNEL_PRESSURE:
case MIDI_PITCH_BEND_CHANGE:
case MIDI_CONTROL_CHANGE:
hr = midi_parser_handle_control(parser, &event);
break;
case MIDI_PROGRAM_CHANGE:
hr = midi_parser_handle_program_change(parser, &event);
break;

View File

@ -16,6 +16,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "dmusic_midi.h"
#include "dmime_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmime);
@ -130,27 +131,46 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *state
for (i = 0; SUCCEEDED(hr) &&i < This->count; i++)
{
DMUS_IO_SEQ_ITEM *item = This->items + i;
DMUS_NOTE_PMSG *msg;
DMUS_PMSG *msg;
if (item->mtTime < start_time) continue;
if (item->mtTime >= end_time) continue;
if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg),
(DMUS_PMSG **)&msg)))
break;
if (item->bStatus == MIDI_NOTE_ON)
{
DMUS_NOTE_PMSG *note;
if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*note),
(DMUS_PMSG **)&note)))
break;
note->dwType = DMUS_PMSGT_NOTE;
note->mtDuration = item->mtDuration;
note->wMusicValue = item->bByte1;
note->nOffset = item->nOffset;
note->bVelocity = item->bByte2;
note->bFlags = 1;
note->bMidiValue = item->bByte1;
msg = (DMUS_PMSG *)note;
}
else
{
DMUS_MIDI_PMSG *midi;
if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*midi),
(DMUS_PMSG **)&midi)))
break;
midi->dwType = DMUS_PMSGT_MIDI;
midi->bStatus = item->bStatus;
midi->bByte1 = item->bByte1;
midi->bByte2 = item->bByte2;
msg = (DMUS_PMSG *)midi;
}
msg->mtTime = item->mtTime + time_offset;
msg->dwFlags = DMUS_PMSGF_MUSICTIME;
msg->dwPChannel = item->dwPChannel;
msg->dwVirtualTrackID = track_id;
msg->dwType = DMUS_PMSGT_NOTE;
msg->dwGroupID = 1;
msg->mtDuration = item->mtDuration;
msg->wMusicValue = item->bByte1;
msg->nOffset = item->nOffset;
msg->bVelocity = item->bByte2;
msg->bFlags = 1;
msg->bMidiValue = item->bByte1;
if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg))
|| FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, (DMUS_PMSG *)msg)))

View File

@ -1633,6 +1633,13 @@ static void test_midi(void)
0xc1, /* event type, program change, channel 1 */
0x30, /* event data, patch 48 */
};
static const char midi_control_change[] =
{
0x04, /* delta time = 4 */
0xb1, /* event type, control change, channel 1 */
0x07, /* event data, channel volume */
0x40, /* event data, 64 */
};
static const char midi_note_on[] =
{
0x04, /* delta time = 4 */
@ -1667,10 +1674,11 @@ static void test_midi(void)
WCHAR test_mid[MAX_PATH], bogus_mid[MAX_PATH];
HRESULT hr;
ULONG ret;
DWORD track_length;
DWORD track_length, trace2_length;
MUSIC_TIME next;
DMUS_PMSG *msg;
DMUS_NOTE_PMSG *note;
DMUS_MIDI_PMSG *midi;
DMUS_PATCH_PMSG *patch;
DMUS_TEMPO_PARAM tempo_param;
#include <pshpack1.h>
@ -1873,13 +1881,16 @@ static void test_midi(void)
ok(hr == S_OK, "got %#lx\n", hr);
/* Add a second track, to test the duration of the trailing note. */
track_header.length = RtlUlongByteSwap(sizeof(track_header) - 8 + sizeof(midi_note_on) + sizeof(midi_note_off));
trace2_length = sizeof(midi_note_on) + sizeof(midi_note_off2) + sizeof(midi_control_change);
track_header.length = RtlUlongByteSwap(sizeof(track_header) - 8 + trace2_length);
hr = IStream_Write(stream, &track_header, sizeof(track_header), NULL);
ok(hr == S_OK, "got %#lx\n", hr);
hr = IStream_Write(stream, midi_note_on, sizeof(midi_note_on), NULL);
ok(hr == S_OK, "got %#lx\n", hr);
hr = IStream_Write(stream, midi_note_off2, sizeof(midi_note_off2), NULL);
ok(hr == S_OK, "got %#lx\n", hr);
hr = IStream_Write(stream, midi_control_change, sizeof(midi_control_change), NULL);
ok(hr == S_OK, "got %#lx\n", hr);
hr = IStream_Seek(stream, zero, 0, NULL);
ok(hr == S_OK, "got %#lx\n", hr);
@ -1887,7 +1898,7 @@ static void test_midi(void)
ok(hr == S_OK, "got %#lx\n", hr);
hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &position);
ok(hr == S_OK, "got %#lx\n", hr);
ok(position.QuadPart == sizeof(header) + sizeof(track_header) * 2 + track_length + sizeof(midi_note_on) + sizeof(midi_note_off),
ok(position.QuadPart == sizeof(header) + sizeof(track_header) * 2 + track_length + trace2_length,
"got %lld\n", position.QuadPart);
IPersistStream_Release(persist);
IStream_Release(stream);
@ -1981,6 +1992,18 @@ static void test_midi(void)
hr = IDirectMusicPerformance_FreePMsg(performance, msg);
ok(hr == S_OK, "got %#lx\n", hr);
ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&msg);
ok(!ret, "got %#lx\n", ret);
ok(msg->dwType == DMUS_PMSGT_MIDI, "got msg type %#lx, expected MIDI\n", msg->dwType);
ok(msg->mtTime == 649, "got mtTime %lu, expected 649\n", msg->mtTime);
ok(msg->dwPChannel == 1, "got pchannel %lu, expected 1\n", msg->dwPChannel);
midi = (DMUS_MIDI_PMSG *)msg;
ok(midi->bStatus == 0xb0, "got status %#x, expected 0xb1\n", midi->bStatus);
ok(midi->bByte1 == 0x07, "got byte1 %#x, expected 0x07\n", midi->bByte1);
ok(midi->bByte2 == 0x40, "got byte2 %#x, expected 0x40\n", midi->bByte2);
hr = IDirectMusicPerformance_FreePMsg(performance, msg);
ok(hr == S_OK, "got %#lx\n", hr);
/* wine generates an extra DIRTY event. */
ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&msg);
todo_wine ok(ret == WAIT_TIMEOUT, "unexpected message\n");