need help on DirectMusic MIDI program change

Started by
7 comments, last by lack o comments 19 years, 2 months ago
hello! any DirectMusic and MIDI experts out there? im having trouble with changing a MIDI file's program/patch number using a DirectMusic tool. Here's the code for the tool:
HRESULT STDMETHODCALLTYPE ProgramChangeTool::ProcessPMsg( IDirectMusicPerformance* pPerf, DMUS_PMSG* pDMUS_PMSG )
{
	if (pDMUS_PMSG->dwType == DMUS_PMSGT_PATCH)
	{
		
		if(( NULL == pDMUS_PMSG->pGraph ) || FAILED(pDMUS_PMSG->pGraph->StampPMsg(pDMUS_PMSG)))
		{
			return DMUS_S_FREE;
		}
                        DMUS_PATCH_PMSG* pMsgNote = (DMUS_PATCH_PMSG*) pDMUS_PMSG;
			pMsgNote->byInstrument = 0x1D; //overdriven guitar 
				
		pPerf->SendPMsg((DMUS_PMSG*)pMsgNote);
	}

	return S_OK;
}

HRESULT STDMETHODCALLTYPE ProgramChangeTool::Flush( IDirectMusicPerformance* pPerf, DMUS_PMSG* pDMUS_PMSG, REFERENCE_TIME rt )
{
	
	return S_OK;
}

ProgramChangeTool::ProgramChangeTool()
{
	
	m_cRef = 1;
	m_boolToolOn= false;

}

ProgramChangeTool::~ProgramChangeTool()
{
	

}

STDMETHODIMP ProgramChangeTool::QueryInterface(const IID &iid, void **ppv)
{
    if (iid == IID_IUnknown || iid == IID_IDirectMusicTool)
    {
        *ppv = static_cast<IDirectMusicTool*>(this);
    } 
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    
    reinterpret_cast<IUnknown*>(this)->AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) ProgramChangeTool::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) ProgramChangeTool::Release()
{
    if( 0 == InterlockedDecrement(&m_cRef) )
    {
        delete this;
        return 0;
    }

    return m_cRef;
}


HRESULT STDMETHODCALLTYPE ProgramChangeTool::Init(IDirectMusicGraph *pGraph)
{
	return S_OK;
}

HRESULT STDMETHODCALLTYPE ProgramChangeTool::GetMsgDeliveryType(DWORD* pdwDeliveryType)
{
	*pdwDeliveryType = DMUS_PMSGF_TOOL_IMMEDIATE;
	return S_OK;
}


HRESULT STDMETHODCALLTYPE ProgramChangeTool::GetMediaTypeArraySize( DWORD* pdwNumElements )
{
	*pdwNumElements = 1;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE ProgramChangeTool::GetMediaTypes( DWORD** padwMediaTypes, DWORD dwNumElements)
{
	(*padwMediaTypes)[0] = DMUS_PMSGT_PATCH;


	return S_OK;
}

Below is the code for inserting the tool into the graph:

	m_pProgChangeTool = new ProgramChangeTool();
	IDirectMusicGraph* pGraph;

	IDirectMusicAudioPath8* pDefaultAudioPath;
	m_pPerformance->GetDefaultAudioPath(&pDefaultAudioPath);
 
    HRESULT hr = pDefaultAudioPath->GetObjectInPath( 0, DMUS_PATH_PERFORMANCE_GRAPH, 0,GUID_NULL, 0, IID_IDirectMusicGraph, (LPVOID*) &pGraph );

	pGraph->InsertTool(m_pProgChangeTool, NULL, 0, 0);
	pGraph->Release();

So what the tool basically does is to just intercept patch messages and then change the instrument number into 0x1D for an overdriven guitar. This worked on some MIDIs (try c:\winnt\media\canyon.mid if it exists) but not for all (try c:\winnt\media\passport.mid if it exists). I also have a short plain and simple sample midi (plays a few notes using acoustic guitar (nylon) instrument) that simply gets muted when the tool is applied. I can email the file to anyone who wants to test it. When the tool "fails", it simply mutes the sound when played. What could be the reason for this? Is it because some notes on some instruments don't apply on other instruments? Or is there something wrong with my implementation? Is there other alternatives for changing the patch number of a channel programatically? Thanks for your help! [Edited by - Coder on February 15, 2005 11:50:35 PM]
Advertisement
I may have a clue as to what your dilemma is, but I'm not entirely sure. As far as your code: it looks ok. But I'm not too familiar with using DirectMusic tools and graphs. In the future, please use the "source" tags mentioned on the faq page to make your code more readable for others.

It may actually be a problem with the MIDI songs themselves. I've recently been having trouble with getting songs to play all of their notes and it all comes down to "invalid" tracks that DirectAudio can't seem to handle. Just out of curiosity, I checked PASSPORT.mid and found that, it too, has this problem.

Basically, what you may need to do is get a MIDI sequencer and manually edit the files. I use Anvil Studio. You should check for several things.

-If multiple-track music is placed on a single track, split them.
-If you find empty tracks (ones with no or unimportant events) remove them.
-Make sure there is only ONE rhythm track, that it exists on channel 10, and that the instrument assigned to it is 1 (acoustic grand on GM).
-Make sure no two tracks share the same channel.
-Often, there are cases where a track is supposed to be a tempo track. So far I haven't had any problems when removing them, but watch out.

Unfortunately, this doesn't always solve the problem. As was the case with PASSPORT. I can't even get it to play right on DirectMusic Producer.

Anyway, for patches. You can also try looking into IDirectMusicCollection8::GetInstrument()and IDirectMusicInstrument::SetPatch().

Good luck.
thanks for the reply lack_o_comments and sorry about the source text. thanks to whoever fixed it too.

Is the MIDI file really "invalid"?

Actually, I was able to play PASSPORT using DirectMusic properly. It only mutes when I apply the tool for the instrument 0x1D (overdriven guitar, and other instruments). The drums still plays though. And here's the weird part, I tried using instrument 0x01 (acoustic piano) on PASSPORT and it worked! I also edited the MIDI file manually using a hex editor, putting in the instrument 0x1D, and it also played properly in DirectMusic. The bug seems to be when you try to programatically intercept and modify the patch message. I just have no idea where the bug is. I just hope it isn't an inherent DirectMusic flaw or else we would just have to wait for Microsoft to fix it.

Here's the link to some of the MIDI files I used:

-file that simply mutes when tool is applied; works if manually edited
http://www.geocities.com/bjutus/eb_guitar.mid

-manually edited PASSPORT file; plays with an overdriven guitar
http://www.geocities.com/bjutus/overdriven_guitar_passport.mid

Does anybody have a clue about this? Everybody's welcome to take a guess no matter how weird it is. haha. alternatives to changing the instrument per channel?

I'll also check out the DirectMusicCollection and DirectMusicInstrument.

Thanks again!



Well, the PASSPORT file I downloaded does seem to play fine, but the overall structure is different, did you also change the order of any of the tracks, or was it just the instruments? Maybe it's a different version of the file.

Well, I just don't know anymore. Maybe the problem is local to my PC. But if Microsoft's own DirectMusic Producer can't play some stuff correctly, at least I know it isn't my code.

Let me know how things turn out. This is all getting very agitating. ;)
helo again!

I only changed the instruments into 0x1D. That's all. I got this PASSPORT from Windows 2000.

The instrument changes work if you edit the file directly and play them using DirectMusic with no intervention. But if you go through the tool it fails. Well not exactly. Some patch/instrument numbers worked and some didn't which is the weird part.

I'll post an update again if it gets any weirder haha
yessssss! hey lack_o_comments! I think I got it. haha

After careful reading of the DirectMusic help file (in desperation of hoping to find a clue...any clue to this problem) and analyzing the "weirdness" of random instruments working differently on each of the MIDI files that I tested, I finally got it. hehe. It seems that calling Segment->Download() would only download instruments that are specified IN the MIDI file! And not the whole default instrument collection. That's why some instruments worked on one MIDI file (because it was already specified in the file) and not on the other. Especially the MIDI file which only used a Guitar_Nylon instrument. None of the other instruments worked on this particular file because only the guitar was loaded. It was just coincidence that the instruments I picked for the other 2 files worked.

Here's what I did after getting the instrument collection,

//get instrument HRESULT hr = m_pCollection->GetInstrument( dwPatchNumber, &pInstrument);//download instrument to the port  hr = pPerformance->DownloadInstrument(pInstrument, pMsgNote->dwPChannel, &pDInstrument, NULL, 0, &pPort, &dwGrp, &dwMChannel);


Check out the DirectMusic help file for details about these 2 methods.

whew! thanks again for all the feedback. It also helps in keeping you sane when you have someone to talk to. haha

Another dark mystery of DirectAudio solved. Great Work! I would have never guessed that only the sounds used were being downloaded, but it does make sense.

Unfortunately, I still can't I find any answers to my problem of tracks not playing for certain songs. Strangely, sometimes the songs that do not play one time, might (accent of the might) play when I increase the number of audio-path channels. I think it may have something to do with ChannelPriority and too many voices per port. Then again, it may not. I just really don't know.

Ah well, thanks again for the tip. That does help me with a few other issues I had ;)
Thanks!

Can your file be played by other players? Or maybe there are too many instruments on the file. coz i've read that only 16 instruments can be stored in a MIDI file and only one instrument can be used on a single channel.
DirectAudio can overcome the 16-channel limitation by using groups of them. Each performance channel is mapped to a particular channel within a particular group.

Yes, other players can play the files correctly. However, they don't use DirectAudio. As I mentioned before, DirectMusic Producer (wich obviously DOES use DirectAudio) has the same problem as my software. I don't like to pointer the finger at APIs, but this leads me to belive it is in fact an issue with the DirectAudio.

This topic is closed to new replies.

Advertisement