Click here to Skip to main content
15,885,881 members
Articles / Artificial Intelligence

Developing MIDI applications with DirectMusic

Rate me:
Please Sign up or sign in to vote.
4.91/5 (45 votes)
11 Apr 2008LGPL325 min read 609.4K   9.5K   147  
A wrapper class library for the DirectMusic MIDI.
/*
 ____   _  ____  _____ _____ ______  __  __  _  ____   _
|  _ \ | ||  _ \|  __//  __//_   _/ |  \/  || ||  _ \ | |
| |_| || || |> /|  _| | <__   | |   | |\/| || || |_| || | 
|____/ |_||_|\_\|____\\____/  |_|   |_|  |_||_||____/ |_|			 

////////////////////////////////////////////////////////////////////////
  
  This library 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 library 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 library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
Copyright (c) 2002-2003 by Carlos Jim�nez de Parga  
All rights reserved.
For any suggestion or failure report, please contact me by:
e-mail: cjimenez@servitel.es

Note: I would appreciate very much if you could provide a brief description of your project, 
as well as any report on bugs or errors. This will help me improve the code for the benefit of your
application and the other users    
///////////////////////////////////////////////////////////////////////
Version: 2.1b
Module : COutputPort.cpp
Purpose: Code implementation for the COutput port class
Created: CJP / 02-08-2002
History: CJP / 03-25-2003 
	
	Update: 09/20/2002

	1. Improved class destructors

	2. Check member variables for NULL values before performing an operation
		
	3. Restructured the class system
	
	4. Better method to enumerate and select MIDI ports
	
	5. Adapted the external thread to a pure virtual function

	6. SysEx reception and sending enabled

	7. Added flexible conversion functions

	8. Find out how to activate the Microsoft software synth. 

	9. Added DLS support

  Update: 03/25/2003

	10. Added exception handling

    11. Fixed bugs with channel groups
  
    12. Redesigned class hierarchy
	
    13. DirectMidi wrapped in the directmidi namespace
  
    14. UNICODE/MBCS support for instruments and ports

	15. Added DELAY effect to software synthesizer ports
	
	16. Project released under GNU (General Public License) terms		 

*/

#include "CDirectMidi.h"

using namespace directmidi;

// Class constructor, member variables initialization

COutputPort::COutputPort():CMidiPort(DMUS_PC_OUTPUTCLASS)
{
	m_pClock		= NULL;
}	


// Function to select and activate an Input MIDI port given an INFOPORT structure

HRESULT COutputPort::ActivatePort(LPINFOPORT InfoPort,DWORD dwSysExSize)
{
	HRESULT hr = DM_FAILED;
	TCHAR strMembrFunc[] = _T("COutputPort::ActivatePort");
	
	if (m_pClock) throw CDMusicException(strMembrFunc,hr,__LINE__); 

	CMidiPort::ActivatePort(InfoPort,dwSysExSize);
	
	if (FAILED(hr = m_pPort->GetLatencyClock(&m_pClock))) throw CDMusicException(strMembrFunc,hr,__LINE__); 
	
	// Instructs the port to compact DLS or wave-table memory 
	m_pPort->Compact();

	return S_OK;
}

// This function removes the selected port 
// and releases all their asociated interfaces for a new port selection

HRESULT COutputPort::ReleasePort()
{
	HRESULT hr = DM_FAILED;
	TCHAR strMembrFunc[] = _T("COutputPort::ReleasePort()");
	
	if (m_pPort)
		if (FAILED(hr = m_pPort->Activate(FALSE))) throw CDMusicException(strMembrFunc,hr,__LINE__); 
	
	SAFE_RELEASE(m_pPort);
	SAFE_RELEASE(m_pBuffer);
	SAFE_RELEASE(m_pClock);
	
	return hr;
}


// Download an instrument to the port wave-table memory

HRESULT COutputPort::DownLoadInstrument(CInstrument &pInstrument)
{
	HRESULT hr = DM_FAILED;
	TCHAR strMembrFunc[] = _T("COutputPort::DownLoadInstrument()");
	
	if ((m_pPort) && (pInstrument.m_pInstrument))
	{	
		
		UnLoadInstrument(pInstrument);	// Unload the instrument object in case is downloaded
		
		// Download the instrument to the port and keep a reference to
		// this instrument in the port with the m_pDownloadedInstrument

		if (FAILED(hr = m_pPort->DownloadInstrument(pInstrument.m_pInstrument,
			&pInstrument.m_pDownLoadedInstrument,NULL,0))) 
				throw CDMusicException(strMembrFunc,hr,__LINE__);
		pInstrument.m_pPort = m_pPort;	// Updates the Outputport reference 
	} else return hr; 
	
	return S_OK;
}


// Unload an instrument from the port

HRESULT COutputPort::UnLoadInstrument(CInstrument &pInstrument)
{
	HRESULT hr = DM_FAILED;
	TCHAR strMembrFunc[] = _T("COutputPort::UnLoadInstrument()");
	
	if ((m_pPort) && (pInstrument.m_pDownLoadedInstrument))
	{
		if (FAILED(hr = m_pPort->UnloadInstrument(pInstrument.m_pDownLoadedInstrument))) 
			throw CDMusicException(strMembrFunc,hr,__LINE__); 
		SAFE_RELEASE(pInstrument.m_pDownLoadedInstrument);
	} else return hr;
	return S_OK;
}	


// Sends an unstructured MIDI message

HRESULT COutputPort::SendMidiMsg(LPBYTE lpMsg,DWORD dwLength,DWORD dwChannelGroup)
{
	HRESULT hr = DM_FAILED;
	REFERENCE_TIME rt;
	TCHAR strMembrFunc[] = _T("COutputPort::SendMidiMsg() long version");

	if ((m_pPort) && (m_pBuffer) && (m_pClock))
	{
		if (FAILED(hr = m_pClock->GetTime(&rt))) 
			 throw CDMusicException(strMembrFunc,hr,__LINE__); // Gets the exact time to play it
		if (FAILED(hr = m_pBuffer->PackUnstructured(rt,dwChannelGroup,dwLength,lpMsg))) 
			 throw CDMusicException(strMembrFunc,hr,__LINE__); 
		if (FAILED(hr = m_pPort->PlayBuffer(m_pBuffer))) 
			 throw CDMusicException(strMembrFunc,hr,__LINE__); // Sends the data
		if (FAILED(hr = m_pBuffer->Flush())) 
			 throw CDMusicException(strMembrFunc,hr,__LINE__); // Discards all data in the buffer
	} else throw CDMusicException(strMembrFunc,hr,__LINE__); 
	
	return S_OK;
}	

// Function to send a MIDI normal message to the selected output port

HRESULT COutputPort::SendMidiMsg(DWORD dwMsg,DWORD dwChannelGroup)
{
	HRESULT hr = DM_FAILED;
	REFERENCE_TIME rt;
	TCHAR strMembrFunc[] = _T("COutputPort::SendMidiMsg() short version");

	if ((m_pPort) && (m_pBuffer) && (m_pClock))
	{
		if (FAILED(hr = m_pClock->GetTime(&rt))) 
			throw CDMusicException(strMembrFunc,hr,__LINE__); // Gets the exact time to play it
		if (FAILED(hr = m_pBuffer->PackStructured(rt,dwChannelGroup,dwMsg))) 
			throw CDMusicException(strMembrFunc,hr,__LINE__); 
		if (FAILED(hr = m_pPort->PlayBuffer(m_pBuffer))) 
			throw CDMusicException(strMembrFunc,hr,__LINE__); // Sends the data
		if (FAILED(hr = m_pBuffer->Flush())) 
			throw CDMusicException(strMembrFunc,hr,__LINE__); // Discards all data in the buffer
	} else throw CDMusicException(strMembrFunc,hr,__LINE__);

	return S_OK;
	
}


// Function to activate an effect in the port

HRESULT COutputPort::SetEffect(BOOL bActivate,BYTE Effect)
{
	IKsControl   *pControl;
    KSPROPERTY   ksp;
    DWORD        dwEffects = 0;
    ULONG        cb;
	HRESULT      hr = DM_FAILED;
	TCHAR strMembrFunc[] = _T("COutputPort::SetEffect()");
    
	// Get the IKsControl interface
	if (m_pPort)
	{
		hr = m_pPort->QueryInterface(IID_IKsControl,(void**)&pControl);

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

        
			if (!bActivate)
				dwEffects = 0;
			else
			{
				// Select an audio effect
				switch(Effect)
				{
					case 0x1:
						dwEffects = DMUS_EFFECT_REVERB;
						break;
					case 0x2:
						dwEffects = DMUS_EFFECT_CHORUS;
						break;
					case 0x4:
						dwEffects = DMUS_EFFECT_DELAY;
						break;
					case 0x3:
						dwEffects = DMUS_EFFECT_CHORUS | DMUS_EFFECT_REVERB;
						break;
					case 0x5:
						dwEffects = DMUS_EFFECT_REVERB | DMUS_EFFECT_DELAY;
						break;
					case 0x6:
						dwEffects = DMUS_EFFECT_CHORUS | DMUS_EFFECT_DELAY;
						break;
					case 0x7:
						dwEffects = DMUS_EFFECT_CHORUS | DMUS_EFFECT_REVERB | DMUS_EFFECT_DELAY;
						break;
				}		
			}
		
		ksp.Set = GUID_DMUS_PROP_Effects;
		ksp.Id = 0;
		ksp.Flags = KSPROPERTY_TYPE_SET;
	
                            
		if (FAILED(hr = pControl->KsProperty(&ksp,sizeof(ksp),(LPVOID)&dwEffects,
        sizeof(dwEffects),&cb))) throw CDMusicException(strMembrFunc,hr,__LINE__);

		SAFE_RELEASE(pControl);
	
		} else throw CDMusicException(strMembrFunc,hr,__LINE__);
	} else throw CDMusicException(strMembrFunc,hr,__LINE__);
	
	return S_OK;

}

// Code a MIDI message given the three bytes of a MIDI message

DWORD COutputPort::EncodeMidiMsg(BYTE Status,BYTE DataByte1,BYTE DataByte2)
{
	DWORD dwMsg;
	dwMsg = Status;
    dwMsg |= DataByte1 << SHIFT_8;
    dwMsg |= DataByte2 << SHIFT_16;
	return dwMsg;
}

// Code a MIDI message given the Command and the Channel

DWORD COutputPort::EncodeMidiMsg(BYTE Command,BYTE Channel,BYTE DataByte1,BYTE DataByte2)
{
    DWORD dwMsg;
	dwMsg = Command | Channel;
    dwMsg |= DataByte1 << SHIFT_8;
    dwMsg |= DataByte2 << SHIFT_16;
	return dwMsg;
}


// Class destructor
// Release all the interfaces

COutputPort::~COutputPort()
{
	ReleasePort();
}

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