windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/dmcompos/dmcomp2.cpp

295 lines
11 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//
// DMComp2.cpp : Further implementation of CDMCompos
//
// Copyright (c) 1999-2001 Microsoft Corporation
//
// @doc EXTERNAL
//
#include "DMCompos.h"
#include "debug.h"
#include "DMPers.h"
#include "DMTempl.h"
#include "..\shared\Validate.h"
#include "..\dmstyle\iostru.h"
V_INAME(DMCompose)
void CDMCompos::ChordConnections2(TList<DMChordEntry>& ChordMap,
CompositionCommand& rCommand,
SearchInfo *pSearch,
short nBPM,
DMChordData *pCadence1,
DMChordData *pCadence2)
{
int mint, maxt, top, bottom, total;
short oldbeats = pSearch->m_nBeats;
//, error;
TListItem<PlayChord> *pChord;
SearchInfo tempSearch;
// Compose a chord list.
pSearch->m_nMinBeats = 0;
pSearch->m_nMaxBeats = 0;
pSearch->m_nChords = 0;
pSearch->m_Fail.m_nTooManybeats = 0;
pSearch->m_Fail.m_nTooFewbeats = 0;
pSearch->m_Fail.m_nTooManychords = 0;
pSearch->m_Fail.m_nTooFewchords = 0;
if (pCadence1 || pCadence2)
{
pSearch->m_nMinBeats++;
pSearch->m_nMaxBeats = nBPM;
pSearch->m_nChords++;
if (pCadence1 && pCadence2)
{
pSearch->m_nMinBeats++;
pSearch->m_nChords++;
}
}
tempSearch = *pSearch;
rCommand.m_PlayList.RemoveAll();
Compose(ChordMap, pSearch, rCommand);
pChord = rCommand.m_PlayList.GetHead();
/////////
*pSearch = tempSearch;
pSearch->m_nBeats = oldbeats;
// Tally the min and max beats.
mint = 0;
maxt = 0;
for (; pChord; pChord = pChord->GetNext())
{
mint += pChord->GetItemValue().m_nMinbeats;
maxt += pChord->GetItemValue().m_nMaxbeats;
}
pChord = rCommand.m_PlayList.GetHead();
// If no chord connection was found, create one.
if (!pChord)
{
int nextDuration = oldbeats;
pChord = AddCadence(rCommand.m_PlayList, &pSearch->m_Start, 0);
if (pChord)
{
pChord->GetItemValue().m_nMinbeats = 0;
}
if (pCadence1)
{
AddCadence(rCommand.m_PlayList, pCadence1, nextDuration);
mint++;
maxt += nextDuration;
nextDuration = nBPM + 1;
}
if (pCadence2)
{
AddCadence(rCommand.m_PlayList, pCadence2, nextDuration);
mint++;
maxt += nextDuration;
nextDuration = nBPM + 1;
}
AddCadence(rCommand.m_PlayList, &pSearch->m_Start, nextDuration);
mint++;
maxt += nextDuration;
}
else
{
int chordCount = (int) rCommand.m_PlayList.GetCount();
int avMax;
if (chordCount > 1) chordCount--;
avMax = maxt / chordCount;
if (avMax < 1) avMax = 1;
if (pCadence1)
{
if (pCadence2)
{
AddCadence(rCommand.m_PlayList, pCadence2, avMax);
maxt += avMax;
mint++;
}
AddCadence(rCommand.m_PlayList, &pSearch->m_End, avMax);
maxt += avMax;
mint++;
}
else if (pCadence2)
{
AddCadence(rCommand.m_PlayList, &pSearch->m_End, avMax);
maxt += avMax;
mint++;
}
}
// Prepare a ratio to apply to each connection.
top = pSearch->m_nBeats - mint;
bottom = maxt - mint;
if (bottom <= 0) bottom = 1;
// Assign each connection a time based on the ratio.
total = 0;
pChord = rCommand.m_PlayList.GetHead();
for (; pChord; pChord = pChord->GetNext())
{
PlayChord& rChord = pChord->GetItemValue();
int beat = rChord.m_nMaxbeats - rChord.m_nMinbeats;
beat *= top;
beat += (bottom >> 1);
beat /= bottom;
if (beat < rChord.m_nMinbeats) beat = rChord.m_nMinbeats;
if (beat > rChord.m_nMaxbeats) beat = rChord.m_nMaxbeats;
total += beat;
rChord.m_nBeat = (short)total;
}
// We should now have a close approximation of the correct time.
// Stretch or shrink the range to fit exactly. Err on the side
// of too long, since jostleback will scrunch them back in place.
// But DON'T violate min/max for each chord.
pChord = rCommand.m_PlayList.GetHead();
int lastbeat = 0;
for (; pChord; pChord = pChord->GetNext())
{
PlayChord& rChord = pChord->GetItemValue();
int newbeat = (rChord.m_nBeat * pSearch->m_nBeats) + total - 1;
newbeat /= total;
if ((newbeat - lastbeat) < rChord.m_nMinbeats) newbeat = lastbeat + rChord.m_nMinbeats;
if ((newbeat - lastbeat) > rChord.m_nMaxbeats) newbeat = lastbeat + rChord.m_nMaxbeats;
rChord.m_nBeat = (short)newbeat;
lastbeat = newbeat;
if (!pChord->GetNext()) total = rChord.m_nBeat;
}
// Now we should have times close to the real thing.
pChord = rCommand.m_PlayList.GetItem(rCommand.m_PlayList.GetCount() - 1);
if (pChord)
{
JostleBack(rCommand.m_PlayList, pChord, pSearch->m_nBeats - total);
}
// Now, add the starting time offset to each chord.
// And, remove the straggler last chord.
AlignChords(rCommand.m_PlayList.GetHead(), 0, nBPM);
pChord = rCommand.m_PlayList.GetHead();
for (; pChord; )
{
pChord->GetItemValue().m_nMeasure =
(short)( ( pChord->GetItemValue().m_nBeat / nBPM ) + rCommand.m_nMeasure );
pChord->GetItemValue().m_nBeat %= nBPM;
if (pChord->GetNext())
{
pChord = pChord->GetNext();
}
else
{
rCommand.m_PlayList.Remove(pChord);
delete pChord;
break;
}
}
}
void CDMCompos::ComposePlayList2(TList<PlayChord>& PlayList,
IDirectMusicStyle* pStyle,
IDirectMusicChordMap* pPersonality,
TList<TemplateCommand>& rCommandList)
{
// Extract the style's time signature.
DMUS_TIMESIGNATURE TimeSig;
pStyle->GetTimeSignature(&TimeSig);
short nBPM = TimeSig.bBeatsPerMeasure;
IDMPers* pDMP;
pPersonality->QueryInterface(IID_IDMPers, (void**)&pDMP);
DMPersonalityStruct* pPers;
pDMP->GetPersonalityStruct((void**)&pPers);
TList<DMChordEntry> &ChordMap = pPers->m_ChordMap;
TList<DMSignPost> &SignPostList = pPers->m_SignPostList;
TListItem<DMSignPost> *pSign = SignPostList.GetHead();
for (; pSign; pSign = pSign->GetNext())
{
pSign->GetItemValue().m_dwTempFlags = 0;
}
// Assign specific root sign posts, then letter based sign posts.
TList<CompositionCommand> CommandList;
TListItem<TemplateCommand>* pTC = rCommandList.GetHead();
for(; pTC; pTC = pTC->GetNext())
{
TemplateCommand& rTC = pTC->GetItemValue();
TListItem<CompositionCommand>* pNew = new TListItem<CompositionCommand>;
if (pNew)
{
CompositionCommand& rNew = pNew->GetItemValue();
rNew.m_nMeasure = rTC.m_nMeasure;
rNew.m_Command = rTC.m_Command;
rNew.m_dwChord = rTC.m_dwChord;
rNew.m_pSignPost = NULL;
rNew.m_pFirstChord = NULL;
CommandList.AddTail(pNew);
}
}
ChooseSignPosts(SignPostList.GetHead(), CommandList.GetHead(),DMUS_SIGNPOSTF_ROOT, false);
ChooseSignPosts(SignPostList.GetHead(), CommandList.GetHead(),DMUS_SIGNPOSTF_LETTER, false);
ChooseSignPosts(SignPostList.GetHead(), CommandList.GetHead(),DMUS_SIGNPOSTF_ROOT, true);
ChooseSignPosts(SignPostList.GetHead(), CommandList.GetHead(),DMUS_SIGNPOSTF_LETTER, true);
// Now, we should have a chord assigned for each node in the template.
TListItem<CompositionCommand>* pCommand = CommandList.GetHead();
for (; pCommand; pCommand = pCommand->GetNext())
{
CompositionCommand& rCommand = pCommand->GetItemValue();
if (rCommand.m_dwChord == 0) continue; // Only command, no chord.
if (rCommand.m_pSignPost)
{
TListItem<CompositionCommand>* pNext = GetNextChord(pCommand);
if (pNext)
{
CompositionCommand& rNext = pNext->GetItemValue();
SearchInfo *pSearch = &rCommand.m_SearchInfo;
DMChordData *pCadence1 = NULL;
DMChordData *pCadence2 = NULL;
pSearch->m_Start = rCommand.m_pSignPost->GetItemValue().m_ChordData;
if (rNext.m_dwChord & DMUS_SIGNPOSTF_CADENCE)
{
pSign = rNext.m_pSignPost;
DMSignPost& rSign = pSign->GetItemValue();
if (rSign.m_dwFlags & DMUS_SPOSTCADENCEF_1)
{
pSearch->m_End = rSign.m_aCadence[0];
pCadence1 = &rSign.m_aCadence[0];
if (rSign.m_dwFlags & DMUS_SPOSTCADENCEF_2)
{
pCadence2 = &rSign.m_aCadence[1];
}
}
else if (rSign.m_dwFlags & DMUS_SPOSTCADENCEF_2)
{
pSearch->m_End = rSign.m_aCadence[1];
pCadence2 = &rSign.m_aCadence[1];
}
else
{
pSearch->m_End = rSign.m_ChordData;
}
}
else
{
pSearch->m_End = rNext.m_pSignPost->GetItemValue().m_ChordData;
}
//**********pSearch->m_nActivity = (short) wActivity;
pSearch->m_nBeats = (short)( (rNext.m_nMeasure - rCommand.m_nMeasure) * nBPM );
pSearch->m_nMaxChords = (short)( pSearch->m_nBeats );
pSearch->m_nMinChords = 0; // should be 1?
FindEarlierSignpost(CommandList.GetHead(), pCommand, pSearch);
// rCommand holds the playlist and the measure used by ChordConnections
// (it should be passed by reference since the playlist changes)
ChordConnections2(ChordMap, rCommand, pSearch, nBPM, pCadence1, pCadence2);
}
else
{
AddChord(rCommand.m_PlayList, &rCommand.m_pSignPost->GetItemValue().m_ChordData,
rCommand.m_nMeasure,0);
}
}
}
// Take all the Chord references and fold 'em into one list.
pCommand = CommandList.GetHead();
for (; pCommand; pCommand = pCommand->GetNext())
{
PlayList.Cat(pCommand->GetItemValue().m_PlayList.GetHead());
pCommand->GetItemValue().m_PlayList.RemoveAll();
}
CleanUpBreaks(PlayList, CommandList.GetHead());
pDMP->Release();
}