Click here to Skip to main content
15,880,405 members
Articles / Desktop Programming / MFC

Playing Midi Files with DirectMusic

Rate me:
Please Sign up or sign in to vote.
4.83/5 (17 votes)
30 Dec 2010LGPL34 min read 376.2K   6.4K   64  
A DirectX 8 class to perform Midi music playback
/*
Module : dmusic.cpp
Purpose: Defines the code implementation for the CMidiMusic class
Created: CJP / 17-02-2001
History: CJP / 21-10-2001 
	
	1. Fixed problems related to midi files and others formats

	2. Fixed bugs with MIDI port selection

	3. Fixed many problems discovered with the demo aplication

	CJP / 28-12-2010

	1. Fixed bug when using DirectMusic performance

	2. Solved memory leaks

Copyright (c) 2001 by C. Jim�nez de Parga  
All rights reserved.
*/

#include "stdafx.h"
#include "dmusic.h"



// The constructor, member variables initialisation

CMidiMusic::CMidiMusic()
{
    m_pMusic			= NULL; 
	m_pMusic8			= NULL; 
	m_pLoader			= NULL;
	m_pPerformance		= NULL; 
	m_pSegment			= NULL;
	m_pMusicPort		= NULL; 		
	m_pSegmentState		= NULL; 
	m_pSegmentState8	= NULL; 
	m_p3DAudioPath		= NULL; 
	m_pDSB				= NULL;
	// Initialize COM
	CoInitialize(NULL);	
}


// Intialisation of midi music class.
// Creates instances of the most important interfaces

HRESULT CMidiMusic::Initialize(BOOL b3DPosition)
{
	HRESULT hr;
	
	
	if (FAILED(hr=CoCreateInstance(CLSID_DirectMusicLoader,NULL, 
                  CLSCTX_INPROC,IID_IDirectMusicLoader8,
                  (void**)&m_pLoader))) return hr;

    if (FAILED(hr=CoCreateInstance(CLSID_DirectMusicPerformance,NULL,
                  CLSCTX_INPROC,IID_IDirectMusicPerformance8,
                  (void**)&m_pPerformance))) return hr;
	
	
	if (b3DPosition)
	{
		
		// Initialize audio for 3D environment with the appropriate performance
		// function and the standard audiopath
		if (FAILED(hr=m_pPerformance->InitAudio(&m_pMusic,NULL,NULL, 
								      DMUS_APATH_DYNAMIC_STEREO,
									  64,DMUS_AUDIOF_ALL,NULL))) return hr;
		
		if (FAILED(hr=m_pPerformance->CreateStandardAudioPath(DMUS_APATH_DYNAMIC_3D, 
                                      64,TRUE,&m_p3DAudioPath))) return hr;
		
		// Creates the 3D directsound buffer
		if (FAILED(hr=m_p3DAudioPath->GetObjectInPath(DMUS_PCHANNEL_ALL,
									  DMUS_PATH_BUFFER,0,GUID_NULL,0, 
									  IID_IDirectSound3DBuffer, 
									  (LPVOID*) &m_pDSB))) return hr;
	}
	else	
		// Initialisation for a not 3D environment 
		if (FAILED(hr=m_pPerformance->Init(&m_pMusic,NULL,NULL))) return hr;
		
	
	// Invoke the new interface pointer for IDirectMusic8 
	if (FAILED(hr=m_pMusic->QueryInterface(IID_IDirectMusic8,(LPVOID*)&m_pMusic8))) return hr;
	// Release the old interface, not needed any more
	SAFE_RELEASE(m_pMusic);

	m_b3DPosition=b3DPosition;

	return S_OK;
}
      

// Port enumeration function

HRESULT CMidiMusic::PortEnumeration(DWORD dwIndex,LPINFOPORT lpInfoPort)
{
	HRESULT hr;
	DMUS_PORTCAPS portinf;
	
	// Set to 0 the DMUS_PORTCAPS structure  
	ZeroMemory(&portinf,sizeof(portinf));
	portinf.dwSize=sizeof(DMUS_PORTCAPS);
     
	//Call the DirectMusic8 member function to enumerate systems ports
	if (FAILED(hr=m_pMusic8->EnumPort(dwIndex,&portinf))) return hr;
											 

	// Converts port description to char string 
	WideCharToMultiByte(CP_ACP,0,portinf.wszDescription,-1,
                        lpInfoPort->szPortDescription,
                        sizeof(lpInfoPort->szPortDescription)/
						sizeof(lpInfoPort->szPortDescription[0]),0,0);


   // Copy the GUID of DMUS_PORTCAP structure to CMidiMusic port structure
   CopyMemory(&(lpInfoPort->guidSynthGUID),&portinf.guidPort,sizeof(GUID));
   
   lpInfoPort->dwClass					=portinf.dwClass;
   lpInfoPort->dwEffectFlags			=portinf.dwEffectFlags;
   lpInfoPort->dwFlags	 				=portinf.dwFlags;
   lpInfoPort->dwMaxAudioChannels	    =portinf.dwMaxAudioChannels;
   lpInfoPort->dwMaxChannelGroups		=portinf.dwMaxChannelGroups;
   lpInfoPort->dwMaxVoices				=portinf.dwMaxVoices;
   lpInfoPort->dwType					=portinf.dwType;
   
   return hr;
}
	

// Function to select a MIDI port

HRESULT CMidiMusic::SelectPort(LPINFOPORT InfoPort)
{
	HRESULT hr;
	DMUS_PORTPARAMS dmus;	
	
	// Sets to 0 the port capabilities structure
	ZeroMemory(&dmus,sizeof(DMUS_PORTPARAMS));
	// Sets the params for this port
	dmus.dwSize=sizeof(DMUS_PORTPARAMS);
	dmus.dwValidParams=DMUS_PORTPARAMS_CHANNELGROUPS;
	dmus.dwChannelGroups=1;
    
	// If the port allows an audiopath
	if (InfoPort->dwFlags & DMUS_PC_AUDIOPATH)
		dmus.dwFeatures=DMUS_PORT_FEATURE_AUDIOPATH;    

				
	// The midi port is created here 
	if (FAILED(hr=m_pMusic8->CreatePort(InfoPort->guidSynthGUID,&dmus, 
        &m_pMusicPort,NULL))) return hr;
	
	// We have to activate it
	if (FAILED(hr=m_pMusicPort->Activate(TRUE))) return hr;

	
	// In case we aren't in a 3D mode
	if (!m_b3DPosition)
	{
		// Add the port to the performance
		if (FAILED(hr=m_pPerformance->AddPort(m_pMusicPort))) return hr;
		// Assigns a block of 16 performance channels to the performance 
		if (FAILED(hr=m_pPerformance->AssignPChannelBlock(0,m_pMusicPort,1)))
				   return hr;
    }
    
	return S_OK;
}

// Function to load a midi, segment or wave file into a segment

HRESULT CMidiMusic::LoadMidiFromFile(LPCSTR szMidi)
{
	WCHAR wstrMidi[256];
    HRESULT hr;

	// If exists a segment before, then release it
	if (m_pSegment) 
		SAFE_RELEASE(m_pSegment);
	
    // Converts ANSI (8-bits) to the UNICODE (16-bit) string
	MultiByteToWideChar(CP_ACP,0,szMidi,-1,wstrMidi,256);
	
	// Then load it into the segment
	if (FAILED(hr=m_pLoader->LoadObjectFromFile(CLSID_DirectMusicSegment,   
							 IID_IDirectMusicSegment8,wstrMidi,                   
							 (LPVOID*) &m_pSegment))) return hr;
        

    // In case it is a midi file mark it is a standard midi file
	
	if(FAILED(hr=m_pSegment->SetParam(GUID_StandardMIDIFile, 
                                 0xFFFFFFFF,0,0,NULL)))
	    return hr;
	
    
	// Finally, download band data to the performance  
	m_pSegment->Download(m_pPerformance);
	
	return S_OK;
}
        		
// Function to load MIDI or WAVE file from a resource
HRESULT CMidiMusic::LoadMidiFromResource(TCHAR *strResource,TCHAR *strResourceType)
{
	HRESULT hr;
    HRSRC hres=NULL;
    void* pMem=NULL;
    DWORD dwSize=0;
    DMUS_OBJECTDESC objdesc;

    
	SAFE_RELEASE(m_pSegment);

	// Find the resource
    hres=FindResource(NULL,strResource,strResourceType);
    if(hres==NULL) 
        return E_FAIL;

    // Load the resource
    pMem=(void*)LoadResource(NULL,hres);
    if(NULL==pMem) 
        return E_FAIL;

    // Store the size of the resource
    dwSize=SizeofResource(NULL,hres); 
    
    // Set up our object description 
    ZeroMemory(&objdesc,sizeof(DMUS_OBJECTDESC));
    objdesc.dwSize=sizeof(DMUS_OBJECTDESC);
    objdesc.dwValidData=DMUS_OBJ_MEMORY | DMUS_OBJ_CLASS;
    objdesc.guidClass=CLSID_DirectMusicSegment;
    objdesc.llMemLength=(LONGLONG)dwSize;
    objdesc.pbMemData=(BYTE*)pMem;
    
    if (FAILED(hr=m_pLoader->GetObject(&objdesc,
                                      IID_IDirectMusicSegment8,
                                      (void**)&m_pSegment)))
    {
        if(hr==DMUS_E_LOADER_FAILEDOPEN) return hr;
    }

	
	// Specify a standard midi file
	if(FAILED(hr=m_pSegment->SetParam(GUID_StandardMIDIFile, 
							 0xFFFFFFFF,0,0,NULL)))
     return hr;

	
	
	// Download band data to the performance  
	
	m_pSegment->Download(m_pPerformance);
	
	return S_OK;
}

// Function to play a segment

HRESULT CMidiMusic::Play()
{
	HRESULT hr;	

	// Plays a segment and stores the segment state
	if (FAILED(hr=m_pPerformance->PlaySegmentEx(
        m_pSegment,  
        NULL,        
        NULL,         
  		0,           
        0,           
        &m_pSegmentState,        
        NULL,        
        m_p3DAudioPath         
    ))) return hr;


    // Gets the new interface for SegmentState
	if (FAILED(hr=m_pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8,
	   (LPVOID*)&m_pSegmentState8))) return hr;
	
	
	// Release the references to SegmentState
	SAFE_RELEASE(m_pSegmentState);
	
	return hr;
}	

// Functions to pause a segment 

HRESULT CMidiMusic::Pause()
{
	MUSIC_TIME          mtNow;
    HRESULT hr;

	// Stops the segment playback
	if (FAILED(hr=m_pPerformance->Stop(
		NULL,
		NULL,
		0,
		0))) return hr;
	
	// Get the position before the pause
	if (FAILED(hr=m_pSegmentState8->GetSeek(&mtNow))) return hr;
    
	// Set restart point
	if (FAILED(hr=m_pSegment->SetStartPoint(mtNow)))  return hr;
	
	return S_OK;

}

// Function to resume after the pause, any sound must be listened

HRESULT CMidiMusic::Resume()
{
	HRESULT hr;
    
	if (IsPlaying()==S_FALSE)
	{
		hr=Play();
		return hr;
	}
	return S_FALSE;
}


// Funtion to find out when the music is actually played

HRESULT CMidiMusic::IsPlaying()
{
	HRESULT hr;
	
	if (FAILED(hr=m_pPerformance->IsPlaying(m_pSegment,NULL))) return hr;
	
	return hr;

}


// Function to repeat a segment

HRESULT CMidiMusic::SetRepeat(BOOL bRepeat)
{
	HRESULT hr;

	if (bRepeat==TRUE)
		hr=m_pSegment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);
	else 
		hr=m_pSegment->SetRepeats(0);
  
    return hr; 
}


// Function to stop a segment

HRESULT CMidiMusic::Stop()
{
	HRESULT hr;

	if (FAILED(hr=m_pSegment->SetStartPoint(0))) return hr;
		
	if (FAILED(hr=m_pPerformance->Stop(NULL,NULL,0,0))) return hr;
	
	return S_FALSE;
}

// Function to get the length in ticks of a segment

HRESULT CMidiMusic::GetLength(MUSIC_TIME *mtMusicTime)
{
	HRESULT hr;

	if (FAILED(hr=m_pSegment->GetLength(mtMusicTime))) return hr;
		
	return hr;

}

// Function to get the playback position of a segment

HRESULT CMidiMusic::GetSeek(MUSIC_TIME *mtMusicTime)
{
	HRESULT hr;

	if (FAILED(hr=m_pSegmentState8->GetSeek(mtMusicTime))) return hr;
	
	return hr;	

}


// Function to move the pointer to a specific position

HRESULT CMidiMusic::Seek(MUSIC_TIME mtMusicTime)
{
	HRESULT hr;
 		
	if (FAILED(hr=m_pSegment->SetStartPoint(mtMusicTime))) return hr;
	
	return S_OK;

}



// This function does the same of GetSeek

HRESULT CMidiMusic::GetTicks(MUSIC_TIME *mtMusicTime)
{
	HRESULT hr;

	hr=GetSeek(mtMusicTime);
				
    return hr;	
}  


//Function to get the reference time

HRESULT CMidiMusic::GetReferenceTime(REFERENCE_TIME *rtReferenceTime)
{
  
	MUSIC_TIME mtMusicTime;
	
	m_pSegmentState8->GetStartPoint(&mtMusicTime);
  	
	m_pPerformance->MusicToReferenceTime(mtMusicTime,rtReferenceTime);
	
	return S_OK;
}



// Function to set the 3D audio position
// DS3D_IMMEDIATE set the parameters immediately

HRESULT CMidiMusic::SetPosition(D3DVALUE x,D3DVALUE y,D3DVALUE z)
{	
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->SetPosition(x,y,z,DS3D_IMMEDIATE);
	return hr;
}



// Function to get the 3D audio position

HRESULT CMidiMusic::GetPosition(D3DVALUE *x,D3DVALUE *y,D3DVALUE *z)
{	
	HRESULT hr;
	D3DVECTOR d3dVect;
	hr=S_FALSE;
	if (m_b3DPosition)
	{
		hr=m_pDSB->GetPosition(&d3dVect);
		if (x) *x=d3dVect.x;
		if (y) *y=d3dVect.y;
		if (z) *z=d3dVect.z;
	}
	
	return hr;
}


// Function to set the audio velocity

HRESULT CMidiMusic::SetVelocity(D3DVALUE x,D3DVALUE y,D3DVALUE z)
{	
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->SetVelocity(x,y,z,DS3D_IMMEDIATE);
	return hr;
}


// Function to set the 3D audio mode

HRESULT CMidiMusic::SetMode(DWORD dwMode)
{
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->SetMode(dwMode,DS3D_IMMEDIATE);
	return hr;
}

// Function to set the maximum distance of audio range

HRESULT CMidiMusic::SetMaxDistance(D3DVALUE flMaxDistance)
{	
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->SetMaxDistance(flMaxDistance,DS3D_IMMEDIATE);
	return hr;
}


// Function set the minimum distance of audio range

HRESULT CMidiMusic::SetMinDistance(D3DVALUE flMinDistance)
{	
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->SetMinDistance(flMinDistance,DS3D_IMMEDIATE);
	return hr;
}


//Function to set the audio cone orientation

HRESULT CMidiMusic::SetConeOrientation(D3DVALUE x,D3DVALUE y,D3DVALUE z)
{
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->SetConeOrientation(x,y,z,DS3D_IMMEDIATE);
	return hr;
}


//Function to get the audio cone orientation

HRESULT CMidiMusic::GetConeOrientation(D3DVALUE *x,D3DVALUE *y,D3DVALUE *z)
{
	HRESULT hr;
	D3DVECTOR d3dVect;
	hr=S_FALSE;
	if (m_b3DPosition)
	{	
		hr=m_pDSB->GetConeOrientation(&d3dVect);
		if (x) *x=d3dVect.x;
		if (y) *y=d3dVect.y;
		if (z) *z=d3dVect.z;
	}
	return hr;
}

// Function to set the audio cone angles

HRESULT CMidiMusic::SetConeAngles(DWORD dwInsideConeAngle,DWORD dwOutsideConeAngle)
{
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->SetConeAngles(dwInsideConeAngle,dwOutsideConeAngle,DS3D_IMMEDIATE);
	return hr;
}

// Function to get the audio cone angles

HRESULT CMidiMusic::GetConeAngles(LPDWORD dwInsideConeAngle,LPDWORD dwOutsideConeAngle)
{
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->GetConeAngles(dwInsideConeAngle,dwOutsideConeAngle);
	return hr;
}


// Function to set the cone outside volume

HRESULT CMidiMusic::SetConeOutsideVolume(LONG lConeOutsideVolume)
{
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->SetConeOutsideVolume(lConeOutsideVolume,DS3D_IMMEDIATE);
	return hr;
}

// Function to retrieve the cone outside volume

HRESULT CMidiMusic::GetConeOutsideVolume(LPLONG plConeOutsideVolume)
{
	HRESULT hr;
	hr=S_FALSE;
	if (m_b3DPosition)
		hr=m_pDSB->GetConeOutsideVolume(plConeOutsideVolume);
	return hr;
}


// Function to set the master tempo

HRESULT CMidiMusic::SetMasterTempo(float fTempo)
{
	HRESULT hr;

	hr=m_pPerformance->SetGlobalParam(GUID_PerfMasterTempo, 
                                     (void*)&fTempo, sizeof(float));                    
	return hr;
}


// Function to set the audio master volume (only in software synth. mode)
HRESULT CMidiMusic::SetMasterVolume(long nVolume)
{
	HRESULT hr;
	
	hr=m_pPerformance->SetGlobalParam(GUID_PerfMasterVolume,
									 (void*)&nVolume,sizeof(long));           

	return hr;
}

// Function to set audio effects

HRESULT CMidiMusic::SetEffect(BOOL bActivate,int nEffect)
{
	IKsControl   *pControl;
    KSPROPERTY   ksp;
    DWORD        dwEffects=0;
    ULONG        cb;
	HRESULT      hr;

    // Get the IKsControl interface
	hr=m_pMusicPort->QueryInterface(IID_IKsControl,(void**)&pControl);

    if (SUCCEEDED(hr))
    {
        ZeroMemory(&ksp, sizeof(ksp));

        
		if (!bActivate)
			dwEffects=0;
		else
		{
			// Select an audio effect
			switch(nEffect)
				{
				case 0x1:
					dwEffects=DMUS_EFFECT_REVERB;
					break;
				case 0x2:
					dwEffects=DMUS_EFFECT_CHORUS;
					break;
				case 0x3:
					dwEffects=DMUS_EFFECT_CHORUS | DMUS_EFFECT_REVERB;
					break;
				}
		//Possible error here if you don't have /Ze compiler option set
		}
		
		ksp.Set=GUID_DMUS_PROP_Effects;
		ksp.Id=0;
		ksp.Flags=KSPROPERTY_TYPE_SET;
	
                            
		pControl->KsProperty(&ksp,
			                 sizeof(ksp),
                             (LPVOID)&dwEffects,
                             sizeof(dwEffects),
                             &cb);

		SAFE_RELEASE(pControl);
	
	}

	return hr;

}

// The class destructor
// Release all the interfaces
// Remove ports and closedown the performance
// Uninitialize COM

CMidiMusic::~CMidiMusic()
{
	if (m_pLoader)
	{	
		m_pLoader->ReleaseObjectByUnknown(m_pSegment);
		SAFE_RELEASE(m_pLoader);
	}
	
	SAFE_RELEASE(m_pSegmentState);
	SAFE_RELEASE(m_pSegmentState8);
	SAFE_RELEASE(m_p3DAudioPath);
	SAFE_RELEASE(m_pDSB);
	SAFE_RELEASE(m_pSegment);
	SAFE_RELEASE(m_pMusic);
	SAFE_RELEASE(m_pMusic8);	
	
	if (m_pPerformance)		
	{
		m_pPerformance->RemovePort(m_pMusicPort);
		m_pPerformance->CloseDown();
		SAFE_RELEASE(m_pMusicPort);
	}
	
	SAFE_RELEASE(m_pMusicPort);
	
	CoUninitialize();
}	

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer
Spain Spain
I obtained my PhD degree in Computer Graphics at the National Distance Education University (UNED) in October 2019. I also hold a Ms. degree in Software Engineering and Computer Systems and a Bs. degree in Computer Science from the National Distance Education University (UNED).
I have been employed as a C++ software developer in several companies since year 2000.
I currently work as a Tutor-Professor of Symbolic Logic, Discrete Math and Java Object-Oriented Programming at UNED-Cartagena (Spain) since 2015.

Comments and Discussions