Create a bandtrack for MIDI files by picking out the MIDI 0xCn events, and creating matching DMUS_PATCH_PMSG.
* * *
This also has a commit adding a stub chordtrack, which is small so I think a separate MR is unwarranted. Can be split out if necessary.
-- v6: dmime: Add a stub chordtrack for MIDI segments.
From: Yuxuan Shui yshui@codeweavers.com
--- dlls/dmband/bandtrack.c | 1 + dlls/dmband/dmband_main.c | 1 + dlls/dmband/dmband_private.h | 10 ---------- dlls/dmusic/Makefile.in | 1 + dlls/{dmband => dmusic}/band.c | 3 +-- dlls/dmusic/dmusic_band.h | 25 +++++++++++++++++++++++++ 6 files changed, 29 insertions(+), 12 deletions(-) rename dlls/{dmband => dmusic}/band.c (99%) create mode 100644 dlls/dmusic/dmusic_band.h
diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index b410cf81d3a..45346ad1f7a 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -17,6 +17,7 @@ */
#include "dmband_private.h" +#include "dmusic_band.h" #include "dmobject.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmband); diff --git a/dlls/dmband/dmband_main.c b/dlls/dmband/dmband_main.c index 55f979e3fbc..43790fbff61 100644 --- a/dlls/dmband/dmband_main.c +++ b/dlls/dmband/dmband_main.c @@ -20,6 +20,7 @@ #include "initguid.h" #include "dmband_private.h" #include "rpcproxy.h" +#include "dmusic_band.h" #include "dmobject.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmband); diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index 48c5ec02aeb..3b2617322fe 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -20,21 +20,16 @@ #ifndef __WINE_DMBAND_PRIVATE_H #define __WINE_DMBAND_PRIVATE_H
-#include <stdio.h> #include <stdarg.h> -#include <string.h>
#define COBJMACROS
#include "windef.h" #include "winbase.h" #include "winnt.h" -#include "wingdi.h" -#include "winuser.h"
#include "wine/debug.h" #include "wine/list.h" -#include "winreg.h" #include "objbase.h"
#include "dmusici.h" @@ -44,11 +39,6 @@ /***************************************************************************** * ClassFactory */ -extern HRESULT create_dmband(REFIID riid, void **ret_iface); extern HRESULT create_dmbandtrack(REFIID riid, void **ret_iface);
-extern HRESULT band_connect_to_collection(IDirectMusicBand *iface, IDirectMusicCollection *collection); -extern HRESULT band_send_messages(IDirectMusicBand *iface, IDirectMusicPerformance *performance, - IDirectMusicGraph *graph, MUSIC_TIME time, DWORD track_id); - #endif /* __WINE_DMBAND_PRIVATE_H */ diff --git a/dlls/dmusic/Makefile.in b/dlls/dmusic/Makefile.in index 8e1c07e90a7..446eca15543 100644 --- a/dlls/dmusic/Makefile.in +++ b/dlls/dmusic/Makefile.in @@ -2,6 +2,7 @@ MODULE = dmusic.dll IMPORTS = dxguid uuid ole32 advapi32 dsound user32 winmm
SOURCES = \ + band.c \ buffer.c \ clock.c \ collection.c \ diff --git a/dlls/dmband/band.c b/dlls/dmusic/band.c similarity index 99% rename from dlls/dmband/band.c rename to dlls/dmusic/band.c index 78ec68ae035..370b85c2941 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmusic/band.c @@ -17,8 +17,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
-#include "dmband_private.h" -#include "dmobject.h" +#include "dmusic_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmband);
diff --git a/dlls/dmusic/dmusic_band.h b/dlls/dmusic/dmusic_band.h new file mode 100644 index 00000000000..e189acb7230 --- /dev/null +++ b/dlls/dmusic/dmusic_band.h @@ -0,0 +1,25 @@ +/* DirectMusicBand Private Include + * + * Copyright (C) 2024 Yuxuan Shui for CodeWeavers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "dmusici.h" + +extern HRESULT create_dmband(REFIID riid, void **ret_iface); +extern HRESULT band_connect_to_collection(IDirectMusicBand *iface, IDirectMusicCollection *collection); +extern HRESULT band_send_messages(IDirectMusicBand *iface, IDirectMusicPerformance *performance, + IDirectMusicGraph *graph, MUSIC_TIME time, DWORD track_id);
From: Yuxuan Shui yshui@codeweavers.com
MIDI tracks don't map one-to-one to segment tracks, so it doesn't make sense to use a "get_next_track()" interface. --- dlls/dmime/dmime_private.h | 7 ++----- dlls/dmime/midi.c | 23 +++++++++++++++++------ dlls/dmime/segment.c | 16 +--------------- 3 files changed, 20 insertions(+), 26 deletions(-)
diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index cdf0810381d..1c030dc314d 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -70,11 +70,8 @@ extern HRESULT create_dmtempotrack(REFIID riid, void **ret_iface); extern HRESULT create_dmtimesigtrack(REFIID riid, void **ret_iface); extern HRESULT create_dmwavetrack(REFIID riid, void **ret_iface);
-/* Create a new MIDI file parser. Note the stream might still be modified even - * when this function fails. */ -extern HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser); -extern HRESULT midi_parser_next_track(struct midi_parser *parser, IDirectMusicTrack **out_track, MUSIC_TIME *out_length); -extern void midi_parser_destroy(struct midi_parser *parser); +/* Parse a MIDI file. Note the stream might still be modified even when this function fails. */ +extern HRESULT parse_midi(IStream *stream, IDirectMusicSegment8 *segment);
extern void set_audiopath_perf_pointer(IDirectMusicAudioPath*,IDirectMusicPerformance8*); extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); diff --git a/dlls/dmime/midi.c b/dlls/dmime/midi.c index a31146e636f..40db1d2232c 100644 --- a/dlls/dmime/midi.c +++ b/dlls/dmime/midi.c @@ -125,10 +125,10 @@ static HRESULT read_midi_event(IStream *stream, BYTE *last_status, ULONG *bytes_ return S_OK; }
-HRESULT midi_parser_next_track(struct midi_parser *parser, IDirectMusicTrack **out_track, MUSIC_TIME *out_length) +static HRESULT midi_parser_parse(struct midi_parser *parser, IDirectMusicSegment8 *segment) { WORD i = 0; - TRACE("(%p, %p): stub\n", parser, out_length); + TRACE("(%p, %p): stub\n", parser, segment); for (i = 0;; i++) { HRESULT hr; @@ -159,7 +159,13 @@ HRESULT midi_parser_next_track(struct midi_parser *parser, IDirectMusicTrack **o return S_FALSE; }
-HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser) +static void midi_parser_destroy(struct midi_parser *parser) +{ + IStream_Release(parser->stream); + free(parser); +} + +static HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser) { LARGE_INTEGER offset; DWORD length; @@ -202,8 +208,13 @@ HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser) return hr; }
-void midi_parser_destroy(struct midi_parser *parser) +HRESULT parse_midi(IStream *stream, IDirectMusicSegment8 *segment) { - IStream_Release(parser->stream); - free(parser); + struct midi_parser *parser; + HRESULT hr; + + if (FAILED(hr = midi_parser_new(stream, &parser))) return hr; + hr = midi_parser_parse(parser, segment); + midi_parser_destroy(parser); + return hr; } diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index b0485aaedd9..b96f0e2e401 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -791,10 +791,7 @@ static inline struct segment *impl_from_IPersistStream(IPersistStream *iface) static HRESULT WINAPI segment_persist_stream_Load(IPersistStream *iface, IStream *stream) { struct segment *This = impl_from_IPersistStream(iface); - IDirectMusicTrack *track; - MUSIC_TIME length; struct chunk_entry chunk = {0}; - struct midi_parser *midi_parser; HRESULT hr;
TRACE("(%p, %p): Loading\n", This, stream); @@ -810,18 +807,7 @@ static HRESULT WINAPI segment_persist_stream_Load(IPersistStream *iface, IStream break;
case mmioFOURCC('M','T','h','d'): - hr = midi_parser_new(stream, &midi_parser); - if (FAILED(hr)) break; - This->header.mtLength = 0; - while ((hr = midi_parser_next_track(midi_parser, &track, &length)) == S_OK) - { - hr = segment_append_track(This, track, 1, 0); - IDirectMusicTrack_Release(track); - if (FAILED(hr)) break; - if (length > This->header.mtLength) - This->header.mtLength = length; - } - midi_parser_destroy(midi_parser); + hr = parse_midi(stream, &This->IDirectMusicSegment8_iface); break;
case MAKE_IDTYPE(FOURCC_RIFF, mmioFOURCC('W','A','V','E')):
From: Yuxuan Shui yshui@codeweavers.com
--- dlls/dmime/Makefile.in | 1 + dlls/dmime/midi.c | 112 ++++++++++++++++++++++++++------ dlls/dmime/tests/dmime.c | 130 ++++++++++++++++++++++++++++++++++++-- dlls/dmusic/band.c | 14 ++++ dlls/dmusic/dmusic_band.h | 2 + 5 files changed, 236 insertions(+), 23 deletions(-)
diff --git a/dlls/dmime/Makefile.in b/dlls/dmime/Makefile.in index f8f622f8f3d..0f7b7db609e 100644 --- a/dlls/dmime/Makefile.in +++ b/dlls/dmime/Makefile.in @@ -4,6 +4,7 @@ PARENTSRC = ../dmusic
SOURCES = \ audiopath.c \ + band.c \ dmime.idl \ dmime_main.c \ dmobject.c \ diff --git a/dlls/dmime/midi.c b/dlls/dmime/midi.c index 40db1d2232c..439511d15cc 100644 --- a/dlls/dmime/midi.c +++ b/dlls/dmime/midi.c @@ -18,6 +18,7 @@
#include "dmusic_midi.h" #include "dmime_private.h" +#include "dmusic_band.h" #include "winternl.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmime); @@ -30,9 +31,19 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); #define GET_BE_DWORD(x) RtlUlongByteSwap(x) #endif
+struct midi_event +{ + MUSIC_TIME delta_time; + BYTE status; + BYTE data[2]; +}; + struct midi_parser { + IDirectMusicTrack *bandtrack; + MUSIC_TIME time; IStream *stream; + DWORD division; };
static HRESULT stream_read_at_most(IStream *stream, void *buffer, ULONG size, ULONG *bytes_left) @@ -62,26 +73,27 @@ static HRESULT read_variable_length_number(IStream *stream, DWORD *out, ULONG *b return S_OK; }
-static HRESULT read_midi_event(IStream *stream, BYTE *last_status, ULONG *bytes_left) +static HRESULT read_midi_event(IStream *stream, struct midi_event *event, BYTE *last_status, ULONG *bytes_left) { - BYTE byte, status, meta_type; + BYTE byte, status_type, 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; + event->delta_time = delta_time;
if ((hr = stream_read_at_most(stream, &byte, 1, bytes_left)) != S_OK) return hr;
if (byte & 0x80) { - status = *last_status = byte; + event->status = *last_status = byte; if ((hr = stream_read_at_most(stream, &byte, 1, bytes_left)) != S_OK) return hr; } - else status = *last_status; + else event->status = *last_status;
- if (status == MIDI_META) + if (event->status == MIDI_META) { meta_type = byte;
@@ -98,8 +110,9 @@ static HRESULT read_midi_event(IStream *stream, BYTE *last_status, ULONG *bytes_ *bytes_left -= length; } TRACE("MIDI meta event type %#02x, length %lu, time +%lu\n", meta_type, length, delta_time); + return S_OK; } - else if (status == MIDI_SYSEX1 || status == MIDI_SYSEX2) + else if (event->status == MIDI_SYSEX1 || event->status == MIDI_SYSEX2) { if (byte & 0x80) { @@ -112,33 +125,74 @@ static HRESULT read_midi_event(IStream *stream, BYTE *last_status, ULONG *bytes_ 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); + FIXME("MIDI sysex event type %#02x, length %lu, time +%lu. not supported\n", event->status, + length, delta_time); + return S_OK; + } + + status_type = event->status & 0xf0; + if (status_type == MIDI_PROGRAM_CHANGE) + { + event->data[0] = byte; + TRACE("MIDI program change event status %#02x, data: %#02x, time +%lu\n", event->status, + event->data[0], 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) + if (status_type != 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); + FIXME("MIDI event status %#02x, time +%lu, not supported\n", event->status, delta_time); }
return S_OK; }
+static HRESULT midi_parser_handle_program_change(struct midi_parser *parser, struct midi_event *event) +{ + HRESULT hr; + DMUS_IO_INSTRUMENT instrument; + IDirectMusicBand *band; + DMUS_BAND_PARAM band_param; + MUSIC_TIME dmusic_time = (ULONGLONG)parser->time * DMUS_PPQ / parser->division; + instrument.dwPChannel = event->status & 0xf; + instrument.dwFlags = DMUS_IO_INST_PATCH; + instrument.dwPatch = event->data[0]; + if (FAILED(hr = CoCreateInstance(&CLSID_DirectMusicBand, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicBand, (void **)&band))) + return hr; + hr = band_add_instrument(band, &instrument); + + if (SUCCEEDED(hr)) + { + TRACE("Adding band at time %lu\n", dmusic_time); + band_param.pBand = band; + band_param.mtTimePhysical = dmusic_time; + hr = IDirectMusicTrack_SetParam(parser->bandtrack, &GUID_BandParam, dmusic_time, &band_param); + } + else WARN("Failed to add instrument to band\n"); + + IDirectMusicBand_Release(band); + return hr; +} + static HRESULT midi_parser_parse(struct midi_parser *parser, IDirectMusicSegment8 *segment) { WORD i = 0; - TRACE("(%p, %p): stub\n", parser, segment); + HRESULT hr; + MUSIC_TIME music_length = 0; + + TRACE("(%p, %p): semi-stub\n", parser, segment); + for (i = 0;; i++) { - HRESULT hr; BYTE magic[4] = {0}, last_status = 0; DWORD length_be; ULONG length; ULONG read = 0; + struct midi_event event = {0};
TRACE("Start parsing track %u\n", i); - if ((hr = IStream_Read(parser->stream, magic, sizeof(magic), &read)) != S_OK) return hr; + if ((hr = IStream_Read(parser->stream, magic, sizeof(magic), &read)) != S_OK) break; if (read < sizeof(magic)) break; if (memcmp(magic, "MTrk", 4) != 0) break;
@@ -148,20 +202,32 @@ static HRESULT midi_parser_parse(struct midi_parser *parser, IDirectMusicSegment 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) - ; + while ((hr = read_midi_event(parser->stream, &event, &last_status, &length)) == S_OK) + { + parser->time += event.delta_time; + if ((event.status & 0xf0) == MIDI_PROGRAM_CHANGE) + hr = midi_parser_handle_program_change(parser, &event); + if (FAILED(hr)) break; + }
- if (FAILED(hr)) return hr; + if (FAILED(hr)) break; TRACE("End of track %u\n", i); + if (parser->time > music_length) music_length = parser->time; + parser->time = 0; }
TRACE("End of file\n"); - return S_FALSE; + + music_length = (ULONGLONG)music_length * DMUS_PPQ / parser->division + 1; + if (SUCCEEDED(hr)) hr = IDirectMusicSegment8_SetLength(segment, music_length); + if (SUCCEEDED(hr)) hr = IDirectMusicSegment8_InsertTrack(segment, parser->bandtrack, 0xffff); + return hr; }
static void midi_parser_destroy(struct midi_parser *parser) { IStream_Release(parser->stream); + IDirectMusicTrack_Release(parser->bandtrack); free(parser); }
@@ -202,9 +268,17 @@ static HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser) parser = calloc(1, sizeof(struct midi_parser)); if (!parser) return E_OUTOFMEMORY; parser->stream = stream; - IStream_AddRef(stream); - *out_parser = parser; + parser->division = division; + hr = CoCreateInstance(&CLSID_DirectMusicBandTrack, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicTrack, (void **)&parser->bandtrack); + if (FAILED(hr)) + { + free(parser); + return hr; + }
+ *out_parser = parser; + IStream_AddRef(stream); return hr; }
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 7e1b419251e..41b34944f9d 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1600,6 +1600,25 @@ static void _expect_track(IDirectMusicSegment8 *seg, REFCLSID expect, const char
static void test_midi(void) { + static const DWORD message_types[] = + { + DMUS_PMSGT_MIDI, + DMUS_PMSGT_NOTE, + DMUS_PMSGT_SYSEX, + DMUS_PMSGT_NOTIFICATION, + DMUS_PMSGT_TEMPO, + DMUS_PMSGT_CURVE, + DMUS_PMSGT_TIMESIG, + DMUS_PMSGT_PATCH, + DMUS_PMSGT_TRANSPOSE, + DMUS_PMSGT_CHANNEL_PRIORITY, + DMUS_PMSGT_STOP, + DMUS_PMSGT_DIRTY, + DMUS_PMSGT_WAVE, + DMUS_PMSGT_LYRIC, + DMUS_PMSGT_SCRIPTLYRIC, + DMUS_PMSGT_USER, + }; static const char midi_meta_set_tempo[] = { 0x04, /* delta time = 4 */ @@ -1608,15 +1627,27 @@ static void test_midi(void) 0x03, /* event data lenght, 3 bytes */ 0x03,0x0d,0x40 /* tempo, 200000 us per quarter-note, i.e. 300 bpm */ }; + static const char midi_program_change[] = + { + 0x04, /* delta time = 4 */ + 0xc1, /* event type, program change, channel 1 */ + 0x30, /* event data, patch 48 */ + }; IDirectMusicSegment8 *segment = NULL; IDirectMusicTrack *track = NULL; IDirectMusicLoader8 *loader; + IDirectMusicTool *tool; + IDirectMusicPerformance *performance; + IDirectMusicGraph *graph; 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; + ULONG ret; + DMUS_PMSG *msg; + DMUS_PATCH_PMSG *patch; #include <pshpack1.h> struct { @@ -1649,7 +1680,7 @@ static void test_midi(void) &IID_IDirectMusicSegment, test_mid, (void **)&segment); ok(hr == S_OK, "got %#lx\n", hr);
- todo_wine expect_track(segment, BandTrack, -1, 0); + 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); @@ -1691,7 +1722,7 @@ static void test_midi(void) IPersistStream_Release(persist); IStream_Release(stream); /* TempoTrack and TimeSigTrack seems to be optional. */ - todo_wine expect_track(segment, BandTrack, -1, 0); + 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); @@ -1727,7 +1758,7 @@ static void test_midi(void) "got %lld\n", position.QuadPart); IPersistStream_Release(persist); IStream_Release(stream); - todo_wine expect_track(segment, BandTrack, -1, 0); + 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); @@ -1763,11 +1794,102 @@ static void test_midi(void) 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); + 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); + + /* parse MIDI file with program change 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_program_change)); + hr = IStream_Write(stream, &track_header, sizeof(track_header), NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Write(stream, midi_program_change, sizeof(midi_program_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); + 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_program_change), + "got %lld\n", position.QuadPart); + IPersistStream_Release(persist); + IStream_Release(stream); + expect_track(segment, BandTrack, -1, 0); + todo_wine expect_track(segment, ChordTrack, -1, 1); + todo_wine expect_track(segment, SeqTrack, -1, 2); + + hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); + + /* now play the segment, and check produced messages + * wine generates: DIRTY, PATCH, DIRTY. + * native generates: DIRTY, PATCH + */ + + hr = IDirectMusicPerformance_Init(performance, NULL, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_PlaySegment(performance, (IDirectMusicSegment *)segment, 0x800, 0, NULL); + 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_DIRTY, "got %#lx, expected DIRTY\n", msg->dwType); + 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_PATCH, "got msg type %#lx, expected PATCH\n", msg->dwType); + ok(msg->dwPChannel == 1, "got pchannel %lu, expected 1\n", msg->dwPChannel); + todo_wine ok(msg->mtTime == 23, "got mtTime %lu, expected 23\n", msg->mtTime); + patch = (DMUS_PATCH_PMSG *)msg; + ok(patch->byInstrument == 0x30, "got instrument %#x, expected 0x30\n", patch->byInstrument); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&msg); + todo_wine ok(ret == WAIT_TIMEOUT, "unexpected message\n"); + if (!ret) + { + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicPerformance_Release(performance); + IDirectMusicTool_Release(tool); + IDirectMusicSegment_Release(segment); IDirectMusicLoader8_Release(loader); }
diff --git a/dlls/dmusic/band.c b/dlls/dmusic/band.c index 370b85c2941..cfced6692a8 100644 --- a/dlls/dmusic/band.c +++ b/dlls/dmusic/band.c @@ -527,3 +527,17 @@ HRESULT band_send_messages(IDirectMusicBand *iface, IDirectMusicPerformance *per
return hr; } + +HRESULT band_add_instrument(IDirectMusicBand *iface, DMUS_IO_INSTRUMENT *instrument) +{ + struct band *This = impl_from_IDirectMusicBand(iface); + struct instrument_entry *entry; + + TRACE("%p, %p\n", iface, instrument); + + if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; + entry->instrument = *instrument; + list_add_tail(&This->instruments, &entry->entry); + + return S_OK; +} diff --git a/dlls/dmusic/dmusic_band.h b/dlls/dmusic/dmusic_band.h index e189acb7230..fe40e3fb7f1 100644 --- a/dlls/dmusic/dmusic_band.h +++ b/dlls/dmusic/dmusic_band.h @@ -18,8 +18,10 @@ */
#include "dmusici.h" +#include "dmusicf.h"
extern HRESULT create_dmband(REFIID riid, void **ret_iface); extern HRESULT band_connect_to_collection(IDirectMusicBand *iface, IDirectMusicCollection *collection); extern HRESULT band_send_messages(IDirectMusicBand *iface, IDirectMusicPerformance *performance, IDirectMusicGraph *graph, MUSIC_TIME time, DWORD track_id); +HRESULT band_add_instrument(IDirectMusicBand *iface, DMUS_IO_INSTRUMENT *instrument);
From: Yuxuan Shui yshui@codeweavers.com
--- dlls/dmime/midi.c | 20 +++++++++++--------- dlls/dmime/tests/dmime.c | 10 +++++----- 2 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/dlls/dmime/midi.c b/dlls/dmime/midi.c index 439511d15cc..647ef1d123c 100644 --- a/dlls/dmime/midi.c +++ b/dlls/dmime/midi.c @@ -40,6 +40,7 @@ struct midi_event
struct midi_parser { + IDirectMusicTrack *chordtrack; IDirectMusicTrack *bandtrack; MUSIC_TIME time; IStream *stream; @@ -221,13 +222,16 @@ static HRESULT midi_parser_parse(struct midi_parser *parser, IDirectMusicSegment music_length = (ULONGLONG)music_length * DMUS_PPQ / parser->division + 1; if (SUCCEEDED(hr)) hr = IDirectMusicSegment8_SetLength(segment, music_length); if (SUCCEEDED(hr)) hr = IDirectMusicSegment8_InsertTrack(segment, parser->bandtrack, 0xffff); + if (SUCCEEDED(hr)) hr = IDirectMusicSegment8_InsertTrack(segment, parser->chordtrack, 0xffff); + return hr; }
static void midi_parser_destroy(struct midi_parser *parser) { IStream_Release(parser->stream); - IDirectMusicTrack_Release(parser->bandtrack); + if (parser->bandtrack) IDirectMusicTrack_Release(parser->bandtrack); + if (parser->chordtrack) IDirectMusicTrack_Release(parser->chordtrack); free(parser); }
@@ -267,18 +271,16 @@ static HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser)
parser = calloc(1, sizeof(struct midi_parser)); if (!parser) return E_OUTOFMEMORY; + IStream_AddRef(stream); parser->stream = stream; parser->division = division; hr = CoCreateInstance(&CLSID_DirectMusicBandTrack, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicTrack, (void **)&parser->bandtrack); - if (FAILED(hr)) - { - free(parser); - return hr; - } - - *out_parser = parser; - IStream_AddRef(stream); + if (SUCCEEDED(hr)) + hr = CoCreateInstance(&CLSID_DirectMusicChordTrack, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicTrack, (void **)&parser->chordtrack); + if (FAILED(hr)) midi_parser_destroy(parser); + else *out_parser = parser; return hr; }
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 41b34944f9d..97a6088936e 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1681,7 +1681,7 @@ static void test_midi(void) ok(hr == S_OK, "got %#lx\n", hr);
expect_track(segment, BandTrack, -1, 0); - todo_wine expect_track(segment, ChordTrack, -1, 1); + 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); @@ -1723,7 +1723,7 @@ static void test_midi(void) IStream_Release(stream); /* TempoTrack and TimeSigTrack seems to be optional. */ expect_track(segment, BandTrack, -1, 0); - todo_wine expect_track(segment, ChordTrack, -1, 1); + expect_track(segment, ChordTrack, -1, 1); todo_wine expect_track(segment, SeqTrack, -1, 2); IDirectMusicSegment_Release(segment);
@@ -1759,7 +1759,7 @@ static void test_midi(void) IPersistStream_Release(persist); IStream_Release(stream); expect_track(segment, BandTrack, -1, 0); - todo_wine expect_track(segment, ChordTrack, -1, 1); + 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); @@ -1795,7 +1795,7 @@ static void test_midi(void) IPersistStream_Release(persist); IStream_Release(stream); expect_track(segment, BandTrack, -1, 0); - todo_wine expect_track(segment, ChordTrack, -1, 1); + expect_track(segment, ChordTrack, -1, 1); /* there is no tempo track. */ todo_wine expect_track(segment, SeqTrack, -1, 2); IDirectMusicSegment_Release(segment); @@ -1832,7 +1832,7 @@ static void test_midi(void) IPersistStream_Release(persist); IStream_Release(stream); expect_track(segment, BandTrack, -1, 0); - todo_wine expect_track(segment, ChordTrack, -1, 1); + expect_track(segment, ChordTrack, -1, 1); todo_wine expect_track(segment, SeqTrack, -1, 2);
hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool);
This merge request was approved by Rémi Bernon.
This merge request was approved by Michael Stefaniuc.