summaryrefslogtreecommitdiffstats
path: root/client/windows/record.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/windows/record.cpp')
-rw-r--r--client/windows/record.cpp193
1 files changed, 193 insertions, 0 deletions
diff --git a/client/windows/record.cpp b/client/windows/record.cpp
new file mode 100644
index 00000000..52fc478e
--- /dev/null
+++ b/client/windows/record.cpp
@@ -0,0 +1,193 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+#include "record.h"
+#include "utils.h"
+#include "debug.h"
+
+#define RING_SIZE_MS 500
+
+static void CALLBACK in_proc(HWAVEIN handle, UINT msg, DWORD user_data, DWORD param1,
+ DWORD param2)
+{
+ WaveRecorder* recorder = (WaveRecorder*)user_data;
+ recorder->trigger();
+}
+
+WaveRecorder::WaveRecorder(Platform::RecordClinet& client, uint32_t sampels_per_sec,
+ uint32_t bits_per_sample, uint32_t channels)
+ : _client (client)
+ , _ring (NULL)
+ , _frame (NULL)
+ , _head (0)
+ , _in_use (0)
+{
+ WAVEFORMATEX info;
+ uint32_t frame_align;
+
+ info.wFormatTag = WAVE_FORMAT_PCM;
+ info.nChannels = channels;
+ info.nSamplesPerSec = sampels_per_sec;
+ info.nBlockAlign = frame_align = channels * bits_per_sample / 8;
+ info.nAvgBytesPerSec = sampels_per_sec * info.nBlockAlign;
+ info.wBitsPerSample = bits_per_sample;
+
+
+ if (waveInOpen(&_wave_in, WAVE_MAPPER, &info, (DWORD_PTR)in_proc, (DWORD_PTR)this,
+ CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+ throw Exception("cannot open playback device");
+ }
+
+ try {
+ const int frame_size = WavePlaybackAbstract::FRAME_SIZE;
+ uint32_t frame_bytes = frame_size * channels * bits_per_sample / 8;
+
+ _frame = new uint8_t[frame_bytes];
+ _frame_pos = _frame;
+ _frame_end = _frame + frame_bytes;
+ init_ring(sampels_per_sec, frame_bytes, frame_align);
+ _client.add_evnet_sorce(*this);
+ } catch (...) {
+ delete[] _ring;
+ delete[] _frame;
+ waveInClose(_wave_in);
+ throw;
+ }
+}
+
+WaveRecorder::~WaveRecorder()
+{
+ waveInReset(_wave_in);
+ reclaim();
+ _client.remove_evnet_sorce(*this);
+ waveInClose(_wave_in);
+ delete[] _ring;
+ delete[] _frame;
+}
+
+void WaveRecorder::init_ring(uint32_t sampels_per_sec, uint32_t frame_bytes, uint32_t frame_align)
+{
+ const int frame_size = WavePlaybackAbstract::FRAME_SIZE;
+
+ _ring_size = (sampels_per_sec * RING_SIZE_MS / 1000) / frame_size;
+ _ring_item_size = sizeof(WAVEHDR) + frame_bytes + frame_align;
+
+ int ring_bytes = _ring_size * _ring_item_size;
+ _ring = new uint8_t[ring_bytes];
+
+ uint8_t* ptr = _ring;
+ uint8_t* end = ptr + ring_bytes;
+ for (; ptr != end; ptr += _ring_item_size) {
+ WAVEHDR* buf = (WAVEHDR*)ptr;
+ memset(ptr, 0, _ring_item_size);
+ buf->dwBufferLength = frame_bytes;
+ ULONG_PTR ptr = (ULONG_PTR)(buf + 1);
+ ptr = (ptr + frame_align - 1) / frame_align * frame_align;
+ buf->lpData = (LPSTR)(buf + 1);
+ }
+}
+
+void WaveRecorder::start()
+{
+ _frame_pos = _frame;
+ waveInReset(_wave_in);
+ push_frames();
+ waveInStart(_wave_in);
+}
+
+inline WAVEHDR* WaveRecorder::wave_hader(uint32_t position)
+{
+ ASSERT(position < _ring_size);
+ return (WAVEHDR*)(_ring + position * _ring_item_size);
+}
+
+inline void WaveRecorder::move_head()
+{
+ _head = (_head + 1) % _ring_size;
+ _in_use--;
+}
+
+void WaveRecorder::push_frames()
+{
+ while (_in_use != _ring_size) {
+ WAVEHDR* buff = wave_hader((_head + _in_use) % _ring_size);
+ ++_in_use;
+
+ MMRESULT err = waveInPrepareHeader(_wave_in, buff, sizeof(WAVEHDR));
+ if (err != MMSYSERR_NOERROR) {
+ THROW("waveInPrepareHeader filed %d", err);
+ }
+ err = waveInAddBuffer(_wave_in, buff, sizeof(WAVEHDR));
+ if (err != MMSYSERR_NOERROR) {
+ THROW("waveInAddBuffer filed %d", err);
+ }
+ }
+}
+
+void WaveRecorder::on_event()
+{
+ while (_in_use) {
+ WAVEHDR* front_buf = wave_hader(_head);
+ if (!(front_buf->dwFlags & WHDR_DONE)) {
+ break;
+ }
+ waveInUnprepareHeader(_wave_in, front_buf, sizeof(WAVEHDR));
+ front_buf->dwFlags &= ~WHDR_DONE;
+ int n = front_buf->dwBytesRecorded;
+ front_buf->dwBytesRecorded = 0;
+ uint8_t* ptr = (uint8_t*)front_buf->lpData;
+ while (n) {
+ int now = MIN(n, _frame_end - _frame_pos);
+ memcpy(_frame_pos, ptr, now);
+ if ((_frame_pos += n) == _frame_end) {
+ _client.push_frame(_frame);
+ _frame_pos = _frame;
+ }
+ n -= now;
+ ptr += now;
+ }
+ move_head();
+ push_frames();
+ }
+}
+
+void WaveRecorder::reclaim()
+{
+ while (_in_use) {
+ WAVEHDR* front_buf = wave_hader(_head);
+ if (!(front_buf->dwFlags & WHDR_DONE)) {
+ break;
+ }
+ waveInUnprepareHeader(_wave_in, front_buf, sizeof(WAVEHDR));
+ front_buf->dwFlags &= ~WHDR_DONE;
+ front_buf->dwBytesRecorded = 0;
+ move_head();
+ }
+}
+
+void WaveRecorder::stop()
+{
+ waveInReset(_wave_in);
+ reclaim();
+}
+
+bool WaveRecorder::abort()
+{
+ return true;
+}
+