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:
parent
5ff94358a0
commit
28b94c4d42
3 changed files with 89 additions and 30 deletions
|
@ -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;
|
||||
|
|
|
@ -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 **)¬e)))
|
||||
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)))
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Add table
Reference in a new issue