Wednesday, September 28, 2005

movie disk requirements

The data rate of a WHM movie file is linearly proportional to scene complexity (as measured by ring count), and is also affected by the frame rate. Typical data rates range from .4 - 3.0 MBytes @ second, which means a 1-hour recording can require anywhere from 1.5 to 10 GB.

Both ATA 100 and Firewire A can sustain write data rates of at least 30 MB @ second, with minimal CPU loading, so performance probably isn't an issue. Disk space could be an issue, particularly on older machines.

Using WinZip on a WHM file typically yields a 60% - 70% size reduction. Recording to a compressed folder yields a 40% - 60% reduction, but causes the display to freeze at regular intervals on a PIII machine. The Gnu zlib compression library might have less impact, but some increase in CPU load is inevitable. To reduce disk usage, it might make sense to compress WHM files after recording. The app could include an option that does this automatically, but it would have to be done from a separate low-priority thread, and even then performance might be affected by context switching.

A higher-order solution would record user input instead of recording ring data. This would reduce the data rate to a negligible amount, but at the expense of greatly increased complexity and fragility. This method should be explored for a future version.

A very rough estimate of the data rate resulting from input recording:

Record cursor delta for every frame: 8 bytes (2 ints) @ frame
Record 16 MIDI controllers for every frame (worst case): 48 bytes @ frame
Keyboard commands are too infrequent to affect the estimate
8 + 48 = 56 bytes per frame; at 30 FPS, 56 * 30 = 1680 bytes @ second
That's 6MB per hour in the absolute worst case

Some benchmarks:

Uncompressed 1024 x 768 video at 30 FPS: 70.5 MB @ sec
Lotus light, speed around 700, 30 FPS, 1 minute = 131 MB (2.2 MB @ sec)
Cross, speed around 700, 30 FPS, 1:36 = 80 MB (.83 MB @ sec)
Kaleid, speed around 700, 30 FPS, 1:36 = 282 MB (2.9 MB @ sec)

Another interesting possibility: Recording the view's oscillators and STATE member but not the rings, and then regenerating the rings on playback/export

1 oscillator = 44 bytes * 16 = 704 bytes @ frame
view STATE member = 104 bytes + 704 = 808 bytes @ frame
call it 1K bytes @ frame, at 30 FPS = 30K bytes @ second (108 MB @ hour)

Thursday, September 22, 2005

making a WMV-9 DVD

Toshi says no need to change frame rate for WMV; between 5 and 20 MBits per sec should be good, and 1024 x 768 is fine. Must compress the bitmaps first to make enough room to create an AVI, expect on the order of 75% compression overall.

Monday, September 12, 2005

additional MIDI control

on/off: mirror, fill, outline, xray, invert hue, invert fill, invert outline, invert xray, rotate hue, reverse

zoom should be damped, no?

Saturday, September 10, 2005

show size in status bar message while resizing

You must handle WM_ENTERSIZEMOVE and wM_EXITSIZEMOVE to show the initial size and restore the default message.

LRESULT CMainFrame::OnEnterSizeMove(WPARAM wParam, LPARAM lParam)
{
CRect r;
m_View->GetClientRect(r);
CString s;
s.Format("%d x %d\n", r.Width(), r.Height());
SetMessageText(s); // must do all the above in OnSize too, but only if window is visible
return(TRUE);
}

LRESULT CMainFrame::OnExitSizeMove(WPARAM wParam, LPARAM lParam)
{
SetMessageText(AFX_IDS_IDLEMESSAGE);
return(TRUE);
}

Note that this only works if the system property "show window contents while dragging" is set. Otherwise you only see the initial size, because WM_SIZE isn't sent until the drag ends. WM_SIZING *is* sent, but it doesn't help, because it passes us the size of the frame, whereas we want the size of the view. Oddly, there seems to be no way to determine the client area that would result from a given window size. CalcWindowRect does the reverse: it gives us the size of the window needed for a given client area.

Tuesday, September 06, 2005

CalcWindowRect bug

This bug first appeared during the development of movie export. Moving the export to a separate process made it go away, but now it's back in version 1.0.73. Most likely the AVI file DLL was falsely accused and the movie export could have been part of the main app, but that's a secondary issue.

The symptom is, the app crashes in RowDialogForm's CalcWindowRect during the construction of the Parms dialog. It doesn't crash in debug, only in release, and creating a console window makes it go away (great!). Deleting the unused m_Template member variable from CPersistDlg.h also makes it go away. This last symptom clearly points towards a memory corruption problem of some kind. In the release debugger, the problem begins when CRowDialog::OnInitDialog calls CViewDialog::OnInitDialog. After this call, CRowDialog's "this" and HWND are trashed. Note that neither CViewDialog nor its immediate base class (CPersistDlg) override OnInitDialog, so this is effectively a call to CDialog::OnInitDialog.

Removing the call to MakeVJAccelTable in CMainFrame's ctor also makes it go away.

The really bad news: the app only crashes when you execute it from the IDE; running from Explorer is OK.

Other observations:
in Frame's ctor list, m_ResultsDlg(m_View), is incorrect initialization, m_View isn't the dialog's parent and in any case it's NULL! deleting this line doesn't help though...

could be passing 'this' to dialogs isn't such a good idea after all, seems like CMainFrame's 'this' keeps changing somehow or is that just the debugger being bad in release mode?

The solution: in CalcWindowRect, casting GetParent() down to CRowDialog isn't always correct. Initially, the parent is the dummy frame, not CRowDialog. Use a dynamic downcast to determine the parent.

The moral of the story: Downcasting is dangerous so use dynamic downcast and verify the result!