Source code of Windows XP (NT5)
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

  1. //
  2. // DMComp2.cpp : Further implementation of CDMCompos
  3. //
  4. // Copyright (c) 1999-2001 Microsoft Corporation
  5. //
  6. // @doc EXTERNAL
  7. //
  8. #include "DMCompos.h"
  9. #include "debug.h"
  10. #include "DMPers.h"
  11. #include "DMTempl.h"
  12. #include "..\shared\Validate.h"
  13. #include "..\dmstyle\iostru.h"
  14. V_INAME(DMCompose)
  15. void CDMCompos::ChordConnections2(TList<DMChordEntry>& ChordMap,
  16. CompositionCommand& rCommand,
  17. SearchInfo *pSearch,
  18. short nBPM,
  19. DMChordData *pCadence1,
  20. DMChordData *pCadence2)
  21. {
  22. int mint, maxt, top, bottom, total;
  23. short oldbeats = pSearch->m_nBeats;
  24. //, error;
  25. TListItem<PlayChord> *pChord;
  26. SearchInfo tempSearch;
  27. // Compose a chord list.
  28. pSearch->m_nMinBeats = 0;
  29. pSearch->m_nMaxBeats = 0;
  30. pSearch->m_nChords = 0;
  31. pSearch->m_Fail.m_nTooManybeats = 0;
  32. pSearch->m_Fail.m_nTooFewbeats = 0;
  33. pSearch->m_Fail.m_nTooManychords = 0;
  34. pSearch->m_Fail.m_nTooFewchords = 0;
  35. if (pCadence1 || pCadence2)
  36. {
  37. pSearch->m_nMinBeats++;
  38. pSearch->m_nMaxBeats = nBPM;
  39. pSearch->m_nChords++;
  40. if (pCadence1 && pCadence2)
  41. {
  42. pSearch->m_nMinBeats++;
  43. pSearch->m_nChords++;
  44. }
  45. }
  46. tempSearch = *pSearch;
  47. rCommand.m_PlayList.RemoveAll();
  48. Compose(ChordMap, pSearch, rCommand);
  49. pChord = rCommand.m_PlayList.GetHead();
  50. /////////
  51. *pSearch = tempSearch;
  52. pSearch->m_nBeats = oldbeats;
  53. // Tally the min and max beats.
  54. mint = 0;
  55. maxt = 0;
  56. for (; pChord; pChord = pChord->GetNext())
  57. {
  58. mint += pChord->GetItemValue().m_nMinbeats;
  59. maxt += pChord->GetItemValue().m_nMaxbeats;
  60. }
  61. pChord = rCommand.m_PlayList.GetHead();
  62. // If no chord connection was found, create one.
  63. if (!pChord)
  64. {
  65. int nextDuration = oldbeats;
  66. pChord = AddCadence(rCommand.m_PlayList, &pSearch->m_Start, 0);
  67. if (pChord)
  68. {
  69. pChord->GetItemValue().m_nMinbeats = 0;
  70. }
  71. if (pCadence1)
  72. {
  73. AddCadence(rCommand.m_PlayList, pCadence1, nextDuration);
  74. mint++;
  75. maxt += nextDuration;
  76. nextDuration = nBPM + 1;
  77. }
  78. if (pCadence2)
  79. {
  80. AddCadence(rCommand.m_PlayList, pCadence2, nextDuration);
  81. mint++;
  82. maxt += nextDuration;
  83. nextDuration = nBPM + 1;
  84. }
  85. AddCadence(rCommand.m_PlayList, &pSearch->m_Start, nextDuration);
  86. mint++;
  87. maxt += nextDuration;
  88. }
  89. else
  90. {
  91. int chordCount = (int) rCommand.m_PlayList.GetCount();
  92. int avMax;
  93. if (chordCount > 1) chordCount--;
  94. avMax = maxt / chordCount;
  95. if (avMax < 1) avMax = 1;
  96. if (pCadence1)
  97. {
  98. if (pCadence2)
  99. {
  100. AddCadence(rCommand.m_PlayList, pCadence2, avMax);
  101. maxt += avMax;
  102. mint++;
  103. }
  104. AddCadence(rCommand.m_PlayList, &pSearch->m_End, avMax);
  105. maxt += avMax;
  106. mint++;
  107. }
  108. else if (pCadence2)
  109. {
  110. AddCadence(rCommand.m_PlayList, &pSearch->m_End, avMax);
  111. maxt += avMax;
  112. mint++;
  113. }
  114. }
  115. // Prepare a ratio to apply to each connection.
  116. top = pSearch->m_nBeats - mint;
  117. bottom = maxt - mint;
  118. if (bottom <= 0) bottom = 1;
  119. // Assign each connection a time based on the ratio.
  120. total = 0;
  121. pChord = rCommand.m_PlayList.GetHead();
  122. for (; pChord; pChord = pChord->GetNext())
  123. {
  124. PlayChord& rChord = pChord->GetItemValue();
  125. int beat = rChord.m_nMaxbeats - rChord.m_nMinbeats;
  126. beat *= top;
  127. beat += (bottom >> 1);
  128. beat /= bottom;
  129. if (beat < rChord.m_nMinbeats) beat = rChord.m_nMinbeats;
  130. if (beat > rChord.m_nMaxbeats) beat = rChord.m_nMaxbeats;
  131. total += beat;
  132. rChord.m_nBeat = (short)total;
  133. }
  134. // We should now have a close approximation of the correct time.
  135. // Stretch or shrink the range to fit exactly. Err on the side
  136. // of too long, since jostleback will scrunch them back in place.
  137. // But DON'T violate min/max for each chord.
  138. pChord = rCommand.m_PlayList.GetHead();
  139. int lastbeat = 0;
  140. for (; pChord; pChord = pChord->GetNext())
  141. {
  142. PlayChord& rChord = pChord->GetItemValue();
  143. int newbeat = (rChord.m_nBeat * pSearch->m_nBeats) + total - 1;
  144. newbeat /= total;
  145. if ((newbeat - lastbeat) < rChord.m_nMinbeats) newbeat = lastbeat + rChord.m_nMinbeats;
  146. if ((newbeat - lastbeat) > rChord.m_nMaxbeats) newbeat = lastbeat + rChord.m_nMaxbeats;
  147. rChord.m_nBeat = (short)newbeat;
  148. lastbeat = newbeat;
  149. if (!pChord->GetNext()) total = rChord.m_nBeat;
  150. }
  151. // Now we should have times close to the real thing.
  152. pChord = rCommand.m_PlayList.GetItem(rCommand.m_PlayList.GetCount() - 1);
  153. if (pChord)
  154. {
  155. JostleBack(rCommand.m_PlayList, pChord, pSearch->m_nBeats - total);
  156. }
  157. // Now, add the starting time offset to each chord.
  158. // And, remove the straggler last chord.
  159. AlignChords(rCommand.m_PlayList.GetHead(), 0, nBPM);
  160. pChord = rCommand.m_PlayList.GetHead();
  161. for (; pChord; )
  162. {
  163. pChord->GetItemValue().m_nMeasure =
  164. (short)( ( pChord->GetItemValue().m_nBeat / nBPM ) + rCommand.m_nMeasure );
  165. pChord->GetItemValue().m_nBeat %= nBPM;
  166. if (pChord->GetNext())
  167. {
  168. pChord = pChord->GetNext();
  169. }
  170. else
  171. {
  172. rCommand.m_PlayList.Remove(pChord);
  173. delete pChord;
  174. break;
  175. }
  176. }
  177. }
  178. void CDMCompos::ComposePlayList2(TList<PlayChord>& PlayList,
  179. IDirectMusicStyle* pStyle,
  180. IDirectMusicChordMap* pPersonality,
  181. TList<TemplateCommand>& rCommandList)
  182. {
  183. // Extract the style's time signature.
  184. DMUS_TIMESIGNATURE TimeSig;
  185. pStyle->GetTimeSignature(&TimeSig);
  186. short nBPM = TimeSig.bBeatsPerMeasure;
  187. IDMPers* pDMP;
  188. pPersonality->QueryInterface(IID_IDMPers, (void**)&pDMP);
  189. DMPersonalityStruct* pPers;
  190. pDMP->GetPersonalityStruct((void**)&pPers);
  191. TList<DMChordEntry> &ChordMap = pPers->m_ChordMap;
  192. TList<DMSignPost> &SignPostList = pPers->m_SignPostList;
  193. TListItem<DMSignPost> *pSign = SignPostList.GetHead();
  194. for (; pSign; pSign = pSign->GetNext())
  195. {
  196. pSign->GetItemValue().m_dwTempFlags = 0;
  197. }
  198. // Assign specific root sign posts, then letter based sign posts.
  199. TList<CompositionCommand> CommandList;
  200. TListItem<TemplateCommand>* pTC = rCommandList.GetHead();
  201. for(; pTC; pTC = pTC->GetNext())
  202. {
  203. TemplateCommand& rTC = pTC->GetItemValue();
  204. TListItem<CompositionCommand>* pNew = new TListItem<CompositionCommand>;
  205. if (pNew)
  206. {
  207. CompositionCommand& rNew = pNew->GetItemValue();
  208. rNew.m_nMeasure = rTC.m_nMeasure;
  209. rNew.m_Command = rTC.m_Command;
  210. rNew.m_dwChord = rTC.m_dwChord;
  211. rNew.m_pSignPost = NULL;
  212. rNew.m_pFirstChord = NULL;
  213. CommandList.AddTail(pNew);
  214. }
  215. }
  216. ChooseSignPosts(SignPostList.GetHead(), CommandList.GetHead(),DMUS_SIGNPOSTF_ROOT, false);
  217. ChooseSignPosts(SignPostList.GetHead(), CommandList.GetHead(),DMUS_SIGNPOSTF_LETTER, false);
  218. ChooseSignPosts(SignPostList.GetHead(), CommandList.GetHead(),DMUS_SIGNPOSTF_ROOT, true);
  219. ChooseSignPosts(SignPostList.GetHead(), CommandList.GetHead(),DMUS_SIGNPOSTF_LETTER, true);
  220. // Now, we should have a chord assigned for each node in the template.
  221. TListItem<CompositionCommand>* pCommand = CommandList.GetHead();
  222. for (; pCommand; pCommand = pCommand->GetNext())
  223. {
  224. CompositionCommand& rCommand = pCommand->GetItemValue();
  225. if (rCommand.m_dwChord == 0) continue; // Only command, no chord.
  226. if (rCommand.m_pSignPost)
  227. {
  228. TListItem<CompositionCommand>* pNext = GetNextChord(pCommand);
  229. if (pNext)
  230. {
  231. CompositionCommand& rNext = pNext->GetItemValue();
  232. SearchInfo *pSearch = &rCommand.m_SearchInfo;
  233. DMChordData *pCadence1 = NULL;
  234. DMChordData *pCadence2 = NULL;
  235. pSearch->m_Start = rCommand.m_pSignPost->GetItemValue().m_ChordData;
  236. if (rNext.m_dwChord & DMUS_SIGNPOSTF_CADENCE)
  237. {
  238. pSign = rNext.m_pSignPost;
  239. DMSignPost& rSign = pSign->GetItemValue();
  240. if (rSign.m_dwFlags & DMUS_SPOSTCADENCEF_1)
  241. {
  242. pSearch->m_End = rSign.m_aCadence[0];
  243. pCadence1 = &rSign.m_aCadence[0];
  244. if (rSign.m_dwFlags & DMUS_SPOSTCADENCEF_2)
  245. {
  246. pCadence2 = &rSign.m_aCadence[1];
  247. }
  248. }
  249. else if (rSign.m_dwFlags & DMUS_SPOSTCADENCEF_2)
  250. {
  251. pSearch->m_End = rSign.m_aCadence[1];
  252. pCadence2 = &rSign.m_aCadence[1];
  253. }
  254. else
  255. {
  256. pSearch->m_End = rSign.m_ChordData;
  257. }
  258. }
  259. else
  260. {
  261. pSearch->m_End = rNext.m_pSignPost->GetItemValue().m_ChordData;
  262. }
  263. //**********pSearch->m_nActivity = (short) wActivity;
  264. pSearch->m_nBeats = (short)( (rNext.m_nMeasure - rCommand.m_nMeasure) * nBPM );
  265. pSearch->m_nMaxChords = (short)( pSearch->m_nBeats );
  266. pSearch->m_nMinChords = 0; // should be 1?
  267. FindEarlierSignpost(CommandList.GetHead(), pCommand, pSearch);
  268. // rCommand holds the playlist and the measure used by ChordConnections
  269. // (it should be passed by reference since the playlist changes)
  270. ChordConnections2(ChordMap, rCommand, pSearch, nBPM, pCadence1, pCadence2);
  271. }
  272. else
  273. {
  274. AddChord(rCommand.m_PlayList, &rCommand.m_pSignPost->GetItemValue().m_ChordData,
  275. rCommand.m_nMeasure,0);
  276. }
  277. }
  278. }
  279. // Take all the Chord references and fold 'em into one list.
  280. pCommand = CommandList.GetHead();
  281. for (; pCommand; pCommand = pCommand->GetNext())
  282. {
  283. PlayList.Cat(pCommand->GetItemValue().m_PlayList.GetHead());
  284. pCommand->GetItemValue().m_PlayList.RemoveAll();
  285. }
  286. CleanUpBreaks(PlayList, CommandList.GetHead());
  287. pDMP->Release();
  288. }