987 lines
41 KiB
C++
987 lines
41 KiB
C++
//
|
|
// DMStyle2.cpp : Further Implementation of CDMStyle
|
|
//
|
|
// Copyright (c) 1999-2001 Microsoft Corporation
|
|
//
|
|
// @doc EXTERNAL
|
|
//
|
|
|
|
#include "DMStyle.h"
|
|
#include "debug.h"
|
|
|
|
#include "..\shared\Validate.h"
|
|
#include "iostru.h"
|
|
#include "mgentrk.h"
|
|
|
|
struct FirstTimePair
|
|
{
|
|
DWORD dwPart;
|
|
MUSIC_TIME mtTime;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IDirectMusicStyle2
|
|
|
|
/*
|
|
@method:(EXTERNAL) HRESULT | IDirectMusicComposer | ComposeMelodyFromTemplate | Creates a sequence segment from a
|
|
style and a Melody template (containing a Melody Generation track, a Chord track, and an
|
|
optional Style track). Clones the segment and adds a Sequence track containing melodic
|
|
information.
|
|
|
|
@rdesc Returns:
|
|
|
|
@flag S_OK | Success
|
|
@flag E_POINTER | One or both of <p pTempSeg> and <p ppSeqSeg> is an invalid pointer.
|
|
@flag E_INVALIDARG | <p pStyle> is NULL and there is no Style track.
|
|
|
|
@comm If <p pStyle> is non-NULL, it is used in composing the segment; if it is NULL,
|
|
a Style is retrieved from <p pTempSeg>'s Style track.
|
|
The length of the section segment is equal to the length of the template section
|
|
passed in.
|
|
*/
|
|
|
|
HRESULT CDMStyle::ComposeMelodyFromTemplate(
|
|
IDirectMusicStyle* pStyle, // @parm The style from which to create the sequence segment.
|
|
IDirectMusicSegment* pTempSeg, // @parm The template from which to create the sequence segment.
|
|
IDirectMusicSegment** ppSeqSeg // @parm Returns the created sequence segment.
|
|
)
|
|
{
|
|
V_INAME(ComposeMelodyFromTemplate)
|
|
|
|
V_PTR_WRITE_OPT(pStyle, 1);
|
|
V_PTR_WRITE(pTempSeg, 1);
|
|
V_PTRPTR_WRITE(ppSeqSeg);
|
|
|
|
DWORD dwTrackGroup = 0xffffffff;
|
|
BOOL fStyleFromTrack = FALSE;
|
|
HRESULT hr = S_OK;
|
|
IDirectMusicTrack* pPatternTrack = NULL;
|
|
IDirectMusicTrack* pMelGenTrack = NULL;
|
|
MUSIC_TIME mtLength = 0;
|
|
hr = pTempSeg->GetLength(&mtLength);
|
|
if (FAILED(hr)) goto ON_END;
|
|
|
|
// get the MelGen track and its track group.
|
|
hr = pTempSeg->GetTrack(CLSID_DirectMusicMelodyFormulationTrack, 0xffffffff, 0, &pMelGenTrack);
|
|
if (S_OK != hr) goto ON_END;
|
|
if (FAILED(pTempSeg->GetTrackGroup(pMelGenTrack, &dwTrackGroup)))
|
|
{
|
|
dwTrackGroup = 0xffffffff;
|
|
}
|
|
|
|
// Get the style (either use the passed-in style or get one from a style track)
|
|
if (!pStyle)
|
|
{
|
|
if (FAILED(hr = GetStyle(pTempSeg, 0, dwTrackGroup, pStyle)))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto ON_END;
|
|
}
|
|
fStyleFromTrack = TRUE;
|
|
}
|
|
|
|
// Using style, and melgen track, create a pattern track
|
|
hr = GenerateTrack(pTempSeg, NULL, dwTrackGroup, pStyle, pMelGenTrack, mtLength, pPatternTrack);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (hr == S_FALSE)
|
|
{
|
|
if (pPatternTrack) pPatternTrack->Release();
|
|
pPatternTrack = NULL;
|
|
}
|
|
HRESULT hrCopy = CopySegment(pTempSeg, pStyle, pPatternTrack, dwTrackGroup, ppSeqSeg);
|
|
if (FAILED(hrCopy)) hr = hrCopy;
|
|
}
|
|
|
|
ON_END:
|
|
// release from Addref in GetTrack
|
|
if (pMelGenTrack) pMelGenTrack->Release();
|
|
// release from CoCreateInstance in CreatePatternTrack
|
|
if (pPatternTrack) pPatternTrack->Release();
|
|
// Release from Addref in GetStyle
|
|
if (fStyleFromTrack) pStyle->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDMStyle::GetStyle(IDirectMusicSegment* pFromSeg, MUSIC_TIME mt, DWORD dwTrackGroup, IDirectMusicStyle*& rpStyle)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
// Get the segment's style track.
|
|
IDirectMusicTrack* pStyleTrack;
|
|
hr = pFromSeg->GetTrack(CLSID_DirectMusicStyleTrack, dwTrackGroup, 0, &pStyleTrack);
|
|
if (S_OK != hr) return hr;
|
|
// Get the style from the style track
|
|
hr = pStyleTrack->GetParam(GUID_IDirectMusicStyle, mt, NULL, (void*) &rpStyle);
|
|
pStyleTrack->Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDMStyle::CopySegment(IDirectMusicSegment* pTempSeg,
|
|
IDirectMusicStyle* pStyle,
|
|
IDirectMusicTrack* pPatternTrack,
|
|
DWORD dwTrackGroup,
|
|
IDirectMusicSegment** ppSectionSeg)
|
|
{
|
|
if (!ppSectionSeg) return E_INVALIDARG;
|
|
|
|
HRESULT hr = S_OK;
|
|
long nClocks = 0;
|
|
IDirectMusicTrack* pIStyleTrack = NULL;
|
|
IDirectMusicTrack* pDMTrack = NULL;
|
|
IDirectMusicTrack* pBandTrack = NULL;
|
|
IDirectMusicBand* pBand = NULL;
|
|
|
|
DMUS_BAND_PARAM DMBandParam;
|
|
pTempSeg->GetLength(&nClocks);
|
|
/////////////////////////////////////////////////////////////
|
|
// clone the template segment to get a section segment
|
|
hr = pTempSeg->Clone(0, nClocks, ppSectionSeg);
|
|
if (!SUCCEEDED(hr)) goto ON_END;
|
|
// Extract the style's time signature.
|
|
DMUS_TIMESIGNATURE TimeSig;
|
|
pStyle->GetTimeSignature(&TimeSig);
|
|
|
|
// Remove all style tracks from the new segment.
|
|
do
|
|
{
|
|
hr = (*ppSectionSeg)->GetTrack(CLSID_DirectMusicStyleTrack, dwTrackGroup, 0, &pIStyleTrack);
|
|
if (S_OK == hr)
|
|
{
|
|
(*ppSectionSeg)->RemoveTrack(pIStyleTrack);
|
|
pIStyleTrack->Release();
|
|
pIStyleTrack = NULL;
|
|
}
|
|
} while (S_OK == hr);
|
|
|
|
// hr is no longer S_OK, so reset it.
|
|
hr = S_OK;
|
|
|
|
// if there's no tempo track in the template segment, create one and add it
|
|
if (FAILED(pTempSeg->GetTrack(CLSID_DirectMusicTempoTrack, dwTrackGroup, 0, &pDMTrack)))
|
|
{
|
|
// Create a Tempo Track in which to store the tempo events
|
|
DMUS_TEMPO_PARAM tempo;
|
|
tempo.mtTime = 0;
|
|
|
|
pStyle->GetTempo(&tempo.dblTempo);
|
|
if( SUCCEEDED( CoCreateInstance( CLSID_DirectMusicTempoTrack,
|
|
NULL, CLSCTX_INPROC, IID_IDirectMusicTrack,
|
|
(void**)&pDMTrack )))
|
|
{
|
|
if ( SUCCEEDED(pDMTrack->SetParam(GUID_TempoParam, 0, &tempo)) )
|
|
{
|
|
(*ppSectionSeg)->InsertTrack( pDMTrack, dwTrackGroup );
|
|
}
|
|
}
|
|
}
|
|
// if there's no band track in the template segment, create one and add it
|
|
if (FAILED(pTempSeg->GetTrack(CLSID_DirectMusicBandTrack, dwTrackGroup, 0, &pBandTrack)))
|
|
{
|
|
// Create band track
|
|
hr = ::CoCreateInstance(
|
|
CLSID_DirectMusicBandTrack,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IDirectMusicTrack,
|
|
(void**)&pBandTrack
|
|
);
|
|
|
|
if(!SUCCEEDED(hr)) goto ON_END;
|
|
|
|
// Load default band from style into track
|
|
hr = pStyle->GetDefaultBand(&pBand);
|
|
if (!SUCCEEDED(hr)) goto ON_END;
|
|
DMBandParam.mtTimePhysical = -64;
|
|
DMBandParam.pBand = pBand;
|
|
hr = pBandTrack->SetParam(GUID_BandParam, 0, (void*)&DMBandParam);
|
|
if (!SUCCEEDED(hr)) goto ON_END;
|
|
(*ppSectionSeg)->InsertTrack(pBandTrack, dwTrackGroup);
|
|
}
|
|
|
|
// Add the pattern track
|
|
if (pPatternTrack)
|
|
{
|
|
(*ppSectionSeg)->InsertTrack(pPatternTrack, dwTrackGroup);
|
|
}
|
|
|
|
// Initialize the segment
|
|
(*ppSectionSeg)->SetRepeats(0);
|
|
TraceI(4, "Segment Length: %d\n", nClocks);
|
|
(*ppSectionSeg)->SetLength(nClocks);
|
|
|
|
ON_END:
|
|
if (pDMTrack)
|
|
{
|
|
// This releases the Addref made either by GetTrack or (if GetTrack failed)
|
|
// by CoCreateInstance
|
|
pDMTrack->Release();
|
|
}
|
|
if (pBandTrack)
|
|
{
|
|
// This releases the Addref made either by GetTrack or (if GetTrack failed)
|
|
// by CoCreateInstance
|
|
pBandTrack->Release();
|
|
}
|
|
if (pIStyleTrack) pIStyleTrack->Release();
|
|
if (pBand) pBand->Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDMStyle::GenerateTrack(IDirectMusicSegment* pTempSeg,
|
|
IDirectMusicSong* pSong,
|
|
DWORD dwTrackGroup,
|
|
IDirectMusicStyle* pStyle,
|
|
IDirectMusicTrack* pMelGenTrack,
|
|
MUSIC_TIME mtLength,
|
|
IDirectMusicTrack*& pNewTrack)
|
|
{
|
|
if (!pStyle || !pMelGenTrack) return E_INVALIDARG;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// CoCreate the pattern track
|
|
hr = ::CoCreateInstance(
|
|
CLSID_DirectMusicPatternTrack,
|
|
NULL,
|
|
CLSCTX_INPROC,
|
|
IID_IDirectMusicTrack,
|
|
(void**)&pNewTrack
|
|
);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
// Get the Style's info struct
|
|
IDMStyle* pDMStyle = NULL;
|
|
hr = pStyle->QueryInterface(IID_IDMStyle, (void**) &pDMStyle);
|
|
if (FAILED(hr)) return hr;
|
|
DMStyleStruct* pStyleStruct = NULL;
|
|
pDMStyle->GetStyleInfo((void**)&pStyleStruct);
|
|
|
|
MUSIC_TIME mtNewFragment = 0;
|
|
MUSIC_TIME mtNext = 0;
|
|
MUSIC_TIME mtRealNextChord = 0;
|
|
MUSIC_TIME mtNextChord = 0;
|
|
MUSIC_TIME mtLaterChord = 0;
|
|
DMUS_MELODY_FRAGMENT DMUS_Fragment;
|
|
memset(&DMUS_Fragment, 0, sizeof(DMUS_Fragment));
|
|
DMUS_CHORD_PARAM CurrentChord;
|
|
DMUS_CHORD_PARAM RealCurrentChord;
|
|
DMUS_CHORD_PARAM NextChord;
|
|
CDirectMusicPattern* pPattern;
|
|
TList<CompositionFragment> listFragments;
|
|
TListItem<CompositionFragment>* pLastFragment = NULL;
|
|
CompositionFragment CompRepeat;
|
|
FirstTimePair* aFirstTimes = NULL;
|
|
BYTE bPlaymode = 0;
|
|
pMelGenTrack->GetParam(GUID_MelodyPlaymode, 0, NULL, (void*)&bPlaymode);
|
|
if (bPlaymode & DMUS_PLAYMODE_NONE)
|
|
{
|
|
bPlaymode = DMUS_PLAYMODE_ALWAYSPLAY;
|
|
}
|
|
// for each melody fragment:
|
|
do
|
|
{
|
|
pLastFragment = listFragments.GetHead();
|
|
// get the fragment
|
|
HRESULT hrFragment = pMelGenTrack->GetParam(GUID_MelodyFragment, mtNewFragment, &mtNext, (void*)&DMUS_Fragment);
|
|
if (FAILED(hrFragment)) break;
|
|
MelodyFragment Fragment = DMUS_Fragment;
|
|
if (mtNext) mtNewFragment += mtNext;
|
|
else mtNewFragment = 0;
|
|
|
|
// get its repeat
|
|
MelodyFragment repeatFragment = Fragment;
|
|
hr = pMelGenTrack->GetParam(GUID_MelodyFragmentRepeat, 0, NULL, (void*)&DMUS_Fragment);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
repeatFragment = DMUS_Fragment;
|
|
}
|
|
else // failing to get a repeat just means this fragment doesn't repeat; composition can still continue
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
// If the fragment repeats an earlier fragment, get the earlier fragment.
|
|
// Regardless, get the fragment's pattern.
|
|
ZeroMemory( &CompRepeat, sizeof(CompRepeat));
|
|
if (SUCCEEDED(hrFragment) && Fragment.UsesRepeat())
|
|
{
|
|
TListItem<CompositionFragment>* pScan = listFragments.GetHead();
|
|
for (; pScan; pScan = pScan->GetNext())
|
|
{
|
|
if (pScan->GetItemValue().GetID() == repeatFragment.GetID())
|
|
{
|
|
CompRepeat = pScan->GetItemValue();
|
|
}
|
|
}
|
|
pPattern = CompRepeat.m_pPattern;
|
|
}
|
|
else
|
|
{
|
|
Fragment.GetPattern(pStyleStruct, pPattern, pLastFragment);
|
|
}
|
|
// bail if we couldn't get a pattern
|
|
if (!pPattern)
|
|
{
|
|
hr = DMUS_E_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// get the pattern's partrefs
|
|
TListItem<DirectMusicPartRef>* pPartRef = pPattern->m_PartRefList.GetHead();
|
|
int nParts = pPattern->m_PartRefList.GetCount();
|
|
|
|
// clear all inversion groups for the fragment
|
|
Fragment.ClearInversionGroups();
|
|
|
|
// get the starting chords for the fragment
|
|
Fragment.GetChord(pTempSeg, pSong, dwTrackGroup, mtNextChord, CurrentChord, mtRealNextChord, RealCurrentChord);
|
|
|
|
Fragment.GetChord(mtNextChord, pTempSeg, pSong, dwTrackGroup, mtLaterChord, NextChord);
|
|
|
|
// initializations
|
|
TListItem<CompositionFragment>* pFragmentItem = new TListItem<CompositionFragment>(Fragment);
|
|
if (!pFragmentItem)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
CompositionFragment& rFragment = pFragmentItem->GetItemValue();
|
|
hr = rFragment.Init(pPattern, pStyleStruct, nParts);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
if (aFirstTimes) delete [] aFirstTimes;
|
|
aFirstTimes = new FirstTimePair[nParts];
|
|
if (!aFirstTimes)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
// If we're repeating using transposition intervals:
|
|
// go through each part, attempting to fit the repeated part to the constraints.
|
|
// If this process fails for any part, abort the process.
|
|
// If it succeeds for every part, we can skip everything else that follows.
|
|
// (ASSUMPTION 1: constraints are pattern-wide, and so must be satisfied by every part)
|
|
// (ASSUMPTION 2: different parts are allowed to transpose by different intervals)
|
|
HRESULT hrSkipVariations = S_OK;
|
|
if (Fragment.RepeatsWithConstraints()) // if repeating using transposition intervals
|
|
{
|
|
for (int i = 0; pPartRef != NULL; pPartRef = pPartRef->GetNext(), i++)
|
|
{
|
|
aFirstTimes[i].dwPart = pPartRef->GetItemValue().m_dwLogicalPartID;
|
|
aFirstTimes[i].mtTime = 0;
|
|
hrSkipVariations = Fragment.GetRepeatedEvents(CompRepeat,
|
|
CurrentChord,
|
|
RealCurrentChord,
|
|
bPlaymode,
|
|
i,
|
|
pPartRef->GetItemValue(),
|
|
pLastFragment,
|
|
aFirstTimes[i].mtTime,
|
|
rFragment.EventList(i));
|
|
if (FAILED(hrSkipVariations)) break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hrSkipVariations = E_FAIL;
|
|
}
|
|
|
|
if (FAILED(hrSkipVariations))
|
|
{
|
|
// If we're repeating, make sure the repeat fragment actually has variations,
|
|
// and is not itself repeating an earlier fragment.
|
|
// (we don't need to get the pattern again; that will be the same for all repeats)
|
|
CompositionFragment CompLast = CompRepeat;
|
|
while (repeatFragment.UsesRepeat())
|
|
{
|
|
DWORD dwRepeatID = repeatFragment.GetRepeatID();
|
|
ZeroMemory( &CompRepeat, sizeof(CompRepeat));
|
|
TListItem<CompositionFragment>* pScan = listFragments.GetHead();
|
|
for (; pScan; pScan = pScan->GetNext())
|
|
{
|
|
if (pScan->GetItemValue().GetID() == dwRepeatID)
|
|
{
|
|
CompRepeat = pScan->GetItemValue();
|
|
repeatFragment = CompRepeat;
|
|
if (!CompLast.m_abVariations && CompRepeat.m_abVariations)
|
|
{
|
|
ZeroMemory( &CompLast, sizeof(CompLast));
|
|
CompLast = CompRepeat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get variations for the Fragment
|
|
Fragment.GetVariations(rFragment, CompRepeat, CompLast, CurrentChord, NextChord, mtNextChord, pLastFragment);
|
|
|
|
bool fNeedChord = false;
|
|
|
|
// for each part in the pattern:
|
|
for (int i = 0; pPartRef != NULL; pPartRef = pPartRef->GetNext(), i++)
|
|
{
|
|
// Clean up anything that might have happened with repeats
|
|
rFragment.CleanupEvents(i);
|
|
|
|
if (fNeedChord)
|
|
{
|
|
Fragment.GetChord(pTempSeg, pSong, dwTrackGroup, mtNextChord, CurrentChord, mtRealNextChord, RealCurrentChord);
|
|
Fragment.GetChord(mtNextChord, pTempSeg, pSong, dwTrackGroup, mtLaterChord, NextChord);
|
|
fNeedChord = false;
|
|
}
|
|
|
|
DirectMusicPart* pPart = pPartRef->GetItemValue().m_pDMPart;
|
|
DirectMusicTimeSig& TimeSig = rFragment.GetTimeSig(pPart);
|
|
aFirstTimes[i].dwPart = pPartRef->GetItemValue().m_dwLogicalPartID;
|
|
aFirstTimes[i].mtTime = 0;
|
|
bool fFoundFirst = false;
|
|
// for each note in the variation:
|
|
CDirectMusicEventItem* pEvent = pPart->EventList.GetHead();
|
|
for (; pEvent; pEvent = pEvent->GetNext())
|
|
{
|
|
if ( pEvent->m_dwVariation & (1 << rFragment.m_abVariations[i]) )
|
|
{
|
|
TListItem<EventWrapper>* pEventItem = NULL;
|
|
// get the time (offset from the start of the track)
|
|
MUSIC_TIME mtNow = Fragment.GetTime() +
|
|
TimeSig.GridToClocks(pEvent->m_nGridStart) + pEvent->m_nTimeOffset;
|
|
// Make sure this doesn't overlap with the next fragment.
|
|
MUSIC_TIME mtDuration = 0;
|
|
switch (pEvent->m_dwEventTag)
|
|
{
|
|
case DMUS_EVENT_NOTE:
|
|
mtDuration = ((CDMStyleNote*)pEvent)->m_mtDuration;
|
|
break;
|
|
case DMUS_EVENT_CURVE:
|
|
mtDuration = ((CDMStyleCurve*)pEvent)->m_mtDuration;
|
|
break;
|
|
}
|
|
bool fAddToOverlap = false;
|
|
if ( !mtNewFragment || mtNow + mtDuration <= mtNewFragment )
|
|
{
|
|
if (pEvent->m_dwEventTag == DMUS_EVENT_ANTICIPATION)
|
|
{
|
|
fAddToOverlap = true;
|
|
}
|
|
else
|
|
{
|
|
// use proper chord for non-anticipated notes
|
|
if (mtRealNextChord != mtNextChord && mtNow >= mtRealNextChord)
|
|
{
|
|
mtRealNextChord = mtNextChord;
|
|
RealCurrentChord = CurrentChord;
|
|
}
|
|
// get a new chord if necessary
|
|
if (mtNextChord && mtNow >= mtNextChord)
|
|
{
|
|
Fragment.GetChord(mtNow, pTempSeg, pSong, dwTrackGroup, mtNextChord, CurrentChord);
|
|
mtRealNextChord = mtNextChord;
|
|
RealCurrentChord = CurrentChord;
|
|
fNeedChord = true;
|
|
}
|
|
// Convert the event to a wrapped event
|
|
hr = Fragment.GetEvent(pEvent, CurrentChord, RealCurrentChord, mtNow, pPartRef->GetItemValue(), pEventItem);
|
|
// Add the new event to a list of wrapped events.
|
|
if (hr == S_OK)
|
|
{
|
|
rFragment.AddEvent(i, pEventItem);
|
|
}
|
|
if (!fFoundFirst || mtNow < aFirstTimes[i].mtTime)
|
|
{
|
|
fFoundFirst = true;
|
|
aFirstTimes[i].mtTime = mtNow;
|
|
}
|
|
}
|
|
}
|
|
// ignore anticipations that start after the next fragment
|
|
else if (pEvent->m_dwEventTag != DMUS_EVENT_ANTICIPATION)
|
|
{
|
|
fAddToOverlap = true;
|
|
}
|
|
if (fAddToOverlap)
|
|
{
|
|
TListItem<EventOverlap>* pOverlap = new TListItem<EventOverlap>;
|
|
if (pOverlap)
|
|
{
|
|
EventOverlap& rOverlap = pOverlap->GetItemValue();
|
|
rOverlap.m_PartRef = pPartRef->GetItemValue();
|
|
rOverlap.m_pEvent = pEvent;
|
|
rOverlap.m_mtTime = mtNow;
|
|
rOverlap.m_mtDuration = mtDuration;
|
|
rOverlap.m_Chord = CurrentChord;
|
|
rOverlap.m_RealChord = RealCurrentChord;
|
|
rFragment.AddOverlap(pOverlap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!fFoundFirst) aFirstTimes[i].mtTime = mtNewFragment;
|
|
// Sort the sequence items in reverse order, so that the last element is easy to find
|
|
rFragment.SortEvents(i);
|
|
}
|
|
// Clear this so the pointers it may reference aren't deleted twice.
|
|
ZeroMemory( &CompLast, sizeof(CompLast));
|
|
}
|
|
listFragments.AddHead(pFragmentItem);
|
|
// Go through the list of overlaps for the last fragment, adding any events that don't
|
|
// actually overlap (and processing anticipations)
|
|
// alg for processing anticipations:
|
|
// if the overlap list contains an anticipation for a part:
|
|
// find the first event in that part's fragment list
|
|
// make the start time of that event be the start time of the anticipation
|
|
// add to the duration of the event the difference between the event's start time
|
|
// and the anticipation's start time
|
|
if (pLastFragment)
|
|
{
|
|
TListItem<EventWrapper>* pEventItem = NULL;
|
|
CompositionFragment& rLastFragment = pLastFragment->GetItemValue();
|
|
TListItem<EventOverlap>* pTupleItem;
|
|
pTupleItem = rLastFragment.GetOverlapHead();
|
|
for (; pTupleItem; pTupleItem = pTupleItem->GetNext() )
|
|
{
|
|
EventOverlap& rTuple = pTupleItem->GetItemValue();
|
|
for (int i = 0; i < nParts; i++)
|
|
{
|
|
if (rTuple.m_PartRef.m_dwLogicalPartID == aFirstTimes[i].dwPart) break;
|
|
}
|
|
if (i >= nParts ||
|
|
!aFirstTimes[i].mtTime ||
|
|
rTuple.m_mtTime < aFirstTimes[i].mtTime)
|
|
{
|
|
if (rTuple.m_pEvent->m_dwEventTag == DMUS_EVENT_ANTICIPATION)
|
|
{
|
|
if (i < nParts && aFirstTimes[i].mtTime)
|
|
{
|
|
TListItem<EventWrapper>* pFirstNote = NULL;
|
|
TListItem<EventWrapper>* pScan = rFragment.GetEventHead(i);
|
|
// since the list is sorted in reverse order, the first note in
|
|
// the fragment will be the last one in the list.
|
|
for (; pScan; pScan = pScan->GetNext())
|
|
{
|
|
if (pScan->GetItemValue().m_pEvent->m_dwEventTag == DMUS_EVENT_NOTE)
|
|
{
|
|
pFirstNote = pScan;
|
|
}
|
|
}
|
|
if (pFirstNote)
|
|
{
|
|
EventWrapper& rFirstNote = pFirstNote->GetItemValue();
|
|
CDMStyleNote* pNoteEvent = (CDMStyleNote*)rFirstNote.m_pEvent;
|
|
pNoteEvent->m_mtDuration += (rFirstNote.m_mtTime - rTuple.m_mtTime);
|
|
rFirstNote.m_mtTime = rTuple.m_mtTime;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = rLastFragment.GetEvent(rTuple.m_pEvent, rTuple.m_Chord, rTuple.m_RealChord, rTuple.m_mtTime, rTuple.m_PartRef, pEventItem);
|
|
if (i < nParts &&
|
|
aFirstTimes[i].mtTime &&
|
|
rTuple.m_mtTime + rTuple.m_mtDuration >= aFirstTimes[i].mtTime)
|
|
{
|
|
int nDiff = rTuple.m_mtTime + rTuple.m_mtDuration - aFirstTimes[i].mtTime;
|
|
if (pEventItem && pEventItem->GetItemValue().m_pEvent)
|
|
{
|
|
switch (pEventItem->GetItemValue().m_pEvent->m_dwEventTag)
|
|
{
|
|
case DMUS_EVENT_NOTE:
|
|
((CDMStyleNote*)pEventItem->GetItemValue().m_pEvent)->m_mtDuration -= nDiff;
|
|
break;
|
|
case DMUS_EVENT_CURVE:
|
|
((CDMStyleCurve*)pEventItem->GetItemValue().m_pEvent)->m_mtDuration -= nDiff;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
rLastFragment.InsertEvent(i, pEventItem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// NOTE: Need to apply transformations (invert, reverse...) here.
|
|
// I should also move the individual sorts out of the overlapped notes code,
|
|
// and put it directly preceding the transformations.
|
|
|
|
} while (mtNext != 0);
|
|
|
|
// Clear this so the pointers it may reference aren't deleted twice.
|
|
ZeroMemory( &CompRepeat, sizeof(CompRepeat));
|
|
|
|
// Once pattern tracks are introduced, I need to change this to create a pattern track.
|
|
// I should allow an option to create one or the other (?).
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreatePatternTrack(listFragments,
|
|
pStyleStruct->m_TimeSignature,
|
|
pStyleStruct->m_dblTempo,
|
|
mtLength,
|
|
bPlaymode,
|
|
pNewTrack);
|
|
}
|
|
|
|
if (aFirstTimes) delete [] aFirstTimes;
|
|
|
|
pDMStyle->Release();
|
|
return hr;
|
|
}
|
|
|
|
TListItem<DMUS_IO_SEQ_ITEM>* ConvertToSequenceEvent(TListItem<EventWrapper>* pEventItem)
|
|
{
|
|
TListItem<DMUS_IO_SEQ_ITEM>* pResult = new TListItem<DMUS_IO_SEQ_ITEM>;
|
|
if (pResult)
|
|
{
|
|
DMUS_IO_SEQ_ITEM& rSeq = pResult->GetItemValue();
|
|
EventWrapper& rEvent = pEventItem->GetItemValue();
|
|
rSeq.bStatus = 0x90; // MIDI note on (with channel nibble stripped out)
|
|
rSeq.mtTime = rEvent.m_mtTime;
|
|
rSeq.bByte1 = rEvent.m_bMIDI;
|
|
rSeq.dwPChannel = rEvent.m_dwPChannel;
|
|
rSeq.nOffset = rEvent.m_pEvent->m_nTimeOffset;
|
|
if (rEvent.m_pEvent)
|
|
{
|
|
switch (rEvent.m_pEvent->m_dwEventTag)
|
|
{
|
|
case DMUS_EVENT_NOTE:
|
|
rSeq.mtDuration = ((CDMStyleNote*)rEvent.m_pEvent)->m_mtDuration;
|
|
rSeq.bByte2 = ((CDMStyleNote*)rEvent.m_pEvent)->m_bVelocity;
|
|
break;
|
|
case DMUS_EVENT_CURVE:
|
|
rSeq.mtDuration = ((CDMStyleCurve*)rEvent.m_pEvent)->m_mtDuration;
|
|
rSeq.bByte2 = 0; // Actually, curves shouldn't end up as note events...
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pResult;
|
|
}
|
|
|
|
HRESULT CDMStyle::CreateSequenceTrack(TList<CompositionFragment>& rlistFragments,
|
|
IDirectMusicTrack*& pSequenceTrack)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TList<DMUS_IO_SEQ_ITEM> SeqList;
|
|
|
|
// fold all the individual event lists into one list
|
|
TListItem<CompositionFragment>* pFragmentItem = rlistFragments.GetHead();
|
|
for (; pFragmentItem; pFragmentItem = pFragmentItem->GetNext())
|
|
{
|
|
CompositionFragment& rFragment = pFragmentItem->GetItemValue();
|
|
int nParts = rFragment.m_pPattern->m_PartRefList.GetCount();
|
|
for (int i = 0; i < nParts; i++)
|
|
{
|
|
while (!rFragment.IsEmptyEvents(i))
|
|
{
|
|
TListItem<EventWrapper>* pHead = rFragment.RemoveEventHead(i);
|
|
SeqList.AddHead(ConvertToSequenceEvent(pHead));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort the sequence items
|
|
SeqList.MergeSort(Less);
|
|
|
|
// Now, persist the sequence events into the Sequence track
|
|
IPersistStream* pIPSTrack = NULL;
|
|
if( SUCCEEDED( pSequenceTrack->QueryInterface( IID_IPersistStream, (void**)&pIPSTrack )))
|
|
{
|
|
// Create a stream in which to place the events so we can
|
|
// give it to the SeqTrack.Load.
|
|
IStream* pEventStream;
|
|
if( S_OK == CreateStreamOnHGlobal( NULL, TRUE, &pEventStream ) )
|
|
{
|
|
// Save the events into the stream
|
|
TListItem<DMUS_IO_SEQ_ITEM>* pSeqItem = NULL;
|
|
ULONG cb, cbWritten;
|
|
// Save the chunk id
|
|
DWORD dwTemp = DMUS_FOURCC_SEQ_TRACK;
|
|
pEventStream->Write( &dwTemp, sizeof(DWORD), NULL );
|
|
// Save the overall size. Count the number of events to determine.
|
|
DWORD dwSize = 0;
|
|
for( pSeqItem = SeqList.GetHead(); pSeqItem; pSeqItem = pSeqItem->GetNext() )
|
|
{
|
|
dwSize++;
|
|
}
|
|
dwSize *= sizeof(DMUS_IO_SEQ_ITEM);
|
|
// add 12 --- 8 for the subchunk header and overall size,
|
|
// and 4 for the DMUS_IO_SEQ_ITEM size DWORD in the subchunk
|
|
dwSize += 12;
|
|
pEventStream->Write( &dwSize, sizeof(DWORD), NULL );
|
|
// Save the subchunk id
|
|
dwTemp = DMUS_FOURCC_SEQ_LIST;
|
|
pEventStream->Write( &dwTemp, sizeof(DWORD), NULL );
|
|
// Subtract the previously added 8 (for subchunk header and overall size)
|
|
dwSize -= 8;
|
|
// Save the size of the subchunk (including the DMUS_IO_SEQ_ITEM size DWORD)
|
|
pEventStream->Write( &dwSize, sizeof(DWORD), NULL );
|
|
// Save the structure size.
|
|
dwTemp = sizeof(DMUS_IO_SEQ_ITEM);
|
|
pEventStream->Write( &dwTemp, sizeof(DWORD), NULL );
|
|
// Save the events.
|
|
cb = sizeof(DMUS_IO_SEQ_ITEM);
|
|
for( pSeqItem = SeqList.GetHead(); pSeqItem; pSeqItem = pSeqItem->GetNext() )
|
|
{
|
|
DMUS_IO_SEQ_ITEM& rSeqItem = pSeqItem->GetItemValue();
|
|
pEventStream->Write( &rSeqItem, cb, &cbWritten );
|
|
if( cb != cbWritten ) // error!
|
|
{
|
|
pEventStream->Release();
|
|
pEventStream = NULL;
|
|
hr = DMUS_E_CANNOTREAD;
|
|
break;
|
|
}
|
|
}
|
|
if( pEventStream ) // may be NULL
|
|
{
|
|
StreamSeek( pEventStream, 0, STREAM_SEEK_SET );
|
|
pIPSTrack->Load( pEventStream );
|
|
pEventStream->Release();
|
|
}
|
|
}
|
|
pIPSTrack->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
CDirectMusicEventItem* ConvertToPatternEvent(TListItem<EventWrapper>* pEventWrapper,
|
|
DWORD dwID,
|
|
BYTE bPlaymode,
|
|
DirectMusicTimeSig& TimeSig)
|
|
{
|
|
if (!pEventWrapper) return NULL;
|
|
BYTE bEventPlaymode = pEventWrapper->GetItemValue().m_bPlaymode;
|
|
if (bPlaymode == bEventPlaymode)
|
|
{
|
|
bEventPlaymode = DMUS_PLAYMODE_NONE;
|
|
}
|
|
CDirectMusicEventItem* pWrappedEvent = pEventWrapper->GetItemValue().m_pEvent;
|
|
if (!pWrappedEvent) return NULL;
|
|
MUSIC_TIME mtClocksPerGrid = TimeSig.ClocksPerGrid();
|
|
short nGrid = 0;
|
|
short nOffset = 0;
|
|
if (mtClocksPerGrid)
|
|
{
|
|
nGrid = (short) (pEventWrapper->GetItemValue().m_mtTime / mtClocksPerGrid);
|
|
nOffset = (short) (pWrappedEvent->m_nTimeOffset + pEventWrapper->GetItemValue().m_mtTime % mtClocksPerGrid);
|
|
}
|
|
DWORD dwVariations = 0xffffffff;
|
|
BYTE bFlags = 0;
|
|
if (pEventWrapper->GetItemValue().m_pEvent &&
|
|
pEventWrapper->GetItemValue().m_pEvent->m_dwEventTag == DMUS_EVENT_NOTE)
|
|
{
|
|
bFlags = ((CDMStyleNote*)pEventWrapper->GetItemValue().m_pEvent)->m_bFlags;
|
|
}
|
|
return pWrappedEvent->ReviseEvent(nGrid, nOffset, &dwVariations, &dwID, &pEventWrapper->GetItemValue().m_wMusic, &bEventPlaymode, &bFlags);
|
|
}
|
|
|
|
HRESULT CDMStyle::CreatePatternTrack(TList<CompositionFragment>& rlistFragments,
|
|
DirectMusicTimeSig& rTimeSig,
|
|
double dblTempo,
|
|
MUSIC_TIME mtLength,
|
|
BYTE bPlaymode,
|
|
IDirectMusicTrack*& pPatternTrack)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CDirectMusicPattern* pPattern = new CDirectMusicPattern(&rTimeSig);
|
|
if (pPattern == NULL) return E_OUTOFMEMORY;
|
|
|
|
pPattern->m_strName = "<Composed Pattern>";
|
|
|
|
// fold all the individual event lists into corresponding parts in the pattern
|
|
TListItem<CompositionFragment>* pFragmentItem = rlistFragments.GetHead();
|
|
for (; pFragmentItem; pFragmentItem = pFragmentItem->GetNext())
|
|
{
|
|
CompositionFragment& rFragment = pFragmentItem->GetItemValue();
|
|
TListItem<DirectMusicPartRef>* pPartRef = rFragment.m_pPattern->m_PartRefList.GetHead();
|
|
int nParts = rFragment.m_pPattern->m_PartRefList.GetCount();
|
|
for (int i = 0; i < nParts && pPartRef; i++, pPartRef = pPartRef->GetNext() )
|
|
{
|
|
DirectMusicPartRef& rPartRef = pPartRef->GetItemValue();
|
|
TListItem<DirectMusicPartRef>* pNewPartRef = pPattern->CreatePart(rPartRef, bPlaymode);
|
|
if (!pNewPartRef)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
DirectMusicPart* pPart = pNewPartRef->GetItemValue().m_pDMPart;
|
|
if (!pPart)
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
while (!rFragment.IsEmptyEvents(i))
|
|
{
|
|
TListItem<EventWrapper>* pHead = rFragment.RemoveEventHead(i);
|
|
CDirectMusicEventItem* pNew = ConvertToPatternEvent(pHead, rFragment.GetID(), bPlaymode, rTimeSig);
|
|
if (pNew)
|
|
{
|
|
pPart->EventList.AddHead(pNew);
|
|
}
|
|
delete pHead;
|
|
}
|
|
}
|
|
if (FAILED(hr)) break;
|
|
}
|
|
WORD wNumMeasures = 0;
|
|
MUSIC_TIME mtClocksPerMeasure = rTimeSig.ClocksPerMeasure();
|
|
if (mtClocksPerMeasure)
|
|
{
|
|
wNumMeasures = (WORD)(mtLength / mtClocksPerMeasure);
|
|
if (mtLength % mtClocksPerMeasure)
|
|
{
|
|
wNumMeasures++;
|
|
}
|
|
}
|
|
pPattern->m_wNumMeasures = wNumMeasures;
|
|
pPattern->m_pRhythmMap = new DWORD[wNumMeasures];
|
|
if (pPattern->m_pRhythmMap)
|
|
{
|
|
for (int i = 0; i < wNumMeasures; i++)
|
|
{
|
|
pPattern->m_pRhythmMap[i] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TListItem<DirectMusicPartRef>* pPartRef = pPattern->m_PartRefList.GetHead();
|
|
int nParts = pPattern->m_PartRefList.GetCount();
|
|
// Sort the event lists for each part, and set the number of measures.
|
|
for (int i = 0; i < nParts && pPartRef; i++, pPartRef = pPartRef->GetNext() )
|
|
{
|
|
DirectMusicPart* pPart = pPartRef->GetItemValue().m_pDMPart;
|
|
if (pPart)
|
|
{
|
|
pPart->m_wNumMeasures = wNumMeasures;
|
|
pPart->EventList.MergeSort(rTimeSig);
|
|
// remove notes so close that they might as well be overlapping
|
|
CDirectMusicEventItem* pThisEvent = pPart->EventList.GetHead();
|
|
CDirectMusicEventItem* pLastEvent = NULL;
|
|
for (; pThisEvent; pThisEvent = pThisEvent->GetNext())
|
|
{
|
|
if (pThisEvent->m_dwEventTag == DMUS_EVENT_NOTE)
|
|
{
|
|
CDMStyleNote* pThisNote = (CDMStyleNote*)pThisEvent;
|
|
CDirectMusicEventItem* pNextEvent = pThisEvent->GetNext();
|
|
for (; pNextEvent; pNextEvent = pNextEvent->GetNext())
|
|
{
|
|
if (pNextEvent->m_dwEventTag == DMUS_EVENT_NOTE) break;
|
|
}
|
|
if (pNextEvent)
|
|
{
|
|
CDMStyleNote* pNextNote = (CDMStyleNote*)pNextEvent;
|
|
MUSIC_TIME mtThis = rTimeSig.GridToClocks(pThisNote->m_nGridStart) + pThisNote->m_nTimeOffset;
|
|
MUSIC_TIME mtNext = rTimeSig.GridToClocks(pNextNote->m_nGridStart) + pNextNote->m_nTimeOffset;
|
|
if ( (pNextNote->m_dwFragmentID != pThisNote->m_dwFragmentID) &&
|
|
((mtThis < mtNext && mtThis + OVERLAP_DELTA > mtNext) ||
|
|
(mtThis > mtNext && mtThis + OVERLAP_DELTA < mtNext)) ) // could happen with negative offsets...
|
|
{
|
|
if (pLastEvent)
|
|
{
|
|
pLastEvent->SetNext(pThisEvent->GetNext());
|
|
}
|
|
else // the note I want to remove is the first event
|
|
{
|
|
pPart->EventList.RemoveHead();
|
|
}
|
|
pThisEvent->SetNext(NULL);
|
|
delete pThisEvent;
|
|
pThisEvent = pLastEvent;
|
|
}
|
|
}
|
|
}
|
|
pLastEvent = pThisEvent;
|
|
}
|
|
}
|
|
}
|
|
// Now, save the newly created pattern to a pattern track
|
|
IPersistStream* pIPSTrack = NULL;
|
|
IAARIFFStream* pIRiffStream;
|
|
MMCKINFO ckMain;
|
|
if( SUCCEEDED( pPatternTrack->QueryInterface( IID_IPersistStream, (void**)&pIPSTrack )))
|
|
{
|
|
// Create a stream in which to place the events so we can
|
|
// give it to the PatternTrack.Load.
|
|
IStream* pEventStream;
|
|
if( S_OK == CreateStreamOnHGlobal( NULL, TRUE, &pEventStream ) )
|
|
{
|
|
if( SUCCEEDED( AllocRIFFStream( pEventStream, &pIRiffStream ) ) )
|
|
{
|
|
ckMain.fccType = DMUS_FOURCC_PATTERN_FORM;
|
|
if( pIRiffStream->CreateChunk( &ckMain, MMIO_CREATERIFF ) == 0 )
|
|
{
|
|
MMCKINFO ckHeader;
|
|
ckHeader.ckid = DMUS_FOURCC_STYLE_CHUNK;
|
|
if( pIRiffStream->CreateChunk( &ckHeader, 0 ) != 0 )
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Prepare DMUS_IO_STYLE
|
|
DMUS_IO_STYLE oDMStyle;
|
|
DWORD dwBytesWritten = 0;
|
|
memset( &oDMStyle, 0, sizeof(DMUS_IO_STYLE) );
|
|
oDMStyle.timeSig.bBeatsPerMeasure = rTimeSig.m_bBeatsPerMeasure;
|
|
oDMStyle.timeSig.bBeat = rTimeSig.m_bBeat;
|
|
oDMStyle.timeSig.wGridsPerBeat = rTimeSig.m_wGridsPerBeat;
|
|
oDMStyle.dblTempo = dblTempo;
|
|
// Write chunk data
|
|
hr = pEventStream->Write( &oDMStyle, sizeof(DMUS_IO_STYLE), &dwBytesWritten);
|
|
if( FAILED( hr ) || dwBytesWritten != sizeof(DMUS_IO_STYLE) )
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if( SUCCEEDED(hr) && pIRiffStream->Ascend( &ckHeader, 0 ) != 0 )
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = pPattern->Save( pEventStream );
|
|
pPartRef = pPattern->m_PartRefList.GetHead();
|
|
for (; pPartRef; pPartRef = pPartRef->GetNext())
|
|
{
|
|
if (pPartRef->GetItemValue().m_pDMPart)
|
|
{
|
|
delete pPartRef->GetItemValue().m_pDMPart;
|
|
pPartRef->GetItemValue().m_pDMPart = NULL;
|
|
}
|
|
}
|
|
pPattern->CleanUp();
|
|
delete pPattern;
|
|
if ( SUCCEEDED( hr ) && pIRiffStream->Ascend( &ckMain, 0 ) != 0 )
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
pIRiffStream->Release();
|
|
}
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
StreamSeek( pEventStream, 0, STREAM_SEEK_SET );
|
|
pIPSTrack->Load( pEventStream );
|
|
}
|
|
pEventStream->Release();
|
|
}
|
|
pIPSTrack->Release();
|
|
}
|
|
}
|
|
|
|
if (hr == S_OK && !rlistFragments.GetHead()) hr = S_FALSE;
|
|
return hr;
|
|
}
|