1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
4 #include "WaveGraphCtrl.h"
5 #include "Controls/MemDC.h"
6 #include "Util/GridUtils.h"
8 #include "Controls/SharedFonts.h"
10 IMPLEMENT_DYNAMIC(CWaveGraphCtrl
, CWnd
)
12 #define ACTIVE_BKG_COLOR RGB(190, 190, 190)
13 #define GRID_COLOR RGB(110, 110, 110)
14 LINK_SYSTEM_LIBRARY("Winmm.lib")
16 //////////////////////////////////////////////////////////////////////////
17 CWaveGraphCtrl::CWaveGraphCtrl()
20 m_editMode
= eNothingMode
;
26 m_timeRange
.Set(0, 1);
27 m_TimeUpdateRect
.SetRectEmpty();
29 m_bottomWndHeight
= 0;
34 m_fPlaybackSpeed
= 1.0f
;
36 //m_fLastTimeCheck = gEnv->pTimer->GetAsyncTime();
37 m_lastTimeCheck
= timeGetTime();
40 CWaveGraphCtrl::~CWaveGraphCtrl()
46 //////////////////////////////////////////////////////////////////////////
47 int CWaveGraphCtrl::AddWaveform()
49 int index
= m_waveforms
.size();
50 m_waveforms
.resize(m_waveforms
.size() + 1);
51 m_waveforms
[index
].itSound
= m_soundCache
.end();
55 //////////////////////////////////////////////////////////////////////////
56 void CWaveGraphCtrl::DeleteWaveform(int index
)
58 //REINST("do we still need this?");
59 std::vector
<Waveform
>::iterator itWaveform
= m_waveforms
.begin() + index
;
60 SoundCache::iterator itSound
= (*itWaveform
).itSound
;
61 /*if (itSound != m_soundCache.end())
62 --(*itSound).second.refcount;*/
63 m_waveforms
.erase(itWaveform
);
66 //////////////////////////////////////////////////////////////////////////
67 int CWaveGraphCtrl::GetWaveformCount()
69 return m_waveforms
.size();
72 //////////////////////////////////////////////////////////////////////////
73 void CWaveGraphCtrl::SetWaveformTime(int index
, float time
)
75 m_waveforms
[index
].time
= time
;
78 //////////////////////////////////////////////////////////////////////////
79 void CWaveGraphCtrl::LoadWaveformSound(int index
, const CString
& soundFile
)
83 // Check whether the sound is in the cache.
84 bool bSoundLoaded
= false;
86 //SoundCache::iterator itSound = m_soundCache.find(soundFile);
88 //if (itSound == m_soundCache.end())
90 // CWaveFileReader* pWaveFileReader = new CWaveFileReader;
92 // // [MichaelS 06/03/2007] Temporarily removed FLAG_SOUND_START_PAUSED since new culling rules stop sound from playing if this is set.
93 // // When Tomas returns from GDC I shall confront him about it!
94 // _smart_ptr<ISound> pSound = gEnv->pAudioSystem->CreateSound( soundFile,FLAG_SOUND_EDITOR|FLAG_SOUND_3D|FLAG_SOUND_RELATIVE|FLAG_SOUND_16BITS|FLAG_SOUND_LOAD_SYNCHRONOUSLY/*|FLAG_SOUND_START_PAUSED*/|FLAG_SOUND_LOOP );
98 // pSound->SetPosition(Vec3(1,1,1));
99 // pSound->SetPosition(Vec3(0));
100 // pSound->GetInterfaceExtended()->SetPitch(1000 * m_fPlaybackSpeed);
102 // pSound->SetPaused(true);
104 // if ( !pWaveFileReader->LoadFile(soundFile) )
106 // pWaveFileReader->GetSoundBufferInfo(pSound->GetSoundBufferInfo());
107 // pWaveFileReader->SetLoaded(false);
110 // itSound = m_soundCache.insert(std::make_pair(soundFile, SoundCacheEntry(soundFile, pWaveFileReader, pSound, 0))).first;
113 //++(*itSound).second.refcount;
114 //m_waveforms[index].itSound = itSound;
116 //m_fLastTimeCheck = gEnv->pTimer->GetAsyncTime();
117 m_lastTimeCheck
= timeGetTime();
120 //////////////////////////////////////////////////////////////////////////
121 void CWaveGraphCtrl::DeleteUnusedSounds()
123 //for (SoundCache::iterator itSound = m_soundCache.begin(), itSoundEnd = m_soundCache.end(); itSound != itSoundEnd;)
125 // if ((*itSound).second.refcount == 0)
127 // // Also need to stop the sound before releasing it (since it may not be deleted if it is already playing).
128 // if ((*itSound).second.pSound && (*itSound).second.pSound->IsPlaying())
129 // (*itSound).second.pSound->Stop();
130 // delete (*itSound).second.pWaveFileReader;
131 // m_soundCache.erase(itSound++);
140 //////////////////////////////////////////////////////////////////////////
141 float CWaveGraphCtrl::GetWaveformLength(int waveformIndex
)
144 /*if (waveformIndex < 0 || waveformIndex >= int(m_waveforms.size()))
146 CWaveFileReader* pReader = (*m_waveforms[waveformIndex].itSound).second.pWaveFileReader;
147 return (pReader ? pReader->GetLengthMs() / 1000.0f : 0.0f);*/
150 //////////////////////////////////////////////////////////////////////////
151 BEGIN_MESSAGE_MAP(CWaveGraphCtrl
, CWnd
)
164 /////////////////////////////////////////////////////////////////////////////
165 // CSplineCtrl message handlers
167 /////////////////////////////////////////////////////////////////////////////
168 BOOL
CWaveGraphCtrl::Create(DWORD dwStyle
, const CRect
& rc
, CWnd
* pParentWnd
, UINT nID
)
170 return CreateEx(0, NULL
, "SplineCtrl", dwStyle
, rc
, pParentWnd
, nID
);
173 //////////////////////////////////////////////////////////////////////////
174 BOOL
CWaveGraphCtrl::OnEraseBkgnd(CDC
* pDC
)
179 //////////////////////////////////////////////////////////////////////////
180 void CWaveGraphCtrl::OnSize(UINT nType
, int cx
, int cy
)
182 __super::OnSize(nType
, cx
, cy
);
183 GetClientRect(m_rcClient
);
184 m_rcWaveGraph
= m_rcClient
;
185 m_rcWaveGraph
.left
= m_nLeftOffset
;
189 m_rcWaveGraph
.bottom
-= m_bottomWndHeight
;
190 CRect
rc(m_rcWaveGraph
);
191 rc
.top
= m_rcClient
.bottom
- m_bottomWndHeight
;
192 rc
.bottom
= m_rcClient
.bottom
;
193 m_pBottomWnd
->MoveWindow(rc
);
196 m_grid
.rect
= m_rcWaveGraph
;
198 m_offscreenBitmap
.DeleteObject();
199 if (!m_offscreenBitmap
.GetSafeHandle())
202 m_offscreenBitmap
.CreateCompatibleBitmap(pDC
, cx
, cy
);
207 //////////////////////////////////////////////////////////////////////////
208 void CWaveGraphCtrl::SetContext(CFacialEdContext
* pContext
)
210 m_pContext
= pContext
;
212 m_pContext
->RegisterListener(this);
215 //////////////////////////////////////////////////////////////////////////
216 void CWaveGraphCtrl::OnFacialEdEvent(EFacialEdEvent event
, IFacialEffector
* pEffector
, int nChannelCount
, IFacialAnimChannel
** ppChannels
)
222 case EFD_EVENT_REMOVE
:
224 case EFD_EVENT_CHANGE
:
226 case EFD_EVENT_SELECT_EFFECTOR
:
231 //////////////////////////////////////////////////////////////////////////
232 int CWaveGraphCtrl::HitTestWaveforms(const CPoint point
)
234 float time
= ClientToWorld(point
).x
;
235 int hitWaveform
= -1;
236 /*for (int waveFormIndex = 0, waveFormCount = m_waveforms.size(); waveFormIndex < waveFormCount; ++waveFormIndex)
238 ISound* pSound = (*m_waveforms[waveFormIndex].itSound).second.pSound;
239 CWaveFileReader* pReader = (*m_waveforms[waveFormIndex].itSound).second.pWaveFileReader;
240 float startTime = m_waveforms[waveFormIndex].time;
241 float soundLength = max((pReader ? pReader->GetLengthMs() / 1000.0f : 0.0f), 0.5f);
242 float endTime = startTime + soundLength;
243 if (time >= startTime && time <= endTime)
244 hitWaveform = waveFormIndex;
249 //////////////////////////////////////////////////////////////////////////
250 void CWaveGraphCtrl::SendNotifyMessage(int code
)
252 ASSERT(::IsWindow(m_hWnd
));
254 nmh
.hwndFrom
= m_hWnd
;
255 nmh
.idFrom
= ::GetDlgCtrlID(m_hWnd
);
257 SendNotifyMessageStructure(&nmh
);
260 //////////////////////////////////////////////////////////////////////////
261 void CWaveGraphCtrl::SendNotifyMessageStructure(NMHDR
* hdr
)
263 GetOwner()->SendMessage(WM_NOTIFY
, (WPARAM
)GetDlgCtrlID(), (LPARAM
)hdr
);
266 //////////////////////////////////////////////////////////////////////////
267 void CWaveGraphCtrl::OnPaint()
269 CPaintDC
PaintDC(this);
272 GetClientRect(&rcClient
);
274 if (!m_offscreenBitmap
.GetSafeHandle())
276 m_offscreenBitmap
.CreateCompatibleBitmap(&PaintDC
, rcClient
.Width(), rcClient
.Height());
280 CMemoryDC
dc(PaintDC
, &m_offscreenBitmap
);
282 if (m_TimeUpdateRect
!= CRect(PaintDC
.m_ps
.rcPaint
))
285 bkBrush
.CreateSolidBrush(RGB(160, 160, 160));
286 dc
.FillRect(&PaintDC
.m_ps
.rcPaint
, &bkBrush
);
288 m_grid
.CalculateGridLines();
293 m_TimeUpdateRect
.SetRectEmpty();
296 DrawTimeMarker(&PaintDC
);
299 //////////////////////////////////////////////////////////////////////////
300 void CWaveGraphCtrl::DrawTimeMarker(CDC
* pDC
)
302 if (!(GetStyle() & WAVCTRLN_STYLE_NO_TIME_MARKER
))
305 timePen
.CreatePen(PS_SOLID
, 1, RGB(255, 0, 255));
306 CPen
* pOldPen
= pDC
->SelectObject(&timePen
);
307 CPoint pt
= WorldToClient(Vec2(m_fTimeMarker
, 0));
308 if (pt
.x
>= m_rcWaveGraph
.left
&& pt
.x
<= m_rcWaveGraph
.right
)
310 pDC
->MoveTo(pt
.x
, m_rcWaveGraph
.top
+ 1);
311 pDC
->LineTo(pt
.x
, m_rcWaveGraph
.bottom
- 1);
313 pDC
->SelectObject(pOldPen
);
317 //////////////////////////////////////////////////////////////////////////
318 class VerticalLineDrawer
321 VerticalLineDrawer(CDC
& dc
, const CRect
& rect
)
327 void operator()(int frameIndex
, int x
)
329 dc
.MoveTo(x
, rect
.top
);
330 dc
.LineTo(x
, rect
.bottom
);
336 void CWaveGraphCtrl::DrawGrid(CDC
* pDC
)
338 if (GetStyle() & WAVCTRLN_STYLE_NOGRID
)
341 CPoint pt0
= WorldToClient(Vec2(m_timeRange
.start
, 0));
342 CPoint pt1
= WorldToClient(Vec2(m_timeRange
.end
, 0));
343 CRect timeRc
= CRect(pt0
.x
- 2, m_rcWaveGraph
.top
, pt1
.x
+ 2, m_rcWaveGraph
.bottom
);
344 timeRc
.IntersectRect(timeRc
, m_rcWaveGraph
);
345 pDC
->FillSolidRect(timeRc
, ACTIVE_BKG_COLOR
);
348 penGridSolid
.CreatePen(PS_SOLID
, 1, GRID_COLOR
);
349 //////////////////////////////////////////////////////////////////////////
350 CPen
* pOldPen
= pDC
->SelectObject(&penGridSolid
);
352 /// Draw Left Separator.
353 CRect leftRect
= CRect(m_rcClient
.left
, m_rcClient
.top
, m_rcClient
.left
+ m_nLeftOffset
- 1, m_rcClient
.bottom
);
354 pDC
->FillSolidRect(leftRect
, ACTIVE_BKG_COLOR
);
355 pDC
->MoveTo(m_rcClient
.left
+ m_nLeftOffset
, m_rcClient
.bottom
);
356 pDC
->LineTo(m_rcClient
.left
+ m_nLeftOffset
, m_rcClient
.top
);
357 pDC
->SetTextColor(RGB(0, 0, 0));
358 pDC
->SetBkMode(TRANSPARENT
);
359 pDC
->SelectObject(SMFCFonts::GetInstance().hSystemFont
);
360 pDC
->DrawText("WAV", CRect(m_rcClient
.left
, m_rcWaveGraph
.top
, m_rcClient
.left
+ m_nLeftOffset
- 1, m_rcWaveGraph
.bottom
), DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
364 pDC
->DrawText("Lip\r\nSync", CRect(m_rcClient
.left
, m_rcWaveGraph
.bottom
+ 4, m_rcClient
.left
+ m_nLeftOffset
- 1, m_rcClient
.bottom
), DT_CENTER
| DT_VCENTER
);
367 //////////////////////////////////////////////////////////////////////////
371 logBrush
.lbStyle
= BS_SOLID
;
372 logBrush
.lbColor
= GRID_COLOR
;
375 pen
.CreatePen(PS_COSMETIC
| PS_ALTERNATE
, 1, &logBrush
);
376 pDC
->SelectObject(&pen
);
378 pDC
->SetTextColor(RGB(0, 0, 0));
379 pDC
->SetBkMode(TRANSPARENT
);
380 pDC
->SelectObject(SMFCFonts::GetInstance().hSystemFont
);
382 // Draw horizontal grid lines.
383 for (gy
= m_grid
.firstGridLine
.y
; gy
< m_grid
.firstGridLine
.y
+ m_grid
.numGridLines
.y
+ 1; gy
++)
385 int y
= m_grid
.GetGridLineY(gy
);
388 int py
= m_rcWaveGraph
.bottom
- y
;
389 if (py
< m_rcWaveGraph
.top
|| py
> m_rcWaveGraph
.bottom
)
391 pDC
->MoveTo(m_rcWaveGraph
.left
, py
);
392 pDC
->LineTo(m_rcWaveGraph
.right
, py
);
395 // Draw vertical grid lines.
396 VerticalLineDrawer
verticalLineDrawer(*pDC
, m_rcWaveGraph
);
397 GridUtils::IterateGrid(verticalLineDrawer
, 50.0f
, m_grid
.zoom
.x
, m_grid
.origin
.x
, FACIAL_EDITOR_FPS
, m_grid
.rect
.left
, m_grid
.rect
.right
);
399 //////////////////////////////////////////////////////////////////////////
402 pen0
.CreatePen(PS_SOLID
, 2, RGB(110, 100, 100));
403 CPoint p
= WorldToClient(Vec2(0, 0));
405 pDC
->SelectObject(&pen0
);
408 pDC
->MoveTo(m_rcWaveGraph
.left
, p
.y
);
409 pDC
->LineTo(m_rcWaveGraph
.right
, p
.y
);
412 if (p
.x
> m_rcWaveGraph
.left
&& p
.y
< m_rcWaveGraph
.right
)
414 pDC
->MoveTo(p
.x
, m_rcWaveGraph
.top
);
415 pDC
->LineTo(p
.x
, m_rcWaveGraph
.bottom
);
418 //////////////////////////////////////////////////////////////////////////
420 pDC
->SelectObject(pOldPen
);
423 //////////////////////////////////////////////////////////////////////////
424 void CWaveGraphCtrl::DrawWaveGraph(CDC
* pDC
)
426 //int cx = m_rcWaveGraph.Width();
427 //int cy = m_rcWaveGraph.Height();
429 //for (int waveFormIndex = 0, waveFormCount = m_waveforms.size(); waveFormIndex < waveFormCount; ++waveFormIndex)
431 // CString& soundFilename = (*m_waveforms[waveFormIndex].itSound).second.soundFilename;
432 // _smart_ptr<ISound>& pSound = (*m_waveforms[waveFormIndex].itSound).second.pSound;
433 // CWaveFileReader* pWaveFileReader = (*m_waveforms[waveFormIndex].itSound).second.pWaveFileReader;
435 // if ( !pWaveFileReader->IsLoaded() )
438 // float startTime = m_waveforms[waveFormIndex].time;
439 // CString& text = m_waveforms[waveFormIndex].text;
441 // int minX = WorldToClient( Vec2(startTime,0) ).x;
443 // // Draw sound name.
445 // pDC->SetTextColor( RGB(70, 70, 70) );
446 // pDC->SetBkMode( TRANSPARENT );
448 // pDC->TextOut( minX+4,m_rcWaveGraph.top+4,soundFilename );
450 // if (!text.IsEmpty())
451 // pDC->TextOut( minX+4,m_rcWaveGraph.bottom-16,text );
455 // // create and select a thick, white pen
457 // pen.CreatePen(PS_SOLID, 1, RGB(128, 255, 128));
458 // CPen* pOldPen = pDC->SelectObject(&pen);
461 // pDC->GetClipBox(rcClip);
462 // rcClip.IntersectRect(rcClip,m_rcWaveGraph);
464 // float fRatio = pWaveFileReader->GetSampleCount()/(float)cx;
465 // uint32 nSamplesPerSec = pWaveFileReader->GetSamplesPerSec();
466 // uint32 nSamplesPerPixel = pWaveFileReader->GetSamplesPerSec() / m_grid.zoom.x;
467 // float fWavLengthSec = pWaveFileReader->GetLengthMs() / 1000.0f;
468 // float fPixelsPerSec = float(nSamplesPerSec) / float(nSamplesPerPixel ? nSamplesPerPixel : 1);
469 // int nWavLengthPixels = fPixelsPerSec * fWavLengthSec;
471 // if (nSamplesPerPixel <= 0)
472 // nSamplesPerPixel = 1;
474 // if (nSamplesPerPixel > 1000)
475 // nSamplesPerPixel = 1000;
477 // int maxX = minX + nWavLengthPixels;
479 // bool bFirst = true;
480 // for (int x = max(minX,(int)rcClip.left), right = min(maxX, (int)rcClip.right); x < right; x++)
482 // Vec2 v = ClientToWorld( CPoint(x,0) ) - Vec2(startTime, 0.0f);
485 // if (v.x > fWavLengthSec)
487 // float fMinValue = 0.0f;
488 // float fMaxValue = 0.0f;
489 // int nSample = v.x*nSamplesPerSec;
490 // pWaveFileReader->GetSamplesMinMax( nSample,nSamplesPerPixel,fMinValue,fMaxValue );
492 // pDC->MoveTo(x,cy/2+fMaxValue*(cy/2));
493 // pDC->LineTo(x,cy/2+fMinValue*(cy/2));
496 // // Put back the old objects
497 // pDC->SelectObject(pOldPen);
501 //////////////////////////////////////////////////////////////////////////
502 void CWaveGraphCtrl::SetWaveformTextString(int waveformIndex
, const CString
& text
)
504 if (waveformIndex
< 0 || waveformIndex
>= int(m_waveforms
.size()))
507 m_waveforms
[waveformIndex
].text
= text
;
510 /////////////////////////////////////////////////////////////////////////////
511 //Mouse Message Handlers
512 //////////////////////////////////////////////////////////////////////////
513 void CWaveGraphCtrl::OnLButtonDown(UINT nFlags
, CPoint point
)
515 CWnd::OnLButtonDown(nFlags
, point
);
517 m_StartClickPoint
= point
;
519 if (nFlags
& MK_CONTROL
)
521 int waveformIndex
= HitTestWaveforms(point
);
522 if (waveformIndex
>= 0)
524 m_editMode
= eWaveDragMode
;
525 m_nWaveformBeingDragged
= waveformIndex
;
526 SendNotifyMessage(WAVECTRLN_BEGIN_MOVE_WAVEFORM
);
531 m_editMode
= eClickingMode
;
533 //m_fLastTimeCheck = gEnv->pTimer->GetAsyncTime();
534 m_lastTimeCheck
= timeGetTime();
535 SetTimeMarkerInternal(max(m_timeRange
.start
, min(m_timeRange
.end
, FacialEditorSnapTimeToFrame(ClientToWorld(point
).x
))));
537 SendNotifyMessage(WAVCTRLN_TIME_CHANGE
);
543 //////////////////////////////////////////////////////////////////////////
544 void CWaveGraphCtrl::OnRButtonDown(UINT nFlags
, CPoint point
)
546 CWnd::OnRButtonDown(nFlags
, point
);
548 WaveGraphCtrlRClickNotification notification
;
549 notification
.hdr
.hwndFrom
= m_hWnd
;
550 notification
.hdr
.idFrom
= ::GetDlgCtrlID(m_hWnd
);
551 notification
.hdr
.code
= WAVECTRLN_RCLICK
;
553 notification
.waveformIndex
= HitTestWaveforms(point
);
555 SendNotifyMessageStructure(¬ification
.hdr
);
558 //////////////////////////////////////////////////////////////////////////
559 void CWaveGraphCtrl::OnMouseMove(UINT nFlags
, CPoint point
)
561 CWnd::OnMouseMove(nFlags
, point
);
567 SendNotifyMessage(WAVECTRLN_RESET_CHANGES
);
569 // Work out how far to drag the waveform in seconds.
570 float dt
= (point
.x
- m_StartClickPoint
.x
) / m_grid
.zoom
.x
;
572 // Pass on the changes to our parent.
573 ASSERT(m_nWaveformBeingDragged
>= 0 && m_nWaveformBeingDragged
< int(m_waveforms
.size()));
574 WaveGraphCtrlWaveformChangeNotification notification
;
575 notification
.hdr
.hwndFrom
= m_hWnd
;
576 notification
.hdr
.idFrom
= ::GetDlgCtrlID(m_hWnd
);
577 notification
.hdr
.code
= WAVECTRLN_MOVE_WAVEFORMS
;
578 notification
.waveformIndex
= m_nWaveformBeingDragged
;
579 notification
.deltaTime
= dt
;
580 SendNotifyMessageStructure(¬ification
.hdr
);
587 if (point
.x
== m_StartClickPoint
.x
&& point
.y
== m_StartClickPoint
.y
)
590 //m_fLastTimeCheck = gEnv->pTimer->GetAsyncTime();
591 m_editMode
= eScrubbingMode
;
593 m_lastTimeCheck
= timeGetTime();
594 float fNewTime
= min(m_timeRange
.end
, ClientToWorld(point
).x
);
595 SetTimeMarkerInternal(fNewTime
);
596 StartSoundsAtTime(fNewTime
, true);
599 nmh
.hwndFrom
= m_hWnd
;
600 nmh
.idFrom
= ::GetDlgCtrlID(m_hWnd
);
601 nmh
.code
= WAVCTRLN_TIME_CHANGE
;
603 GetOwner()->SendMessage(WM_NOTIFY
, (WPARAM
)GetDlgCtrlID(), (LPARAM
)&nmh
);
608 if (point
.x
== m_StartClickPoint
.x
&& point
.y
== m_StartClickPoint
.y
)
611 float fNewTime
= min(m_timeRange
.end
, ClientToWorld(point
).x
);
612 SetTimeMarkerInternal(fNewTime
);
618 //////////////////////////////////////////////////////////////////////////
619 void CWaveGraphCtrl::OnLButtonUp(UINT nFlags
, CPoint point
)
624 //m_fLastTimeCheck = gEnv->pTimer->GetAsyncTime();
625 m_lastTimeCheck
= timeGetTime();
626 SetTimeMarkerInternal(min(m_timeRange
.end
, FacialEditorSnapTimeToFrame(ClientToWorld(point
).x
)));
628 m_editMode
= eNothingMode
;
632 SendNotifyMessage(WAVECTRLN_END_MOVE_WAVEFORM
);
633 m_editMode
= eNothingMode
;
637 //SendNotifyMessage(WAVECTRLN_END_MOVE_WAVEFORM);
638 m_editMode
= eNothingMode
;
642 m_bScrubbing
= false;
646 CWnd::OnLButtonUp(nFlags
, point
);
649 /////////////////////////////////////////////////////////////////////////////
650 BOOL
CWaveGraphCtrl::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
655 GetCursorPos(&point
);
656 ScreenToClient(&point
);
659 return CWnd::OnSetCursor(pWnd
, nHitTest
, message
);
663 void CWaveGraphCtrl::StartPlayback()
666 m_bScrubbing
= false;
667 //m_fLastTimeCheck = gEnv->pTimer->GetAsyncTime();
668 m_lastTimeCheck
= timeGetTime();
672 void CWaveGraphCtrl::StopPlayback()
676 /*for (int waveformIndex = 0, waveformCount = m_waveforms.size(); waveformIndex < waveformCount; ++waveformIndex)
678 ISound* pSound = (*m_waveforms[waveformIndex].itSound).second.pSound;
679 if (pSound && pSound->IsPlaying())
681 pSound->GetInterfaceExtended()->SetCurrentSamplePos(0.0f, true);
682 pSound->SetPaused(true);
685 m_fTimeMarker
= 0.0f
;
689 void CWaveGraphCtrl::PausePlayback()
693 /*for (int waveformIndex = 0, waveformCount = m_waveforms.size(); waveformIndex < waveformCount; ++waveformIndex)
695 ISound* pSound = (*m_waveforms[waveformIndex].itSound).second.pSound;
696 if (pSound && pSound->IsPlaying())
698 pSound->GetInterfaceExtended()->SetCurrentSamplePos((m_fTimeMarker - m_waveforms[waveformIndex].time) * 1000.0f, true);
699 pSound->SetPaused(true);
704 void CWaveGraphCtrl::BeginScrubbing()
709 void CWaveGraphCtrl::EndScrubbing()
711 /*m_bScrubbing = false;
712 for (int waveformIndex = 0, waveformCount = m_waveforms.size(); waveformIndex < waveformCount; ++waveformIndex)
714 ISound* pSound = (*m_waveforms[waveformIndex].itSound).second.pSound;
715 if (pSound && pSound->IsPlaying() && m_bPlaying)
717 pSound->GetInterfaceExtended()->SetCurrentSamplePos((m_fTimeMarker - m_waveforms[waveformIndex].time * 1000.0f), true);
718 pSound->SetPaused(false);
719 pSound->GetInterfaceExtended()->SetPitch(1000 * m_fPlaybackSpeed);
724 void CWaveGraphCtrl::SetPlaybackSpeed(float fSpeed
)
726 /*if (m_fPlaybackSpeed != fSpeed)
728 m_fPlaybackSpeed = fSpeed;
729 for (int waveformIndex = 0, waveformCount = m_waveforms.size(); waveformIndex < waveformCount; ++waveformIndex)
731 ISound* pSound = (*m_waveforms[waveformIndex].itSound).second.pSound;
732 if (pSound && pSound->IsPlaying())
733 pSound->GetInterfaceExtended()->SetPitch(1000 * m_fPlaybackSpeed);
738 float CWaveGraphCtrl::GetPlaybackSpeed()
740 return m_fPlaybackSpeed
;
743 float CWaveGraphCtrl::GetTimeMarker()
745 return m_fTimeMarker
;
748 //////////////////////////////////////////////////////////////////////////
749 float CWaveGraphCtrl::CalculateTimeRange()
751 float minT
= 0, maxT
= 1;
752 /*for (int waveformIndex = 0, waveformCount = m_waveforms.size(); waveformIndex < waveformCount; ++waveformIndex)
754 float startTime = m_waveforms[waveformIndex].time;
755 CWaveFileReader* pWaveFileReader = (*m_waveforms[waveformIndex].itSound).second.pWaveFileReader;
756 float endTime = startTime + (pWaveFileReader->GetLengthMs() / 1000.0f);
757 minT = min(minT, startTime), maxT = max(maxT, endTime);
762 //////////////////////////////////////////////////////////////////////////
763 void CWaveGraphCtrl::SetTimeMarker(float fTime
)
765 if (fTime
== m_fTimeMarker
)
768 SetTimeMarkerInternal(fTime
);
769 StartSoundsAtTime(fTime
, true);
772 //////////////////////////////////////////////////////////////////////////
773 void CWaveGraphCtrl::SetTimeMarkerInternal(float fTime
)
775 if (fTime
== m_fTimeMarker
)
779 CPoint pt0
= WorldToClient(Vec2(m_fTimeMarker
, 0));
780 CPoint pt1
= WorldToClient(Vec2(fTime
, 0));
781 CRect rc
= CRect(pt0
.x
, m_rcWaveGraph
.top
, pt1
.x
, m_rcWaveGraph
.bottom
);
783 rc
.InflateRect(5, 0);
784 rc
.IntersectRect(rc
, m_rcWaveGraph
);
786 m_TimeUpdateRect
= rc
;
794 m_nTimer
= SetTimer(1, 300, 0);
797 m_fTimeMarker
= fTime
;
801 //////////////////////////////////////////////////////////////////////////
802 void CWaveGraphCtrl::OnTimer(UINT_PTR nIDEvent
)
804 /*for (int waveformIndex = 0, waveformCount = m_waveforms.size(); waveformIndex < waveformCount; ++waveformIndex)
806 ISound* pSound = (*m_waveforms[waveformIndex].itSound).second.pSound;
807 if (pSound && pSound->IsPlaying() && !m_bPlaying)
808 pSound->SetPaused(true);
817 //////////////////////////////////////////////////////////////////////////
818 CPoint
CWaveGraphCtrl::WorldToClient(Vec2 v
)
820 CPoint p
= m_grid
.WorldToClient(v
);
821 p
.y
= m_rcWaveGraph
.bottom
- p
.y
;
825 //////////////////////////////////////////////////////////////////////////
826 Vec2
CWaveGraphCtrl::ClientToWorld(CPoint point
)
828 Vec2 v
= m_grid
.ClientToWorld(CPoint(point
.x
, m_rcWaveGraph
.bottom
- point
.y
));
832 //////////////////////////////////////////////////////////////////////////
833 void CWaveGraphCtrl::SetZoom(Vec2 zoom
, CPoint center
)
835 m_grid
.SetZoom(zoom
, CPoint(center
.x
, m_rcWaveGraph
.bottom
- center
.y
));
836 SetScrollOffset(m_grid
.origin
);
839 //////////////////////////////////////////////////////////////////////////
840 void CWaveGraphCtrl::SetZoom(Vec2 zoom
)
843 SetScrollOffset(m_grid
.origin
);
846 //////////////////////////////////////////////////////////////////////////
847 void CWaveGraphCtrl::SetScrollOffset(Vec2 ofs
)
854 //////////////////////////////////////////////////////////////////////////
855 void CWaveGraphCtrl::SetLeftOffset(int nLeft
)
857 m_nLeftOffset
= nLeft
;
861 //////////////////////////////////////////////////////////////////////////
862 void CWaveGraphCtrl::SetBottomWnd(CWnd
* pWnd
, int nHeight
)
865 m_bottomWndHeight
= nHeight
;
869 //////////////////////////////////////////////////////////////////////////
870 float CWaveGraphCtrl::FindEndOfWaveforms()
872 // Check whether we need to start playing any sounds at this new time.
873 float endTime
= 0.0f
;
874 /*for (int waveFormIndex = 0, waveFormCount = m_waveforms.size(); waveFormIndex < waveFormCount; ++waveFormIndex)
876 ISound* pSound = (*m_waveforms[waveFormIndex].itSound).second.pSound;
877 CWaveFileReader* pReader = (*m_waveforms[waveFormIndex].itSound).second.pWaveFileReader;
878 float startTime = m_waveforms[waveFormIndex].time;
879 endTime = max(endTime, startTime + (pReader ? (pReader->GetLengthMs() / 1000.0f) : 0.0f));
884 //////////////////////////////////////////////////////////////////////////
885 void CWaveGraphCtrl::UpdatePlayback()
887 if (!m_bPlaying
|| m_bScrubbing
)
890 float fTime
= m_fTimeMarker
;
892 // Update the time marker based on real time.
893 //CTimeValue currentRealTime = gEnv->pTimer->GetAsyncTime();
894 DWORD currentRealTime
= timeGetTime();
895 float elapsedTime
= float(currentRealTime
- m_lastTimeCheck
) / 1000.0f
;
896 fTime
= m_fTimeMarker
+ elapsedTime
* m_fPlaybackSpeed
;
898 // Try to keep the time synched with the sound engine - adjust the time if there are any sounds playing.
899 /*for (int waveFormIndex = 0, waveFormCount = m_waveforms.size(); waveFormIndex < waveFormCount; ++waveFormIndex)
901 ISound* pSound = (*m_waveforms[waveFormIndex].itSound).second.pSound;
902 float startTime = m_waveforms[waveFormIndex].time;
903 float fTimeAccordingToSound = fTime;
904 bool soundFound = false;
905 if (pSound && !pSound->GetPaused())
906 soundFound = true, fTimeAccordingToSound = startTime + pSound->GetInterfaceExtended()->GetCurrentSamplePos(true) / 1000.0f;
907 if (soundFound && fabs(fTimeAccordingToSound - fTime) < 0.1f)
909 fTime = fTimeAccordingToSound;
913 // Check whether the time is past the end.
914 if (fTime
> m_timeRange
.end
)
915 fTime
= m_timeRange
.start
;
917 StartSoundsAtTime(fTime
, false);
919 // If the time has changed, update the view.
920 if (fTime
!= m_fTimeMarker
)
923 CPoint pt0
= WorldToClient(Vec2(m_fTimeMarker
, 0));
924 CPoint pt1
= WorldToClient(Vec2(fTime
, 0));
925 CRect rc
= CRect(pt0
.x
, m_rcWaveGraph
.top
, pt1
.x
, m_rcWaveGraph
.bottom
);
927 rc
.InflateRect(5, 0);
928 rc
.IntersectRect(rc
, m_rcWaveGraph
);
930 m_TimeUpdateRect
= rc
;
933 m_fTimeMarker
= fTime
;
936 //m_fLastTimeCheck = currentRealTime;
937 m_lastTimeCheck
= currentRealTime
;
940 //////////////////////////////////////////////////////////////////////////
941 struct WaveformSortPredicate
943 WaveformSortPredicate(const std::vector
<CWaveGraphCtrl::Waveform
>& waveforms
) : waveforms(waveforms
) {}
944 bool operator()(int left
, int right
) { return this->waveforms
[left
].time
< this->waveforms
[right
].time
; }
945 const std::vector
<CWaveGraphCtrl::Waveform
>& waveforms
;
948 //////////////////////////////////////////////////////////////////////////
949 void CWaveGraphCtrl::StartSoundsAtTime(float fTime
, bool bForceStart
)
951 // Created a sorted list of waveforms.
952 //std::vector<int> sortedWaveformIndices(m_waveforms.size());
953 //for (int i = 0, count = sortedWaveformIndices.size(); i < count; ++i)
954 // sortedWaveformIndices[i] = i;
955 //std::sort(sortedWaveformIndices.begin(), sortedWaveformIndices.end(), WaveformSortPredicate(m_waveforms));
957 //// Create a list of waveforms that should be playing.
958 //std::vector<int> waveformsThatShouldBePlaying;
959 //waveformsThatShouldBePlaying.reserve(m_waveforms.size());
960 //for (int waveformIndex = 0, waveformCount = m_waveforms.size(); waveformIndex < waveformCount; ++waveformIndex)
962 // CWaveFileReader* pReader = (*m_waveforms[sortedWaveformIndices[waveformIndex]].itSound).second.pWaveFileReader;
963 // float startTime = m_waveforms[sortedWaveformIndices[waveformIndex]].time;
964 // float samplesPerSecond = (pReader ? pReader->GetSamplesPerSec() : 0);
965 // float endTime = startTime + (pReader ? float(pReader->GetSampleCount()) : 0.0f) / max(samplesPerSecond, 0.1f);
966 // if (fTime >= startTime && fTime <= endTime)
967 // waveformsThatShouldBePlaying.push_back(waveformIndex);
970 //// Check whether the user only wants to hear one sound at a time - if so, only the sound that starts latest should be playing.
971 //bool overlapSounds = (m_pContext ? m_pContext->GetOverlapSounds() : true);
972 //if (!overlapSounds)
974 // int latestEntryIndex = -1;
975 // float latestStartTime = -FLT_MAX;
976 // for (int entryIndex = 0, entryCount = waveformsThatShouldBePlaying.size(); entryIndex < entryCount; ++entryIndex)
978 // float startTime = m_waveforms[sortedWaveformIndices[waveformsThatShouldBePlaying[entryIndex]]].time;
979 // if (startTime > latestStartTime)
981 // latestStartTime = startTime;
982 // latestEntryIndex = waveformsThatShouldBePlaying[entryIndex];
985 // waveformsThatShouldBePlaying.clear();
986 // if (latestEntryIndex >= 0)
987 // waveformsThatShouldBePlaying.push_back(latestEntryIndex);
990 //// Create a list of waveforms that are playing.
991 //std::vector<int> waveformsThatArePlaying;
992 //waveformsThatArePlaying.reserve(m_waveforms.size());
993 //for (int waveformIndex = 0, waveformCount = m_waveforms.size(); waveformIndex < waveformCount; ++waveformIndex)
995 // ISound* pSound = (*m_waveforms[sortedWaveformIndices[waveformIndex]].itSound).second.pSound;
996 // if (pSound && !pSound->GetPaused())
997 // waveformsThatArePlaying.push_back(waveformIndex);
1000 //// Check which waveforms need to be started and which need to be stopped. Relies on waveform lists being sorted.
1001 //std::vector<int> waveformsToStop, waveformsToStart, waveformsToUpdate;
1002 //waveformsToStart.reserve(m_waveforms.size());
1003 //waveformsToStop.reserve(m_waveforms.size());
1004 //waveformsToUpdate.reserve(m_waveforms.size());
1006 // int playingPosition = 0, toPlayPosition = 0, playingCount = waveformsThatArePlaying.size(), toPlayCount = waveformsThatShouldBePlaying.size();
1007 // while (playingPosition < playingCount || toPlayPosition < toPlayCount)
1009 // for (; playingPosition < playingCount && (toPlayPosition >= toPlayCount || waveformsThatArePlaying[playingPosition] < waveformsThatShouldBePlaying[toPlayPosition]); ++playingPosition)
1010 // waveformsToStop.push_back(waveformsThatArePlaying[playingPosition]);
1012 // for (; toPlayPosition < toPlayCount && (playingPosition >= playingCount || waveformsThatShouldBePlaying[toPlayPosition] < waveformsThatArePlaying[playingPosition]); ++toPlayPosition)
1013 // waveformsToStart.push_back(waveformsThatShouldBePlaying[toPlayPosition]);
1015 // for (; toPlayPosition < toPlayCount && playingPosition < playingCount && waveformsThatShouldBePlaying[toPlayPosition] == waveformsThatArePlaying[playingPosition]; ++toPlayPosition, ++playingPosition)
1016 // waveformsToUpdate.push_back(waveformsThatShouldBePlaying[toPlayPosition]);
1020 //// Create a list of sounds that need to be playing - this is required in case the same sound is played multiple times.
1021 //std::set<ISound*> soundsThatShouldBePlaying;
1022 //for (int entryIndex = 0, entryCount = waveformsThatShouldBePlaying.size(); entryIndex < entryCount; ++entryIndex)
1024 // ISound* pSound = (*m_waveforms[sortedWaveformIndices[waveformsThatShouldBePlaying[entryIndex]]].itSound).second.pSound;
1026 // soundsThatShouldBePlaying.insert(pSound);
1029 //// Start all the sounds that should be started.
1030 //for (int entryIndex = 0, entryCount = waveformsToStart.size(); entryIndex < entryCount; ++entryIndex)
1032 // ISound* pSound = (*m_waveforms[sortedWaveformIndices[waveformsToStart[entryIndex]]].itSound).second.pSound;
1033 // float startTime = m_waveforms[sortedWaveformIndices[waveformsToStart[entryIndex]]].time;
1036 // pSound->GetInterfaceExtended()->SetCurrentSamplePos((fTime - startTime) * 1000.0f, true);
1037 // pSound->SetPaused(false);
1038 // pSound->GetInterfaceExtended()->SetPitch(1000 * m_fPlaybackSpeed);
1042 //// Stop all the sounds that should be stopped.
1043 //for (int entryIndex = 0, entryCount = waveformsToStop.size(); entryIndex < entryCount; ++entryIndex)
1045 // ISound* pSound = (*m_waveforms[sortedWaveformIndices[waveformsToStop[entryIndex]]].itSound).second.pSound;
1046 // if (pSound && soundsThatShouldBePlaying.find(pSound) == soundsThatShouldBePlaying.end())
1048 // float startTime = m_waveforms[sortedWaveformIndices[waveformsToStop[entryIndex]]].time;
1049 // pSound->GetInterfaceExtended()->SetCurrentSamplePos((fTime - startTime) * 1000.0f, true);
1050 // pSound->SetPaused(true);
1051 // pSound->GetInterfaceExtended()->SetPitch(1000 * m_fPlaybackSpeed);
1055 //// Update all the sounds that are still playing.
1058 // for (int entryIndex = 0, entryCount = waveformsToUpdate.size(); entryIndex < entryCount; ++entryIndex)
1060 // ISound* pSound = (*m_waveforms[sortedWaveformIndices[waveformsToUpdate[entryIndex]]].itSound).second.pSound;
1061 // float startTime = m_waveforms[sortedWaveformIndices[waveformsToUpdate[entryIndex]]].time;
1064 // pSound->GetInterfaceExtended()->SetCurrentSamplePos((fTime - startTime) * 1000.0f, true);
1065 // pSound->GetInterfaceExtended()->SetPitch(1000 * m_fPlaybackSpeed);