00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "win32.h"
00021 #include <sstream>
00022 #include <iomanip>
00023 #include <fstream>
00024 #include <stdexcept>
00025 #include <cstring>
00026 #include <mmsystem.h>
00027 #include <dsound.h>
00028 #ifdef DEBUG_WIN32
00029 #include <iostream>
00030 #endif
00031
00032
00033 using namespace std;
00034
00035
00036 const GUID GUID_NULL = {0};
00045 class ds_wave_out : public sound_renderer {
00046 private:
00047 IDirectSound8 * _ds;
00048 IDirectSoundBuffer8 * _dsb;
00049 int _writepos;
00050 int _oldwritepos;
00051
00052
00053 int _size;
00054 bool _started;
00055 public:
00056 ds_wave_out() : _ds(0), _dsb(0), _started(false) {};
00057 virtual ~ds_wave_out() {
00058 if(_dsb) { _dsb->Release(); _dsb = 0; }
00059 if(_ds) { _ds->Release(); _ds = 0; }
00060
00061
00062 }
00063 bool init(int frequency, bool stereo) {
00064 HRESULT hr = DirectSoundCreate8(&DSDEVID_DefaultPlayback, &_ds, NULL);
00065 if(FAILED(hr)) throw runtime_error("DirectSoundCreate8() failed");
00066 hr = _ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_NORMAL);
00067 if(FAILED(hr)) throw runtime_error("SetCooperativeLevel() failed");
00068 WAVEFORMATEX wf;
00069 wf.wBitsPerSample = 16;
00070 wf.nChannels = stereo ? 2 : 1;
00071 wf.nSamplesPerSec = frequency;
00072 wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
00073 wf.wFormatTag = WAVE_FORMAT_PCM;
00074 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
00075 wf.cbSize =0;
00076
00077 _size = wf.nAvgBytesPerSec * 5;
00078 _writepos = 0;
00079 _oldwritepos = 0;
00080
00081
00082 DSBUFFERDESC desc;
00083 desc.dwSize = sizeof(desc);
00084 desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS|DSBCAPS_STICKYFOCUS;
00085 desc.dwBufferBytes = _size;
00086 desc.dwReserved = 0;
00087 desc.lpwfxFormat = &wf;
00088 desc.guid3DAlgorithm = GUID_NULL;
00089 hr = _ds->CreateSoundBuffer(&desc, (IDirectSoundBuffer**)&_dsb, NULL);
00090 if(FAILED(hr)) throw runtime_error("CreateSoundBuffer() failed");
00091
00092 return true;
00093 }
00094 bool write(unsigned short * ptr, int size) {
00095 assert(_dsb && _ds);
00096 assert(size && (size < _size) && ((size % 4) == 0));
00097
00098 unsigned short * ptr1, *ptr2;
00099 DWORD size1, size2;
00100
00101
00102
00103 _dsb->GetCurrentPosition(&size1, &size2);
00104 #ifdef DEBUG_WIN32
00105 cout << "ds_wave_out::write() == " << _writepos << ", " << size << ", " << _writepos + size << endl;
00106 cout << "DirectSoundBuffer8->GetCurrentPosition() == " << size1 << ", " << size2 << endl;
00107 #endif
00108 HRESULT hr = _dsb->Lock(_writepos, size, (void**)&ptr1, &size1, (void**)&ptr2, &size2, 0);
00109 if(SUCCEEDED(hr)) {
00110 memcpy(ptr1, ptr, size1);
00111 if(size2) memcpy(ptr2, reinterpret_cast<char*>(ptr)+size1, size2);
00112 _dsb->Unlock((void*)ptr1, size1, (void*)ptr2, size2);
00113 } else {
00114 ostringstream oss;
00115 oss << "_dsb->Lock() failed : 0x" << hex << hr << " == " << DSERR_INVALIDPARAM << endl;
00116 throw runtime_error(oss.str());
00117 }
00118 if(!_started) {
00119 #ifdef DEBUG_WIN32
00120 cout << "Starting DirectX Sound playback" << endl;
00121 #endif
00122 _dsb->Play(0, 0, DSBPLAY_LOOPING);
00123 _started = true;
00124 }
00125 _oldwritepos = _writepos;
00126 _writepos += size;
00127 if(_writepos >= _size) _writepos -= _size;
00128
00129 return true;
00130 }
00131 bool wait() {
00132 assert(_dsb && _ds);
00133 if(!_started) return false;
00134 DWORD play;
00135 while(1) {
00136 HRESULT hr = _dsb->GetCurrentPosition(&play, NULL);
00137 if(FAILED(hr)) return false;
00138 if(_writepos < _oldwritepos && (play > _oldwritepos || play < _writepos)) return true;
00139 if(_writepos > _oldwritepos && play > _oldwritepos && play < _writepos) return true;
00140
00141
00142 }
00143 }
00144 bool stop() {
00145 const int SILENCE_SIZE = 2205*4;
00146 assert(_dsb && _ds);
00147 if(!_started) return true;
00148
00149 unsigned short * ptr1, *ptr2;
00150 DWORD size1, size2;
00151 HRESULT hr = _dsb->Lock(_writepos, SILENCE_SIZE, (void**)&ptr1, &size1, (void**)&ptr2, &size2, 0);
00152 if(SUCCEEDED(hr)) {
00153 memset(ptr1, 0, size1);
00154 if(size2) memset(ptr2, 0, size2);
00155 _dsb->Unlock((void*)ptr1, size1, (void*)ptr2, size2);
00156 } else {
00157 throw runtime_error("_dsb->Lock() failed");
00158 }
00159
00160
00161
00162
00163 if(_writepos + SILENCE_SIZE > _size)
00164 _writepos = 0;
00165 while(1) {
00166 DWORD play;
00167 _dsb->GetCurrentPosition(&play, NULL);
00168 if(play > _writepos) {
00169 _dsb->Stop();
00170 _started = false;
00171 return true;
00172 }
00173 }
00174 return true;
00175 }
00176 };
00177
00178
00179 bitmap_renderer::bitmap_renderer() throw(exception) : _padding(0) {
00180 _bmi = reinterpret_cast<BITMAPINFO *>(new char[HEADER_SIZE]);
00181 if(!_bmi) throw runtime_error("could not allocate bitmap header");
00182 _bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
00183 _bmi->bmiHeader.biWidth = 0;
00184 _bmi->bmiHeader.biHeight = 0;
00185 _bmi->bmiHeader.biPlanes = 1;
00186 _bmi->bmiHeader.biBitCount = 8;
00187 _bmi->bmiHeader.biCompression = BI_RGB;
00188 _bmi->bmiHeader.biSizeImage = 0;
00189 _bmi->bmiHeader.biXPelsPerMeter = 300;
00190 _bmi->bmiHeader.biYPelsPerMeter = 300;
00191 _bmi->bmiHeader.biClrUsed = 256;
00192 _bmi->bmiHeader.biClrImportant = 0;
00193 for(int i = 0; i < 256; i++)
00194 {
00195 _bmi->bmiColors[i].rgbBlue = 0;
00196 _bmi->bmiColors[i].rgbGreen = 0;
00197 _bmi->bmiColors[i].rgbRed = 0;
00198 _bmi->bmiColors[i].rgbReserved = 0;
00199 }
00200 }
00201
00202 bitmap_renderer::~bitmap_renderer() throw() {
00203 delete[] reinterpret_cast<char *>(_bmi);
00204 }
00205
00206 bool bitmap_renderer::init_frame(const point &p) throw(exception) {
00207 _bmi->bmiHeader.biWidth = p.get_x();
00208 _bmi->bmiHeader.biHeight = p.get_y();
00209 _padding = p.get_x();
00210 if(_padding & 3) {
00211 _padding = ((_padding >> 2) + 1) << 2;
00212 }
00213 _padding -= p.get_x();
00214 return base_renderer::init_frame(p);
00215 }
00216
00217 bool bitmap_renderer::set_palette(const palette & p) throw() {
00218 base_renderer::set_palette(p);
00219 for(int i = 0; i < 256; i++)
00220 {
00221 _bmi->bmiColors[i].rgbRed = pal()[i].red();
00222 _bmi->bmiColors[i].rgbGreen = pal()[i].green();
00223 _bmi->bmiColors[i].rgbBlue = pal()[i].blue();
00224 }
00225 return true;
00226 }
00227
00228 void bitmap_renderer::save(int frame) throw(exception) {
00229 int oldframe = get_frame();
00230 if(frame == -1) frame = get_frame();
00231 else set_frame(frame);
00232
00233 int real_width = get_width()+_padding;
00234 char * bitmap = new char[get_height() * real_width];
00235 if(!bitmap) throw runtime_error("could not allocate bitmap buffer");
00236 for(int i = 0; i < get_height(); i++) {
00237 memcpy(bitmap + i * real_width, data() + (get_height()-1-i)*get_width(), get_width());
00238 }
00239 dump(bitmap, get_height() * real_width);
00240 delete []bitmap;
00241 set_frame(oldframe);
00242 }
00243
00244 bitmap_file_renderer::bitmap_file_renderer(const string & name) throw() : _name(name) {
00245 _bfi.bfType = 'MB';
00246 _bfi.bfSize = HEADER_SIZE + sizeof(_bfi);
00247 _bfi.bfReserved1 = 0;
00248 _bfi.bfReserved2 = 0;
00249 _bfi.bfOffBits = HEADER_SIZE + sizeof(_bfi);
00250 }
00251
00252 bool bitmap_file_renderer::save_current() throw()
00253 {
00254 static int _current = 10000;
00255 save(_current++);
00256 return true;
00257 }
00258
00259 bool bitmap_file_renderer::init_frame(const point &p) throw() {
00260 int padding = p.get_x();
00261 if(padding & 3) {
00262 padding = ((padding >> 2) + 1) << 2;
00263 }
00264 _bfi.bfSize = padding * p.get_y() + HEADER_SIZE + sizeof(_bfi);
00265 return bitmap_renderer::init_frame(p);
00266 }
00267
00268 void bitmap_file_renderer::dump(char * data, int size) throw(exception) {
00269 ostringstream oss;
00270 oss << _name << setw(5) << setfill('0') << get_frame() << setw(0) << ".bmp";
00271 ofstream ofs;
00272 ofs.open(oss.str().c_str(), ios::binary);
00273 ofs.write(reinterpret_cast<const char *>(&_bfi), sizeof(_bfi));
00274 ofs.write(reinterpret_cast<const char *>(get_bmi()), HEADER_SIZE);
00275 ofs.write(data, size);
00276 }
00277
00278 LRESULT WINAPI window_renderer::windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
00279 {
00280 switch(uMsg) {
00281 case WM_USER:
00282 DestroyWindow(hwnd);
00283 break;
00284 case WM_USER+1:
00285 {
00286 window_renderer * wr = reinterpret_cast<window_renderer *>(GetWindowLong(hwnd, 0));
00287 SetEvent(wr->_hEvent);
00288 } break;
00289 case WM_DESTROY:
00290 PostQuitMessage(0);
00291 break;
00292 case WM_CREATE:
00293 {
00294 LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
00295 SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(lpcs->lpCreateParams));
00296 PostMessage(hwnd, WM_USER+1, 0, 0);
00297 } break;
00298 case WM_PAINT:
00299 {
00300 PAINTSTRUCT ps;
00301 window_renderer * wr = reinterpret_cast<window_renderer *>(GetWindowLong(hwnd, 0));
00302 HDC hdc = BeginPaint(hwnd, &ps);
00303 WaitForSingleObject(wr->_hMutex, INFINITE);
00304 SetDIBitsToDevice(hdc,
00305 0, 0,
00306 wr->get_bmi()->bmiHeader.biWidth, abs(wr->get_bmi()->bmiHeader.biHeight),
00307 0, 0,
00308 0, abs(wr->get_bmi()->bmiHeader.biHeight),
00309 wr->_bitmap, wr->get_bmi(), DIB_RGB_COLORS);
00310 ReleaseMutex(wr->_hMutex);
00311 EndPaint(hwnd, &ps);
00312 } break;
00313 }
00314 return DefWindowProc(hwnd, uMsg, wParam, lParam);
00315 }
00316
00317 DWORD CALLBACK window_renderer::threadProc(void * wnd)
00318 {
00319 WNDCLASS wc;
00320 wc.style = 0;
00321 wc.lpfnWndProc = windowProc;
00322 wc.cbClsExtra = 0;
00323 wc.cbWndExtra = 4;
00324 wc.hInstance = NULL;
00325 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
00326 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
00327 wc.hbrBackground = NULL;
00328 wc.lpszMenuName = NULL;
00329 wc.lpszClassName = "myclassname";
00330 RegisterClass(&wc);
00331 HWND hwnd;
00332 reinterpret_cast<window_renderer *>(wnd)->_hwnd = hwnd = CreateWindow("myclassname", "SMUSH Viewer", WS_POPUPWINDOW | WS_CAPTION,
00333 CW_USEDEFAULT, CW_USEDEFAULT,
00334 CW_USEDEFAULT, CW_USEDEFAULT,
00335 NULL, NULL, NULL, wnd);
00336 ShowWindow(hwnd, SW_SHOW);
00337 UpdateWindow(hwnd);
00338 MSG msg;
00339 while(GetMessage(&msg, NULL, 0, 0)) {
00340 TranslateMessage(&msg);
00341 DispatchMessage(&msg);
00342 }
00343 return 0;
00344 }
00345
00346 window_renderer::window_renderer() throw(exception) :
00347 _bitmap(0), _size(0), _clock(0), _out(0) {
00348 _hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
00349 _hMutex = CreateMutex(NULL, FALSE, NULL);
00350 _hThread = CreateThread(NULL, 0, threadProc, this, 0, NULL);
00351 WaitForSingleObject(_hEvent, INFINITE);
00352 }
00353
00354 window_renderer::~window_renderer() throw() {
00355 if(_out) _out->stop();
00356 PostMessage(_hwnd, WM_USER, 0, 0);
00357 WaitForSingleObject(_hThread, INFINITE);
00358 CloseHandle(_hThread);
00359 CloseHandle(_hMutex);
00360 CloseHandle(_hEvent);
00361 clean();
00362 if(_out) delete _out;
00363 }
00364
00365 bool window_renderer::init_frame(const point & p) throw(exception) {
00366 SetWindowPos(_hwnd, NULL, 0, 0,
00367 p.get_x() + 2*GetSystemMetrics(SM_CXFIXEDFRAME),
00368 p.get_y() + 2*GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION),
00369 SWP_NOMOVE|SWP_NOZORDER);
00370 return bitmap_renderer::init_frame(p);
00371 }
00372 void window_renderer::dump(char * data, int size) throw(exception) {
00373 ostringstream oss;
00374 oss << "SMUSH Viewer : " << get_filename() << " : " << get_frame() + 1 << "/" << get_nbframes();
00375 SetWindowText(_hwnd, oss.str().c_str());
00376 WaitForSingleObject(_hMutex, INFINITE);
00377 if((!_bitmap) || (_size != size)) {
00378 clean();
00379 _bitmap = new char[size];
00380 _size = size;
00381 }
00382 if(!_bitmap) {
00383 throw runtime_error("window_renderer unable to allocate frame buffer");
00384 }
00385 memcpy(_bitmap, data, size);
00386 ReleaseMutex(_hMutex);
00387 InvalidateRect(_hwnd, NULL, FALSE);
00388 }
00389
00390 void window_renderer::clean() throw() {
00391 if(_bitmap) {
00392 delete []_bitmap;
00393 _bitmap = 0;
00394 }
00395 }
00396
00397 bool window_renderer::wait(int ms) throw() {
00398 int old = _clock;
00399 _clock = GetTickCount();
00400 if(_out && _out->wait()) return true;
00401 if(!old) return true;
00402 old += ms;
00403 while(_clock < old) {
00404
00405
00406 _clock = GetTickCount();
00407 if(_out && _out->wait()) return true;
00408 }
00409 return true;
00410 }
00411
00412 sound_renderer * window_renderer::get_sound_renderer() throw(exception)
00413 {
00414 if(! _out) _out = new ds_wave_out;
00415 return _out;
00416 }
00417
00418 bool win32_save_as_wave(const string & fname, char * data, int size, int freq)
00419 {
00420 HMMIO hmmio;
00421 MMCKINFO mmckinfoParent;
00422 MMCKINFO mmckinfoSubchunk;
00423
00424 WAVEFORMATEX wf;
00425 wf.wBitsPerSample = 8;
00426 wf.nChannels = 1;
00427 wf.nSamplesPerSec = freq;
00428 wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
00429 wf.wFormatTag = WAVE_FORMAT_PCM;
00430 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
00431 wf.cbSize =0;
00432
00433 hmmio = mmioOpen(const_cast<char*>(fname.c_str()), NULL, MMIO_CREATE | MMIO_WRITE | MMIO_ALLOCBUF);
00434 mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
00435 mmioCreateChunk(hmmio, &mmckinfoParent, MMIO_CREATERIFF);
00436 mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
00437 mmioCreateChunk(hmmio, &mmckinfoSubchunk, 0);
00438 mmioWrite(hmmio, reinterpret_cast<char*>(&wf), sizeof(wf));
00439 mmioAscend(hmmio, &mmckinfoSubchunk, 0);
00440 mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
00441 mmioCreateChunk(hmmio, &mmckinfoSubchunk, 0);
00442 mmioWrite(hmmio, data, size);
00443 mmioAscend(hmmio, &mmckinfoSubchunk, 0);
00444 mmioAscend(hmmio, &mmckinfoParent, 0);
00445 mmioClose(hmmio, 0);
00446
00447 return true;
00448 }