318 lines
9.5 KiB
C++
318 lines
9.5 KiB
C++
// Transpose.cpp : Implementation of CTransposeTool
|
|
//
|
|
// Copyright (C) 1999 Microsoft Corporation. All Rights Reserved
|
|
//
|
|
|
|
#include "dmusicc.h"
|
|
#include "dmusici.h"
|
|
#include "debug.h"
|
|
#include "transpose.h"
|
|
#include "toolhelp.h"
|
|
|
|
// We keep a default chord of C7 in the scale of C, in case there is no chord track
|
|
// at the time an in scale transposition is requested.
|
|
|
|
DMUS_CHORD_KEY CTransposeTool::m_gDefaultChord;
|
|
|
|
CTransposeTool::CTransposeTool()
|
|
{
|
|
ParamInfo Params[DMUS_TRANSPOSE_PARAMCOUNT] =
|
|
{
|
|
{ DMUS_TRANSPOSE_AMOUNT, MPT_INT,MP_CAPS_ALL,-24,24,0,
|
|
L"Intervals",L"Transpose",NULL}, // Tranpose - none by default
|
|
{ DMUS_TRANSPOSE_TYPE, MPT_ENUM,MP_CAPS_ALL,
|
|
DMUS_TRANSPOSET_LINEAR,DMUS_TRANSPOSET_SCALE,DMUS_TRANSPOSET_SCALE,
|
|
L"",L"Type",L"Linear,In Scale"} // Type - transpose in scale by default
|
|
};
|
|
InitParams(DMUS_TRANSPOSE_PARAMCOUNT,Params);
|
|
m_fMusicTime = TRUE; // override default setting.
|
|
wcscpy(m_gDefaultChord.wszName, L"M7");
|
|
m_gDefaultChord.wMeasure = 0;
|
|
m_gDefaultChord.bBeat = 0;
|
|
m_gDefaultChord.bSubChordCount = 1;
|
|
m_gDefaultChord.bKey = 12;
|
|
m_gDefaultChord.dwScale = 0xab5ab5; // default scale is C Major
|
|
m_gDefaultChord.bFlags = 0;
|
|
for (int n = 0; n < DMUS_MAXSUBCHORD; n++)
|
|
{
|
|
m_gDefaultChord.SubChordList[n].dwChordPattern = 0x891; // default chord is major 7
|
|
m_gDefaultChord.SubChordList[n].dwScalePattern = 0xab5ab5; // default scale is C Major
|
|
m_gDefaultChord.SubChordList[n].dwInversionPoints = 0xffffff;
|
|
m_gDefaultChord.SubChordList[n].dwLevels = 0xffffffff;
|
|
m_gDefaultChord.SubChordList[n].bChordRoot = 12; // 2C
|
|
m_gDefaultChord.SubChordList[n].bScaleRoot = 0;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CTransposeTool::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CTransposeTool::Release()
|
|
{
|
|
if( 0 == InterlockedDecrement(&m_cRef) )
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP CTransposeTool::QueryInterface(const IID &iid, void **ppv)
|
|
{
|
|
if (iid == IID_IUnknown || iid == IID_IDirectMusicTool || iid == IID_IDirectMusicTool8)
|
|
{
|
|
*ppv = static_cast<IDirectMusicTool8*>(this);
|
|
}
|
|
else if(iid == IID_IPersistStream)
|
|
{
|
|
*ppv = static_cast<IPersistStream*>(this);
|
|
}
|
|
else if(iid == IID_IDirectMusicTransposeTool)
|
|
{
|
|
*ppv = static_cast<IDirectMusicTransposeTool*>(this);
|
|
}
|
|
else if(iid == IID_IMediaParams)
|
|
{
|
|
*ppv = static_cast<IMediaParams*>(this);
|
|
}
|
|
else if(iid == IID_IMediaParamInfo)
|
|
{
|
|
*ppv = static_cast<IMediaParamInfo*>(this);
|
|
}
|
|
else if(iid == IID_ISpecifyPropertyPages)
|
|
{
|
|
*ppv = static_cast<ISpecifyPropertyPages*>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IPersistStream
|
|
|
|
STDMETHODIMP CTransposeTool::GetClassID(CLSID* pClassID)
|
|
|
|
{
|
|
if (pClassID)
|
|
{
|
|
*pClassID = CLSID_DirectMusicTransposeTool;
|
|
return S_OK;
|
|
}
|
|
return E_POINTER;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IPersistStream Methods:
|
|
|
|
STDMETHODIMP CTransposeTool::IsDirty()
|
|
|
|
{
|
|
if (m_fDirty) return S_OK;
|
|
else return S_FALSE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CTransposeTool::Load(IStream* pStream)
|
|
{
|
|
EnterCriticalSection(&m_CrSec);
|
|
DWORD dwChunkID;
|
|
DWORD dwSize;
|
|
|
|
HRESULT hr = pStream->Read(&dwChunkID, sizeof(dwChunkID), NULL);
|
|
hr = pStream->Read(&dwSize, sizeof(dwSize), NULL);
|
|
|
|
if(SUCCEEDED(hr) && (dwChunkID == FOURCC_TRANSPOSE_CHUNK))
|
|
{
|
|
DMUS_IO_TRANSPOSE_HEADER Header;
|
|
memset(&Header,0,sizeof(Header));
|
|
hr = pStream->Read(&Header, min(sizeof(Header),dwSize), NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SetParam(DMUS_TRANSPOSE_AMOUNT,(float) Header.lTranspose);
|
|
SetParam(DMUS_TRANSPOSE_TYPE,(float) Header.dwType);
|
|
}
|
|
}
|
|
m_fDirty = FALSE;
|
|
LeaveCriticalSection(&m_CrSec);
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CTransposeTool::Save(IStream* pStream, BOOL fClearDirty)
|
|
|
|
{
|
|
EnterCriticalSection(&m_CrSec);
|
|
DWORD dwChunkID = FOURCC_TRANSPOSE_CHUNK;
|
|
DWORD dwSize = sizeof(DMUS_IO_TRANSPOSE_HEADER);
|
|
|
|
HRESULT hr = pStream->Write(&dwChunkID, sizeof(dwChunkID), NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pStream->Write(&dwSize, sizeof(dwSize), NULL);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DMUS_IO_TRANSPOSE_HEADER Header;
|
|
GetParamInt(DMUS_TRANSPOSE_AMOUNT,MAX_REF_TIME,(long *) &Header.lTranspose);
|
|
GetParamInt(DMUS_TRANSPOSE_TYPE,MAX_REF_TIME,(long *) &Header.dwType);
|
|
hr = pStream->Write(&Header, sizeof(Header),NULL);
|
|
}
|
|
if (fClearDirty) m_fDirty = FALSE;
|
|
LeaveCriticalSection(&m_CrSec);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CTransposeTool::GetSizeMax(ULARGE_INTEGER* pcbSize)
|
|
|
|
{
|
|
if (pcbSize == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
pcbSize->QuadPart = sizeof(DMUS_IO_TRANSPOSE_HEADER) + 8; // Data plus RIFF header.
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CTransposeTool::GetPages(CAUUID * pPages)
|
|
|
|
{
|
|
pPages->cElems = 1;
|
|
pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID));
|
|
if (pPages->pElems == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*(pPages->pElems) = CLSID_TransposePage;
|
|
return NOERROR;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// IDirectMusicTool
|
|
|
|
STDMETHODIMP CTransposeTool::ProcessPMsg( IDirectMusicPerformance* pPerf,
|
|
DMUS_PMSG* pPMsg )
|
|
{
|
|
// returning S_FREE frees the message. If StampPMsg()
|
|
// fails, there is no destination for this message so
|
|
// free it.
|
|
if(NULL == pPMsg->pGraph )
|
|
{
|
|
return DMUS_S_FREE;
|
|
}
|
|
if (FAILED(pPMsg->pGraph->StampPMsg(pPMsg)))
|
|
{
|
|
return DMUS_S_FREE;
|
|
}
|
|
// Only transpose notes that are not on the drum pChannel.
|
|
if( (pPMsg->dwType == DMUS_PMSGT_NOTE ) && ((pPMsg->dwPChannel & 0xF) != 0x9))
|
|
{
|
|
// We need to know the time format so we can call GetParamInt() to read control parameters.
|
|
REFERENCE_TIME rtTime;
|
|
if (m_fMusicTime) rtTime = pPMsg->mtTime;
|
|
else rtTime = pPMsg->rtTime;
|
|
DMUS_NOTE_PMSG *pNote = (DMUS_NOTE_PMSG *) pPMsg;
|
|
long lTranspose, lType;
|
|
long lNote = pNote->bMidiValue;
|
|
|
|
GetParamInt(DMUS_TRANSPOSE_AMOUNT,rtTime,&lTranspose);
|
|
GetParamInt(DMUS_TRANSPOSE_TYPE,rtTime,&lType);
|
|
if (lType == DMUS_TRANSPOSET_LINEAR)
|
|
{
|
|
lNote += lTranspose;
|
|
while (lNote < 0) lNote += 12;
|
|
while (lNote > 127) lNote -= 12;
|
|
pNote->bMidiValue = (BYTE) lNote;
|
|
}
|
|
else
|
|
{
|
|
IDirectMusicPerformance8 *pPerf8;
|
|
if (SUCCEEDED(pPerf->QueryInterface(IID_IDirectMusicPerformance8,(void **) &pPerf8)))
|
|
{
|
|
DMUS_CHORD_KEY Chord;
|
|
DMUS_CHORD_KEY *pChord = &Chord;
|
|
if (FAILED(pPerf8->GetParamEx(GUID_ChordParam,pNote->dwVirtualTrackID,
|
|
pNote->dwGroupID,0,pNote->mtTime - pNote->nOffset, NULL, pChord)))
|
|
{
|
|
// Couldn't find an active scale, use major scale instead.
|
|
pChord = &m_gDefaultChord;
|
|
}
|
|
WORD wVal;
|
|
// First, use the current chord & scale to convert the note's midi value into scale position.
|
|
if (SUCCEEDED(pPerf->MIDIToMusic(pNote->bMidiValue ,pChord,DMUS_PLAYMODE_PEDALPOINT,pNote->bSubChordLevel,&wVal)))
|
|
{
|
|
// Scale position is octave position * 7 plus chord position * 2 plus the scale position.
|
|
long lScalePosition = (((wVal & 0xF000) >> 12) * 7) + (((wVal & 0xF00) >> 8) * 2) + ((wVal & 0xF0) >> 4);
|
|
// Now that we are looking at scale position, we can add the transposition.
|
|
lScalePosition += lTranspose;
|
|
// Make sure we don't wrap around.
|
|
while (lScalePosition < 0) lScalePosition += 7;
|
|
// A high MIDI value of 127 translates to scale position 74.
|
|
while (lScalePosition > 74) lScalePosition -= 7;
|
|
wVal &= 0x000F; // Keep only the accidental.
|
|
// Now, insert the values back in. Start with chord.
|
|
wVal |= ((lScalePosition / 7) << 12);
|
|
// Then, scale position.
|
|
wVal |= ((lScalePosition % 7) << 4);
|
|
pPerf->MusicToMIDI(wVal,pChord,DMUS_PLAYMODE_PEDALPOINT,
|
|
pNote->bSubChordLevel,&pNote->bMidiValue);
|
|
}
|
|
pPerf8->Release();
|
|
}
|
|
}
|
|
}
|
|
return DMUS_S_REQUEUE;
|
|
}
|
|
|
|
STDMETHODIMP CTransposeTool::Clone( IDirectMusicTool ** ppTool)
|
|
|
|
{
|
|
CTransposeTool *pNew = new CTransposeTool;
|
|
if (pNew)
|
|
{
|
|
HRESULT hr = pNew->CopyParamsFromSource(this);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppTool = (IDirectMusicTool *) pNew;
|
|
}
|
|
else
|
|
{
|
|
delete pNew;
|
|
}
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CTransposeTool::SetTranspose(long lTranpose)
|
|
{
|
|
return SetParam(DMUS_TRANSPOSE_AMOUNT,(float) lTranpose);
|
|
}
|
|
|
|
STDMETHODIMP CTransposeTool::SetType(DWORD dwType)
|
|
{
|
|
return SetParam(DMUS_TRANSPOSE_TYPE,(float) dwType);
|
|
}
|
|
|
|
#define MAX_REF_TIME 0x7FFFFFFFFFFFFFFF
|
|
|
|
STDMETHODIMP CTransposeTool::GetTranspose(long * plTranspose)
|
|
{
|
|
return GetParamInt(DMUS_TRANSPOSE_AMOUNT,MAX_REF_TIME, plTranspose);
|
|
}
|
|
|
|
STDMETHODIMP CTransposeTool::GetType(DWORD * pdwType)
|
|
{
|
|
return GetParamInt(DMUS_TRANSPOSE_TYPE,MAX_REF_TIME,(long *) pdwType);
|
|
} |