// // 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& ChordMap, CompositionCommand& rCommand, SearchInfo *pSearch, short nBPM, DMChordData *pCadence1, DMChordData *pCadence2) { int mint, maxt, top, bottom, total; short oldbeats = pSearch->m_nBeats; //, error; TListItem *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& PlayList, IDirectMusicStyle* pStyle, IDirectMusicChordMap* pPersonality, TList& 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 &ChordMap = pPers->m_ChordMap; TList &SignPostList = pPers->m_SignPostList; TListItem *pSign = SignPostList.GetHead(); for (; pSign; pSign = pSign->GetNext()) { pSign->GetItemValue().m_dwTempFlags = 0; } // Assign specific root sign posts, then letter based sign posts. TList CommandList; TListItem* pTC = rCommandList.GetHead(); for(; pTC; pTC = pTC->GetNext()) { TemplateCommand& rTC = pTC->GetItemValue(); TListItem* pNew = new TListItem; 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* 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* 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(); }