uploaded original spice-server-0.12.4 and celt-0.5.1.3
[sdk/emulator/libs/spice-server.git] / client / windows / record.cpp
1 /*
2    Copyright (C) 2009 Red Hat, Inc.
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif
20
21 #include "common.h"
22 #include "record.h"
23 #include "utils.h"
24 #include "debug.h"
25
26 #define RING_SIZE_MS 500
27
28 static void CALLBACK in_proc(HWAVEIN handle, UINT msg, DWORD user_data, DWORD param1,
29                              DWORD param2)
30 {
31     WaveRecorder* recorder = (WaveRecorder*)user_data;
32     recorder->trigger();
33 }
34
35 WaveRecorder::WaveRecorder(Platform::RecordClient& client, uint32_t sampels_per_sec,
36                            uint32_t bits_per_sample, uint32_t channels)
37     : _client (client)
38     , _ring (NULL)
39     , _head (0)
40     , _in_use (0)
41     , _frame (NULL)
42 {
43     WAVEFORMATEX info;
44     uint32_t frame_align;
45
46     info.wFormatTag = WAVE_FORMAT_PCM;
47     info.nChannels = channels;
48     info.nSamplesPerSec = sampels_per_sec;
49     info.nBlockAlign = frame_align = channels * bits_per_sample / 8;
50     info.nAvgBytesPerSec = sampels_per_sec * info.nBlockAlign;
51     info.wBitsPerSample = bits_per_sample;
52
53
54     if (waveInOpen(&_wave_in, WAVE_MAPPER, &info, (DWORD_PTR)in_proc, (DWORD_PTR)this,
55                    CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
56         throw Exception("cannot open playback device");
57     }
58
59     try {
60         const int frame_size = WavePlaybackAbstract::FRAME_SIZE;
61         uint32_t frame_bytes = frame_size * channels * bits_per_sample / 8;
62
63         _frame = new uint8_t[frame_bytes];
64         _frame_pos = _frame;
65         _frame_end = _frame + frame_bytes;
66         init_ring(sampels_per_sec, frame_bytes, frame_align);
67         _client.add_event_source(*this);
68     } catch (...) {
69         delete[] _ring;
70         delete[] _frame;
71         waveInClose(_wave_in);
72         throw;
73     }
74 }
75
76 WaveRecorder::~WaveRecorder()
77 {
78     waveInReset(_wave_in);
79     reclaim();
80     _client.remove_event_source(*this);
81     waveInClose(_wave_in);
82     delete[] _ring;
83     delete[] _frame;
84 }
85
86 void WaveRecorder::init_ring(uint32_t sampels_per_sec, uint32_t frame_bytes, uint32_t frame_align)
87 {
88     const int frame_size = WavePlaybackAbstract::FRAME_SIZE;
89
90     _ring_size = (sampels_per_sec * RING_SIZE_MS / 1000) / frame_size;
91     _ring_item_size = sizeof(WAVEHDR) + frame_bytes + frame_align;
92
93     int ring_bytes = _ring_size * _ring_item_size;
94     _ring = new uint8_t[ring_bytes];
95
96     uint8_t* ptr = _ring;
97     uint8_t* end = ptr + ring_bytes;
98     for (; ptr != end; ptr += _ring_item_size) {
99         WAVEHDR* buf = (WAVEHDR*)ptr;
100         memset(ptr, 0, _ring_item_size);
101         buf->dwBufferLength = frame_bytes;
102         ULONG_PTR ptr = (ULONG_PTR)(buf + 1);
103         ptr = (ptr + frame_align - 1) / frame_align * frame_align;
104         buf->lpData = (LPSTR)(buf + 1);
105     }
106 }
107
108 void WaveRecorder::start()
109 {
110     _frame_pos = _frame;
111     waveInReset(_wave_in);
112     push_frames();
113     waveInStart(_wave_in);
114 }
115
116 inline WAVEHDR* WaveRecorder::wave_hader(uint32_t position)
117 {
118     ASSERT(position < _ring_size);
119     return (WAVEHDR*)(_ring + position * _ring_item_size);
120 }
121
122 inline void WaveRecorder::move_head()
123 {
124     _head = (_head + 1) % _ring_size;
125     _in_use--;
126 }
127
128 void WaveRecorder::push_frames()
129 {
130     while (_in_use != _ring_size) {
131         WAVEHDR* buff = wave_hader((_head + _in_use) % _ring_size);
132         ++_in_use;
133
134         MMRESULT err = waveInPrepareHeader(_wave_in, buff, sizeof(WAVEHDR));
135         if (err != MMSYSERR_NOERROR) {
136             THROW("waveInPrepareHeader filed %d", err);
137         }
138         err = waveInAddBuffer(_wave_in, buff, sizeof(WAVEHDR));
139         if (err != MMSYSERR_NOERROR) {
140             THROW("waveInAddBuffer filed %d", err);
141         }
142     }
143 }
144
145 void WaveRecorder::on_event()
146 {
147     while (_in_use) {
148         WAVEHDR* front_buf = wave_hader(_head);
149         if (!(front_buf->dwFlags & WHDR_DONE)) {
150             break;
151         }
152         waveInUnprepareHeader(_wave_in, front_buf, sizeof(WAVEHDR));
153         front_buf->dwFlags &= ~WHDR_DONE;
154         int n = front_buf->dwBytesRecorded;
155         front_buf->dwBytesRecorded = 0;
156         uint8_t* ptr = (uint8_t*)front_buf->lpData;
157         while (n) {
158             int now = MIN(n, _frame_end - _frame_pos);
159             memcpy(_frame_pos, ptr, now);
160             if ((_frame_pos += n) == _frame_end) {
161                 _client.push_frame(_frame);
162                 _frame_pos = _frame;
163             }
164             n -= now;
165             ptr += now;
166         }
167         move_head();
168         push_frames();
169     }
170 }
171
172 void WaveRecorder::reclaim()
173 {
174     while (_in_use) {
175         WAVEHDR* front_buf = wave_hader(_head);
176         if (!(front_buf->dwFlags & WHDR_DONE)) {
177             break;
178         }
179         waveInUnprepareHeader(_wave_in, front_buf, sizeof(WAVEHDR));
180         front_buf->dwFlags &= ~WHDR_DONE;
181         front_buf->dwBytesRecorded = 0;
182         move_head();
183     }
184 }
185
186 void WaveRecorder::stop()
187 {
188     waveInReset(_wave_in);
189     reclaim();
190 }
191
192 bool WaveRecorder::abort()
193 {
194     return true;
195 }