You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
11 KiB
294 lines
11 KiB
//
|
|
// 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();
|
|
}
|
|
|