dmime: Read through a MIDI file.
Add a dummy track parser, that reads through MIDI tracks and events. To make sure we understand the file structure correctly, no actual tracks or events are generated. Also add test cases to make sure we read through a MIDI file correctly.
This commit is contained in:
parent
813b35c9d1
commit
8710a1cb0a
3 changed files with 311 additions and 64 deletions
|
@ -16,6 +16,7 @@
|
|||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "dmusic_midi.h"
|
||||
#include "dmime_private.h"
|
||||
#include "winternl.h"
|
||||
|
||||
|
@ -34,9 +35,127 @@ struct midi_parser
|
|||
IStream *stream;
|
||||
};
|
||||
|
||||
static HRESULT stream_read_at_most(IStream *stream, void *buffer, ULONG size, ULONG *bytes_left)
|
||||
{
|
||||
HRESULT hr;
|
||||
ULONG read = 0;
|
||||
if (size > *bytes_left) hr = IStream_Read(stream, buffer, *bytes_left, &read);
|
||||
else hr = IStream_Read(stream, buffer, size, &read);
|
||||
if (hr != S_OK) return hr;
|
||||
*bytes_left -= read;
|
||||
if (read < size) return S_FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT read_variable_length_number(IStream *stream, DWORD *out, ULONG *bytes_left)
|
||||
{
|
||||
BYTE byte;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
*out = 0;
|
||||
do
|
||||
{
|
||||
hr = stream_read_at_most(stream, &byte, 1, bytes_left);
|
||||
if (hr != S_OK) return hr;
|
||||
*out = (*out << 7) | (byte & 0x7f);
|
||||
} while (byte & 0x80);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT read_midi_event(IStream *stream, BYTE *last_status, ULONG *bytes_left)
|
||||
{
|
||||
BYTE byte, status, meta_type;
|
||||
DWORD length;
|
||||
LARGE_INTEGER offset;
|
||||
HRESULT hr = S_OK;
|
||||
DWORD delta_time;
|
||||
|
||||
if ((hr = read_variable_length_number(stream, &delta_time, bytes_left)) != S_OK) return hr;
|
||||
|
||||
if ((hr = stream_read_at_most(stream, &byte, 1, bytes_left)) != S_OK) return hr;
|
||||
|
||||
if (byte & 0x80)
|
||||
{
|
||||
status = *last_status = byte;
|
||||
if ((hr = stream_read_at_most(stream, &byte, 1, bytes_left)) != S_OK) return hr;
|
||||
}
|
||||
else status = *last_status;
|
||||
|
||||
if (status == MIDI_META)
|
||||
{
|
||||
meta_type = byte;
|
||||
|
||||
if ((hr = read_variable_length_number(stream, &length, bytes_left)) != S_OK) return hr;
|
||||
|
||||
switch (meta_type)
|
||||
{
|
||||
default:
|
||||
if (*bytes_left < length) return S_FALSE;
|
||||
offset.QuadPart = length;
|
||||
if (FAILED(hr = IStream_Seek(stream, offset, STREAM_SEEK_CUR, NULL))) return hr;
|
||||
FIXME("MIDI meta event type %#02x, length %lu, time +%lu. not supported\n", meta_type,
|
||||
length, delta_time);
|
||||
*bytes_left -= length;
|
||||
}
|
||||
TRACE("MIDI meta event type %#02x, length %lu, time +%lu\n", meta_type, length, delta_time);
|
||||
}
|
||||
else if (status == MIDI_SYSEX1 || status == MIDI_SYSEX2)
|
||||
{
|
||||
if (byte & 0x80)
|
||||
{
|
||||
if ((hr = read_variable_length_number(stream, &length, bytes_left)) != S_OK) return hr;
|
||||
length = length << 8 | (byte & 0x7f);
|
||||
}
|
||||
else length = byte;
|
||||
|
||||
if (*bytes_left < length) return S_FALSE;
|
||||
offset.QuadPart = length;
|
||||
if (FAILED(hr = IStream_Seek(stream, offset, STREAM_SEEK_CUR, NULL))) return hr;
|
||||
*bytes_left -= length;
|
||||
FIXME("MIDI sysex event type %#02x, length %lu, time +%lu. not supported\n", status, length, delta_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((status & 0xf0) != MIDI_PROGRAM_CHANGE && (status & 0xf0) != MIDI_CHANNEL_PRESSURE &&
|
||||
(hr = stream_read_at_most(stream, &byte, 1, bytes_left)) != S_OK)
|
||||
return hr;
|
||||
FIXME("MIDI event status %#02x, time +%lu, not supported\n", status, delta_time);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT midi_parser_next_track(struct midi_parser *parser, IDirectMusicTrack **out_track, MUSIC_TIME *out_length)
|
||||
{
|
||||
TRACE("(%p, %p, %p): stub\n", parser, out_track, out_length);
|
||||
WORD i = 0;
|
||||
TRACE("(%p, %p): stub\n", parser, out_length);
|
||||
for (i = 0;; i++)
|
||||
{
|
||||
HRESULT hr;
|
||||
BYTE magic[4] = {0}, last_status = 0;
|
||||
DWORD length_be;
|
||||
ULONG length;
|
||||
ULONG read = 0;
|
||||
|
||||
TRACE("Start parsing track %u\n", i);
|
||||
if ((hr = IStream_Read(parser->stream, magic, sizeof(magic), &read)) != S_OK) return hr;
|
||||
if (read < sizeof(magic)) break;
|
||||
if (memcmp(magic, "MTrk", 4) != 0) break;
|
||||
|
||||
if ((hr = IStream_Read(parser->stream, &length_be, sizeof(length_be), &read)) != S_OK)
|
||||
break;
|
||||
if (read < sizeof(length_be)) break;
|
||||
length = GET_BE_DWORD(length_be);
|
||||
TRACE("Track %u, length %lu bytes\n", i, length);
|
||||
|
||||
while ((hr = read_midi_event(parser->stream, &last_status, &length)) == S_OK)
|
||||
;
|
||||
|
||||
if (FAILED(hr)) return hr;
|
||||
TRACE("End of track %u\n", i);
|
||||
}
|
||||
|
||||
TRACE("End of file\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
|
@ -56,24 +175,9 @@ HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser)
|
|||
|
||||
if (FAILED(hr = IStream_Read(stream, &length, sizeof(length), NULL))) return hr;
|
||||
length = GET_BE_DWORD(length);
|
||||
if (length != 6)
|
||||
{
|
||||
WARN("Invalid MIDI header length %lu\n", length);
|
||||
return DMUS_E_UNSUPPORTED_STREAM;
|
||||
}
|
||||
|
||||
if (FAILED(hr = IStream_Read(stream, &format, sizeof(format), NULL))) return hr;
|
||||
format = GET_BE_WORD(format);
|
||||
if (format > 2)
|
||||
{
|
||||
WARN("Invalid MIDI format %u\n", format);
|
||||
return DMUS_E_UNSUPPORTED_STREAM;
|
||||
}
|
||||
if (format == 2)
|
||||
{
|
||||
FIXME("MIDI format 2 not implemented yet\n");
|
||||
return DMUS_E_UNSUPPORTED_STREAM;
|
||||
}
|
||||
|
||||
if (FAILED(hr = IStream_Read(stream, &number_of_tracks, sizeof(number_of_tracks), NULL)))
|
||||
return hr;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
#include <windef.h>
|
||||
#include <winternl.h>
|
||||
#include <wine/test.h>
|
||||
#include <initguid.h>
|
||||
#include <ole2.h>
|
||||
|
@ -32,6 +33,14 @@
|
|||
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
|
||||
DEFINE_GUID(GUID_Bunk,0xFFFFFFFF,0xFFFF,0xFFFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF);
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define GET_BE_WORD(x) (x)
|
||||
#define GET_BE_DWORD(x) (x)
|
||||
#else
|
||||
#define GET_BE_WORD(x) RtlUshortByteSwap(x)
|
||||
#define GET_BE_DWORD(x) RtlUlongByteSwap(x)
|
||||
#endif
|
||||
|
||||
static ULONG get_refcount(void *iface)
|
||||
{
|
||||
IUnknown *unknown = iface;
|
||||
|
@ -1554,14 +1563,81 @@ static void test_segment(void)
|
|||
while (IDirectMusicSegment_Release(dms));
|
||||
}
|
||||
|
||||
static void _expect_track(IDirectMusicSegment8 *seg, REFCLSID expect, const char *name, DWORD group,
|
||||
DWORD index, BOOL ignore_guid)
|
||||
{
|
||||
IDirectMusicTrack *track;
|
||||
IPersistStream *ps;
|
||||
CLSID class;
|
||||
HRESULT hr;
|
||||
|
||||
if (ignore_guid)
|
||||
hr = IDirectMusicSegment8_GetTrack(seg, &GUID_NULL, group, index, &track);
|
||||
else
|
||||
hr = IDirectMusicSegment8_GetTrack(seg, expect, group, index, &track);
|
||||
if (!expect) {
|
||||
ok(hr == DMUS_E_NOT_FOUND, "GetTrack failed: %#lx, expected DMUS_E_NOT_FOUND\n", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
ok(hr == S_OK, "GetTrack failed: %#lx, expected S_OK\n", hr);
|
||||
if (FAILED(hr)) return;
|
||||
hr = IDirectMusicTrack_QueryInterface(track, &IID_IPersistStream, (void**)&ps);
|
||||
ok(hr == S_OK, "QueryInterface for IID_IPersistStream failed: %#lx\n", hr);
|
||||
hr = IPersistStream_GetClassID(ps, &class);
|
||||
ok(hr == S_OK, "IPersistStream_GetClassID failed: %#lx\n", hr);
|
||||
ok(IsEqualGUID(&class, expect), "For group %#lx index %lu: Expected class %s got %s\n",
|
||||
group, index, name, wine_dbgstr_guid(&class));
|
||||
|
||||
IPersistStream_Release(ps);
|
||||
IDirectMusicTrack_Release(track);
|
||||
}
|
||||
|
||||
#define expect_track(seg, class, group, index) \
|
||||
_expect_track(seg, &CLSID_DirectMusic ## class, #class, group, index, TRUE)
|
||||
#define expect_guid_track(seg, class, group, index) \
|
||||
_expect_track(seg, &CLSID_DirectMusic ## class, #class, group, index, FALSE)
|
||||
|
||||
static void test_midi(void)
|
||||
{
|
||||
static const char midi_meta_set_tempo[] =
|
||||
{
|
||||
0x04, /* delta time = 4 */
|
||||
0xff, /* event type, MIDI meta event */
|
||||
0x51, /* meta event type, Set Tempo */
|
||||
0x03, /* event data lenght, 3 bytes */
|
||||
0x03,0x0d,0x40 /* tempo, 200000 us per quarter-note, i.e. 300 bpm */
|
||||
};
|
||||
IDirectMusicSegment8 *segment = NULL;
|
||||
IDirectMusicTrack *track = NULL;
|
||||
IDirectMusicLoader8 *loader;
|
||||
IPersistStream *persist;
|
||||
IStream *stream;
|
||||
LARGE_INTEGER zero = { .QuadPart = 0 };
|
||||
ULARGE_INTEGER position = { .QuadPart = 0 };
|
||||
WCHAR test_mid[MAX_PATH], bogus_mid[MAX_PATH];
|
||||
HRESULT hr;
|
||||
|
||||
#include <pshpack1.h>
|
||||
struct
|
||||
{
|
||||
char magic[4];
|
||||
UINT32 length;
|
||||
WORD format;
|
||||
WORD count;
|
||||
WORD ppqn;
|
||||
} header =
|
||||
{
|
||||
.magic = "MThd",
|
||||
};
|
||||
struct
|
||||
{
|
||||
char magic[4];
|
||||
UINT32 length;
|
||||
} track_header =
|
||||
{
|
||||
.magic = "MTrk",
|
||||
};
|
||||
#include <poppack.h>
|
||||
load_resource(L"test.mid", test_mid);
|
||||
/* This is a MIDI file with wrong track length. */
|
||||
load_resource(L"bogus.mid", bogus_mid);
|
||||
|
@ -1573,19 +1649,14 @@ static void test_midi(void)
|
|||
&IID_IDirectMusicSegment, test_mid, (void **)&segment);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
|
||||
/* test.mid has 1 seq track, 1 tempo track, and 1 band track */
|
||||
hr = IDirectMusicSegment8_GetTrack(segment, &CLSID_DirectMusicBandTrack, 0xffffffff, 0, &track);
|
||||
todo_wine ok(hr == S_OK, "unable to get band track from midi file: %#lx\n", hr);
|
||||
if (track)IDirectMusicTrack_Release(track);
|
||||
track = NULL;
|
||||
hr = IDirectMusicSegment8_GetTrack(segment, &CLSID_DirectMusicSeqTrack, 0xffffffff, 0, &track);
|
||||
todo_wine ok(hr == S_OK, "got %#lx\n", hr);
|
||||
if (track) IDirectMusicTrack_Release(track);
|
||||
track = NULL;
|
||||
hr = IDirectMusicSegment8_GetTrack(segment, &CLSID_DirectMusicTempoTrack, 0xffffffff, 0, &track);
|
||||
todo_wine ok(hr == S_OK, "got %#lx\n", hr);
|
||||
if (track) IDirectMusicTrack_Release(track);
|
||||
track = NULL;
|
||||
todo_wine expect_track(segment, BandTrack, -1, 0);
|
||||
todo_wine expect_track(segment, ChordTrack, -1, 1);
|
||||
todo_wine expect_track(segment, TempoTrack, -1, 2);
|
||||
todo_wine expect_track(segment, TimeSigTrack, -1, 3);
|
||||
todo_wine expect_track(segment, SeqTrack, -1, 4);
|
||||
/* no more tracks */
|
||||
hr = IDirectMusicSegment8_GetTrack(segment, &GUID_NULL, -1, 5, &track);
|
||||
ok(hr == DMUS_E_NOT_FOUND, "unexpected extra track\n");
|
||||
if (segment) IDirectMusicSegment8_Release(segment);
|
||||
segment = NULL;
|
||||
|
||||
|
@ -1594,6 +1665,109 @@ static void test_midi(void)
|
|||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
if (segment) IDirectMusicSegment8_Release(segment);
|
||||
|
||||
/* parse MIDI file without any track */
|
||||
|
||||
hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_IDirectMusicSegment, (void **)&segment);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
|
||||
hr = IDirectMusicSegment_QueryInterface(segment, &IID_IPersistStream, (void **)&persist);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = CreateStreamOnHGlobal(0, TRUE, &stream);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
header.format = GET_BE_WORD(123);
|
||||
header.count = GET_BE_WORD(123);
|
||||
header.ppqn = GET_BE_WORD(123);
|
||||
header.length = GET_BE_DWORD(sizeof(header) - 8);
|
||||
hr = IStream_Write(stream, &header, sizeof(header), NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = IPersistStream_Load(persist, stream);
|
||||
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), "got %lld\n", position.QuadPart);
|
||||
IPersistStream_Release(persist);
|
||||
IStream_Release(stream);
|
||||
/* TempoTrack and TimeSigTrack seems to be optional. */
|
||||
todo_wine expect_track(segment, BandTrack, -1, 0);
|
||||
todo_wine expect_track(segment, ChordTrack, -1, 1);
|
||||
todo_wine expect_track(segment, SeqTrack, -1, 2);
|
||||
IDirectMusicSegment_Release(segment);
|
||||
|
||||
/* parse MIDI file with 1 track that has 1 event. */
|
||||
|
||||
hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_IDirectMusicSegment, (void **)&segment);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
|
||||
hr = IDirectMusicSegment_QueryInterface(segment, &IID_IPersistStream, (void **)&persist);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = CreateStreamOnHGlobal(0, TRUE, &stream);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
header.format = GET_BE_WORD(123);
|
||||
header.count = GET_BE_WORD(123);
|
||||
header.ppqn = GET_BE_WORD(123);
|
||||
header.length = GET_BE_DWORD(sizeof(header) - 8);
|
||||
hr = IStream_Write(stream, &header, sizeof(header), NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
track_header.length = RtlUlongByteSwap(sizeof(track_header) - 8 + sizeof(midi_meta_set_tempo));
|
||||
hr = IStream_Write(stream, &track_header, sizeof(track_header), NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = IStream_Write(stream, midi_meta_set_tempo, sizeof(midi_meta_set_tempo), NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = IStream_Seek(stream, zero, 0, NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = IPersistStream_Load(persist, stream);
|
||||
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) + sizeof(midi_meta_set_tempo),
|
||||
"got %lld\n", position.QuadPart);
|
||||
IPersistStream_Release(persist);
|
||||
IStream_Release(stream);
|
||||
todo_wine expect_track(segment, BandTrack, -1, 0);
|
||||
todo_wine expect_track(segment, ChordTrack, -1, 1);
|
||||
todo_wine expect_track(segment, TempoTrack, -1, 2);
|
||||
todo_wine expect_track(segment, SeqTrack, -1, 3);
|
||||
IDirectMusicSegment_Release(segment);
|
||||
|
||||
/* parse MIDI file with a track with 0 length, but has an event. */
|
||||
|
||||
hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER,
|
||||
&IID_IDirectMusicSegment, (void **)&segment);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
|
||||
hr = IDirectMusicSegment_QueryInterface(segment, &IID_IPersistStream, (void **)&persist);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = CreateStreamOnHGlobal(0, TRUE, &stream);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
header.format = GET_BE_WORD(123);
|
||||
header.count = GET_BE_WORD(123);
|
||||
header.ppqn = GET_BE_WORD(123);
|
||||
header.length = 0;
|
||||
hr = IStream_Write(stream, &header, sizeof(header), NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
track_header.length = 0;
|
||||
hr = IStream_Write(stream, &track_header, sizeof(track_header), NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = IStream_Write(stream, midi_meta_set_tempo, sizeof(midi_meta_set_tempo), NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = IStream_Seek(stream, zero, 0, NULL);
|
||||
ok(hr == S_OK, "got %#lx\n", hr);
|
||||
hr = IPersistStream_Load(persist, stream);
|
||||
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) + 4, "got %lld\n", position.QuadPart);
|
||||
IPersistStream_Release(persist);
|
||||
IStream_Release(stream);
|
||||
todo_wine expect_track(segment, BandTrack, -1, 0);
|
||||
todo_wine expect_track(segment, ChordTrack, -1, 1);
|
||||
/* there is no tempo track. */
|
||||
todo_wine expect_track(segment, SeqTrack, -1, 2);
|
||||
IDirectMusicSegment_Release(segment);
|
||||
IDirectMusicLoader8_Release(loader);
|
||||
}
|
||||
|
||||
|
@ -1615,40 +1789,6 @@ static void _add_track(IDirectMusicSegment8 *seg, REFCLSID class, const char *na
|
|||
|
||||
#define add_track(seg, class, group) _add_track(seg, &CLSID_DirectMusic ## class, #class, group)
|
||||
|
||||
static void _expect_track(IDirectMusicSegment8 *seg, REFCLSID expect, const char *name, DWORD group,
|
||||
DWORD index, BOOL ignore_guid)
|
||||
{
|
||||
IDirectMusicTrack *track;
|
||||
IPersistStream *ps;
|
||||
CLSID class;
|
||||
HRESULT hr;
|
||||
|
||||
if (ignore_guid)
|
||||
hr = IDirectMusicSegment8_GetTrack(seg, &GUID_NULL, group, index, &track);
|
||||
else
|
||||
hr = IDirectMusicSegment8_GetTrack(seg, expect, group, index, &track);
|
||||
if (!expect) {
|
||||
ok(hr == DMUS_E_NOT_FOUND, "GetTrack failed: %#lx, expected DMUS_E_NOT_FOUND\n", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
ok(hr == S_OK, "GetTrack failed: %#lx, expected S_OK\n", hr);
|
||||
hr = IDirectMusicTrack_QueryInterface(track, &IID_IPersistStream, (void**)&ps);
|
||||
ok(hr == S_OK, "QueryInterface for IID_IPersistStream failed: %#lx\n", hr);
|
||||
hr = IPersistStream_GetClassID(ps, &class);
|
||||
ok(hr == S_OK, "IPersistStream_GetClassID failed: %#lx\n", hr);
|
||||
ok(IsEqualGUID(&class, expect), "For group %#lx index %lu: Expected class %s got %s\n",
|
||||
group, index, name, wine_dbgstr_guid(&class));
|
||||
|
||||
IPersistStream_Release(ps);
|
||||
IDirectMusicTrack_Release(track);
|
||||
}
|
||||
|
||||
#define expect_track(seg, class, group, index) \
|
||||
_expect_track(seg, &CLSID_DirectMusic ## class, #class, group, index, TRUE)
|
||||
#define expect_guid_track(seg, class, group, index) \
|
||||
_expect_track(seg, &CLSID_DirectMusic ## class, #class, group, index, FALSE)
|
||||
|
||||
static void test_gettrack(void)
|
||||
{
|
||||
IDirectMusicSegment8 *seg;
|
||||
|
|
|
@ -31,7 +31,10 @@ enum midi_message
|
|||
MIDI_PROGRAM_CHANGE = 0xc0,
|
||||
MIDI_CHANNEL_PRESSURE = 0xd0,
|
||||
MIDI_PITCH_BEND_CHANGE = 0xe0,
|
||||
MIDI_SYSEX1 = 0xf0,
|
||||
MIDI_SYSEX2 = 0xf7,
|
||||
MIDI_SYSTEM_RESET = 0xff,
|
||||
MIDI_META = 0xff,
|
||||
};
|
||||
|
||||
enum midi_control
|
||||
|
|
Loading…
Add table
Reference in a new issue