1491 lines
57 KiB
C++
1491 lines
57 KiB
C++
// Synth.h
|
|
// Copyright (c) 1996-1999 Microsoft Corporation
|
|
//
|
|
|
|
/* For internal representation, volume is stored in Volume Cents,
|
|
where each increment represents 1/100 of a dB.
|
|
Pitch is stored in Pitch Cents, where each increment
|
|
represents 1/100 of a semitone.
|
|
*/
|
|
|
|
#ifndef __SYNTH_H__
|
|
#define __SYNTH_H__
|
|
|
|
#pragma warning(disable:4296)
|
|
|
|
#include "clist.h"
|
|
#include "dmdls.h"
|
|
#include "dls2.h"
|
|
#include "dsound.h"
|
|
#include "dmusicc.h"
|
|
|
|
#ifdef DBG
|
|
extern DWORD sdwDebugLevel;
|
|
#endif
|
|
|
|
#define MIDI_NOTEOFF 0x80
|
|
#define MIDI_NOTEON 0x90
|
|
#define MIDI_PTOUCH 0xA0
|
|
#define MIDI_CCHANGE 0xB0
|
|
#define MIDI_PCHANGE 0xC0
|
|
#define MIDI_MTOUCH 0xD0
|
|
#define MIDI_PBEND 0xE0
|
|
#define MIDI_SYSX 0xF0
|
|
#define MIDI_MTC 0xF1
|
|
#define MIDI_SONGPP 0xF2
|
|
#define MIDI_SONGS 0xF3
|
|
#define MIDI_EOX 0xF7
|
|
#define MIDI_CLOCK 0xF8
|
|
#define MIDI_START 0xFA
|
|
#define MIDI_CONTINUE 0xFB
|
|
#define MIDI_STOP 0xFC
|
|
#define MIDI_SENSE 0xFE
|
|
|
|
// controller numbers
|
|
#define CC_BANKSELECTH 0x00
|
|
#define CC_BANKSELECTL 0x20
|
|
|
|
#define CC_MODWHEEL 0x01
|
|
#define CC_VOLUME 0x07
|
|
#define CC_PAN 0x0A
|
|
#define CC_EXPRESSION 0x0B
|
|
#define CC_SUSTAIN 0x40
|
|
#define CC_CUTOFFFREQ 0x4A
|
|
#define CC_REVERB 0x5B
|
|
#define CC_CHORUS 0x5D
|
|
#define CC_ALLSOUNDSOFF 0x78
|
|
#define CC_RESETALL 0x79
|
|
#define CC_ALLNOTESOFF 0x7B
|
|
#define CC_MONOMODE 0x7E
|
|
#define CC_POLYMODE 0x7F
|
|
|
|
// rpn controllers
|
|
#define CC_DATAENTRYMSB 0x06
|
|
#define CC_DATAENTRYLSB 0x26
|
|
#define CC_NRPN_LSB 0x62
|
|
#define CC_NRPN_MSB 0x63
|
|
#define CC_RPN_LSB 0x64
|
|
#define CC_RPN_MSB 0x65
|
|
|
|
// registered parameter numbers
|
|
#define RPN_PITCHBEND 0x00
|
|
#define RPN_FINETUNE 0x01
|
|
#define RPN_COARSETUNE 0x02
|
|
|
|
/* Sample format and Sample playback flags are organized
|
|
together because together they determine which
|
|
mix loop to use.
|
|
*/
|
|
|
|
#define SFORMAT_16 1 // Sixteen bit sample.
|
|
#define SFORMAT_8 2 // Eight bit sample.
|
|
#define SPLAY_MMX 0x10 // Use MMX processor (16 bit only).
|
|
#define SPLAY_INTERLEAVED 0x40 // Interleave Buffer
|
|
#define SPLAY_FILTERED 0x80 // Non-trivial filter coeff's
|
|
|
|
|
|
/* Output buffer format flags, defines whether the buffers being
|
|
played are multi-buffer, interleave or just plain mono
|
|
*/
|
|
#define BUFFERFLAG_MONO 0x00000000
|
|
#define BUFFERFLAG_INTERLEAVED 0x00000001
|
|
#define BUFFERFLAG_MULTIBUFFER 0x00000002
|
|
|
|
|
|
typedef long PREL; // Pitch cents, for relative pitch.
|
|
typedef short PRELS; // Pitch cents, in storage form.
|
|
typedef long VREL; // Volume cents, for relative volume.
|
|
typedef short VRELS; // Volume cents, in storage form.
|
|
typedef long TREL; // Time cents, for relative time
|
|
typedef short TRELS; // Time Cents, in storage form.
|
|
typedef LONGLONG STIME; // Time value, in samples.
|
|
typedef long MTIME; // Time value, in milliseconds.
|
|
typedef long PFRACT; // Pitch increment, where upper 20 bits are
|
|
// the index and the lower 12 are the fractional
|
|
// component.
|
|
typedef long VFRACT; // Volume, where lower 12 bits are the fraction.
|
|
|
|
typedef long TCENT;
|
|
typedef short SPERCENT;
|
|
|
|
#define COEFF_UNITY 0x40000000 // 1.0 multiplier as a 2.30 number
|
|
typedef unsigned long COEFF; // 2.30 fixed point filter coefficient
|
|
typedef long COEFFDELTA; // 2.30 fixed point filter coefficient delta value
|
|
|
|
#define FILTER_PARMS_DIM_Q 16 // the number of different resonances in the filter parameter table (rows)
|
|
#define FILTER_PARMS_DIM_FC 89 // the number of different cutoff frequencies in the filter parameter table (cols)
|
|
#define FILTER_FREQ_RANGE 10688 // the difference in pitch cents between the sample rate of the filter design and the
|
|
|
|
#define MAX_VOLUME 0 // No attenuation and no amplification
|
|
#define MIN_VOLUME -9600 // Below 96 db down is considered off.
|
|
#define PERCEIVED_MIN_VOLUME -8000 // But, we cheat.
|
|
#define SAMPLE_RATE_22 22050 // 22 kHz is the standard rate.
|
|
#define SAMPLE_RATE_44 44100 // 44 kHz is the high quality rate.
|
|
#define SAMPLE_RATE_11 11025 // 11 kHz should not be allowed!
|
|
#define STEREO_ON 1
|
|
#define STEREO_OFF 0
|
|
|
|
#define MAX_DAUD_CHAN 32
|
|
|
|
#define FORCEBOUNDS(data,min,max) {if (data < min) data = min; else if (data > max) data = max;}
|
|
|
|
class CControlLogic;
|
|
|
|
/*
|
|
>>>>>>>>> comment
|
|
*/
|
|
|
|
class CBusIds
|
|
{
|
|
public:
|
|
CBusIds();
|
|
~CBusIds();
|
|
|
|
HRESULT Initialize();
|
|
HRESULT AssignBuses(LPDWORD pdwBusIds, DWORD dwBusCount);
|
|
|
|
public:
|
|
DWORD m_dwBusCount; // Number of Bus Id's
|
|
DWORD m_dwBusIds[MAX_DAUD_CHAN]; // Array of bus IDs
|
|
};
|
|
|
|
/* CSourceLFO is the file format definition of the LFO in an
|
|
instrument. This is used to represent an LFO as part of
|
|
a specific articulation set within an instrument that
|
|
has been loaded from disk. Once the instrument is chosen
|
|
to play a note, this is also copied into the CVoice
|
|
object.
|
|
*/
|
|
|
|
class CSourceLFO
|
|
{
|
|
public:
|
|
CSourceLFO();
|
|
void Init(DWORD dwSampleRate);
|
|
void SetSampleRate(long lDirection);
|
|
void Verify(); // Verifies that the data is valid.
|
|
PFRACT m_pfFrequency; // Frequency, in increments through the sine table.
|
|
STIME m_stDelay; // How long to delay in sample units.
|
|
VRELS m_vrMWVolumeScale; // Scaling of volume LFO by Mod Wheel.
|
|
PRELS m_prMWPitchScale; // Scaling of pitch LFO by Mod Wheel.
|
|
VRELS m_vrVolumeScale; // Scaling of straight volume signal from LFO.
|
|
PRELS m_prPitchScale; // Scaling of straight pitch signal from LFO.
|
|
|
|
/* DirectX8 members */
|
|
PRELS m_prCPPitchScale; // Scaling of pitch signal from channel pressure.
|
|
VRELS m_vrCPVolumeScale; // Scaling of volume signal from channel pressure.
|
|
//>>>>>>>> comments
|
|
PRELS m_prCutoffScale; // Scaling of Cutoff feq >>>>>>
|
|
PRELS m_prMWCutoffScale; // Scaling of Cutoff feq mod wheel
|
|
PRELS m_prCPCutoffScale; // Scaling of Cutoff feq channel pressure
|
|
};
|
|
|
|
/* CSourceEG is the file format definition of an Envelope
|
|
generator in an instrument.
|
|
*/
|
|
|
|
class CSourceEG
|
|
{
|
|
public:
|
|
CSourceEG();
|
|
void SetSampleRate(long lDirection);
|
|
void Init();
|
|
void Verify(); // Verifies valid data.
|
|
STIME m_stAttack; // Attack rate.
|
|
STIME m_stDecay; // Decay rate.
|
|
STIME m_stRelease; // Release rate.
|
|
TRELS m_trVelAttackScale; // Scaling of attack by note velocity.
|
|
TRELS m_trKeyDecayScale; // Scaling of decay by note value.
|
|
SPERCENT m_pcSustain; // Sustain level.
|
|
short m_sScale; // Scaling of entire signal.
|
|
|
|
/* DLS2 */
|
|
STIME m_stDelay; // Delay rate.
|
|
STIME m_stHold; // Hold rate.
|
|
TRELS m_trKeyHoldScale; // Scaling of Hold by note value.
|
|
//>>>>>>>> comments
|
|
PRELS m_prCutoffScale; // Scaling of Cutoff feq >>>>>>
|
|
};
|
|
|
|
//>>>>>>>> comments
|
|
|
|
class CSourceFilter
|
|
{
|
|
public:
|
|
CSourceFilter();
|
|
void SetSampleRate(long lDirection);
|
|
void Init(DWORD dwSampleRate);
|
|
void Verify();
|
|
|
|
PRELS m_prSampleRate; // Sample rate in cents
|
|
PRELS m_prCutoff; // Cutoff Frequency in absolute pitch
|
|
PRELS m_prCutoffSRAdjust; // Cutoff Frequency adjusted to the sampel rate
|
|
VRELS m_vrQ; // Resonance
|
|
DWORD m_iQIndex; // Q index
|
|
PRELS m_prVelScale; // Scale by key velocity
|
|
PRELS m_prKeyScale; // Scaling by note value.
|
|
};
|
|
|
|
/* CSourceArticulation is the file format definition of
|
|
a complete articulation set: the LFO and two
|
|
envelope generators.
|
|
Since several regions within one Instrument can
|
|
share one articulation, a counter is used to keep
|
|
track of the usage.
|
|
*/
|
|
|
|
class CSourceArticulation
|
|
|
|
{
|
|
public:
|
|
CSourceArticulation();
|
|
HRESULT Download(DMUS_DOWNLOADINFO * pInfo,
|
|
void * pvOffsetTable[], DWORD dwIndex,
|
|
DWORD dwSampleRate, BOOL fNewFormat);
|
|
#ifdef DDUMP
|
|
void Dump(DWORD dwIndent,DWORD dwLevel);
|
|
#endif
|
|
void Init(DWORD dwSampleRate);
|
|
void Verify(); // Verifies valid data.
|
|
void AddRef();
|
|
void Release();
|
|
void SetSampleRate(DWORD dwSampleRate);
|
|
CSourceEG m_PitchEG; // Pitch envelope.
|
|
CSourceEG m_VolumeEG; // Volume envelope.
|
|
CSourceLFO m_LFO; // Low frequency oscillator.
|
|
DWORD m_dwSampleRate;
|
|
WORD m_wUsageCount; // Keeps track of how many times in use.
|
|
short m_sDefaultPan; // default pan (for drums)
|
|
|
|
/* DLS2 */
|
|
CSourceLFO m_LFO2; // Vibrato
|
|
CSourceFilter m_Filter; // Low pass filter
|
|
};
|
|
|
|
/* Since multiple regions may reference
|
|
the same Wave, a reference count is maintained to
|
|
keep track of how many regions are using the sample.
|
|
*/
|
|
|
|
class CWave : public CListItem
|
|
{
|
|
public:
|
|
CWave();
|
|
~CWave();
|
|
#ifdef DDUMP
|
|
void Dump(DWORD dwIndent,DWORD dwLevel);
|
|
#endif
|
|
void Verify(); // Verifies that the data is valid.
|
|
void Release(); // Remove reference.
|
|
void AddRef(); // Add reference.
|
|
void PlayOn(); // Increment play count.
|
|
void PlayOff(); // Decrement play count.
|
|
BOOL IsPlaying(); // Is currently playing?
|
|
CWave * GetNext() {return(CWave *)CListItem::GetNext();};
|
|
DWORD m_dwSampleLength; // Length of sample.
|
|
DWORD m_dwSampleRate;
|
|
HRESULT ( CALLBACK *m_lpFreeHandle)(HANDLE,HANDLE);
|
|
HANDLE m_hUserData; // Used to notify app when wave released.
|
|
short * m_pnWave;
|
|
DWORD m_dwID; // ID for matching wave with regions.
|
|
WORD m_wUsageCount; // Keeps track of how many times in use.
|
|
WORD m_wPlayCount; // Wave is currently being played.
|
|
BYTE m_bSampleType;
|
|
|
|
/* DirectX 8 members */
|
|
BYTE m_bStream; // This wave is used as a streaming buffer
|
|
BYTE m_bActive; // This buffer is currently be used to play out of
|
|
BYTE m_bValid; // Indicates data in the buffer is valid
|
|
BYTE m_bLastSampleInit; // Indicates the the buffers last sample has been initialize
|
|
};
|
|
|
|
|
|
class CWavePool : public CList
|
|
{
|
|
public:
|
|
CWave * GetHead() {return (CWave *)CList::GetHead();};
|
|
CWave * GetItem(DWORD dwID) {return (CWave *)CList::GetItem((LONG)dwID);};
|
|
CWave * RemoveHead() {return (CWave *)CList::RemoveHead();};
|
|
};
|
|
|
|
|
|
/* The CSourceSample class describes one sample in an
|
|
instrument. The sample is referenced by a CSourceRegion
|
|
structure.
|
|
*/
|
|
class Collection;
|
|
|
|
class CSourceSample
|
|
{
|
|
public:
|
|
CSourceSample();
|
|
~CSourceSample();
|
|
BOOL CopyFromWave();
|
|
void Verify(); // Verifies that the data is valid.
|
|
CWave * m_pWave; // Wave in pool.
|
|
DWORD m_dwLoopStart; // Index of start of loop.
|
|
DWORD m_dwLoopEnd; // Index of end of loop.
|
|
DWORD m_dwSampleLength; // Length of sample.
|
|
DWORD m_dwSampleRate; // Sample rate of recording.
|
|
PRELS m_prFineTune; // Fine tune to correct pitch.
|
|
DWORD m_dwID; // Wave pool id.
|
|
BYTE m_bSampleType; // 16 or 8.
|
|
BYTE m_bOneShot; // Is this a one shot sample?
|
|
BYTE m_bMIDIRootKey; // MIDI note number for sample.
|
|
DWORD m_dwLoopType; // WLOOP_TYPE_xxx
|
|
};
|
|
|
|
/* The CSourceRegion class defines a region within an instrument.
|
|
The sample is managed with a pointer instead of an embedded
|
|
sample. This allows multiple regions to use the same
|
|
sample.
|
|
Each region also has an associated articulation. For drums, there
|
|
is a one to one matching. For melodic instruments, all regions
|
|
share the same articulation. So, to manage this, each region
|
|
points to the articulation.
|
|
*/
|
|
|
|
class CSourceRegion : public CListItem
|
|
{
|
|
public:
|
|
CSourceRegion();
|
|
~CSourceRegion();
|
|
#ifdef DDUMP
|
|
void Dump(DWORD dwIndent,DWORD dwLevel);
|
|
#endif
|
|
CSourceRegion *GetNext() {return(CSourceRegion *)CListItem::GetNext();};
|
|
void Verify(); // Verifies that the data is valid.
|
|
void SetSampleRate(DWORD dwSampleRate);
|
|
HRESULT Download(DMUS_DOWNLOADINFO * pInfo, void * pvOffsetTable[],
|
|
DWORD *pdwRegionIX, DWORD dwSampleRate, BOOL fNewFormat);
|
|
CSourceSample m_Sample; // Sample structure.
|
|
CSourceArticulation * m_pArticulation; // Pointer to associated articulation.
|
|
VRELS m_vrAttenuation; // Volume change to apply to sample.
|
|
PRELS m_prTuning; // Pitch shift to apply to sample.
|
|
BYTE m_bAllowOverlap; // Allow overlapping of note.
|
|
BYTE m_bKeyHigh; // Upper note value for region.
|
|
BYTE m_bKeyLow; // Lower note value.
|
|
BYTE m_bGroup; // Logical group (for drums.)
|
|
|
|
/* DLS2 */
|
|
BYTE m_bVelocityHigh; // Upper velocity value for region.
|
|
BYTE m_bVelocityLow; // Lower velocity value.
|
|
SHORT m_sWaveLinkOptions; // Wave link chunk option flags
|
|
DWORD m_dwChannel; // Region channels, from WAVELINK chunk
|
|
|
|
// Channel in m_dwChannel provides voice destination and overrides anything
|
|
// from the articulation.
|
|
//
|
|
inline BOOL IsMultiChannel() const
|
|
{ return (BOOL)(m_sWaveLinkOptions & F_WAVELINK_MULTICHANNEL); }
|
|
};
|
|
|
|
|
|
class CSourceRegionList : public CList
|
|
{
|
|
public:
|
|
CSourceRegion *GetHead() {return (CSourceRegion *)CList::GetHead();};
|
|
CSourceRegion *RemoveHead() {return (CSourceRegion *)CList::RemoveHead();};
|
|
};
|
|
|
|
|
|
/* The CInstrument class is really the file format definition
|
|
of an instrument.
|
|
The CInstrument can be either a Drum or a Melodic instrument.
|
|
If a drum, it has up to 128 pairings of articulations and
|
|
regions. If melodic, all regions share the same articulation.
|
|
ScanForRegion is called by ControlLogic to get the region
|
|
that corresponds to a note.
|
|
*/
|
|
|
|
class CInstManager;
|
|
|
|
class CInstrument : public CListItem
|
|
{
|
|
public:
|
|
CInstrument();
|
|
~CInstrument();
|
|
#ifdef DDUMP
|
|
void Dump(DWORD dwIndent,DWORD dwLevel);
|
|
#endif
|
|
void Init(DWORD dwSampleRate);
|
|
void Verify(); // Verifies that the data is valid.
|
|
CInstrument * GetInstrument(DWORD dwProgram,DWORD dwAccept);
|
|
CInstrument * GetNext() {return(CInstrument *)CListItem::GetNext();};
|
|
void SetSampleRate(DWORD dwSampleRate);
|
|
CSourceRegion * ScanForRegion(DWORD dwNoteValue, DWORD dwVelocity, CSourceRegion *pRegion = NULL);
|
|
CSourceRegionList m_RegionList; // Linked list of regions.
|
|
DWORD m_dwProgram; // Which program change it represents.
|
|
HRESULT LoadRegions( BYTE *p, BYTE *pEnd, DWORD dwSampleRate);
|
|
HRESULT Load( BYTE *p, BYTE *pEnd, DWORD dwSampleRate);
|
|
};
|
|
|
|
class CInstrumentList : public CList
|
|
{
|
|
public:
|
|
CInstrument * GetHead() {return (CInstrument *)CList::GetHead();};
|
|
CInstrument * RemoveHead() {return (CInstrument *)CList::RemoveHead();};
|
|
};
|
|
|
|
class CWaveBufferList;
|
|
class CWaveBuffer : public CListItem
|
|
{
|
|
friend CWaveBufferList;
|
|
public:
|
|
CWaveBuffer()
|
|
{
|
|
}
|
|
CWaveBuffer * GetNext() { return (CWaveBuffer *)CListItem::GetNext();};
|
|
CWaveBuffer * GetNextLoop()
|
|
{
|
|
// Threat the list as a circular list
|
|
CWaveBuffer *pbuf;
|
|
pbuf = (CWaveBuffer *)CListItem::GetNext();
|
|
if ( pbuf == NULL )
|
|
pbuf = (CWaveBuffer *)*m_ppHead;
|
|
|
|
return pbuf;
|
|
};
|
|
|
|
CWave * m_pWave; // pointer to wave object
|
|
protected:
|
|
CListItem** m_ppHead;
|
|
};
|
|
|
|
class CWaveBufferList : public CList
|
|
{
|
|
public:
|
|
CWaveBuffer *GetHead() {return (CWaveBuffer *)CList::GetHead();};
|
|
CWaveBuffer *RemoveHead() {return (CWaveBuffer *)CList::RemoveHead();};
|
|
|
|
// Overide these methods so that m_pHead can be added to CWaveBuffer ListItem
|
|
// to allow GetNextLoop() to function as a simple circular buffer list
|
|
void InsertBefore(CListItem *pItem,CWaveBuffer *pInsert) {pInsert->m_ppHead = &m_pHead; CList::Cat(pItem);};
|
|
void Cat(CWaveBuffer *pItem) {pItem->m_ppHead = &m_pHead; CList::Cat(pItem);};
|
|
void AddHead(CWaveBuffer *pItem) {pItem->m_ppHead = &m_pHead; CList::AddHead(pItem);};
|
|
void AddTail(CWaveBuffer *pItem) {pItem->m_ppHead = &m_pHead; CList::AddTail(pItem);};
|
|
};
|
|
|
|
class CWaveArt : public CListItem
|
|
{
|
|
public:
|
|
CWaveArt();
|
|
~CWaveArt();
|
|
void Release(); // Remove reference.
|
|
void AddRef(); // Add reference.
|
|
void Verify(); // Verifies that the data is valid.
|
|
CWaveArt * GetNext() {return(CWaveArt *)CListItem::GetNext();};
|
|
DWORD m_dwID; // ID for matching wave with regions.
|
|
DMUS_WAVEARTDL m_WaveArtDl;
|
|
WAVEFORMATEX m_WaveformatEx;
|
|
CWaveBufferList m_pWaves; // Array of Wave buffers associated with dowload id's
|
|
// DWORD m_dwSampleLength;
|
|
BYTE m_bSampleType;
|
|
BOOL m_bStream; // Is this a streaming articulation
|
|
WORD m_wUsageCount; // Keeps track of how many times in use.
|
|
};
|
|
|
|
class CWaveArtList : public CList
|
|
{
|
|
public:
|
|
CWaveArt * GetHead() {return (CWaveArt *)CList::GetHead();};
|
|
CWaveArt * RemoveHead() {return (CWaveArt *)CList::RemoveHead();};
|
|
};
|
|
|
|
#define WAVE_HASH_SIZE 31 // Keep waves in a hash table of linked lists to speed access.
|
|
#define INSTRUMENT_HASH_SIZE 31 // Same with instruments.
|
|
#define WAVEART_HASH_SIZE 31
|
|
|
|
class CInstManager {
|
|
public:
|
|
CInstManager();
|
|
~CInstManager();
|
|
#ifdef DDUMP
|
|
void Dump(DWORD dwIndent,DWORD dwLevel);
|
|
#endif
|
|
CInstrument * GetInstrument(DWORD dwPatch,DWORD dwKey,DWORD dwVelocity);
|
|
void Verify(); // Verifies that the data is valid.
|
|
void SetSampleRate(DWORD dwSampleRate);
|
|
HRESULT Download(LPHANDLE phDownload,
|
|
void * pvData,
|
|
LPBOOL pbFree);
|
|
HRESULT Unload(HANDLE hDownload,
|
|
HRESULT ( CALLBACK *lpFreeHandle)(HANDLE,HANDLE),
|
|
HANDLE hUserData);
|
|
/* DirectX8 Methods */
|
|
CWave * GetWave(DWORD dwDLId);
|
|
CWaveArt * GetWaveArt(DWORD dwDLId);
|
|
|
|
private:
|
|
HRESULT DownloadInstrument(LPHANDLE phDownload,
|
|
DMUS_DOWNLOADINFO *pInfo,
|
|
void *pvOffsetTable[],
|
|
void *pvData,
|
|
BOOL fNewFormat);
|
|
HRESULT DownloadWave(LPHANDLE phDownload,
|
|
DMUS_DOWNLOADINFO *pInfo,
|
|
void *pvOffsetTable[],
|
|
void *pvData);
|
|
/* DirectX8 Private Methods */
|
|
HRESULT DownloadWaveArticulation(LPHANDLE phDownload,
|
|
DMUS_DOWNLOADINFO *pInfo,
|
|
void *pvOffsetTable[],
|
|
void *pvData);
|
|
HRESULT DownloadWaveRaw(LPHANDLE phDownload,
|
|
DMUS_DOWNLOADINFO *pInfo,
|
|
void *pvOffsetTable[],
|
|
void *pvData);
|
|
CInstrumentList m_InstrumentList[INSTRUMENT_HASH_SIZE];
|
|
CWavePool m_WavePool[WAVE_HASH_SIZE];
|
|
CWavePool m_FreeWavePool; // Track waves still in use, but unloaded.
|
|
DWORD m_dwSampleRate; // Sample rate requested by app.
|
|
|
|
/* DirectX8 Private Memmebers */
|
|
CWaveArtList m_WaveArtList[WAVEART_HASH_SIZE];
|
|
public:
|
|
DWORD m_dwSynthMemUse; /* Memory used by synth wave data */
|
|
|
|
CRITICAL_SECTION m_CriticalSection; // Critical section to manage access.
|
|
BOOL m_fCSInitialized;
|
|
};
|
|
|
|
/* CMIDIRecorder is used to keep track of a time
|
|
slice of MIDI continuous controller events.
|
|
This is subclassed by the PitchBend, Volume,
|
|
Expression, and ModWheel Recorder classes, so
|
|
each of them may reliably manage MIDI events
|
|
coming in.
|
|
CMIDIRecorder uses a linked list of CMIDIData
|
|
structures to keep track of the changes within
|
|
the time slice.
|
|
Allocation and freeing of the CMIDIData events
|
|
is kept fast and efficient because they are
|
|
always pulled from the static pool m_pFreeList,
|
|
which is really a list of events pulled directly
|
|
from the static array m_sEventBuffer. This is
|
|
safe because we can make the assumption that
|
|
the maximum MIDI rate is 1000 events per second.
|
|
Since we are managing time slices of roughly
|
|
1/16 of a second, a buffer of 100 events would
|
|
be overkill.
|
|
Although CMIDIRecorder is subclassed to several
|
|
different event types, they all share the one
|
|
staticly declared free list.
|
|
*/
|
|
|
|
class CMIDIData : public CListItem
|
|
{
|
|
public:
|
|
CMIDIData();
|
|
CMIDIData * GetNext() {return (CMIDIData *)CListItem::GetNext();};
|
|
STIME m_stTime; // Time this event was recorded.
|
|
long m_lData; // Data stored in event.
|
|
};
|
|
|
|
class CMIDIDataList : public CList
|
|
{
|
|
public:
|
|
CMIDIData *GetHead() {return (CMIDIData *)CList::GetHead();};
|
|
CMIDIData *RemoveHead() {return (CMIDIData *)CList::RemoveHead();};
|
|
};
|
|
|
|
|
|
class CMIDIRecorder
|
|
{
|
|
public:
|
|
CMIDIRecorder();
|
|
~CMIDIRecorder(); // Be sure to clear local list.
|
|
BOOL FlushMIDI(STIME stTime); // Clear after time stamp.
|
|
BOOL ClearMIDI(STIME stTime); // Clear up to time stamp.
|
|
BOOL RecordMIDI(STIME stTime, long lData); // MIDI input goes here.
|
|
BOOL RecordMIDINote(STIME stTime, long lData); // MIDI input goes here.
|
|
long GetData(STIME stTime); // Gets data at time.
|
|
|
|
static VREL VelocityToVolume(WORD nVelocity);
|
|
protected:
|
|
static VREL m_vrMIDIToVREL[128]; // Array for converting MIDI to volume.
|
|
static VREL m_vrMIDIPercentToVREL[128]; // Array for converting MIDI reverb and chorus percentages to volume.
|
|
private:
|
|
static DWORD m_sUsageCount; // Keeps track of how many instances so free list can be released.
|
|
public:
|
|
static CMIDIDataList m_sFreeList; // Global free list of events.
|
|
protected:
|
|
CMIDIDataList m_EventList; // This recorder's list.
|
|
STIME m_stCurrentTime; // Time for current value.
|
|
long m_lCurrentData; // Current value.
|
|
};
|
|
|
|
class CNote {
|
|
public:
|
|
STIME m_stTime;
|
|
BYTE m_bPart;
|
|
BYTE m_bKey;
|
|
BYTE m_bVelocity;
|
|
};
|
|
|
|
|
|
// Fake note values held in CNoteIn's queue
|
|
// to indicate changes in the sustain pedal
|
|
// and "all notes off".
|
|
// This is a grab bag for synchronous events
|
|
// that should be queued in time, not simply done as
|
|
// soon as received.
|
|
// By putting them in the note queue, we ensure
|
|
// they are evaluated in the exact same order as
|
|
// the notes themselves.
|
|
|
|
const BYTE NOTE_PROGRAMCHANGE = 0xF1;
|
|
const BYTE NOTE_CC_BANKSELECTH = 0xF2;
|
|
const BYTE NOTE_CC_BANKSELECTL = 0xF3;
|
|
const BYTE NOTE_CC_POLYMODE = 0xF4;
|
|
const BYTE NOTE_CC_MONOMODE = 0xF5;
|
|
const BYTE NOTE_CC_RPN_MSB = 0xF6;
|
|
const BYTE NOTE_CC_RPN_LSB = 0xF7;
|
|
const BYTE NOTE_CC_NRPN = 0xF8;
|
|
const BYTE NOTE_CC_DATAENTRYLSB = 0xF9;
|
|
const BYTE NOTE_CC_DATAENTRYMSB = 0xFA;
|
|
const BYTE NOTE_ASSIGNRECEIVE = 0xFB;
|
|
const BYTE NOTE_MASTERVOLUME = 0xFC;
|
|
const BYTE NOTE_SOUNDSOFF = 0xFD;
|
|
const BYTE NOTE_SUSTAIN = 0xFE;
|
|
const BYTE NOTE_ALLOFF = 0xFF;
|
|
|
|
class CNoteIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
void FlushMIDI(STIME stTime);
|
|
void FlushPart(STIME stTime, BYTE bChannel);
|
|
BOOL RecordNote(STIME stTime, CNote * pNote);
|
|
BOOL RecordEvent(STIME stTime, DWORD dwPart, DWORD dwCommand, BYTE bData);
|
|
BOOL GetNote(STIME stTime, CNote * pNote); // Gets the next note.
|
|
};
|
|
|
|
/* CModWheelIn handles one channel of Mod Wheel
|
|
input. As such, it is not embedded in the CVoice
|
|
class, rather it is in the Channel class.
|
|
CModWheelIn's task is simple: keep track of MIDI
|
|
Mod Wheel events, each tagged with millisecond
|
|
time and value, and return the value for a specific
|
|
time request.
|
|
CModWheelIn inherits almost all of its functionality
|
|
from the CMIDIRecorder Class.
|
|
CModWheelIn receives MIDI mod wheel events through
|
|
the RecordMIDI() command, which stores the
|
|
time and value of the event.
|
|
CModWheelIn is called by CVoiceLFO to get the
|
|
current values for the mod wheel to set the amount
|
|
of LFO modulation for pitch and volume.
|
|
*/
|
|
|
|
class CModWheelIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
DWORD GetModulation(STIME stTime); // Gets the current Mod Wheel value.
|
|
};
|
|
|
|
/* CPitchBendIn handles one channel of Pitch Bend
|
|
input. Like the Mod Wheel module, it inherits
|
|
its abilities from the CMIDIRecorder class.
|
|
It has one additional routine, GetPitch(),
|
|
which returns the current pitch bend value.
|
|
*/
|
|
|
|
class CPitchBendIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
CPitchBendIn();
|
|
PREL GetPitch(STIME stTime); // Gets the current pitch in pitch cents.
|
|
|
|
// current pitch bend range. Note that this is not timestamped!
|
|
PREL m_prRange;
|
|
};
|
|
|
|
/* CVolumeIn handles one channel of Volume
|
|
input. It inherits its abilities from
|
|
the CMIDIRecorder class.
|
|
It has one additional routine, GetVolume(),
|
|
which returns the volume in decibels at the
|
|
specified time.
|
|
*/
|
|
|
|
class CVolumeIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
CVolumeIn();
|
|
VREL GetVolume(STIME stTime); // Gets the current volume in db cents.
|
|
};
|
|
|
|
/* CExpressionIn handles one channel of Expression
|
|
input. It inherits its abilities from
|
|
the CMIDIRecorder class.
|
|
It has one additional routine, GetVolume(),
|
|
which returns the volume in decibels at the
|
|
specified time.
|
|
*/
|
|
|
|
class CExpressionIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
CExpressionIn();
|
|
VREL GetVolume(STIME stTime); // Gets the current volume in db cents.
|
|
};
|
|
|
|
/* CPanIn handles one channel of Volume
|
|
input. It inherits its abilities from
|
|
the CMIDIRecorder class.
|
|
It has one additional routine, GetPan(),
|
|
which returns the pan position (MIDI value)
|
|
at the specified time.
|
|
*/
|
|
|
|
class CPanIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
CPanIn();
|
|
long GetPan(STIME stTime); // Gets the current pan.
|
|
};
|
|
|
|
/* CProgramIn handles one channel of Program change
|
|
input. It inherits its abilities from
|
|
the CMIDIRecorder class.
|
|
Unlike the other controllers, it actually
|
|
records a series of bank select and program
|
|
change events, so it's job is a little
|
|
more complex. Three routines handle the
|
|
recording of the three different commands (bank 1,
|
|
bank 2, program change).
|
|
*/
|
|
|
|
/*class CProgramIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
CProgramIn();
|
|
DWORD GetProgram(STIME stTime); // Gets the current program change.
|
|
BOOL RecordBankH(BYTE bBank1);
|
|
BOOL RecordBankL(BYTE bBank2);
|
|
BOOL RecordProgram(STIME stTime, BYTE bProgram);
|
|
private:
|
|
BYTE m_bBankH;
|
|
BYTE m_bBankL;
|
|
};*/
|
|
|
|
/* CPressureIn handles one channel of Channel Pressure
|
|
input. As such, it is not embedded in the CVoice
|
|
class, rather it is in the Channel class.
|
|
CPressureIn's task is simple: keep track of MIDI
|
|
Channel Pressure events, each tagged with millisecond
|
|
time and value, and return the value for a specific
|
|
time request.
|
|
CPressureIn inherits almost all of its functionality
|
|
from the CMIDIRecorder Class.
|
|
CPressureIn receives MIDI Channel Pressure events through
|
|
the RecordMIDI() command, which stores the
|
|
time and value of the event.
|
|
CPressureIn is called by CVoiceLFO to get the
|
|
current values for the channel pressure to set the amount
|
|
of LFO modulation for pitch.
|
|
*/
|
|
|
|
class CPressureIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
DWORD GetPressure(STIME stTime); // Gets the current channel pressure value.
|
|
};
|
|
|
|
//>>>> comment
|
|
class CReverbIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
CReverbIn();
|
|
DWORD GetVolume(STIME stTime); // Gets the current reverb attenuation.
|
|
};
|
|
|
|
//>>>> comment
|
|
class CChorusIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
DWORD GetVolume(STIME stTime); // Gets the current chorus attenuation.
|
|
};
|
|
|
|
//>>>> comment
|
|
class CCutOffFreqIn : public CMIDIRecorder
|
|
{
|
|
public:
|
|
CCutOffFreqIn();
|
|
DWORD GetFrequency(STIME stTime); // Gets the current pan.
|
|
};
|
|
|
|
class CWaveEvent {
|
|
public:
|
|
CWaveEvent() :
|
|
m_stTime(0),
|
|
m_bPart(0),
|
|
m_dwVoiceId(0),
|
|
m_vrVolume(0),
|
|
m_prPitch(0),
|
|
m_pWaveArt(NULL)
|
|
{}
|
|
public:
|
|
STIME m_stTime;
|
|
BYTE m_bPart;
|
|
DWORD m_dwVoiceId;
|
|
VREL m_vrVolume;
|
|
PREL m_prPitch;
|
|
SAMPLE_TIME m_stVoiceStart;
|
|
SAMPLE_TIME m_stLoopStart;
|
|
SAMPLE_TIME m_stLoopEnd;
|
|
CWaveArt* m_pWaveArt;
|
|
};
|
|
|
|
class CWaveData : public CListItem
|
|
{
|
|
public:
|
|
CWaveData();
|
|
CWaveData * GetNext() {return (CWaveData *)CListItem::GetNext();};
|
|
STIME m_stTime; // Time this event was recorded.
|
|
CWaveEvent m_WaveEventData; // Data stored in event.
|
|
};
|
|
|
|
class CWaveDataList : public CList
|
|
{
|
|
public:
|
|
CWaveData *GetHead() {return (CWaveData *)CList::GetHead();};
|
|
CWaveData *RemoveHead() {return (CWaveData *)CList::RemoveHead();};
|
|
};
|
|
|
|
class CWaveIn
|
|
{
|
|
public:
|
|
CWaveIn();
|
|
~CWaveIn(); // Be sure to clear local list.
|
|
// BOOL FlushWave(STIME stTime); // Clear after time stamp.
|
|
BOOL ClearWave(STIME stTime); // Clear up to time stamp.
|
|
BOOL RemoveWave(DWORD dwID); // Remove wave with dwID.
|
|
BOOL RemoveWaveByStopTime(DWORD dwID, STIME stStopTime);
|
|
BOOL RecordWave(STIME stTime, CWaveEvent *pWaveData);
|
|
BOOL GetWave(STIME stTime, CWaveEvent *pWave);
|
|
|
|
private:
|
|
static DWORD m_sUsageCount; // Keeps track of how many instances so free list can be released.
|
|
public:
|
|
static CWaveDataList m_sFreeList; // Global free list of events.
|
|
|
|
protected:
|
|
CWaveDataList m_EventList; // This recorder's list.
|
|
STIME m_stCurrentTime; // Time for current value.
|
|
CWaveEvent m_lCurrentData; // Current value.
|
|
};
|
|
|
|
/* The CVoiceLFO class is used to track the behavior
|
|
of an LFO within a voice. The LFO is hard wired to
|
|
output both volume and pitch values, through separate
|
|
calls to GetVolume and GetPitch.
|
|
It also manages mixing Mod Wheel control of pitch and
|
|
volume LFO output. It tracks the scaling of Mod Wheel
|
|
for each of these in m_nMWVolumeScale and m_nMWPitchScale.
|
|
It calls the Mod Wheel module to get the current values
|
|
if the respective scalings are greater than 0.
|
|
All of the preset values for the LFO are carried in
|
|
the m_CSource field, which is a replica of the file
|
|
CSourceLFO structure. This is initialized with the
|
|
StartVoice call.
|
|
*/
|
|
|
|
class CVoiceLFO
|
|
{
|
|
public:
|
|
CVoiceLFO();
|
|
static void Init(); // Set up sine table.
|
|
STIME StartVoice(CSourceLFO *pSource,
|
|
STIME stStartTime,CModWheelIn * pModWheelIn, CPressureIn * pPressureIn);
|
|
VREL GetVolume(STIME stTime, STIME *pstTime); // Returns volume cents.
|
|
PREL GetPitch(STIME stTime, STIME *pstTime); // Returns pitch cents.
|
|
|
|
/* DirectX8 Methods */
|
|
void Enable(BOOL bEnable) {m_bEnable = bEnable;};
|
|
PREL GetCutoff(STIME stTime); // Return filter cutoff
|
|
|
|
private:
|
|
long GetLevel(STIME stTime, STIME *pstTime);
|
|
CSourceLFO m_Source; // All of the preset information.
|
|
STIME m_stStartTime; // Time the voice started playing.
|
|
CModWheelIn *m_pModWheelIn; // Pointer to Mod Wheel for this channel.
|
|
STIME m_stRepeatTime; // Repeat time for LFO.
|
|
static short m_snSineTable[256]; // Sine lookup table.
|
|
|
|
/* DirectX8 Memmebers */
|
|
CPressureIn *m_pPressureIn; // Pointer to Channel Pressure for this channel.
|
|
BOOL m_bEnable;
|
|
};
|
|
|
|
/* The CVoiceEG class is used to track the behavior of
|
|
an Envelope Generator within a voice. There are two
|
|
EG's, one for pitch and one for volume. However, they
|
|
behave identically.
|
|
All of the preset values for the EG are carried in
|
|
the m_Source field, which is a replica of the file
|
|
CSourceEG structure. This is initialized with the
|
|
StartVoice call.
|
|
*/
|
|
|
|
class CVoiceEG
|
|
{
|
|
public:
|
|
static void Init(); // Set up linear attack table.
|
|
CVoiceEG();
|
|
STIME StartVoice(CSourceEG *pSource, STIME stStartTime,
|
|
WORD nKey, WORD nVelocity, STIME stMinAttack);
|
|
void StopVoice(STIME stTime);
|
|
void QuickStopVoice(STIME stTime, DWORD dwSampleRate);
|
|
VREL GetVolume(STIME stTime, STIME *pstTime); // Returns volume cents.
|
|
PREL GetPitch(STIME stTime, STIME *pstTime); // Returns pitch cents.
|
|
BOOL InAttack(STIME stTime); // is voice still in attack?
|
|
BOOL InRelease(STIME stTime); // is voice in release?
|
|
|
|
/* DirectX8 Methods */
|
|
void Enable(BOOL bEnable) {m_bEnable = bEnable;};
|
|
PREL GetCutoff(STIME stTime); // Return filter cutoff
|
|
|
|
private:
|
|
long GetLevel(STIME stTime, STIME *pstTime, BOOL fVolume);
|
|
CSourceEG m_Source; // Preset values for envelope, copied from file.
|
|
STIME m_stStartTime; // Time note turned on
|
|
STIME m_stStopTime; // Time note turned off
|
|
static short m_snAttackTable[201];
|
|
|
|
/* DirectX8 Memmebers */
|
|
BOOL m_bEnable;
|
|
};
|
|
|
|
//>>>>>>>>>>> comment
|
|
class CVoiceFilter
|
|
{
|
|
public:
|
|
void StartVoice(CSourceFilter *pSource, CVoiceLFO *pLFO, CVoiceEG *pEG, WORD nKey, WORD nVelocity);
|
|
void GetCoeff(STIME stTime, PREL prFreqIn, COEFF &cfK, COEFF &cfB1, COEFF &cfB2);
|
|
BOOL IsFiltered();
|
|
|
|
public:
|
|
CSourceFilter m_Source;
|
|
CVoiceLFO *m_pLFO;
|
|
CVoiceEG *m_pEG;
|
|
CPitchBendIn *m_pPitchBend;
|
|
|
|
PREL m_prVelScale;
|
|
PREL m_prKeyScale;
|
|
|
|
static COEFF m_aK[FILTER_PARMS_DIM_Q][FILTER_PARMS_DIM_FC];
|
|
static COEFF m_aB1[FILTER_PARMS_DIM_Q][FILTER_PARMS_DIM_FC];
|
|
static COEFF m_aB2[FILTER_PARMS_DIM_Q][FILTER_PARMS_DIM_FC];
|
|
};
|
|
|
|
/* The CDigitalAudio class is used to track the playback
|
|
of a sample within a voice.
|
|
It manages the loop points, the pointer to the sample.
|
|
and the base pitch and base volume, which it initially sets
|
|
when called via StartVoice().
|
|
Pitch is stored in a fixed point format, where the leftmost
|
|
20 bits define the sample increment and the right 12 bits
|
|
define the factional increment within the sample. This
|
|
format is also used to track the position in the sample.
|
|
Mix is a critical routine. It is called by the CVoice to blend
|
|
the instrument into the data buffer. It is handed relative change
|
|
values for pitch and volume (semitone cents and decibel
|
|
cents.) These it converts into three linear values:
|
|
Left volume, Right volume, and Pitch.
|
|
It then compares these new values with the values that existed
|
|
for the previous slice and divides by the number of samples to
|
|
determine an incremental change at the sample rate.
|
|
Then, in the critical mix loop, these are added to the
|
|
volume and pitch indices to give a smooth linear slope to the
|
|
change in volume and pitch.
|
|
*/
|
|
|
|
#define MAX_SAMPLE 4095
|
|
#define MIN_SAMPLE (-4096)
|
|
|
|
#define MAXDB 0
|
|
#define MINDB -100
|
|
#define TEST_WRITE_SIZE 3000
|
|
#define TEST_SOURCE_SIZE 44100
|
|
|
|
class CSynth;
|
|
|
|
class CDigitalAudio
|
|
{
|
|
public:
|
|
CDigitalAudio();
|
|
~CDigitalAudio();
|
|
|
|
void ClearVoice();
|
|
STIME StartVoice(CSynth *pSynth,
|
|
CSourceSample *pSample,
|
|
PREL prBasePitch, long lKey);
|
|
STIME StartWave(CSynth *pSynth,
|
|
CWaveArt *pWaveArt,
|
|
PREL prBasePitch,
|
|
SAMPLE_TIME stVoiceStart,
|
|
SAMPLE_TIME stLoopStart,
|
|
SAMPLE_TIME stLoopEnd);
|
|
BOOL Mix(short **ppBuffers,
|
|
DWORD dwInterleaved,
|
|
DWORD dwBufferCount,
|
|
DWORD dwLength,
|
|
VREL vrMaxVolumeDelta,
|
|
VFRACT vrNewVolume[],
|
|
VFRACT vrLastVolume[],
|
|
PREL dwPitch,
|
|
DWORD dwIsFiltered, COEFF cfK, COEFF cfB1, COEFF cfB2);
|
|
|
|
inline void BreakLoop()
|
|
{ m_bOneShot = TRUE; }
|
|
|
|
static void Init(); // Set up lookup tables.
|
|
static PFRACT PRELToPFRACT(PREL prPitch); // Pitch cents to pitch.
|
|
static VFRACT VRELToVFRACT(VREL vrVolume); // dB to absolute.
|
|
|
|
SAMPLE_POSITION GetCurrentPos() {return m_ullSamplesSoFar;};
|
|
|
|
//
|
|
// Optimized Interleaved mixers
|
|
//
|
|
private:
|
|
DWORD Mix8(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaLVolume,
|
|
VFRACT vfDeltaRVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
DWORD Mix16(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaLVolume,
|
|
VFRACT vfDeltaRVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
DWORD MixMono8(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
DWORD MixMono16(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
//
|
|
// Optimized MMX Interleaved mixers
|
|
//
|
|
private:
|
|
DWORD _cdecl Mix8X(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaLVolume,
|
|
VFRACT vfDeltaRVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
DWORD _cdecl Mix16X(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaLVolume,
|
|
VFRACT vfDeltaRVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
DWORD MixMono16X(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
DWORD MixMono8X(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
//
|
|
// Optimize Multi Buffer versions of the mixer
|
|
//
|
|
private:
|
|
DWORD MixMulti8(short *ppBuffer[],
|
|
DWORD dwBufferCount,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaVolume[],
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
DWORD MixMulti8Filter(short *ppBuffer[],
|
|
DWORD dwBufferCount,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaVolume[],
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength,
|
|
COEFF cfdK,
|
|
COEFF cfdB1,
|
|
COEFF cfdB2);
|
|
DWORD MixMulti16(short *ppBuffer[],
|
|
DWORD dwBufferCount,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaVolume[],
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength);
|
|
DWORD MixMulti16Filter(short *ppBuffer[],
|
|
DWORD dwBufferCount,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaVolume[],
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength,
|
|
COEFF cfdK,
|
|
COEFF cfdB1,
|
|
COEFF cfdB2);
|
|
DWORD Mix8Filter(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaLVolume,
|
|
VFRACT vfDeltaRVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength,
|
|
COEFF cfdK,
|
|
COEFF cfdB1,
|
|
COEFF cfdB2);
|
|
DWORD Mix16Filter(short * pBuffer,
|
|
DWORD dwLength,
|
|
DWORD dwDeltaPeriod,
|
|
VFRACT vfDeltaLVolume,
|
|
VFRACT vfDeltaRVolume,
|
|
VFRACT vfLastVolume[],
|
|
PFRACT pfDeltaPitch,
|
|
PFRACT pfSampleLength,
|
|
PFRACT pfLoopLength,
|
|
COEFF cfdK,
|
|
COEFF cfdB1,
|
|
COEFF cfdB2);
|
|
private:
|
|
void BeforeBigSampleMix();
|
|
void AfterBigSampleMix();
|
|
|
|
private:
|
|
CSourceSample m_Source; // Preset values for sample.
|
|
CSynth * m_pSynth; // For access to sample rate, etc.
|
|
|
|
static PFRACT m_spfCents[201]; // Pitch increment lookup.
|
|
static PFRACT m_spfSemiTones[97]; // Four octaves up and down.
|
|
static VFRACT m_svfDbToVolume[(MAXDB - MINDB) * 10 + 1]; // dB conversion table.
|
|
static BOOL m_sfMMXEnabled;
|
|
|
|
private:
|
|
short * m_pnWave; // Private pointer to wave.
|
|
|
|
PFRACT m_pfBasePitch; // Overall pitch.
|
|
PFRACT m_pfLastPitch; // The last pitch value.
|
|
PREL m_prLastPitch; // Same for pitch, in PREL.
|
|
PFRACT m_pfLastSample; // The last sample position.
|
|
PFRACT m_pfLoopStart; // Start of loop.
|
|
PFRACT m_pfLoopEnd; // End of loop.
|
|
PFRACT m_pfSampleLength; // Length of sample buffer.
|
|
BOOL m_fElGrande; // Indicates larger than 1m wave.
|
|
ULONGLONG m_ullLastSample; // Used to track > 1m wave.
|
|
ULONGLONG m_ullLoopStart; // Used to track > 1m wave.
|
|
ULONGLONG m_ullLoopEnd; // Used to track > 1m wave.
|
|
ULONGLONG m_ullSampleLength; // Used to track > 1m wave.
|
|
DWORD m_dwAddressUpper; // Temp storage for upper bits of address.
|
|
BOOL m_bOneShot; // Is the source region we're mixing a one-shot?
|
|
|
|
/* DLS2 filter members */
|
|
COEFF m_cfLastK; // Held filter coefficients
|
|
COEFF m_cfLastB1;
|
|
COEFF m_cfLastB2;
|
|
long m_lPrevSample; // Last two samples, post-filter
|
|
long m_lPrevPrevSample;
|
|
|
|
/* DirectX8 members */
|
|
CWaveBuffer* m_pCurrentBuffer;
|
|
CWaveArt* m_pWaveArt;
|
|
ULONGLONG m_ullSamplesSoFar;
|
|
};
|
|
|
|
|
|
/* The CVoice class pulls together everything needed to perform
|
|
one voice. It has the envelopes, lfo, and sample embedded
|
|
within it.
|
|
|
|
StartVoice() initializes a voice structure for playback. The
|
|
CSourceRegion structure carries the region and sample as well
|
|
as a pointer to the articulation, which is used to set up
|
|
the various articulation modules. It also carries pointers to
|
|
all the MIDI modulation inputs and the values for the note key
|
|
and channel which are used by the parent ControlLogic object
|
|
to match incoming note off events with the right voice.
|
|
*/
|
|
|
|
class CVoice : public CListItem
|
|
{
|
|
public:
|
|
CVoice();
|
|
CVoice * GetNext() {return (CVoice *)CListItem::GetNext();};
|
|
|
|
BOOL StartVoice(CSynth *pControl,
|
|
CSourceRegion *pRegion, STIME stStartTime,
|
|
CModWheelIn * pModWheelIn,
|
|
CPitchBendIn * pPitchBendIn,
|
|
CExpressionIn * pExpressionIn,
|
|
CVolumeIn * pVolumeIn,
|
|
CPanIn * pPanIn,
|
|
CPressureIn * pPressureIn,
|
|
CReverbIn * pReverbSend,
|
|
CChorusIn * pChorusSend,
|
|
CCutOffFreqIn * PCCutOffFreqIn,
|
|
CBusIds * pBusIds,
|
|
WORD nKey,WORD nVelocity,
|
|
VREL vrVolume, // Added for GS
|
|
PREL prPitch); // Added for GS
|
|
|
|
BOOL StartWave(CSynth *pSynth,
|
|
CWaveArt *pWaveArt,
|
|
DWORD dwVoiceId,
|
|
STIME stStartTime,
|
|
CPitchBendIn * pPitchBendIn,
|
|
CExpressionIn * pExpressionIn,
|
|
CVolumeIn * pVolumeIn,
|
|
CPanIn * pPanIn,
|
|
CReverbIn * pReverbSend,
|
|
CChorusIn * pChorusSend,
|
|
CCutOffFreqIn * pCCutOffFreqIn,
|
|
CBusIds * pBusIds,
|
|
VREL vrVolume,
|
|
PREL prPitch,
|
|
SAMPLE_TIME stVoiceStart,
|
|
SAMPLE_TIME stLoopStart,
|
|
SAMPLE_TIME stLoopEnd
|
|
);
|
|
static void Init(); // Initialize LFO, Digital Audio.
|
|
void StopVoice(STIME stTime);// Called on note off event.
|
|
void QuickStopVoice(STIME stTime);// Called to get quick release.
|
|
void SpeedRelease(); // Force an already off envelope to release quickly.
|
|
void ClearVoice(); // Release use of sample.
|
|
void GetNewPitch(STIME stTime, PREL& prPitch);// Return current pitch value
|
|
void GetNewVolume(STIME stTime, VREL& vrVolume, VREL& vrVolumeL, VREL& vrVolumeR, VREL& vrVolumeReverb, VREL& vrVolumeChorus);
|
|
void GetNewCoeff(STIME stTime, PREL& prCutOff, COEFF& cfK, COEFF& cfB1, COEFF& cfB2);
|
|
DWORD Mix(short **ppvBuffer, DWORD dwBufferFlags, DWORD dwLength, STIME stStart,STIME stEnd);
|
|
SAMPLE_POSITION GetCurrentPos();
|
|
|
|
private:
|
|
static VREL m_svrPanToVREL[128];// Converts Pan to db.
|
|
CVoiceLFO m_LFO; // LFO.
|
|
CVoiceEG m_PitchEG; // Pitch Envelope.
|
|
CVoiceEG m_VolumeEG; // Volume Envelope.
|
|
CDigitalAudio m_DigitalAudio; // The Digital Audio Engine structure.
|
|
CPitchBendIn * m_pPitchBendIn; // Pitch bend source.
|
|
CExpressionIn * m_pExpressionIn;// Expression source.
|
|
CVolumeIn * m_pVolumeIn; // Volume source, if allowed to vary
|
|
CPanIn * m_pPanIn; // Pan source, if allowed to vary
|
|
CReverbIn * m_pReverbSend; //>>>> comment
|
|
CChorusIn * m_pChorusSend; //>>>> comment
|
|
CCutOffFreqIn * m_CCutOffFreqIn;//>>>> comment
|
|
CSynth * m_pSynth; // To access sample rate, etc.
|
|
STIME m_stMixTime; // Next time we need a mix.
|
|
STIME m_stLastMix; // Last sample position mixed.
|
|
long m_lDefaultPan; // Default pan
|
|
PREL m_prLastCutOff; // Last cut off value.
|
|
|
|
public:
|
|
DWORD m_dwNoteID; // Unique id to keep all voices that represent layers of one note connected.
|
|
STIME m_stStartTime; // Time the sound starts.
|
|
STIME m_stStopTime; // Time the sound stops.
|
|
STIME m_stWaveStopTime; // Stop time set by direct call to stop the wave voice.
|
|
BOOL m_fInUse; // This is currently in use.
|
|
BOOL m_fNoteOn; // Note is considered on.
|
|
BOOL m_fTag; // Used to track note stealing.
|
|
VREL m_vrVolume; // Volume, used for voice stealing...
|
|
BOOL m_fSustainOn; // Sus pedal kept note on after off event.
|
|
WORD m_nPart; // Part that is playing this (channel).
|
|
WORD m_nKey; // Note played.
|
|
BOOL m_fAllowOverlap; // Allow overlapped note.
|
|
DWORD m_dwGroup; // Group this voice is playing now
|
|
DWORD m_dwProgram; // Bank and Patch choice.
|
|
DWORD m_dwPriority; // Priority.
|
|
CControlLogic * m_pControl; // Which control group is playing voice.
|
|
DWORD m_dwVoiceId; // Used to identify a playing wave
|
|
CSourceRegion *m_pRegion; // Used to determin which region a voice is playing out of
|
|
CVoiceFilter m_Filter; // Low pass filter
|
|
CVoiceLFO m_LFO2; // Vibrato
|
|
CBusIds m_BusIds; // Bus Id's to playe on this voice
|
|
DWORD m_dwLoopType; // Loop type
|
|
BOOL m_fIgnorePan; // If we're part of a multichannel wave/sample
|
|
VREL m_vrLastVolume[MAX_DAUD_CHAN];
|
|
VREL m_vrBaseVolume[MAX_DAUD_CHAN];
|
|
VFRACT m_vfLastVolume[MAX_DAUD_CHAN];
|
|
};
|
|
|
|
|
|
class CVoiceList : public CList
|
|
{
|
|
public:
|
|
CVoice * GetHead() {return (CVoice *)CList::GetHead();};
|
|
CVoice * RemoveHead() {return (CVoice *)CList::RemoveHead();};
|
|
CVoice * GetItem(LONG lIndex) {return (CVoice *) CList::GetItem(lIndex);};
|
|
};
|
|
|
|
/* Finally, ControlLogic is the big Kahuna that manages
|
|
the whole system. It parses incoming MIDI events
|
|
by channel and event type. And, it manages the mixing
|
|
of voices into the buffer.
|
|
|
|
MIDI Input:
|
|
|
|
The most important events are the note on and
|
|
off events. When a note on event comes in,
|
|
ControlLogic searches for an available voice.
|
|
ControlLogic matches the channel and finds the
|
|
instrument on that channel. It then call the instrument's
|
|
ScanForRegion() command which finds the region
|
|
that matches the note. At this point, it can copy
|
|
the region and associated articulation into the
|
|
voice, using the StartVoice command.
|
|
When it receives the sustain pedal command,
|
|
it artificially sets all notes on the channel on
|
|
until a sustain off arrives. To keep track of notes
|
|
that have been shut off while the sustain was on
|
|
it uses an array of 128 shorts, with each bit position
|
|
representing a channel. When the sustain releases,
|
|
it scans through the array and creates a note off for
|
|
each bit that was set.
|
|
It also receives program change events to set the
|
|
instrument choice for the channel. When such
|
|
a command comes in, it consults the softsynth.ini file
|
|
and loads an instrument with the file name described
|
|
in the ini file.
|
|
Additional continuous controller events are managed
|
|
by the CModWheelIn, CPitchBendIn, etc., MIDI input recording
|
|
modules.
|
|
|
|
Mixing:
|
|
|
|
Control Logic is also called to mix the instruments into
|
|
a buffer at regular intervals. The buffer is provided by the
|
|
calling sound driver (initially, AudioMan.)
|
|
Each voice is called to mix its sample into the buffer.
|
|
*/
|
|
|
|
typedef struct PerfStats
|
|
{
|
|
DWORD dwTotalTime;
|
|
DWORD dwTotalSamples;
|
|
DWORD dwNotesLost;
|
|
DWORD dwVoices;
|
|
DWORD dwCPU;
|
|
DWORD dwMaxAmplitude;
|
|
} PerfStats;
|
|
|
|
#define MIX_BUFFER_LEN 500 // Set the sample buffer size to 500 mils
|
|
#define MAX_NUM_VOICES 32
|
|
#define NUM_EXTRA_VOICES 8 // Extra voices for when we overload.
|
|
#define NUM_DEFAULT_BUSES 4
|
|
|
|
|
|
class CControlLogic
|
|
{
|
|
public:
|
|
CControlLogic();
|
|
~CControlLogic();
|
|
HRESULT Init(CInstManager *pInstruments, CSynth *pSynth);
|
|
void ClearAll();
|
|
void Flush(STIME stTime); // Clears all events after time.
|
|
BOOL RecordMIDI(STIME stTime,BYTE bStatus, BYTE bData1, BYTE bData2);
|
|
HRESULT RecordSysEx(DWORD dwSysExLength,BYTE *pSysExData, STIME stTime);
|
|
CSynth * m_pSynth;
|
|
void QueueNotes(STIME stStartTime, STIME stEndTime);
|
|
void ClearMIDI(STIME stEndTime);
|
|
void SetGainAdjust(VREL vrGainAdjust);
|
|
HRESULT SetChannelPriority(DWORD dwChannel,DWORD dwPriority);
|
|
HRESULT GetChannelPriority(DWORD dwChannel,LPDWORD pdwPriority);
|
|
|
|
/* DirectX8 methods */
|
|
void QueueWaves(STIME stEndTime);
|
|
void FlushWaveByStopTime(DWORD dwID, STIME stStopTime);
|
|
BOOL RecordWaveEvent(STIME stTime, BYTE bChannel, DWORD dwVoiceId, VREL prVolume, PREL prPitch, SAMPLE_TIME stVoiceStart, SAMPLE_TIME stLoopStart, SAMPLE_TIME stLoopEnd, CWaveArt* pWaveArt);
|
|
HRESULT AssignChannelToBuses(DWORD dwChannel, LPDWORD pdwBusIds, DWORD dwBusCount);
|
|
|
|
private:
|
|
void GMReset();
|
|
CInstManager * m_pInstruments;
|
|
CNoteIn m_Notes; // All Note ons and offs.
|
|
CModWheelIn m_ModWheel[16]; // Sixteen channels of Mod Wheel.
|
|
CPitchBendIn m_PitchBend[16]; // Sixteen channels of Pitch Bend.
|
|
CVolumeIn m_Volume[16]; // Sixteen channels of Volume.
|
|
CExpressionIn m_Expression[16]; // Sixteen channels of Expression.
|
|
CPanIn m_Pan[16]; // Sixteen channels of Pan.
|
|
CReverbIn m_ReverbSends[16]; //>>>> comment
|
|
CChorusIn m_ChorusSends[16]; //>>>> comment
|
|
CCutOffFreqIn m_CutOffFreqCC[16]; //>>>> comment
|
|
BOOL m_fSustain[16]; // Sustain on / off.
|
|
short m_nCurrentRPN[16]; // RPN number.
|
|
BYTE m_bBankH[16]; // Bank selects for instrument.
|
|
BYTE m_bBankL[16];
|
|
DWORD m_dwProgram[16]; // Instrument choice.
|
|
BOOL m_fEmpty; // Indicates empty lists, no need to flush.
|
|
VREL m_vrGainAdjust; // Final stage gain adjust
|
|
DWORD m_dwPriority[16]; // Priorities for each channel.
|
|
|
|
BOOL m_fXGActive; // Is XG Active?
|
|
BOOL m_fGSActive; // Is GS enabled?
|
|
WORD m_nData[16]; // Used to track RPN reading.
|
|
VREL m_vrMasterVolume; // Master Volume.
|
|
PREL m_prFineTune[16]; // Fine tune for each channel.
|
|
PREL m_prScaleTune[16][12]; // Alternate scale for each channel.
|
|
PREL m_prCoarseTune[16]; // Coarse tune.
|
|
BYTE m_bPartToChannel[16]; // Channel to Part converter.
|
|
BYTE m_bDrums[16]; // Melodic or which drum?
|
|
BOOL m_fMono[16]; // Mono mode?
|
|
|
|
public:
|
|
// This is static to protect the CMIDIRecorder free list, which is also static.
|
|
//
|
|
static CRITICAL_SECTION s_CriticalSection; // Critical section to manage access.
|
|
static BOOL s_fCSInitialized;
|
|
|
|
static BOOL InitCriticalSection();
|
|
static void KillCriticalSection();
|
|
|
|
/* DirectX8 members */
|
|
private:
|
|
CWaveIn m_Waves; // All waves scheduled to be played
|
|
CPressureIn m_Pressure[16]; // Sixteen channels of Channel Pressure.
|
|
CBusIds m_BusIds[16]; // Bus Id's for each channel
|
|
};
|
|
|
|
#endif // __SYNTH_H__
|
|
|