1725 lines
54 KiB
C++
1725 lines
54 KiB
C++
//
|
|
// Copyright (c) 1996-2001 Microsoft Corporation
|
|
// Instrument.cpp
|
|
//
|
|
|
|
#ifdef DMSYNTH_MINIPORT
|
|
#include "common.h"
|
|
#else
|
|
#include "simple.h"
|
|
#include <mmsystem.h>
|
|
#include <dmerror.h>
|
|
#include "synth.h"
|
|
#include "math.h"
|
|
#include "debug.h"
|
|
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample. See ddkreadme.txt for more info.
|
|
#include "..\shared\validate.h"
|
|
#if 0 // The following section will only take affect in the DDK sample.
|
|
// @@END_DDKSPLIT
|
|
#include "validate.h"
|
|
// @@BEGIN_DDKSPLIT -- This section will be removed in the DDK sample.
|
|
#endif
|
|
// @@END_DDKSPLIT
|
|
#endif
|
|
|
|
void MemDump(char * prompt);
|
|
|
|
//#include <windowsx.h>
|
|
|
|
CSourceLFO::CSourceLFO()
|
|
|
|
{
|
|
m_pfFrequency = 3804; // f = (256*4096*16*5hz)/(samplerate)
|
|
m_stDelay = 0;
|
|
m_prMWPitchScale = 0;
|
|
m_vrMWVolumeScale = 0;
|
|
m_vrVolumeScale = 0;
|
|
m_prPitchScale = 0;
|
|
m_prCPPitchScale = 0;
|
|
m_vrCPVolumeScale = 0;
|
|
m_prCutoffScale = 0;
|
|
m_prMWCutoffScale = 0;
|
|
m_prCPCutoffScale = 0;
|
|
}
|
|
|
|
void CSourceLFO::Init(DWORD dwSampleRate)
|
|
|
|
{
|
|
m_pfFrequency = (256 * 4096 * 16 * 5) / dwSampleRate;
|
|
m_stDelay = 0;
|
|
m_prMWPitchScale = 0;
|
|
m_vrMWVolumeScale = 0;
|
|
m_vrVolumeScale = 0;
|
|
m_prPitchScale = 0;
|
|
m_prCPPitchScale = 0;
|
|
m_vrCPVolumeScale = 0;
|
|
m_prCutoffScale = 0;
|
|
m_prMWCutoffScale = 0;
|
|
m_prCPCutoffScale = 0;
|
|
}
|
|
|
|
void CSourceLFO::SetSampleRate(long lChange)
|
|
|
|
{
|
|
if (lChange > 0)
|
|
{
|
|
m_stDelay <<= lChange;
|
|
m_pfFrequency <<= lChange;
|
|
}
|
|
else
|
|
{
|
|
m_stDelay >>= -lChange;
|
|
m_pfFrequency >>= -lChange;
|
|
}
|
|
}
|
|
|
|
void CSourceLFO::Verify()
|
|
|
|
{
|
|
FORCEBOUNDS(m_pfFrequency,64,7600);
|
|
FORCEBOUNDS(m_stDelay,0,441000);
|
|
FORCEBOUNDS(m_vrVolumeScale,-1200,1200);
|
|
FORCEBOUNDS(m_vrMWVolumeScale,-1200,1200);
|
|
FORCEBOUNDS(m_prPitchScale,-1200,1200);
|
|
FORCEBOUNDS(m_prMWPitchScale,-1200,1200);
|
|
FORCEBOUNDS(m_prCPPitchScale,-1200,1200);
|
|
FORCEBOUNDS(m_vrCPVolumeScale,-1200,1200);
|
|
FORCEBOUNDS(m_prCutoffScale, -12800, 12800);
|
|
FORCEBOUNDS(m_prMWCutoffScale, -12800, 12800);
|
|
FORCEBOUNDS(m_prCPCutoffScale, -12800, 12800);
|
|
}
|
|
|
|
CSourceEG::CSourceEG()
|
|
|
|
{
|
|
Init();
|
|
}
|
|
|
|
void CSourceEG::Init()
|
|
|
|
{
|
|
m_stAttack = 0;
|
|
m_stDecay = 0;
|
|
m_pcSustain = 1000;
|
|
m_stRelease = 0;
|
|
m_trVelAttackScale = 0;
|
|
m_trKeyDecayScale = 0;
|
|
m_sScale = 0;
|
|
m_stDelay = 0;
|
|
m_stHold = 0;
|
|
m_prCutoffScale = 0;
|
|
}
|
|
|
|
void CSourceEG::SetSampleRate(long lChange)
|
|
|
|
{
|
|
if (lChange > 0)
|
|
{
|
|
m_stAttack <<= lChange;
|
|
m_stDecay <<= lChange;
|
|
m_stRelease <<= lChange;
|
|
}
|
|
else
|
|
{
|
|
m_stAttack >>= -lChange;
|
|
m_stDecay >>= -lChange;
|
|
m_stRelease >>= -lChange;
|
|
}
|
|
}
|
|
|
|
void CSourceEG::Verify()
|
|
|
|
{
|
|
FORCEBOUNDS(m_stAttack,0,1764000);
|
|
FORCEBOUNDS(m_stDecay,0,1764000);
|
|
FORCEBOUNDS(m_pcSustain,0,1000);
|
|
FORCEBOUNDS(m_stRelease,0,1764000);
|
|
FORCEBOUNDS(m_sScale,-1200,1200);
|
|
FORCEBOUNDS(m_trKeyDecayScale,-12000,12000);
|
|
FORCEBOUNDS(m_trVelAttackScale,-12000,12000);
|
|
FORCEBOUNDS(m_trKeyHoldScale,-12000,12000);
|
|
FORCEBOUNDS(m_prCutoffScale,-12800,12800);
|
|
}
|
|
|
|
CSourceFilter::CSourceFilter()
|
|
{
|
|
Init(22050);
|
|
}
|
|
|
|
void CSourceFilter::Init(DWORD dwSampleRate)
|
|
{
|
|
// First, calculate the playback samplerate in pitch rels.
|
|
// The reference frequency is a440, which is midi note 69.
|
|
// So, calculate the ratio of the sample rate to 440 and
|
|
// convert into prels (1200 per octave), then add the
|
|
// offset of 6900.
|
|
double fSampleRate = (double)dwSampleRate;
|
|
|
|
fSampleRate /= 440.0;
|
|
fSampleRate = log(fSampleRate) / log(2.0);
|
|
fSampleRate *= 1200.0;
|
|
fSampleRate += 6900.0;
|
|
m_prSampleRate = (PRELS)fSampleRate;
|
|
|
|
m_prCutoff = (PRELS)0x7FFF;
|
|
m_vrQ = (VRELS)0;
|
|
m_prVelScale = (PRELS)0;
|
|
m_prCutoffSRAdjust = 0;
|
|
m_iQIndex = 0;
|
|
}
|
|
|
|
void CSourceFilter::SetSampleRate(LONG lChange)
|
|
{
|
|
// lChange == 1 -> doubles -> add 1200 cents
|
|
// lChange == 2 -> quad -> add 2400 cents
|
|
// lChange == -1 -> halves -> sub 1200 cents
|
|
// lChange == -2 -> 1/4ths -> sub 2400 cents
|
|
//
|
|
if (lChange > 0)
|
|
{
|
|
m_prSampleRate += (1200 << (lChange - 1));
|
|
}
|
|
else
|
|
{
|
|
m_prSampleRate -= (1200 << ((-lChange) - 1));
|
|
}
|
|
|
|
m_prCutoffSRAdjust = FILTER_FREQ_RANGE - m_prSampleRate + m_prCutoff;
|
|
}
|
|
|
|
void CSourceFilter::Verify()
|
|
{
|
|
if ( m_prCutoff == 0x7FFF )
|
|
{
|
|
m_vrQ = 0;
|
|
m_prVelScale = 0;
|
|
}
|
|
else
|
|
{
|
|
FORCEBOUNDS(m_prCutoff, 5535, 11921);
|
|
FORCEBOUNDS(m_vrQ, 0, 225);
|
|
FORCEBOUNDS(m_prVelScale, -12800, 12800);
|
|
}
|
|
}
|
|
|
|
CSourceArticulation::CSourceArticulation()
|
|
|
|
{
|
|
// m_sVelToVolScale = -9600;
|
|
m_wUsageCount = 0;
|
|
m_sDefaultPan = 0;
|
|
m_dwSampleRate = 22050;
|
|
m_PitchEG.m_sScale = 0; // pitch envelope defaults to off
|
|
}
|
|
|
|
void CSourceArticulation::Init(DWORD dwSampleRate)
|
|
|
|
{
|
|
m_dwSampleRate = dwSampleRate;
|
|
m_LFO.Init(dwSampleRate); // Set to default values.
|
|
m_PitchEG.Init();
|
|
m_VolumeEG.Init();
|
|
m_LFO2.Init(dwSampleRate);
|
|
m_Filter.Init(dwSampleRate);
|
|
}
|
|
|
|
void CSourceArticulation::SetSampleRate(DWORD dwSampleRate)
|
|
|
|
{
|
|
if (dwSampleRate != m_dwSampleRate)
|
|
{
|
|
long lChange;
|
|
if (dwSampleRate > (m_dwSampleRate * 2))
|
|
{
|
|
lChange = 2; // going from 11 to 44.
|
|
}
|
|
else if (dwSampleRate > m_dwSampleRate)
|
|
{
|
|
lChange = 1; // must be doubling
|
|
}
|
|
else if ((dwSampleRate * 2) < m_dwSampleRate)
|
|
{
|
|
lChange = -2; // going from 44 to 11
|
|
}
|
|
else
|
|
{
|
|
lChange = -1; // that leaves halving.
|
|
}
|
|
m_dwSampleRate = dwSampleRate;
|
|
m_LFO.SetSampleRate(lChange);
|
|
m_PitchEG.SetSampleRate(lChange);
|
|
m_VolumeEG.SetSampleRate(lChange);
|
|
m_LFO2.SetSampleRate(lChange);
|
|
m_Filter.SetSampleRate(lChange);
|
|
}
|
|
}
|
|
|
|
void CSourceArticulation::Verify()
|
|
|
|
{
|
|
m_LFO.Verify();
|
|
m_PitchEG.Verify();
|
|
m_VolumeEG.Verify();
|
|
m_LFO2.Verify();
|
|
m_Filter.Verify();
|
|
}
|
|
|
|
void CSourceArticulation::AddRef()
|
|
|
|
{
|
|
m_wUsageCount++;
|
|
}
|
|
|
|
void CSourceArticulation::Release()
|
|
|
|
{
|
|
m_wUsageCount--;
|
|
if (m_wUsageCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
CSourceSample::CSourceSample()
|
|
|
|
{
|
|
m_pWave = NULL;
|
|
m_dwLoopStart = 0;
|
|
m_dwLoopEnd = 1;
|
|
m_dwLoopType = WLOOP_TYPE_FORWARD;
|
|
m_dwSampleLength = 0;
|
|
m_prFineTune = 0;
|
|
m_dwSampleRate = 22050;
|
|
m_bMIDIRootKey = 60;
|
|
m_bOneShot = TRUE;
|
|
m_bSampleType = 0;
|
|
}
|
|
|
|
CSourceSample::~CSourceSample()
|
|
|
|
{
|
|
if (m_pWave != NULL)
|
|
{
|
|
m_pWave->Release();
|
|
}
|
|
}
|
|
|
|
void CSourceSample::Verify()
|
|
|
|
{
|
|
if (m_pWave != NULL)
|
|
{
|
|
FORCEBOUNDS(m_dwSampleLength,0,m_pWave->m_dwSampleLength);
|
|
FORCEBOUNDS(m_dwLoopEnd,1,m_dwSampleLength);
|
|
FORCEBOUNDS(m_dwLoopStart,0,m_dwLoopEnd);
|
|
if ((m_dwLoopEnd - m_dwLoopStart) < 6)
|
|
{
|
|
m_bOneShot = TRUE;
|
|
}
|
|
}
|
|
FORCEBOUNDS(m_dwSampleRate,3000,200000);
|
|
FORCEBOUNDS(m_bMIDIRootKey,0,127);
|
|
FORCEBOUNDS(m_prFineTune,-1200,1200);
|
|
}
|
|
|
|
BOOL CSourceSample::CopyFromWave()
|
|
|
|
{
|
|
if (m_pWave == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
m_dwSampleLength = m_pWave->m_dwSampleLength;
|
|
m_dwSampleRate = m_pWave->m_dwSampleRate;
|
|
m_bSampleType = m_pWave->m_bSampleType;
|
|
if (m_bOneShot)
|
|
{
|
|
m_dwSampleLength--;
|
|
if (m_pWave->m_bSampleType & SFORMAT_16)
|
|
{
|
|
m_pWave->m_pnWave[m_dwSampleLength] = 0;
|
|
}
|
|
else
|
|
{
|
|
char *pBuffer = (char *) m_pWave->m_pnWave;
|
|
pBuffer[m_dwSampleLength] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_dwLoopStart >= m_dwSampleLength)
|
|
{
|
|
m_dwLoopStart = 0;
|
|
}
|
|
if (m_pWave->m_bSampleType & SFORMAT_16)
|
|
{
|
|
m_pWave->m_pnWave[m_dwSampleLength-1] =
|
|
m_pWave->m_pnWave[m_dwLoopStart];
|
|
}
|
|
else
|
|
{
|
|
char *pBuffer = (char *) m_pWave->m_pnWave;
|
|
pBuffer[m_dwSampleLength-1] =
|
|
pBuffer[m_dwLoopStart];
|
|
}
|
|
}
|
|
Verify();
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
CWave::CWave()
|
|
{
|
|
m_hUserData = NULL;
|
|
m_lpFreeHandle = NULL;
|
|
m_pnWave = NULL;
|
|
m_dwSampleRate = 22050;
|
|
m_bSampleType = SFORMAT_16;
|
|
m_dwSampleLength = 0;
|
|
m_wUsageCount = 0;
|
|
m_dwID = 0;
|
|
m_wPlayCount = 0;
|
|
m_bStream = FALSE;
|
|
m_bActive = FALSE;
|
|
m_bLastSampleInit = FALSE;
|
|
m_bValid = FALSE;
|
|
}
|
|
|
|
CWave::~CWave()
|
|
|
|
{
|
|
if (m_pnWave && m_lpFreeHandle)
|
|
{
|
|
m_lpFreeHandle((HANDLE) this,m_hUserData);
|
|
}
|
|
}
|
|
|
|
void CWave::Verify()
|
|
|
|
{
|
|
FORCEBOUNDS(m_dwSampleRate,3000,200000);
|
|
}
|
|
|
|
void CWave::PlayOn()
|
|
|
|
{
|
|
m_wPlayCount++;
|
|
AddRef();
|
|
}
|
|
|
|
void CWave::PlayOff()
|
|
|
|
{
|
|
m_wPlayCount--;
|
|
Release();
|
|
}
|
|
|
|
BOOL CWave::IsPlaying()
|
|
|
|
{
|
|
return (m_wPlayCount);
|
|
}
|
|
|
|
void CWave::AddRef()
|
|
|
|
{
|
|
m_wUsageCount++;
|
|
}
|
|
|
|
void CWave::Release()
|
|
|
|
{
|
|
m_wUsageCount--;
|
|
if (m_wUsageCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
CWaveArt::CWaveArt()
|
|
{
|
|
m_wUsageCount = 1;
|
|
m_dwID = 0;
|
|
m_bSampleType = 0;
|
|
m_bStream = FALSE;
|
|
memset(&m_WaveArtDl,0,sizeof(DMUS_WAVEARTDL));
|
|
memset(&m_WaveformatEx,0,sizeof(WAVEFORMATEX));
|
|
}
|
|
|
|
CWaveArt::~CWaveArt()
|
|
{
|
|
//>>>>>>>>>> clear list
|
|
while(!m_pWaves.IsEmpty())
|
|
{
|
|
CWaveBuffer* pWaveBuffer = m_pWaves.RemoveHead();
|
|
if(pWaveBuffer)
|
|
{
|
|
pWaveBuffer->m_pWave->Release();
|
|
pWaveBuffer->m_pWave = NULL;
|
|
delete pWaveBuffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CWaveArt::AddRef()
|
|
{
|
|
m_wUsageCount++;
|
|
}
|
|
|
|
void CWaveArt::Release()
|
|
{
|
|
m_wUsageCount--;
|
|
if (m_wUsageCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
void CWaveArt::Verify()
|
|
{
|
|
}
|
|
|
|
CSourceRegion::CSourceRegion()
|
|
{
|
|
m_pArticulation = NULL;
|
|
m_vrAttenuation = 0;
|
|
m_prTuning = 0;
|
|
m_bKeyHigh = 127;
|
|
m_bKeyLow = 0;
|
|
m_bGroup = 0;
|
|
m_bAllowOverlap = FALSE;
|
|
m_bVelocityHigh = 127;
|
|
m_bVelocityLow = 0;
|
|
m_dwChannel = 0;
|
|
m_sWaveLinkOptions = 0;
|
|
}
|
|
|
|
CSourceRegion::~CSourceRegion()
|
|
{
|
|
if (m_pArticulation)
|
|
{
|
|
m_pArticulation->Release();
|
|
}
|
|
}
|
|
|
|
void CSourceRegion::SetSampleRate(DWORD dwSampleRate)
|
|
|
|
{
|
|
if (m_pArticulation != NULL)
|
|
{
|
|
m_pArticulation->SetSampleRate(dwSampleRate);
|
|
}
|
|
}
|
|
|
|
void CSourceRegion::Verify()
|
|
|
|
{
|
|
FORCEBOUNDS(m_bKeyHigh,0,127);
|
|
FORCEBOUNDS(m_bKeyLow,0,127);
|
|
FORCEBOUNDS(m_prTuning,-12000,12000);
|
|
FORCEBOUNDS(m_vrAttenuation,-9600,0);
|
|
m_Sample.Verify();
|
|
if (m_pArticulation != NULL)
|
|
{
|
|
m_pArticulation->Verify();
|
|
}
|
|
}
|
|
|
|
CInstrument::CInstrument()
|
|
{
|
|
m_dwProgram = 0;
|
|
}
|
|
|
|
CInstrument::~CInstrument()
|
|
{
|
|
while (!m_RegionList.IsEmpty())
|
|
{
|
|
CSourceRegion *pRegion = m_RegionList.RemoveHead();
|
|
delete pRegion;
|
|
}
|
|
}
|
|
|
|
void CInstrument::Verify()
|
|
|
|
{
|
|
CSourceRegion *pRegion = m_RegionList.GetHead();
|
|
CSourceArticulation *pArticulation = NULL;
|
|
for (;pRegion != NULL;pRegion = pRegion->GetNext())
|
|
{
|
|
if (pRegion->m_pArticulation != NULL)
|
|
{
|
|
pArticulation = pRegion->m_pArticulation;
|
|
}
|
|
pRegion->Verify();
|
|
}
|
|
pRegion = m_RegionList.GetHead();
|
|
for (;pRegion != NULL;pRegion = pRegion->GetNext())
|
|
{
|
|
if (pRegion->m_pArticulation == NULL && pArticulation)
|
|
{
|
|
pRegion->m_pArticulation = pArticulation;
|
|
pArticulation->AddRef();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CInstrument::SetSampleRate(DWORD dwSampleRate)
|
|
|
|
{
|
|
CSourceRegion *pRegion = m_RegionList.GetHead();
|
|
for (;pRegion;pRegion = pRegion->GetNext())
|
|
{
|
|
pRegion->SetSampleRate(dwSampleRate);
|
|
}
|
|
}
|
|
|
|
CSourceRegion * CInstrument::ScanForRegion(DWORD dwNoteValue, DWORD dwVelocity, CSourceRegion *pRegion)
|
|
|
|
{
|
|
if ( pRegion == NULL )
|
|
pRegion = m_RegionList.GetHead(); // Starting search
|
|
else
|
|
pRegion = pRegion->GetNext(); // Continuing search through the rest of the regions
|
|
|
|
for (;pRegion;pRegion = pRegion->GetNext())
|
|
{
|
|
if (dwNoteValue >= pRegion->m_bKeyLow &&
|
|
dwNoteValue <= pRegion->m_bKeyHigh &&
|
|
dwVelocity >= pRegion->m_bVelocityLow &&
|
|
dwVelocity <= pRegion->m_bVelocityHigh )
|
|
{
|
|
break ;
|
|
}
|
|
}
|
|
return pRegion;
|
|
}
|
|
|
|
void CInstManager::SetSampleRate(DWORD dwSampleRate)
|
|
|
|
{
|
|
DWORD dwIndex;
|
|
m_dwSampleRate = dwSampleRate;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
for (dwIndex = 0; dwIndex < INSTRUMENT_HASH_SIZE; dwIndex++)
|
|
{
|
|
CInstrument *pInstrument = m_InstrumentList[dwIndex].GetHead();
|
|
for (;pInstrument != NULL; pInstrument = pInstrument->GetNext())
|
|
{
|
|
pInstrument->SetSampleRate(dwSampleRate);
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
}
|
|
|
|
CInstManager::CInstManager()
|
|
|
|
{
|
|
m_dwSampleRate = 22050;
|
|
m_fCSInitialized = FALSE;
|
|
InitializeCriticalSection(&m_CriticalSection);
|
|
// Note: on pre-Blackcomb OS's, this call can raise an exception; if it
|
|
// ever pops in stress, we can add an exception handler and retry loop.
|
|
m_fCSInitialized = TRUE;
|
|
m_dwSynthMemUse = 0;
|
|
}
|
|
|
|
CInstManager::~CInstManager()
|
|
|
|
{
|
|
if (m_fCSInitialized)
|
|
{
|
|
DWORD dwIndex;
|
|
for (dwIndex = 0; dwIndex < INSTRUMENT_HASH_SIZE; dwIndex++)
|
|
{
|
|
while (!m_InstrumentList[dwIndex].IsEmpty())
|
|
{
|
|
CInstrument *pInstrument = m_InstrumentList[dwIndex].RemoveHead();
|
|
delete pInstrument;
|
|
}
|
|
}
|
|
for (dwIndex = 0; dwIndex < WAVE_HASH_SIZE; dwIndex++)
|
|
{
|
|
while (!m_WavePool[dwIndex].IsEmpty())
|
|
{
|
|
CWave *pWave = m_WavePool[dwIndex].RemoveHead();
|
|
pWave->Release();
|
|
}
|
|
}
|
|
while (!m_FreeWavePool.IsEmpty())
|
|
{
|
|
CWave *pWave = m_FreeWavePool.RemoveHead();
|
|
pWave->Release();
|
|
}
|
|
|
|
for(int nCount = 0; nCount < WAVEART_HASH_SIZE; nCount++)
|
|
{
|
|
while(!m_WaveArtList[nCount].IsEmpty())
|
|
{
|
|
CWaveArt* pWaveArt = m_WaveArtList[nCount].RemoveHead();
|
|
if(pWaveArt)
|
|
{
|
|
pWaveArt->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
DeleteCriticalSection(&m_CriticalSection);
|
|
}
|
|
}
|
|
|
|
void CInstManager::Verify()
|
|
|
|
{
|
|
DWORD dwIndex;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
for (dwIndex = 0;dwIndex < INSTRUMENT_HASH_SIZE; dwIndex++)
|
|
{
|
|
CInstrument *pInstrument = m_InstrumentList[dwIndex].GetHead();
|
|
for (;pInstrument != NULL;pInstrument = pInstrument->GetNext())
|
|
{
|
|
pInstrument->Verify();
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
}
|
|
|
|
CInstrument * CInstManager::GetInstrument(DWORD dwProgram, DWORD dwKey, DWORD dwVelocity)
|
|
|
|
{
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
CInstrument *pInstrument = m_InstrumentList[dwProgram % INSTRUMENT_HASH_SIZE].GetHead();
|
|
for (;pInstrument != NULL; pInstrument = pInstrument->GetNext())
|
|
{
|
|
if (pInstrument->m_dwProgram == dwProgram)
|
|
{
|
|
if (pInstrument->ScanForRegion(dwKey, dwVelocity, NULL) != NULL)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Trace(2,"Warning: No region was found in instrument # %lx that matched note %ld\n",
|
|
dwProgram,dwKey);
|
|
}
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return (pInstrument);
|
|
}
|
|
|
|
|
|
DWORD TimeCents2Samples(long tcTime, DWORD dwSampleRate)
|
|
{
|
|
if (tcTime == 0x80000000) return (0);
|
|
double flTemp = tcTime;
|
|
flTemp /= (65536 * 1200);
|
|
flTemp = pow(2.0,flTemp);
|
|
flTemp *= dwSampleRate;
|
|
return (DWORD) flTemp;
|
|
}
|
|
|
|
DWORD PitchCents2PitchFract(long pcRate,DWORD dwSampleRate)
|
|
|
|
{
|
|
double fTemp = pcRate;
|
|
fTemp /= 65536;
|
|
fTemp -= 6900;
|
|
fTemp /= 1200;
|
|
fTemp = pow(2.0,fTemp);
|
|
fTemp *= 7381975040.0; // (440*256*16*4096);
|
|
fTemp /= dwSampleRate;
|
|
return (DWORD) (fTemp);
|
|
}
|
|
|
|
HRESULT CSourceArticulation::Download(DMUS_DOWNLOADINFO * pInfo,
|
|
void * pvOffsetTable[],
|
|
DWORD dwIndex,
|
|
DWORD dwSampleRate,
|
|
BOOL fNewFormat)
|
|
{
|
|
if (fNewFormat)
|
|
{
|
|
DMUS_ARTICULATION2 * pdmArtic =
|
|
(DMUS_ARTICULATION2 *) pvOffsetTable[dwIndex];
|
|
while (pdmArtic)
|
|
{
|
|
if (pdmArtic->ulArtIdx)
|
|
{
|
|
if (pdmArtic->ulArtIdx >= pInfo->dwNumOffsetTableEntries)
|
|
{
|
|
Trace(1,"Error: Download failed because articulation chunk has an error.\n");
|
|
return DMUS_E_BADARTICULATION;
|
|
}
|
|
DWORD dwPosition;
|
|
void *pData = pvOffsetTable[pdmArtic->ulArtIdx];
|
|
CONNECTIONLIST * pConnectionList =
|
|
(CONNECTIONLIST *) pData;
|
|
CONNECTION *pConnection;
|
|
dwPosition = sizeof(CONNECTIONLIST);
|
|
for (dwIndex = 0; dwIndex < pConnectionList->cConnections; dwIndex++)
|
|
{
|
|
pConnection = (CONNECTION *) ((BYTE *)pData + dwPosition);
|
|
dwPosition += sizeof(CONNECTION);
|
|
switch (pConnection->usSource)
|
|
{
|
|
case CONN_SRC_NONE :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_LFO_FREQUENCY :
|
|
m_LFO.m_pfFrequency = PitchCents2PitchFract(
|
|
pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_LFO_STARTDELAY :
|
|
m_LFO.m_stDelay = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG1_ATTACKTIME :
|
|
m_VolumeEG.m_stAttack = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG1_DECAYTIME :
|
|
m_VolumeEG.m_stDecay = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG1_SUSTAINLEVEL :
|
|
m_VolumeEG.m_pcSustain =
|
|
(SPERCENT) ((long) (pConnection->lScale >> 16));
|
|
break;
|
|
case CONN_DST_EG1_RELEASETIME :
|
|
m_VolumeEG.m_stRelease = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG2_ATTACKTIME :
|
|
m_PitchEG.m_stAttack = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG2_DECAYTIME :
|
|
m_PitchEG.m_stDecay = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG2_SUSTAINLEVEL :
|
|
m_PitchEG.m_pcSustain =
|
|
(SPERCENT) ((long) (pConnection->lScale >> 16));
|
|
break;
|
|
case CONN_DST_EG2_RELEASETIME :
|
|
m_PitchEG.m_stRelease = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_PAN :
|
|
m_sDefaultPan = (short)
|
|
((long) ((long) pConnection->lScale >> 12) / 125);
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_DST_EG1_DELAYTIME:
|
|
m_VolumeEG.m_stDelay = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG1_HOLDTIME:
|
|
m_VolumeEG.m_stHold = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG2_DELAYTIME:
|
|
m_PitchEG.m_stDelay = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_EG2_HOLDTIME:
|
|
m_PitchEG.m_stHold = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_VIB_FREQUENCY :
|
|
m_LFO2.m_pfFrequency = PitchCents2PitchFract(
|
|
pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_VIB_STARTDELAY :
|
|
m_LFO2.m_stDelay = TimeCents2Samples(
|
|
(TCENT) pConnection->lScale,dwSampleRate);
|
|
break;
|
|
case CONN_DST_FILTER_CUTOFF:
|
|
// First, get the filter cutoff frequency, which is relative to a440.
|
|
m_Filter.m_prCutoff = (PRELS)
|
|
(pConnection->lScale >> 16);
|
|
// Then, calculate the resulting prel, taking into consideration
|
|
// the sample rate and the base of the filter coefficient lookup
|
|
// table, relative to the sample rate (FILTER_FREQ_RANGE).
|
|
// This number can then be used directly look up the coefficients in the
|
|
// filter table.
|
|
m_Filter.m_prCutoffSRAdjust = (PRELS)
|
|
FILTER_FREQ_RANGE - m_Filter.m_prSampleRate + m_Filter.m_prCutoff;
|
|
break;
|
|
case CONN_DST_FILTER_Q:
|
|
m_Filter.m_vrQ = (VRELS)
|
|
(pConnection->lScale >> 16); //>>>>>>>> not really VRELS, but 1/10th's
|
|
m_Filter.m_iQIndex = (DWORD)
|
|
((m_Filter.m_vrQ / 15.0f) + 0.5f);
|
|
break;
|
|
}
|
|
break;
|
|
case CONN_SRC_LFO :
|
|
switch (pConnection->usControl)
|
|
{
|
|
case CONN_SRC_NONE :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_ATTENUATION :
|
|
m_LFO.m_vrVolumeScale = (VRELS)
|
|
((long) ((pConnection->lScale * 10) >> 16));
|
|
break;
|
|
case CONN_DST_PITCH :
|
|
m_LFO.m_prPitchScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_DST_FILTER_CUTOFF:
|
|
m_LFO.m_prCutoffScale = (PRELS)
|
|
(pConnection->lScale >> 16);
|
|
break;
|
|
}
|
|
break;
|
|
case CONN_SRC_CC1 :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_ATTENUATION :
|
|
m_LFO.m_vrMWVolumeScale = (VRELS)
|
|
((long) ((pConnection->lScale * 10) >> 16));
|
|
break;
|
|
case CONN_DST_PITCH :
|
|
m_LFO.m_prMWPitchScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_DST_FILTER_CUTOFF:
|
|
m_LFO.m_prMWCutoffScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_SRC_CHANNELPRESSURE :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_ATTENUATION :
|
|
m_LFO.m_vrCPVolumeScale = (VRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
case CONN_DST_PITCH :
|
|
m_LFO.m_prCPPitchScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_DST_FILTER_CUTOFF:
|
|
m_LFO.m_prCPCutoffScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case CONN_SRC_KEYONVELOCITY :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_EG1_ATTACKTIME :
|
|
m_VolumeEG.m_trVelAttackScale = (TRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
case CONN_DST_EG2_ATTACKTIME :
|
|
m_PitchEG.m_trVelAttackScale = (TRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_DST_FILTER_CUTOFF:
|
|
m_Filter.m_prVelScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
}
|
|
break;
|
|
case CONN_SRC_KEYNUMBER :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_EG1_DECAYTIME :
|
|
m_VolumeEG.m_trKeyDecayScale = (TRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
case CONN_DST_EG2_DECAYTIME :
|
|
m_PitchEG.m_trKeyDecayScale = (TRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_DST_EG1_HOLDTIME :
|
|
m_PitchEG.m_trKeyDecayScale = (TRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
case CONN_DST_EG2_HOLDTIME :
|
|
m_PitchEG.m_trKeyDecayScale = (TRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
case CONN_DST_FILTER_CUTOFF :
|
|
m_Filter.m_prKeyScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
}
|
|
break;
|
|
case CONN_SRC_EG2 :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_PITCH :
|
|
m_PitchEG.m_sScale = (short)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_DST_FILTER_CUTOFF:
|
|
m_PitchEG.m_prCutoffScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* DLS2 */
|
|
case CONN_SRC_VIBRATO :
|
|
switch (pConnection->usControl)
|
|
{
|
|
case CONN_SRC_NONE :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_PITCH :
|
|
m_LFO2.m_prPitchScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
}
|
|
break;
|
|
case CONN_SRC_CC1 :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_PITCH :
|
|
m_LFO2.m_prMWPitchScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
}
|
|
break;
|
|
case CONN_SRC_CHANNELPRESSURE :
|
|
switch (pConnection->usDestination)
|
|
{
|
|
case CONN_DST_PITCH :
|
|
m_LFO2.m_prCPPitchScale = (PRELS)
|
|
((long) (pConnection->lScale >> 16));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pdmArtic->ulNextArtIdx)
|
|
{
|
|
if (pdmArtic->ulNextArtIdx >= pInfo->dwNumOffsetTableEntries)
|
|
{
|
|
Trace(1,"Error: Download failed because articulation chunk has an error.\n");
|
|
return DMUS_E_BADARTICULATION;
|
|
}
|
|
pdmArtic = (DMUS_ARTICULATION2 *) pvOffsetTable[pdmArtic->ulNextArtIdx];
|
|
}
|
|
else
|
|
{
|
|
pdmArtic = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DMUS_ARTICULATION * pdmArtic =
|
|
(DMUS_ARTICULATION *) pvOffsetTable[dwIndex];
|
|
|
|
if (pdmArtic->ulArt1Idx)
|
|
{
|
|
if (pdmArtic->ulArt1Idx >= pInfo->dwNumOffsetTableEntries)
|
|
{
|
|
Trace(1,"Error: Download failed because articulation chunk has an error.\n");
|
|
return DMUS_E_BADARTICULATION;
|
|
}
|
|
DMUS_ARTICPARAMS * pdmArticParams =
|
|
(DMUS_ARTICPARAMS *) pvOffsetTable[pdmArtic->ulArt1Idx];
|
|
|
|
m_LFO.m_pfFrequency = PitchCents2PitchFract(
|
|
pdmArticParams->LFO.pcFrequency,dwSampleRate);
|
|
m_LFO.m_stDelay = TimeCents2Samples(
|
|
(TCENT) pdmArticParams->LFO.tcDelay,dwSampleRate);
|
|
m_LFO.m_vrVolumeScale = (VRELS)
|
|
((long) ((pdmArticParams->LFO.gcVolumeScale * 10) >> 16));
|
|
m_LFO.m_prPitchScale = (PRELS)
|
|
((long) (pdmArticParams->LFO.pcPitchScale >> 16));
|
|
m_LFO.m_vrMWVolumeScale = (VRELS)
|
|
((long) ((pdmArticParams->LFO.gcMWToVolume * 10) >> 16));
|
|
m_LFO.m_prMWPitchScale = (PRELS)
|
|
((long) (pdmArticParams->LFO.pcMWToPitch >> 16));
|
|
|
|
m_VolumeEG.m_stAttack = TimeCents2Samples(
|
|
(TCENT) pdmArticParams->VolEG.tcAttack,dwSampleRate);
|
|
m_VolumeEG.m_stDecay = TimeCents2Samples(
|
|
(TCENT) pdmArticParams->VolEG.tcDecay,dwSampleRate);
|
|
m_VolumeEG.m_pcSustain =
|
|
(SPERCENT) ((long) (pdmArticParams->VolEG.ptSustain >> 16));
|
|
m_VolumeEG.m_stRelease = TimeCents2Samples(
|
|
(TCENT) pdmArticParams->VolEG.tcRelease,dwSampleRate);
|
|
m_VolumeEG.m_trVelAttackScale = (TRELS)
|
|
((long) (pdmArticParams->VolEG.tcVel2Attack >> 16));
|
|
m_VolumeEG.m_trKeyDecayScale = (TRELS)
|
|
((long) (pdmArticParams->VolEG.tcKey2Decay >> 16));
|
|
|
|
m_PitchEG.m_trKeyDecayScale = (TRELS)
|
|
((long) (pdmArticParams->PitchEG.tcKey2Decay >> 16));
|
|
m_PitchEG.m_sScale = (short)
|
|
((long) (pdmArticParams->PitchEG.pcRange >> 16));
|
|
m_PitchEG.m_trVelAttackScale = (TRELS)
|
|
((long) (pdmArticParams->PitchEG.tcVel2Attack >> 16));
|
|
m_PitchEG.m_stAttack = TimeCents2Samples(
|
|
(TCENT) pdmArticParams->PitchEG.tcAttack,dwSampleRate);
|
|
m_PitchEG.m_stDecay = TimeCents2Samples(
|
|
(TCENT) pdmArticParams->PitchEG.tcDecay,dwSampleRate);
|
|
m_PitchEG.m_pcSustain =
|
|
(SPERCENT) ((long) (pdmArticParams->PitchEG.ptSustain >> 16));
|
|
m_PitchEG.m_stRelease = TimeCents2Samples(
|
|
(TCENT) pdmArticParams->PitchEG.tcRelease,dwSampleRate);
|
|
|
|
m_sDefaultPan = (short)
|
|
((long) ((long) pdmArticParams->Misc.ptDefaultPan >> 12) / 125);
|
|
}
|
|
}
|
|
Verify(); // Make sure all parameters are legal.
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSourceRegion::Download(DMUS_DOWNLOADINFO * pInfo,
|
|
void * pvOffsetTable[],
|
|
DWORD *pdwRegionIX,
|
|
DWORD dwSampleRate,
|
|
BOOL fNewFormat)
|
|
{
|
|
DMUS_REGION * pdmRegion = (DMUS_REGION *) pvOffsetTable[*pdwRegionIX];
|
|
*pdwRegionIX = pdmRegion->ulNextRegionIdx; // Clear to avoid loops.
|
|
pdmRegion->ulNextRegionIdx = 0;
|
|
// Read the Region chunk...
|
|
m_bKeyHigh = (BYTE) pdmRegion->RangeKey.usHigh;
|
|
m_bKeyLow = (BYTE) pdmRegion->RangeKey.usLow;
|
|
m_bVelocityHigh = (BYTE) pdmRegion->RangeVelocity.usHigh;
|
|
m_bVelocityLow = (BYTE) pdmRegion->RangeVelocity.usLow;
|
|
|
|
//
|
|
// Fix DLS Designer bug
|
|
// Designer was putting velocity ranges that fail
|
|
// on DLS2 synths
|
|
//
|
|
if ( m_bVelocityHigh == 0 && m_bVelocityLow == 0 )
|
|
m_bVelocityHigh = 127;
|
|
|
|
if (pdmRegion->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE)
|
|
{
|
|
m_bAllowOverlap = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m_bAllowOverlap = FALSE;
|
|
}
|
|
m_bGroup = (BYTE) pdmRegion->usKeyGroup;
|
|
// Now, the WSMP and WLOOP chunks...
|
|
m_vrAttenuation = (short) ((long) ((pdmRegion->WSMP.lAttenuation) * 10) >> 16);
|
|
m_Sample.m_prFineTune = pdmRegion->WSMP.sFineTune;
|
|
m_Sample.m_bMIDIRootKey = (BYTE) pdmRegion->WSMP.usUnityNote;
|
|
if (pdmRegion->WSMP.cSampleLoops == 0)
|
|
{
|
|
m_Sample.m_bOneShot = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m_Sample.m_dwLoopStart = pdmRegion->WLOOP[0].ulStart;
|
|
m_Sample.m_dwLoopEnd = m_Sample.m_dwLoopStart + pdmRegion->WLOOP[0].ulLength;
|
|
m_Sample.m_bOneShot = FALSE;
|
|
m_Sample.m_dwLoopType = pdmRegion->WLOOP[0].ulType;
|
|
}
|
|
m_Sample.m_dwSampleRate = dwSampleRate;
|
|
|
|
m_sWaveLinkOptions = pdmRegion->WaveLink.fusOptions;
|
|
m_dwChannel = pdmRegion->WaveLink.ulChannel;
|
|
|
|
if ( (m_dwChannel != WAVELINK_CHANNEL_LEFT) && !IsMultiChannel() )
|
|
{
|
|
Trace(1, "Download failed: Attempt to use a non-mono channel without setting the multichannel flag.\n");
|
|
return DMUS_E_NOTMONO;
|
|
}
|
|
|
|
m_Sample.m_dwID = (DWORD) pdmRegion->WaveLink.ulTableIndex;
|
|
|
|
// Does it have its own articulation?
|
|
//
|
|
if (pdmRegion->ulRegionArtIdx )
|
|
{
|
|
if (pdmRegion->ulRegionArtIdx >= pInfo->dwNumOffsetTableEntries)
|
|
{
|
|
Trace(1,"Error: Download failed because articulation chunk has an error.\n");
|
|
return DMUS_E_BADARTICULATION;
|
|
}
|
|
|
|
CSourceArticulation *pArticulation = new CSourceArticulation;
|
|
if (pArticulation)
|
|
{
|
|
pArticulation->Init(dwSampleRate);
|
|
HRESULT hr = pArticulation->Download(pInfo, pvOffsetTable,
|
|
pdmRegion->ulRegionArtIdx, dwSampleRate, fNewFormat);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
delete pArticulation;
|
|
return hr;
|
|
}
|
|
m_pArticulation = pArticulation;
|
|
m_pArticulation->AddRef();
|
|
}
|
|
else
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CInstManager::DownloadInstrument(LPHANDLE phDownload,
|
|
DMUS_DOWNLOADINFO *pInfo,
|
|
void *pvOffsetTable[],
|
|
void *pvData,
|
|
BOOL fNewFormat)
|
|
|
|
{
|
|
DMUS_INSTRUMENT *pdmInstrument = (DMUS_INSTRUMENT *) pvData;
|
|
CInstrument *pInstrument = new CInstrument;
|
|
if (pInstrument)
|
|
{
|
|
Trace(3,"Downloading instrument %lx\n",pdmInstrument->ulPatch);
|
|
pInstrument->m_dwProgram = pdmInstrument->ulPatch;
|
|
|
|
DWORD dwRegionIX = pdmInstrument->ulFirstRegionIdx;
|
|
pdmInstrument->ulFirstRegionIdx = 0; // Clear to avoid loops.
|
|
while (dwRegionIX)
|
|
{
|
|
if (dwRegionIX >= pInfo->dwNumOffsetTableEntries)
|
|
{
|
|
Trace(1,"Error: Download failed because instrument has error in region list.\n");
|
|
delete pInstrument;
|
|
return DMUS_E_BADINSTRUMENT;
|
|
}
|
|
CSourceRegion *pRegion = new CSourceRegion;
|
|
if (!pRegion)
|
|
{
|
|
delete pInstrument;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
pInstrument->m_RegionList.AddHead(pRegion);
|
|
HRESULT hr = pRegion->Download(pInfo, pvOffsetTable, &dwRegionIX, m_dwSampleRate, fNewFormat);
|
|
if (FAILED(hr))
|
|
{
|
|
delete pInstrument;
|
|
return hr;
|
|
}
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
CWave *pWave = m_WavePool[pRegion->m_Sample.m_dwID % WAVE_HASH_SIZE].GetHead();
|
|
for (;pWave;pWave = pWave->GetNext())
|
|
{
|
|
if (pRegion->m_Sample.m_dwID == pWave->m_dwID)
|
|
{
|
|
pRegion->m_Sample.m_pWave = pWave;
|
|
pWave->AddRef();
|
|
pRegion->m_Sample.CopyFromWave();
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
}
|
|
if (pdmInstrument->ulGlobalArtIdx)
|
|
{
|
|
if (pdmInstrument->ulGlobalArtIdx >= pInfo->dwNumOffsetTableEntries)
|
|
{
|
|
Trace(1,"Error: Download failed because of out of range articulation chunk.\n");
|
|
delete pInstrument;
|
|
return DMUS_E_BADARTICULATION;
|
|
}
|
|
|
|
CSourceArticulation *pArticulation = new CSourceArticulation;
|
|
if (pArticulation)
|
|
{
|
|
pArticulation->Init(m_dwSampleRate);
|
|
HRESULT hr = pArticulation->Download(pInfo, pvOffsetTable,
|
|
pdmInstrument->ulGlobalArtIdx, m_dwSampleRate, fNewFormat);
|
|
if (FAILED(hr))
|
|
{
|
|
delete pInstrument;
|
|
delete pArticulation;
|
|
return hr;
|
|
}
|
|
for (CSourceRegion *pr = pInstrument->m_RegionList.GetHead();
|
|
pr != NULL;
|
|
pr = pr->GetNext())
|
|
{
|
|
if (pr->m_pArticulation == NULL)
|
|
{
|
|
pr->m_pArticulation = pArticulation;
|
|
pArticulation->AddRef();
|
|
}
|
|
}
|
|
if (!pArticulation->m_wUsageCount)
|
|
{
|
|
delete pArticulation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete pInstrument;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (CSourceRegion *pr = pInstrument->m_RegionList.GetHead();
|
|
pr != NULL;
|
|
pr = pr->GetNext())
|
|
{
|
|
if (pr->m_pArticulation == NULL)
|
|
{
|
|
Trace(1,"Error: Download failed because region has no articulation.\n");
|
|
delete pInstrument;
|
|
return DMUS_E_NOARTICULATION;
|
|
}
|
|
}
|
|
}
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if (pdmInstrument->ulFlags & DMUS_INSTRUMENT_GM_INSTRUMENT)
|
|
{
|
|
pInstrument->SetNext(NULL);
|
|
m_InstrumentList[pInstrument->m_dwProgram % INSTRUMENT_HASH_SIZE].AddTail(pInstrument);
|
|
}
|
|
else
|
|
{
|
|
m_InstrumentList[pInstrument->m_dwProgram % INSTRUMENT_HASH_SIZE].AddHead(pInstrument);
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
*phDownload = (HANDLE) pInstrument;
|
|
return S_OK;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT CInstManager::DownloadWave(LPHANDLE phDownload,
|
|
DMUS_DOWNLOADINFO *pInfo,
|
|
void *pvOffsetTable[],
|
|
void *pvData)
|
|
{
|
|
DMUS_WAVE *pdmWave = (DMUS_WAVE *) pvData;
|
|
if (pdmWave->WaveformatEx.wFormatTag != WAVE_FORMAT_PCM)
|
|
{
|
|
Trace(1,"Error: Download failed because wave data is not PCM format.\n");
|
|
return DMUS_E_NOTPCM;
|
|
}
|
|
|
|
if (pdmWave->WaveformatEx.nChannels != 1)
|
|
{
|
|
Trace(1,"Error: Download failed because wave data is not mono.\n");
|
|
return DMUS_E_NOTMONO;
|
|
}
|
|
|
|
if (pdmWave->ulWaveDataIdx >= pInfo->dwNumOffsetTableEntries)
|
|
{
|
|
Trace(1,"Error: Download failed because wave data is at invalid location.\n");
|
|
return DMUS_E_BADWAVE;
|
|
}
|
|
|
|
CWave *pWave = new CWave;
|
|
if (pWave)
|
|
{
|
|
DMUS_WAVEDATA *pdmWaveData= (DMUS_WAVEDATA *)
|
|
pvOffsetTable[pdmWave->ulWaveDataIdx];
|
|
pWave->m_dwID = pInfo->dwDLId;
|
|
pWave->m_hUserData = NULL;
|
|
pWave->m_lpFreeHandle = NULL;
|
|
pWave->m_dwSampleLength = pdmWaveData->cbSize;
|
|
pWave->m_pnWave = (short *) &pdmWaveData->byData[0];
|
|
pWave->m_dwSampleRate = pdmWave->WaveformatEx.nSamplesPerSec;
|
|
|
|
if (pdmWave->WaveformatEx.wBitsPerSample == 8)
|
|
{
|
|
pWave->m_bSampleType = SFORMAT_8;
|
|
DWORD dwX;
|
|
char *pData = (char *) &pdmWaveData->byData[0];
|
|
for (dwX = 0; dwX < pWave->m_dwSampleLength; dwX++)
|
|
{
|
|
pData[dwX] -= (char) 128;
|
|
}
|
|
}
|
|
else if (pdmWave->WaveformatEx.wBitsPerSample == 16)
|
|
{
|
|
pWave->m_dwSampleLength >>= 1;
|
|
pWave->m_bSampleType = SFORMAT_16;
|
|
}
|
|
else
|
|
{
|
|
Trace(1,"Error: Downloading wave %ld, bad wave format.\n",pInfo->dwDLId);
|
|
delete pWave;
|
|
return DMUS_E_BADWAVE;
|
|
}
|
|
pWave->m_dwSampleLength++; // We always add one sample to the end for interpolation.
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
m_WavePool[pWave->m_dwID % WAVE_HASH_SIZE].AddHead(pWave);
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
*phDownload = (HANDLE) pWave;
|
|
pWave->AddRef();
|
|
|
|
// Track memory usage
|
|
m_dwSynthMemUse += (pWave->m_bSampleType == SFORMAT_16)?pWave->m_dwSampleLength << 1: pWave->m_dwSampleLength;
|
|
Trace(3,"Downloading wave %ld memory usage %ld\n",pInfo->dwDLId,m_dwSynthMemUse);
|
|
|
|
return S_OK;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT CInstManager::Download(LPHANDLE phDownload,
|
|
void * pvData,
|
|
LPBOOL pbFree)
|
|
|
|
|
|
{
|
|
V_INAME(IDirectMusicSynth::Download);
|
|
V_BUFPTR_READ(pvData,sizeof(DMUS_DOWNLOADINFO));
|
|
|
|
HRESULT hr = DMUS_E_UNKNOWNDOWNLOAD;
|
|
void ** ppvOffsetTable; // Array of pointers to chunks in data.
|
|
DMUS_DOWNLOADINFO * pInfo = (DMUS_DOWNLOADINFO *) pvData;
|
|
DMUS_OFFSETTABLE* pOffsetTable = (DMUS_OFFSETTABLE *)(((BYTE*)pvData) + sizeof(DMUS_DOWNLOADINFO));
|
|
char *pcData = (char *) pvData;
|
|
|
|
V_BUFPTR_READ(pvData,pInfo->cbSize);
|
|
|
|
// Code fails if pInfo->dwNumOffsetTableEntries == 0
|
|
// Sanity check here for debug
|
|
assert(pInfo->dwNumOffsetTableEntries);
|
|
|
|
ppvOffsetTable = new void *[pInfo->dwNumOffsetTableEntries];
|
|
if (ppvOffsetTable) // Create the pointer array and validate.
|
|
{
|
|
DWORD dwIndex;
|
|
for (dwIndex = 0; dwIndex < pInfo->dwNumOffsetTableEntries; dwIndex++)
|
|
{
|
|
if (pOffsetTable->ulOffsetTable[dwIndex] >= pInfo->cbSize)
|
|
{
|
|
delete[] ppvOffsetTable;
|
|
Trace(1,"Error: Download failed because of corrupt download tables.\n");
|
|
return DMUS_E_BADOFFSETTABLE; // Bad!
|
|
}
|
|
ppvOffsetTable[dwIndex] = (void *) &pcData[pOffsetTable->ulOffsetTable[dwIndex]];
|
|
}
|
|
if (pInfo->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT) // Instrument.
|
|
{
|
|
*pbFree = TRUE;
|
|
hr = DownloadInstrument(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0],FALSE);
|
|
}
|
|
else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT2) // New instrument format.
|
|
{
|
|
*pbFree = TRUE;
|
|
hr = DownloadInstrument(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0],TRUE);
|
|
}
|
|
else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_WAVE) // Wave.
|
|
{
|
|
*pbFree = FALSE;
|
|
hr = DownloadWave(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
|
|
}
|
|
else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_WAVEARTICULATION) // Wave onshot & streaming
|
|
{
|
|
*pbFree = TRUE;
|
|
hr = DownloadWaveArticulation(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
|
|
}
|
|
else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_STREAMINGWAVE) // Streaming
|
|
{
|
|
*pbFree = FALSE;
|
|
hr = DownloadWaveRaw(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
|
|
}
|
|
else if (pInfo->dwDLType == DMUS_DOWNLOADINFO_ONESHOTWAVE) // Wave onshot
|
|
{
|
|
*pbFree = FALSE;
|
|
hr = DownloadWaveRaw(phDownload, pInfo, ppvOffsetTable, ppvOffsetTable[0]);
|
|
}
|
|
|
|
delete[] ppvOffsetTable;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CInstManager::Unload(HANDLE hDownload,
|
|
HRESULT ( CALLBACK *lpFreeHandle)(HANDLE,HANDLE),
|
|
HANDLE hUserData)
|
|
|
|
{
|
|
DWORD dwIndex;
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
for (dwIndex = 0; dwIndex < INSTRUMENT_HASH_SIZE; dwIndex++)
|
|
{
|
|
CInstrument *pInstrument = m_InstrumentList[dwIndex].GetHead();
|
|
for (;pInstrument != NULL; pInstrument = pInstrument->GetNext())
|
|
{
|
|
if (pInstrument == (CInstrument *) hDownload)
|
|
{
|
|
Trace(3,"Unloading instrument %lx\n",pInstrument->m_dwProgram);
|
|
m_InstrumentList[dwIndex].Remove(pInstrument);
|
|
delete pInstrument;
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
for (dwIndex = 0; dwIndex < WAVE_HASH_SIZE; dwIndex++)
|
|
{
|
|
CWave *pWave = m_WavePool[dwIndex].GetHead();
|
|
for (;pWave != NULL;pWave = pWave->GetNext())
|
|
{
|
|
if (pWave == (CWave *) hDownload)
|
|
{
|
|
// Track memory usage
|
|
m_dwSynthMemUse -= (pWave->m_bSampleType == SFORMAT_16)?pWave->m_dwSampleLength << 1: pWave->m_dwSampleLength;
|
|
|
|
Trace(3,"Unloading wave %ld memory usage %ld\n",pWave->m_dwID,m_dwSynthMemUse);
|
|
m_WavePool[dwIndex].Remove(pWave);
|
|
|
|
pWave->m_hUserData = hUserData;
|
|
pWave->m_lpFreeHandle = lpFreeHandle;
|
|
pWave->Release();
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
for (dwIndex = 0; dwIndex < WAVE_HASH_SIZE; dwIndex++)
|
|
{
|
|
CWaveArt* pWaveArt = m_WaveArtList[dwIndex].GetHead();
|
|
for (;pWaveArt != NULL;pWaveArt = pWaveArt->GetNext())
|
|
{
|
|
if (pWaveArt == (CWaveArt *) hDownload)
|
|
{
|
|
Trace(3,"Unloading wave articulation %ld\n",pWaveArt->m_dwID,m_dwSynthMemUse);
|
|
m_WaveArtList[dwIndex].Remove(pWaveArt);
|
|
|
|
pWaveArt->Release();
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
Trace(1,"Error: Unload failed - downloaded object not found.\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// Directx8 Methods
|
|
|
|
CWave * CInstManager::GetWave(DWORD dwDLId)
|
|
{
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
CWave *pWave = m_WavePool[dwDLId % WAVE_HASH_SIZE].GetHead();
|
|
for (;pWave;pWave = pWave->GetNext())
|
|
{
|
|
if (dwDLId == pWave->m_dwID)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
|
|
return pWave;
|
|
}
|
|
|
|
CWaveArt * CInstManager::GetWaveArt(DWORD dwDLId)
|
|
{
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
CWaveArt *pWaveArt = m_WaveArtList[dwDLId % WAVEART_HASH_SIZE].GetHead();
|
|
for (;pWaveArt;pWaveArt = pWaveArt->GetNext())
|
|
{
|
|
if (dwDLId == pWaveArt->m_dwID)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
|
|
return pWaveArt;
|
|
}
|
|
|
|
HRESULT CInstManager::DownloadWaveArticulation(LPHANDLE phDownload,
|
|
DMUS_DOWNLOADINFO *pInfo,
|
|
void *pvOffsetTable[],
|
|
void *pvData)
|
|
{
|
|
DMUS_WAVEARTDL* pWaveArtDl = (DMUS_WAVEARTDL*)pvData;
|
|
WAVEFORMATEX *pWaveformatEx = (WAVEFORMATEX *) pvOffsetTable[1];
|
|
DWORD *dwDlId = (DWORD*)pvOffsetTable[2];
|
|
DWORD i;
|
|
|
|
CWaveArt* pWaveArt = new CWaveArt();
|
|
if ( pWaveArt )
|
|
{
|
|
pWaveArt->m_dwID = pInfo->dwDLId;
|
|
pWaveArt->m_WaveArtDl = *pWaveArtDl;;
|
|
pWaveArt->m_WaveformatEx = *pWaveformatEx;
|
|
if (pWaveformatEx->wBitsPerSample == 8)
|
|
{
|
|
pWaveArt->m_bSampleType = SFORMAT_8;
|
|
}
|
|
else if (pWaveformatEx->wBitsPerSample == 16)
|
|
{
|
|
pWaveArt->m_bSampleType = SFORMAT_16;
|
|
}
|
|
else
|
|
{
|
|
Trace(1,"Error: Download failed because wave data is %ld bits instead of 8 or 16.\n",(long) pWaveformatEx->wBitsPerSample);
|
|
delete pWaveArt;
|
|
return DMUS_E_BADWAVE;
|
|
}
|
|
|
|
for ( i = 0; i < pWaveArtDl->ulBuffers; i++ )
|
|
{
|
|
// Get wave buffer and fill header with waveformat data
|
|
CWave *pWave = GetWave(dwDlId[i]);
|
|
assert(pWave);
|
|
|
|
if (!pWave)
|
|
{
|
|
delete pWaveArt;
|
|
return E_POINTER;
|
|
}
|
|
|
|
pWave->m_dwSampleRate = pWaveformatEx->nSamplesPerSec;
|
|
|
|
if (pWaveformatEx->wBitsPerSample == 8)
|
|
{
|
|
DWORD dwX;
|
|
char *pData = (char *) pWave->m_pnWave;
|
|
for (dwX = 0; dwX < pWave->m_dwSampleLength; dwX++)
|
|
{
|
|
pData[dwX] -= (char) 128;
|
|
}
|
|
pWave->m_bSampleType = SFORMAT_8;
|
|
}
|
|
else if (pWaveformatEx->wBitsPerSample == 16)
|
|
{
|
|
pWave->m_dwSampleLength >>= 1;
|
|
pWave->m_bSampleType = SFORMAT_16;
|
|
}
|
|
else
|
|
{
|
|
Trace(1,"Error: Download failed because wave data is %ld bits instead of 8 or 16.\n",(long) pWaveformatEx->wBitsPerSample);
|
|
delete pWaveArt;
|
|
return DMUS_E_BADWAVE;
|
|
}
|
|
pWave->m_dwSampleLength++; // We always add one sample to the end for interpolation.
|
|
|
|
// Default is to duplicate last sample. This will be overrwritten for
|
|
// streaming waves.
|
|
//
|
|
if (pWave->m_dwSampleLength > 1)
|
|
{
|
|
if (pWave->m_bSampleType == SFORMAT_8)
|
|
{
|
|
char* pb = (char*)pWave->m_pnWave;
|
|
pb[pWave->m_dwSampleLength - 1] = pb[pWave->m_dwSampleLength - 2];
|
|
}
|
|
else
|
|
{
|
|
short *pn = pWave->m_pnWave;
|
|
pn[pWave->m_dwSampleLength - 1] = pn[pWave->m_dwSampleLength - 2];
|
|
}
|
|
}
|
|
|
|
// Create a WaveBuffer listitem and save the wave in and add it to the circular buffer list
|
|
CWaveBuffer* pWavBuf = new CWaveBuffer();
|
|
if ( pWavBuf == NULL )
|
|
{
|
|
delete pWaveArt;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
pWavBuf->m_pWave = pWave;
|
|
|
|
// This Articulation will be handling streaming data
|
|
if ( pWave->m_bStream )
|
|
pWaveArt->m_bStream = TRUE;
|
|
|
|
pWaveArt->m_pWaves.AddTail(pWavBuf);
|
|
}
|
|
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
if (pWaveArt)
|
|
{
|
|
CWaveBuffer* pCurrentBuffer = pWaveArt->m_pWaves.GetHead();
|
|
for (; pCurrentBuffer; pCurrentBuffer = pCurrentBuffer->GetNext() )
|
|
{
|
|
if (pCurrentBuffer->m_pWave)
|
|
{
|
|
pCurrentBuffer->m_pWave->AddRef();
|
|
}
|
|
}
|
|
}
|
|
m_WaveArtList[pWaveArt->m_dwID % WAVEART_HASH_SIZE].AddHead(pWaveArt);
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
|
|
*phDownload = (HANDLE) pWaveArt;
|
|
|
|
return S_OK;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT CInstManager::DownloadWaveRaw(LPHANDLE phDownload,
|
|
DMUS_DOWNLOADINFO *pInfo,
|
|
void *pvOffsetTable[],
|
|
void *pvData)
|
|
{
|
|
CWave *pWave = new CWave;
|
|
if (pWave)
|
|
{
|
|
DMUS_WAVEDATA *pdmWaveData= (DMUS_WAVEDATA *)pvData;
|
|
Trace(3,"Downloading raw wave data%ld\n",pInfo->dwDLId);
|
|
|
|
pWave->m_dwID = pInfo->dwDLId;
|
|
pWave->m_hUserData = NULL;
|
|
pWave->m_lpFreeHandle = NULL;
|
|
pWave->m_dwSampleLength = pdmWaveData->cbSize;
|
|
pWave->m_pnWave = (short *) &pdmWaveData->byData[0];
|
|
|
|
if ( pInfo->dwDLType == DMUS_DOWNLOADINFO_STREAMINGWAVE )
|
|
{
|
|
pWave->m_bStream = TRUE;
|
|
pWave->m_bValid = TRUE;
|
|
}
|
|
|
|
EnterCriticalSection(&m_CriticalSection);
|
|
m_WavePool[pWave->m_dwID % WAVE_HASH_SIZE].AddHead(pWave);
|
|
LeaveCriticalSection(&m_CriticalSection);
|
|
|
|
*phDownload = (HANDLE) pWave;
|
|
pWave->AddRef();
|
|
|
|
m_dwSynthMemUse += pWave->m_dwSampleLength;
|
|
|
|
return S_OK;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|