windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/dmstyle/ptrntrk.cpp

1273 lines
46 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) 1998-2001 Microsoft Corporation
//
// File: ptrntrk.cpp
//
//--------------------------------------------------------------------------
// PtrnTrk.cpp : Implementation of the Pattern Track info and state structs
#include "PtrnTrk.h"
#include "dmusici.h"
#include "dmusicf.h"
#include "debug.h"
/////////////////////////////////////////////////////////////////////////////
// PatternTrackState
PatternTrackState::PatternTrackState() :
m_pStyle(NULL),
m_pSegState(NULL),
m_pPerformance(NULL),
m_mtPerformanceOffset(0),
m_dwVirtualTrackID(0),
m_pPatternTrack(NULL),
m_pTrack(NULL),
m_mtCurrentChordTime(0),
m_mtNextChordTime(0),
m_mtLaterChordTime(0),
m_pPattern(NULL),
m_pdwPChannels(NULL),
m_pVariations(NULL),
m_pdwVariationMask(NULL),
m_pdwRemoveVariations(NULL),
m_pmtPartOffset(NULL),
m_nInversionGroupCount(0),
m_fNewPattern(TRUE),
m_fStateActive(TRUE),
// m_fStatePlay(TRUE),
m_pMappings(NULL),
m_ppEventSeek(NULL),
m_dwGroupID(0xffffffff),
m_plVariationSeeds(NULL),
m_nTotalGenerators(0),
m_mtPatternStart(0),
m_dwValidate(0),
m_hrPlayCode(S_OK),
m_pfChangedVariation(NULL)
{
ZeroMemory(&m_NextChord, sizeof(DMUS_CHORD_PARAM));
wcscpy(m_CurrentChord.wszName, L"M7");
m_CurrentChord.wMeasure = 0;
m_CurrentChord.bBeat = 0;
m_CurrentChord.bSubChordCount = 1;
m_CurrentChord.bKey = 12;
m_CurrentChord.dwScale = DEFAULT_SCALE_PATTERN;
m_CurrentChord.bFlags = 0;
for (int n = 0; n < DMUS_MAXSUBCHORD; n++)
{
m_CurrentChord.SubChordList[n].dwChordPattern = DEFAULT_CHORD_PATTERN;
m_CurrentChord.SubChordList[n].dwScalePattern = DEFAULT_SCALE_PATTERN;
m_CurrentChord.SubChordList[n].dwInversionPoints = 0xffffff;
m_CurrentChord.SubChordList[n].dwLevels = 0xffffffff;
m_CurrentChord.SubChordList[n].bChordRoot = 12; // 2C
m_CurrentChord.SubChordList[n].bScaleRoot = 0;
}
for (int i = 0; i < INVERSIONGROUPLIMIT; i++)
m_aInversionGroups[i].m_wGroupID = 0;
}
PatternTrackState::~PatternTrackState()
{
if (m_pmtPartOffset)
delete [] m_pmtPartOffset;
if (m_pdwPChannels)
delete [] m_pdwPChannels;
if (m_pVariations)
delete [] m_pVariations;
if (m_pdwVariationMask)
delete [] m_pdwVariationMask;
if (m_pdwRemoveVariations)
delete [] m_pdwRemoveVariations;
if (m_pMappings) delete [] m_pMappings;
if (m_ppEventSeek) delete [] m_ppEventSeek;
if (m_plVariationSeeds) delete [] m_plVariationSeeds;
if (m_pPattern) m_pPattern->Release();
if (m_pfChangedVariation)
{
delete [] m_pfChangedVariation;
}
}
HRESULT PatternTrackState::InitPattern(CDirectMusicPattern* pTargetPattern, MUSIC_TIME mtNow, CDirectMusicPattern* pOldPattern)
{
m_fNewPattern = TRUE;
m_mtPatternStart = mtNow;
short nPartCount = (short) pTargetPattern->m_PartRefList.GetCount();
// initialize an array to keep track of variations in parts.
// if the current pattern is the same as the previous pattern,
// use the existing array.
if (m_pPattern != pTargetPattern || pOldPattern)
{
////////////////// create an array of variation bools //////////////////
if (m_pfChangedVariation) delete [] m_pfChangedVariation;
m_pfChangedVariation = new bool[nPartCount];
if (!m_pfChangedVariation)
{
return E_OUTOFMEMORY;
}
////////////////// create an array of part offsets //////////////////
if (m_pmtPartOffset != NULL) delete [] m_pmtPartOffset;
m_pmtPartOffset = new MUSIC_TIME[nPartCount];
if (!m_pmtPartOffset)
{
return E_OUTOFMEMORY;
}
////////////////// create an array of seek pointers //////////////////
if (m_ppEventSeek) delete [] m_ppEventSeek;
m_ppEventSeek = new CDirectMusicEventItem*[nPartCount];
if (!m_ppEventSeek)
{
return E_OUTOFMEMORY;
}
////////////////// create and initialize PChannels //////////////////
if (m_pdwPChannels != NULL) delete [] m_pdwPChannels;
m_pdwPChannels = new DWORD[nPartCount];
if (!m_pdwPChannels)
{
return E_OUTOFMEMORY;
}
TListItem<DirectMusicPartRef>* pPartRef = pTargetPattern->m_PartRefList.GetHead();
for (int i = 0; pPartRef != NULL; pPartRef = pPartRef->GetNext(), i++)
{
m_pdwPChannels[i] = pPartRef->GetItemValue().m_dwLogicalPartID;
}
if (!pOldPattern ||
pTargetPattern->m_strName != pOldPattern->m_strName ||
nPartCount != pOldPattern->m_PartRefList.GetCount() )
{
////////////////// create and initialize variations //////////////////
if (m_pVariations != NULL) delete [] m_pVariations;
m_pVariations = new BYTE[nPartCount];
if (!m_pVariations)
{
return E_OUTOFMEMORY;
}
if (m_pdwVariationMask != NULL) delete [] m_pdwVariationMask;
m_pdwVariationMask = new DWORD[nPartCount];
if (!m_pdwVariationMask)
{
return E_OUTOFMEMORY;
}
if (m_pdwRemoveVariations != NULL) delete [] m_pdwRemoveVariations;
m_pdwRemoveVariations = new DWORD[nPartCount];
if (!m_pdwRemoveVariations)
{
return E_OUTOFMEMORY;
}
for (int i = 0; i < nPartCount; i++)
{
m_pdwVariationMask[i] = 0;
if ( (pTargetPattern->m_dwFlags & DMUS_PATTERNF_PERSIST_CONTROL) &&
m_pPatternTrack &&
m_pPatternTrack->m_pVariations &&
m_pPatternTrack->m_pdwRemoveVariations )
{
m_pVariations[i] = m_pPatternTrack->m_pVariations[i];
m_pdwRemoveVariations[i] = m_pPatternTrack->m_pdwRemoveVariations[i];
}
else
{
m_pVariations[i] = -1;
m_pdwRemoveVariations[i] = 0;
}
}
}
}
// initialize the part offset array and seek pointer array.
for (int i = 0; i < nPartCount; i++)
{
m_pmtPartOffset[i] = 0;
m_ppEventSeek[i] = NULL;
}
// Set up the new pattern.
if (m_pPattern != pTargetPattern)
{
pTargetPattern->AddRef();
if (m_pPattern) m_pPattern->Release();
m_pPattern = pTargetPattern;
}
return S_OK;
}
// This assumes the time sig remains constant for the length of the segment.
// If time sigs change, we won't necessarily have one generator per beat, but
// this will still give consistent playback behavior under most circumstances;
// the exception is a controlling segment that interupts somewhere after the
// time signature changes.
HRESULT PatternTrackState::InitVariationSeeds(long lBaseSeed)
{
// Get the Segment length
MUSIC_TIME mtLength = 0;
IDirectMusicSegment* pSegment = NULL;
if (m_pSegState)
{
if (SUCCEEDED(m_pSegState->GetSegment(&pSegment)))
{
pSegment->GetLength(&mtLength);
pSegment->Release();
}
}
else
{
return E_POINTER;
}
// Get the current time sig and use it to get the number of beats in the segment
DirectMusicTimeSig TimeSig = PatternTimeSig();
int nBeats = TimeSig.ClocksToBeat(mtLength);
// Create an array with the required number of beats, and use the Base Seed to
// seed a random number generator at each beat
if (m_plVariationSeeds) delete [] m_plVariationSeeds;
m_plVariationSeeds = new CRandomNumbers[nBeats];
if (!m_plVariationSeeds)
{
m_nTotalGenerators = 0;
return E_OUTOFMEMORY;
}
else
{
m_nTotalGenerators = nBeats;
for (int i = 0; i < nBeats; i++)
{
m_plVariationSeeds[i].Seed(lBaseSeed);
lBaseSeed = m_plVariationSeeds[i].Next();
}
return S_OK;
}
}
HRESULT PatternTrackState::RemoveVariationSeeds()
{
if (m_plVariationSeeds) delete [] m_plVariationSeeds;
m_plVariationSeeds = NULL;
m_nTotalGenerators = 0;
return S_OK;
}
long PatternTrackState::RandomVariation(MUSIC_TIME mtTime, long lModulus)
{
if (m_plVariationSeeds)
{
DirectMusicTimeSig TimeSig = PatternTimeSig();
int nBeat = TimeSig.ClocksToBeat(mtTime);
// In case time sigs change somehow, make sure we get a valid generator
if (nBeat >= m_nTotalGenerators) nBeat = m_nTotalGenerators - 1;
return m_plVariationSeeds[nBeat].Next(lModulus);
}
else
{
// regular old rand...
return rand() % lModulus;
}
}
void PatternTrackState::GetNextChord(MUSIC_TIME mtNow, MUSIC_TIME mtOffset, IDirectMusicPerformance* pPerformance, BOOL fStart, BOOL fSkipVariations)
{
HRESULT hr = S_OK;
{
hr = pPerformance->GetParam(GUID_ChordParam, m_dwGroupID, DMUS_SEG_ANYTRACK, mtNow + mtOffset,
&m_mtNextChordTime, (void*) &m_CurrentChord);
if (SUCCEEDED(hr))
{
m_mtCurrentChordTime = mtNow;
if (m_mtNextChordTime) m_mtNextChordTime += mtNow;
TraceI(4, "[1] Offset: %d Next Chord: %d\n", mtOffset, m_mtNextChordTime);
#ifdef DBG
if (!m_CurrentChord.bSubChordCount)
{
Trace(2, "Warning: Attempt to get a chord resulted in a chord with no subchords.\n");
}
#endif
}
}
// instead of failing here completely, I'll just give m_mtNextChordTime and m_CurrentChord
// fallback values
if (FAILED(hr))
{
m_mtCurrentChordTime = 0;
m_mtNextChordTime = 0;
if (!m_pStyle || !m_pStyle->UsingDX8()) // otherwise use current chord info
{
wcscpy(m_CurrentChord.wszName, L"M7");
m_CurrentChord.wMeasure = 0;
m_CurrentChord.bBeat = 0;
m_CurrentChord.bSubChordCount = 1;
m_CurrentChord.bKey = 12;
m_CurrentChord.dwScale = DEFAULT_SCALE_PATTERN;
m_CurrentChord.SubChordList[0].dwChordPattern = DEFAULT_CHORD_PATTERN;
m_CurrentChord.SubChordList[0].dwScalePattern = DEFAULT_SCALE_PATTERN;
m_CurrentChord.SubChordList[0].dwInversionPoints = 0xffffff;
m_CurrentChord.SubChordList[0].dwLevels = 0xffffffff;
m_CurrentChord.SubChordList[0].bChordRoot = 12; // 2C
m_CurrentChord.SubChordList[0].bScaleRoot = 0;
}
}
TraceI(3, "Current Chord: %d %s [%d] HRESULT: %x\n",
m_CurrentChord.SubChordList[0].bChordRoot, m_CurrentChord.wszName, mtNow, hr);
if (m_mtNextChordTime > 0)
{
hr = pPerformance->GetParam(GUID_ChordParam, m_dwGroupID, DMUS_SEG_ANYTRACK, m_mtNextChordTime + mtOffset,
&m_mtLaterChordTime, (void*) &m_NextChord);
if (SUCCEEDED(hr))
{
if (m_mtLaterChordTime) m_mtLaterChordTime += m_mtNextChordTime;
TraceI(4, "[3] Offset: %d Later Chord: %d\n", mtOffset, m_mtLaterChordTime);
#ifdef DBG
if (!m_NextChord.bSubChordCount)
{
Trace(2, "Warning: Attempt to get a chord resulted in a chord with no subchords.\n");
}
#endif
}
}
if (!fSkipVariations)
{
// Select a variation for each part in the pattern, based on the moaw and the
// previous and next chords.
DWORD dwFlags = 0;
if (m_fNewPattern) dwFlags |= COMPUTE_VARIATIONSF_NEW_PATTERN;
if (fStart) dwFlags |= COMPUTE_VARIATIONSF_START;
if (m_pStyle && m_pStyle->UsingDX8()) dwFlags |= COMPUTE_VARIATIONSF_DX8;
m_pPattern->ComputeVariations(dwFlags, m_CurrentChord, m_NextChord,
m_abVariationGroups, m_pdwVariationMask, m_pdwRemoveVariations, m_pVariations, mtNow, m_mtNextChordTime, this);
m_fNewPattern = FALSE;
if ( (m_pPattern->m_dwFlags & DMUS_PATTERNF_PERSIST_CONTROL) &&
m_pPatternTrack &&
m_pPatternTrack->m_pVariations &&
m_pPatternTrack->m_pdwRemoveVariations)
{
// update track's m_pVariations and m_pdwRemoveVariations (for each part)
for (int i = 0; i < m_pPattern->m_PartRefList.GetCount(); i++)
{
m_pPatternTrack->m_pVariations[i] = m_pVariations[i];
m_pPatternTrack->m_pdwRemoveVariations[i] = m_pdwRemoveVariations[i];
}
}
}
}
DMStyleStruct* PatternTrackState::FindStyle(MUSIC_TIME mtTime, MUSIC_TIME& rmtTime)
{
IDMStyle* pStyle = NULL;
DMStyleStruct* pResult = NULL;
if (m_pPatternTrack && m_pPatternTrack->m_pISList.GetHead())
{
TListItem<StylePair>* pScan = m_pPatternTrack->m_pISList.GetHead();
for(; pScan; pScan = pScan->GetNext())
{
if (pScan->GetItemValue().m_pStyle) break;
}
if (pScan)
{
pStyle = pScan->GetItemValue().m_pStyle;
for(pScan = pScan->GetNext(); pScan; pScan = pScan->GetNext())
{
StylePair& rScan = pScan->GetItemValue();
if (rScan.m_pStyle)
{
if ( mtTime < rScan.m_mtTime) break;
pStyle = rScan.m_pStyle;
}
}
rmtTime = (pScan != NULL) ? pScan->GetItemValue().m_mtTime : 0;
if (pStyle)
{
pStyle->GetStyleInfo((void**)&pResult);
}
else
{
return NULL;
}
}
}
return pResult;
}
DWORD PatternTrackState::Variations(DirectMusicPartRef&, int nPartIndex)
{
return (m_pVariations[nPartIndex] == 0xff) ? 0 : (1 << m_pVariations[nPartIndex]);
}
BOOL PatternTrackState::PlayAsIs()
{
return FALSE;
}
BOOL PatternTrackState::MapPChannel(DWORD dwPChannel, DWORD& dwMapPChannel)
{
for (DWORD dw = 0; dw < m_pPatternTrack->m_dwPChannels; dw++)
{
if (m_pPatternTrack->m_pdwPChannels[dw] == dwPChannel)
{
dwMapPChannel = m_pMappings[dw].m_dwPChannelMap;
return m_pMappings[dw].m_fMute;
}
}
dwMapPChannel = 0;
return FALSE;
}
inline int RandomExp(BYTE bRange)
{
int nResult = 0;
if (0 <= bRange && bRange <= 190)
{
nResult = bRange;
}
else if (191 <= bRange && bRange <= 212)
{
nResult = ((bRange - 190) * 5) + 190;
}
else if (213 <= bRange && bRange <= 232)
{
nResult = ((bRange - 212) * 10) + 300;
}
else // bRange > 232
{
nResult = ((bRange - 232) * 50) + 500;
}
return (rand() % nResult) - (nResult >> 1);
}
HRESULT PatternTrackState::PlayParts(MUSIC_TIME mtStart,
MUSIC_TIME mtEnd,
MUSIC_TIME mtOffset,
REFERENCE_TIME rtOffset,
MUSIC_TIME mtSection,
IDirectMusicPerformance* pPerformance,
DWORD dwPartFlags,
DWORD dwPlayFlags,
bool& rfReLoop)
{
if (dwPlayFlags & DMUS_TRACKF_PLAY_OFF)
{
return S_OK;
}
if (!m_pPattern) // This shouldn't happen
{
return DMUS_E_NOT_INIT;
}
HRESULT hr = S_OK;
bool fClockTime = (dwPartFlags & PLAYPARTSF_CLOCKTIME) ? true : false;
bool fStart = (dwPartFlags & PLAYPARTSF_START) ? true : false;
bool fGetChordStart = fStart;
bool fFirstCall = (dwPartFlags & PLAYPARTSF_FIRST_CALL) ? true : false;
bool fReloop = (dwPartFlags & PLAYPARTSF_RELOOP) ? true : false;
bool fFlush = (dwPartFlags & PLAYPARTSF_FLUSH) ? true : false;
MUSIC_TIME mtNewChord = mtStart;
TListItem<DirectMusicPartRef>* pPartRef = m_pPattern->m_PartRefList.GetHead();
for (short i = 0; pPartRef != NULL; pPartRef = pPartRef->GetNext(), i++)
{
m_pfChangedVariation[i] = false;
MUSIC_TIME mtFinish = mtEnd;
CurveSeek Curves;
MUSIC_TIME mtNow = 0;
DirectMusicPart* pPart = pPartRef->GetItemValue().m_pDMPart;
DirectMusicTimeSig& TimeSig =
(pPart->m_timeSig.m_bBeat != 0) ? pPart->m_timeSig : PatternTimeSig();
MUSIC_TIME mtPartLength = TimeSig.ClocksPerMeasure() * pPart->m_wNumMeasures;
if (fFirstCall)
{
if (fFlush)
{
GetNextMute(pPartRef->GetItemValue().m_dwLogicalPartID, 0, mtStart, mtOffset, pPerformance, fClockTime);
m_ppEventSeek[i] = NULL;
}
if (fStart)
{
m_pmtPartOffset[i] = 0;
}
if (mtPartLength)
{
while (mtStart >= mtSection + m_pmtPartOffset[i] + mtPartLength)
{
m_pmtPartOffset[i] += mtPartLength;
}
}
if (mtFinish > mtSection + m_pmtPartOffset[i] + mtPartLength)
{
rfReLoop = TRUE;
mtFinish = mtSection + m_pmtPartOffset[i] + mtPartLength;
}
}
if (!fReloop || mtFinish > mtSection + m_pmtPartOffset[i] + mtPartLength)
{
if (fReloop)
{
m_pmtPartOffset[i] += mtPartLength;
}
CDirectMusicEventItem* pEvent = NULL;
if (fFirstCall) pEvent = m_ppEventSeek[i];
if (!pEvent) pEvent = pPart->EventList.GetHead();
BumpTime(pEvent, TimeSig, mtSection + m_pmtPartOffset[i], mtNow);
if (pEvent)
{
GetNextMute(pPartRef->GetItemValue().m_dwLogicalPartID, mtStart, mtNow, mtOffset, pPerformance, fClockTime);
}
while (pEvent != NULL && mtNow < mtFinish)
{
if (fFirstCall && fStart &&
mtNow < mtStart &&
pEvent->m_dwEventTag == DMUS_EVENT_CURVE)
{
if (Variations(pPartRef->GetItemValue(), i) &
pEvent->m_dwVariation)
{
TraceI(4, "Found a curve\n");
Curves.AddCurve(pEvent, mtNow);
}
}
if (mtNow >= mtStart)
{
if (mtNow < mtNewChord)
{
// Revert to the chord in effect at mtNow
TraceI(4, "WARNING: Reverting to chord at %d\n", mtNow);
GetNextChord(mtNow, mtOffset, pPerformance, (dwPartFlags & PLAYPARTSF_START) ? true : false);
mtNewChord = mtNow;
}
else if ((mtNow >= m_mtNextChordTime) || m_mtNextChordTime == 0)
{
TraceI(4, "Getting new chord. Now: %d Next: %d\n", mtNow, m_mtNextChordTime);
GetNextChord(mtNow, mtOffset, pPerformance, fGetChordStart);
mtNewChord = mtNow;
fGetChordStart = false;
}
TraceI(4, "Play %d (%d + %d + %d)\n", mtNow, TimeSig.GridToClocks(pEvent->m_nGridStart), mtSection, m_pmtPartOffset[i]);
PlayPatternEvent(
mtNow,
pEvent,
TimeSig,
mtSection + m_pmtPartOffset[i],
mtOffset,
rtOffset,
pPerformance,
i,
pPartRef->GetItemValue(),
fClockTime,
0,
m_pfChangedVariation[i]);
}
pEvent = pEvent->GetNext();
BumpTime(pEvent, TimeSig, mtSection + m_pmtPartOffset[i], mtNow);
MUSIC_TIME mtMute = pEvent ? mtNow : mtFinish - 1;
GetNextMute(pPartRef->GetItemValue().m_dwLogicalPartID, mtStart, mtMute, mtOffset, pPerformance, fClockTime);
}
m_ppEventSeek[i] = pEvent;
// If we've got curve events, send them now
if (fFirstCall && fStart)
{
TraceI(4, "Playing curves (after loop)\n");
Curves.PlayCurves(this,
TimeSig,
mtSection + m_pmtPartOffset[i],
mtOffset,
rtOffset,
pPerformance,
i,
pPartRef->GetItemValue(),
fClockTime,
mtStart - (mtSection + m_pmtPartOffset[i]));
}
}
}
return hr;
}
// when creating a note event, both the passed in offset and the note's offset must
// be added to the note's time
void PatternTrackState::PlayPatternEvent(
MUSIC_TIME mtNow,
CDirectMusicEventItem* pEventItem,
DirectMusicTimeSig& TimeSig,
MUSIC_TIME mtPartOffset,
MUSIC_TIME mtSegmentOffset,
REFERENCE_TIME rtOffset,
IDirectMusicPerformance* pPerformance,
short nPart,
DirectMusicPartRef& rPartRef,
BOOL fClockTime,
MUSIC_TIME mtPartStart,
bool& rfChangedVariation)
{
DMUS_NOTE_PMSG* pNote = NULL;
DMUS_CURVE_PMSG* pCurve = NULL;
DWORD dwMapPChannel = 0;
BOOL fMute = MapPChannel(rPartRef.m_dwLogicalPartID, dwMapPChannel);
if ( (!fMute) &&
(Variations(rPartRef, nPart) & pEventItem->m_dwVariation) )
{
CDMStyleCurve* pCurveEvent = NULL;
CDMStyleNote* pNoteEvent = NULL;
CDMStyleMarker* pMarkerEvent = NULL;
if (pEventItem->m_dwEventTag == DMUS_EVENT_MARKER) // we have a marker event
{
// If we're not ignoring marker events and we've hit a variation stop point that's
// either not chord-aligned or on the chord, then get a new variation.
pMarkerEvent = (CDMStyleMarker*)pEventItem;
if ( (rPartRef.m_pDMPart && (rPartRef.m_pDMPart->m_dwFlags & DMUS_PARTF_USE_MARKERS)) &&
(pMarkerEvent->m_wFlags & DMUS_MARKERF_STOP) &&
(mtNow != m_mtPatternStart) &&
(!(pMarkerEvent->m_wFlags & DMUS_MARKERF_CHORD_ALIGN) ||
(mtNow == m_mtCurrentChordTime) ||
(mtNow == m_mtNextChordTime)) )
{
TraceI(3, "Computing variations at %d Pattern start: %d...\n", mtNow, m_mtPatternStart);
DWORD dwFlags = COMPUTE_VARIATIONSF_NEW_PATTERN | COMPUTE_VARIATIONSF_MARKER;
if ((pMarkerEvent->m_wFlags & DMUS_MARKERF_CHORD_ALIGN))
{
dwFlags |= COMPUTE_VARIATIONSF_CHORD_ALIGN;
}
if (m_pStyle && m_pStyle->UsingDX8()) dwFlags |= COMPUTE_VARIATIONSF_DX8;
if (rfChangedVariation) dwFlags |= COMPUTE_VARIATIONSF_CHANGED;
m_pPattern->ComputeVariationGroup(
rPartRef,
nPart,
dwFlags,
m_CurrentChord,
m_NextChord,
m_abVariationGroups,
m_pdwVariationMask,
m_pdwRemoveVariations,
m_pVariations,
mtNow + pMarkerEvent->m_nTimeOffset,
m_mtNextChordTime,
this);
rfChangedVariation = true;
if ( (m_pPattern->m_dwFlags & DMUS_PATTERNF_PERSIST_CONTROL) &&
m_pPatternTrack &&
m_pPatternTrack->m_pVariations &&
m_pPatternTrack->m_pdwRemoveVariations )
{
// update track's m_pVariations and m_pdwRemoveVariations (for this part)
m_pPatternTrack->m_pVariations[nPart] = m_pVariations[nPart];
m_pPatternTrack->m_pdwRemoveVariations[nPart] = m_pdwRemoveVariations[nPart];
}
}
else
{
TraceI(3, "NOT computing variations at %d Pattern start: %d Chord times: %d, %d Flags: %x\n",
mtNow, m_mtPatternStart, m_mtCurrentChordTime, m_mtNextChordTime, pMarkerEvent->m_wFlags);
}
}
else if (pEventItem->m_dwEventTag == DMUS_EVENT_CURVE) // we have a curve event
{
pCurveEvent = (CDMStyleCurve*)pEventItem;
if (SUCCEEDED(pPerformance->AllocPMsg( sizeof(DMUS_CURVE_PMSG),
(DMUS_PMSG**) &pCurve)))
{
MUSIC_TIME mtSegmentTime = TimeSig.GridToClocks(pCurveEvent->m_nGridStart) +
pCurveEvent->m_nTimeOffset + mtPartOffset;
if (fClockTime)
{
pCurve->wMeasure = 0;
pCurve->bBeat = 0;
pCurve->bGrid = 0;
pCurve->nOffset = pCurveEvent->m_nTimeOffset;
pCurve->rtTime = (mtSegmentTime * REF_PER_MIL) + rtOffset;
pCurve->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
}
else
{
pCurve->wMeasure = (WORD)TimeSig.GridsToMeasure(pCurveEvent->m_nGridStart);
pCurve->bBeat = (BYTE)TimeSig.GridsToBeat(pCurveEvent->m_nGridStart);
pCurve->bGrid = (BYTE)TimeSig.GridOffset(pCurveEvent->m_nGridStart);
pCurve->nOffset = pCurveEvent->m_nTimeOffset;
pCurve->mtTime = mtSegmentTime + mtSegmentOffset;
pCurve->dwFlags = DMUS_PMSGF_MUSICTIME;
}
pCurve->mtResetDuration = pCurveEvent->m_mtResetDuration;
pCurve->mtDuration = pCurveEvent->m_mtDuration;
pCurve->nResetValue = pCurveEvent->m_nResetValue;
pCurve->bFlags = pCurveEvent->m_bFlags;
pCurve->dwType = DMUS_PMSGT_CURVE;
pCurve->dwPChannel = dwMapPChannel;
pCurve->dwVirtualTrackID = m_dwVirtualTrackID; // ??
pCurve->nStartValue = pCurveEvent->m_StartValue; // curve's start value
pCurve->nEndValue = pCurveEvent->m_EndValue; // curve's end value
pCurve->bType = pCurveEvent->m_bEventType; // type of curve
pCurve->bCurveShape = pCurveEvent->m_bCurveShape; // shape of curve
pCurve->bCCData = pCurveEvent->m_bCCData; // CC# if this is a control change type
pCurve->dwGroupID = m_dwGroupID;
pCurve->wParamType = pCurveEvent->m_wParamType;
pCurve->wMergeIndex = pCurveEvent->m_wMergeIndex;
// Set the DX8 flag to indicate the wMergeIndex and wParamType fields are valid.
pCurve->dwFlags |= DMUS_PMSGF_DX8;
if (mtPartStart) // only set on invalidation
{
MUSIC_TIME mtOffset = mtPartOffset + mtSegmentOffset;
if (pCurve->mtTime + pCurve->mtDuration >= mtPartStart + mtOffset)
{
pCurve->mtOriginalStart = pCurve->mtTime;
pCurve->mtTime = mtPartStart + mtOffset;
}
else
{
pCurve->mtResetDuration -= (mtPartStart + mtOffset - pCurve->mtTime);
if (pCurve->mtResetDuration < 0) pCurve->mtResetDuration = 0;
pCurve->mtTime = mtPartStart + mtOffset;
pCurve->bCurveShape = DMUS_CURVES_INSTANT;
}
}
IDirectMusicGraph* pGraph;
if( SUCCEEDED( m_pSegState->QueryInterface( IID_IDirectMusicGraph,
(void**)&pGraph )))
{
pGraph->StampPMsg( (DMUS_PMSG*)pCurve );
pGraph->Release();
}
if(FAILED(pPerformance->SendPMsg( (DMUS_PMSG*)pCurve)))
{
pPerformance->FreePMsg( (DMUS_PMSG*)pCurve);
}
}
}
else if (pEventItem->m_dwEventTag == DMUS_EVENT_NOTE) // we have a note event
{
pNoteEvent = (CDMStyleNote*)pEventItem;
BYTE bPlayModeFlags =
(pNoteEvent->m_bPlayModeFlags & DMUS_PLAYMODE_NONE) ?
rPartRef.m_pDMPart->m_bPlayModeFlags :
pNoteEvent->m_bPlayModeFlags;
BYTE bMidiValue = 0;
short nMidiOffset = 0;
HRESULT hr = rPartRef.ConvertMusicValue(pNoteEvent,
m_CurrentChord,
bPlayModeFlags,
PlayAsIs(),
m_aInversionGroups,
pPerformance,
bMidiValue,
nMidiOffset);
if (SUCCEEDED(hr) &&
SUCCEEDED(pPerformance->AllocPMsg( sizeof(DMUS_NOTE_PMSG),
(DMUS_PMSG**) &pNote)))
{
pNote->bFlags = DMUS_NOTEF_NOTEON | pNoteEvent->m_bFlags;
MUSIC_TIME mtSegmentTime = TimeSig.GridToClocks(pNoteEvent->m_nGridStart) +
pNoteEvent->m_nTimeOffset + mtPartOffset;
if (fClockTime)
{
pNote->wMeasure = 0;
pNote->bBeat = 0;
pNote->bGrid = 0;
pNote->nOffset = pNoteEvent->m_nTimeOffset;
pNote->rtTime = (mtSegmentTime * REF_PER_MIL) + rtOffset;
pNote->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME;
}
else
{
pNote->wMeasure = (WORD)TimeSig.GridsToMeasure(pNoteEvent->m_nGridStart);
pNote->bBeat = (BYTE)TimeSig.GridsToBeat(pNoteEvent->m_nGridStart);
pNote->bGrid = (BYTE)TimeSig.GridOffset(pNoteEvent->m_nGridStart);
pNote->nOffset = pNoteEvent->m_nTimeOffset;
pNote->mtTime = mtSegmentTime + mtSegmentOffset;
pNote->dwFlags = DMUS_PMSGF_MUSICTIME;
}
// time needs be jiggled by pNoteEvent->m_bTimeRange
if (pNoteEvent->m_bTimeRange)
pNote->mtTime += RandomExp(pNoteEvent->m_bTimeRange);
pNote->mtDuration = pNoteEvent->m_mtDuration;
// duration needs be jiggled by pNoteEvent->m_bDurRange
if (pNoteEvent->m_bDurRange)
pNote->mtDuration += RandomExp(pNoteEvent->m_bDurRange);
// (rand() % pNoteEvent->m_bDurRange) - (pNoteEvent->m_bDurRange >> 1);
pNote->bVelocity = pNoteEvent->m_bVelocity;
// velocity needs be jiggled by pNoteEvent->m_bVelRange
if (pNoteEvent->m_bVelRange)
pNote->bVelocity +=
(rand() % pNoteEvent->m_bVelRange) - (pNoteEvent->m_bVelRange >> 1);
if (pNote->bVelocity < 1) pNote->bVelocity = 1;
if (pNote->bVelocity > 127) pNote->bVelocity = 127;
pNote->wMusicValue = pNoteEvent->m_wMusicValue;
pNote->bMidiValue = bMidiValue;
pNote->dwType = DMUS_PMSGT_NOTE;
pNote->bPlayModeFlags = bPlayModeFlags;
pNote->dwPChannel = dwMapPChannel;
pNote->dwVirtualTrackID = m_dwVirtualTrackID; // ??
pNote->bSubChordLevel = rPartRef.m_bSubChordLevel;
pNote->dwGroupID = m_dwGroupID;
pNote->bTimeRange = pNoteEvent->m_bTimeRange;
pNote->bDurRange = pNoteEvent->m_bDurRange;
pNote->bVelRange = pNoteEvent->m_bVelRange;
pNote->cTranspose = (char) nMidiOffset;
IDirectMusicGraph* pGraph;
if( SUCCEEDED( m_pSegState->QueryInterface( IID_IDirectMusicGraph,
(void**)&pGraph )))
{
pGraph->StampPMsg( (DMUS_PMSG*)pNote );
pGraph->Release();
}
if (pNote->dwFlags & DMUS_PMSGF_REFTIME)
{
TraceI(5, "PLAY %d @%d\n", rPartRef.m_dwLogicalPartID,
(MUSIC_TIME) (pNote->rtTime/REF_PER_MIL));
}
else
{
TraceI(5, "PLAY %d @%d: %x [%d]{%x}\n", rPartRef.m_dwLogicalPartID, pNote->mtTime,
pNote->wMusicValue, pNote->bMidiValue, Variations(rPartRef, nPart));
}
if(FAILED(pPerformance->SendPMsg( (DMUS_PMSG*)pNote) ))
{
pPerformance->FreePMsg( (DMUS_PMSG*)pNote);
}
}
}
}
}
void PatternTrackState::SendTimeSigMessage(MUSIC_TIME mtNow, MUSIC_TIME mtOffset, MUSIC_TIME mtTime, IDirectMusicPerformance* pPerformance)
{
if (!m_pStyle) return;
IDirectMusicGraph* pGraph = NULL;
DMUS_TIMESIG_PMSG* pTimeSig;
if( FAILED( m_pSegState->QueryInterface( IID_IDirectMusicGraph,
(void**)&pGraph )))
{
pGraph = NULL;
}
if( SUCCEEDED( pPerformance->AllocPMsg( sizeof(DMUS_TIMESIG_PMSG),
(DMUS_PMSG**)&pTimeSig )))
{
if( mtTime < mtNow )
{
// this only happens in the case where we've puposefully seeked
// and need to time stamp this event with the start time
pTimeSig->mtTime = mtNow + mtOffset;
}
else
{
pTimeSig->mtTime = mtTime + mtOffset;
}
pTimeSig->bBeatsPerMeasure = m_pStyle->m_TimeSignature.m_bBeatsPerMeasure;
pTimeSig->bBeat = m_pStyle->m_TimeSignature.m_bBeat;
pTimeSig->wGridsPerBeat = m_pStyle->m_TimeSignature.m_wGridsPerBeat;
pTimeSig->dwFlags |= DMUS_PMSGF_MUSICTIME;
pTimeSig->dwVirtualTrackID = m_dwVirtualTrackID;
pTimeSig->dwType = DMUS_PMSGT_TIMESIG;
pTimeSig->dwGroupID = m_dwGroupID;
if( pGraph )
{
pGraph->StampPMsg( (DMUS_PMSG*)pTimeSig );
pGraph->Release();
}
TraceI(3, "TimeSigtrk: TimeSig event\n");
if(FAILED(pPerformance->SendPMsg( (DMUS_PMSG*)pTimeSig )))
{
pPerformance->FreePMsg( (DMUS_PMSG*)pTimeSig );
}
}
}
// send measure and beat notifications
MUSIC_TIME PatternTrackState::NotifyMeasureBeat(
MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, IDirectMusicPerformance* pPerformance, DWORD dwFlags )
{
if (dwFlags & DMUS_TRACKF_NOTIFY_OFF)
{
return S_OK;
}
DMUS_NOTIFICATION_PMSG* pEvent = NULL;
BYTE bCurrentBeat;
WORD wCurrentMeasure;
DirectMusicTimeSig& rTimeSig = PatternTimeSig();
// now actually generate the beat events.
// Generate events that are on beat boundaries, from mtStart to mtEnd
long lQuantize = ( DMUS_PPQ * 4 ) / rTimeSig.m_bBeat;
long lAbsoluteBeat = mtStart / lQuantize;
bCurrentBeat = (BYTE) (lAbsoluteBeat % rTimeSig.m_bBeatsPerMeasure);
wCurrentMeasure = (WORD) (lAbsoluteBeat / rTimeSig.m_bBeatsPerMeasure);
while( mtStart < mtEnd )
{
if( SUCCEEDED( pPerformance->AllocPMsg( sizeof(DMUS_NOTIFICATION_PMSG),
(DMUS_PMSG**)&pEvent )))
{
pEvent->dwField1 = 0;
pEvent->dwField2 = 0;
pEvent->dwType = DMUS_PMSGT_NOTIFICATION;
pEvent->mtTime = mtStart + mtOffset;
pEvent->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_ATTIME;
m_pSegState->QueryInterface(IID_IUnknown, (void**)&pEvent->punkUser);
pEvent->dwNotificationOption = DMUS_NOTIFICATION_MEASUREBEAT;
pEvent->dwField1 = bCurrentBeat;
pEvent->dwField2 = wCurrentMeasure;
pEvent->guidNotificationType = GUID_NOTIFICATION_MEASUREANDBEAT;
pEvent->dwGroupID = m_dwGroupID;
IDirectMusicGraph* pGraph;
if( SUCCEEDED( m_pSegState->QueryInterface( IID_IDirectMusicGraph,
(void**)&pGraph )))
{
pGraph->StampPMsg((DMUS_PMSG*) pEvent );
pGraph->Release();
}
if(FAILED(pPerformance->SendPMsg((DMUS_PMSG*) pEvent )))
{
pPerformance->FreePMsg( (DMUS_PMSG*)pEvent);;
}
}
bCurrentBeat++;
if( bCurrentBeat >= rTimeSig.m_bBeatsPerMeasure )
{
bCurrentBeat = 0;
wCurrentMeasure++;
}
mtStart += lQuantize;
}
return mtEnd;
}
MUSIC_TIME PatternTrackState::PartOffset(int nPartIndex)
{
return m_pmtPartOffset[nPartIndex];
}
/////////////////////////////////////////////////////////////////////////////
// PatternTrackInfo
PatternTrackInfo::PatternTrackInfo() :
m_fNotifyMeasureBeat(FALSE), m_dwPChannels(0), m_pdwPChannels(NULL),
m_fActive(TRUE),
// m_fTrackPlay(TRUE),
m_fStateSetBySetParam(FALSE),
// m_fStatePlaySetBySetParam(FALSE),
m_fChangeStateMappings(FALSE),
m_lRandomNumberSeed(0),
m_dwValidate(0),
m_pVariations(NULL),
m_pdwRemoveVariations(NULL)
{
}
PatternTrackInfo::PatternTrackInfo(
const PatternTrackInfo* pInfo, MUSIC_TIME mtStart, MUSIC_TIME mtEnd) :
m_dwPChannels(0), m_pdwPChannels(NULL), m_lRandomNumberSeed(0), m_dwValidate(0),
m_pVariations(NULL), m_pdwRemoveVariations(NULL)
{
if (pInfo)
{
m_fChangeStateMappings = pInfo->m_fChangeStateMappings;
m_fNotifyMeasureBeat = pInfo->m_fNotifyMeasureBeat;
m_fActive = pInfo->m_fActive;
// m_fTrackPlay = pInfo->m_fTrackPlay;
m_fStateSetBySetParam = pInfo->m_fStateSetBySetParam;
// m_fStatePlaySetBySetParam = pInfo->m_fStatePlaySetBySetParam;
}
TListItem<StylePair>* pScan = pInfo->m_pISList.GetHead();
//1////////////////////////////////////////
TListItem<StylePair>* pPrevious = NULL;
//1////////////////////////////////////////
for(; pScan; pScan = pScan->GetNext())
{
StylePair& rScan = pScan->GetItemValue();
//2////////////////////////////////////////
if (rScan.m_mtTime < mtStart)
{
pPrevious = pScan;
}
//2////////////////////////////////////////
else if (rScan.m_mtTime < mtEnd)
{
//3////////////////////////////////////////
if (rScan.m_mtTime == mtStart)
{
pPrevious = NULL;
}
//3////////////////////////////////////////
TListItem<StylePair>* pNew = new TListItem<StylePair>;
if (pNew)
{
StylePair& rNew = pNew->GetItemValue();
rNew.m_mtTime = rScan.m_mtTime - mtStart;
rNew.m_pStyle = rScan.m_pStyle;
if (rNew.m_pStyle) rNew.m_pStyle->AddRef();
m_pISList.AddTail(pNew);
}
}
}
//4////////////////////////////////////////
if (pPrevious)
{
TListItem<StylePair>* pNew = new TListItem<StylePair>;
if (pNew)
{
StylePair& rNew = pNew->GetItemValue();
rNew.m_mtTime = 0;
rNew.m_pStyle = pPrevious->GetItemValue().m_pStyle;
if (rNew.m_pStyle) rNew.m_pStyle->AddRef();
m_pISList.AddHead(pNew);
}
}
//4////////////////////////////////////////
}
PatternTrackInfo::~PatternTrackInfo()
{
if (m_pdwPChannels) delete [] m_pdwPChannels;
if (m_pVariations) delete [] m_pVariations;
if (m_pdwRemoveVariations) delete [] m_pdwRemoveVariations;
}
PatternTrackState* PatternTrackInfo::FindState(IDirectMusicSegmentState* pSegState)
{
TListItem<StatePair>* pPair = m_StateList.GetHead();
for (; pPair; pPair = pPair->GetNext())
{
if (pPair->GetItemValue().m_pSegState == pSegState)
{
return pPair->GetItemValue().m_pStateData;
}
}
return NULL;
}
HRESULT PatternTrackInfo::EndPlay(PatternTrackState* pStateData)
{
if (!pStateData) return E_FAIL;
for (TListItem<StatePair>* pScan = m_StateList.GetHead(); pScan; pScan = pScan->GetNext())
{
StatePair& rPair = pScan->GetItemValue();
if (pStateData == rPair.m_pStateData)
{
rPair.m_pSegState = NULL;
rPair.m_pStateData = NULL;
break;
}
}
delete pStateData;
return S_OK;
}
HRESULT STDMETHODCALLTYPE PatternTrackInfo::AddNotificationType(
/* [in] */ REFGUID rGuidNotify)
{
if( rGuidNotify == GUID_NOTIFICATION_MEASUREANDBEAT )
{
m_fNotifyMeasureBeat = TRUE;
return S_OK;
}
else
{
return S_FALSE;
}
}
HRESULT STDMETHODCALLTYPE PatternTrackInfo::RemoveNotificationType(
/* [in] */ REFGUID rGuidNotify)
{
if( rGuidNotify == GUID_NOTIFICATION_MEASUREANDBEAT )
{
m_fNotifyMeasureBeat = FALSE;
return S_OK;
}
else
{
return S_FALSE;
}
}
HRESULT PatternTrackInfo::InitTrackVariations(CDirectMusicPattern* pPattern)
{
HRESULT hr = S_OK;
if ( pPattern && (pPattern->m_dwFlags & DMUS_PATTERNF_PERSIST_CONTROL) )
{
// delete the variation arrays if they exist;
if (m_pVariations)
{
delete [] m_pVariations;
m_pVariations = NULL;
}
if (m_pdwRemoveVariations)
{
delete [] m_pdwRemoveVariations;
m_pdwRemoveVariations = NULL;
}
// init the variation arrays to the number of parts in the pattern
int nPartCount = pPattern->m_PartRefList.GetCount();
m_pVariations = new BYTE[nPartCount];
if (!m_pVariations)
{
return E_OUTOFMEMORY;
}
m_pdwRemoveVariations = new DWORD[nPartCount];
if (!m_pdwRemoveVariations)
{
return E_OUTOFMEMORY;
}
for (int i = 0; i < nPartCount; i++)
{
m_pVariations[i] = -1;
m_pdwRemoveVariations[i] = 0;
}
}
return hr;
}
HRESULT PatternTrackInfo::MergePChannels()
{
TList<DWORD> PChannelList;
DMStyleStruct* pStruct = NULL;
HRESULT hr = S_OK;
TListItem<StylePair>* pScan = m_pISList.GetHead();
for( ; pScan; pScan = pScan->GetNext())
{
if (pScan->GetItemValue().m_pStyle)
{
pScan->GetItemValue().m_pStyle->GetStyleInfo((void**)&pStruct);
TListItem<DWORD>* pChannel = pStruct->m_PChannelList.GetHead();
for (; pChannel; pChannel = pChannel->GetNext() )
{
AdjoinPChannel(PChannelList, pChannel->GetItemValue() );
}
}
}
if (PChannelList.IsEmpty())
{
AdjoinPChannel(PChannelList, 0);
}
TListItem<DWORD>* pPChannel = PChannelList.GetHead();
m_dwPChannels = pPChannel->GetCount();
if (m_pdwPChannels) delete [] m_pdwPChannels;
m_pdwPChannels = new DWORD[m_dwPChannels];
if (!m_pdwPChannels)
{
hr = E_OUTOFMEMORY;
}
else
{
for (int i = 0; i < (int)m_dwPChannels; i++)
{
m_pdwPChannels[i] = pPChannel->GetItemValue();
pPChannel = pPChannel->GetNext();
}
m_fChangeStateMappings = TRUE;
}
return hr;
}
inline int CurveIndex(CDirectMusicEventItem* pEvent)
{
CDMStyleCurve* pCurve = NULL;
if (pEvent->m_dwEventTag == DMUS_EVENT_CURVE)
{
pCurve = (CDMStyleCurve*)pEvent;
switch (pCurve->m_bEventType)
{
case DMUS_CURVET_CCCURVE:
return pCurve->m_bCCData & 0x7f;
case DMUS_CURVET_PATCURVE:
return (pCurve->m_bCCData & 0x7f) + 128;
case DMUS_CURVET_PBCURVE:
return 256;
case DMUS_CURVET_MATCURVE:
return 257;
default:
return -1;
}
}
return -1;
}
CurveSeek::CurveSeek()
{
for (int nType = 0; nType < CURVE_TYPES; nType++)
{
m_apCurves[nType] = NULL;
m_amtTimeStamps[nType] = 0;
}
m_fFoundCurve = false;
}
void CurveSeek::AddCurve(CDirectMusicEventItem* pEvent, MUSIC_TIME mtTimeStamp)
{
int nIndex = CurveIndex(pEvent);
if (nIndex >= 0)
{
if (!m_apCurves[nIndex] ||
m_amtTimeStamps[nIndex] < mtTimeStamp)
{
m_apCurves[nIndex] = pEvent;
m_amtTimeStamps[nIndex] = mtTimeStamp;
m_fFoundCurve = true;
}
}
}
void CurveSeek::PlayCurves(
PatternTrackState* pStateData,
DirectMusicTimeSig& TimeSig,
MUSIC_TIME mtPatternOffset,
MUSIC_TIME mtOffset,
REFERENCE_TIME rtOffset,
IDirectMusicPerformance* pPerformance,
short nPart,
DirectMusicPartRef& rPartRef,
BOOL fClockTime,
MUSIC_TIME mtPartStart)
{
if (m_fFoundCurve)
{
for (int nType = 0; nType < CURVE_TYPES; nType++)
{
CDirectMusicEventItem* pScan = m_apCurves[nType];
if (pScan)
{
int nGrid = pScan->m_nGridStart;
CDirectMusicEventItem* pWinner = pScan;
MUSIC_TIME mtBiggest = pWinner->m_nTimeOffset;
for (; pScan && pScan->m_nGridStart == nGrid; pScan = pScan->GetNext())
{
if (pScan->m_dwEventTag == DMUS_EVENT_CURVE &&
pScan->m_nTimeOffset > mtBiggest)
{
pWinner = pScan;
mtBiggest = pWinner->m_nTimeOffset;
}
}
MUSIC_TIME mtNow = 0;
bool fChange = false;
pStateData->BumpTime(pWinner, TimeSig, mtPatternOffset, mtNow);
pStateData->PlayPatternEvent(
mtNow,
pWinner,
TimeSig,
mtPatternOffset,
mtOffset,
rtOffset,
pPerformance,
nPart,
rPartRef,
fClockTime,
mtPartStart,
fChange);
}
}
m_fFoundCurve = false;
}
}