summaryrefslogtreecommitdiffstats
path: root/client/windows/playback.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/windows/playback.cpp')
-rw-r--r--client/windows/playback.cpp177
1 files changed, 177 insertions, 0 deletions
diff --git a/client/windows/playback.cpp b/client/windows/playback.cpp
new file mode 100644
index 00000000..2e15ad93
--- /dev/null
+++ b/client/windows/playback.cpp
@@ -0,0 +1,177 @@
+/*
+ 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 "playback.h"
+#include "utils.h"
+#include "debug.h"
+
+
+#define RING_SIZE_MS 160
+#define START_MARK_MS 80
+#define LOW_MARK_MS 40
+
+
+WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels)
+ : _wave_out (NULL)
+ , _sampels_per_ms (sampels_per_sec / 1000)
+ , _ring (NULL)
+ , _head (0)
+ , _in_use (0)
+ , _paused (true)
+{
+ WAVEFORMATEX info;
+ uint32_t sample_bytes;
+
+ info.wFormatTag = WAVE_FORMAT_PCM;
+ info.nChannels = channels;
+ info.nSamplesPerSec = sampels_per_sec;
+ sample_bytes = info.nBlockAlign = channels * bits_per_sample / 8;
+ info.nAvgBytesPerSec = sampels_per_sec * info.nBlockAlign;
+ info.wBitsPerSample = bits_per_sample;
+
+ if (waveOutOpen(&_wave_out, WAVE_MAPPER, &info, NULL, NULL, CALLBACK_NULL)
+ != MMSYSERR_NOERROR) {
+ throw Exception("can not open playback device");
+ }
+
+ int frame_size = WavePlaybackAbstract::FRAME_SIZE;
+ _ring_size = (sampels_per_sec * RING_SIZE_MS / 1000) / frame_size;
+ int low_mark = (sampels_per_sec * LOW_MARK_MS / 1000) / frame_size;
+ _start_mark = (sampels_per_sec * START_MARK_MS / 1000) / frame_size;
+ _frame_bytes = frame_size * channels * bits_per_sample / 8;
+ _ring_item_size = sizeof(WAVEHDR) + _frame_bytes + sample_bytes;
+
+ try {
+ _ring = new uint8_t[_ring_size * _ring_item_size];
+ init_ring(sample_bytes);
+ } catch (...) {
+ delete[] _ring;
+ waveOutClose(_wave_out);
+ throw;
+ }
+ waveOutPause(_wave_out);
+}
+
+WavePlayer::~WavePlayer()
+{
+ waveOutReset(_wave_out);
+ reclaim();
+ waveOutClose(_wave_out);
+ delete[] _ring;
+}
+
+void WavePlayer::init_ring(uint32_t sample_bytes)
+{
+ uint8_t* ptr = _ring;
+ uint8_t* end = ptr + _ring_size * _ring_item_size;
+ 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 + sample_bytes - 1) / sample_bytes * sample_bytes;
+ buf->lpData = (LPSTR)ptr;
+ }
+}
+
+inline WAVEHDR* WavePlayer::wave_hader(uint32_t position)
+{
+ ASSERT(position < _ring_size);
+ return (WAVEHDR*)(_ring + position * _ring_item_size);
+}
+
+inline void WavePlayer::move_head()
+{
+ _head = (_head + 1) % _ring_size;
+ _in_use--;
+}
+
+void WavePlayer::reclaim()
+{
+ while (_in_use) {
+ WAVEHDR* front_buf = wave_hader(_head);
+ if (!(front_buf->dwFlags & WHDR_DONE)) {
+ break;
+ }
+ MMRESULT err = waveOutUnprepareHeader(_wave_out, front_buf, sizeof(WAVEHDR));
+ if (err != MMSYSERR_NOERROR) {
+ LOG_WARN("waveOutUnprepareHeader failed %u", err);
+ }
+ front_buf->dwFlags &= ~WHDR_DONE;
+ move_head();
+ }
+}
+
+WAVEHDR* WavePlayer::get_buff()
+{
+ reclaim();
+ if (_in_use == _ring_size) {
+ return NULL;
+ }
+
+ WAVEHDR* buff = wave_hader((_head + _in_use) % _ring_size);
+ ++_in_use;
+ return buff;
+}
+
+bool WavePlayer::write(uint8_t* frame)
+{
+ WAVEHDR* buff = get_buff();
+ if (buff) {
+ memcpy(buff->lpData, frame, _frame_bytes);
+ MMRESULT err;
+ err = waveOutPrepareHeader(_wave_out, buff, sizeof(WAVEHDR));
+
+ if (err != MMSYSERR_NOERROR) {
+ THROW("waveOutPrepareHeader filed %d", err);
+ }
+ err = waveOutWrite(_wave_out, buff, sizeof(WAVEHDR));
+ if (err != MMSYSERR_NOERROR) {
+ THROW("waveOutWrite filed %d", err);
+ }
+ }
+ if (_paused && _in_use == _start_mark) {
+ _paused = false;
+ waveOutRestart(_wave_out);
+ }
+ return true;
+}
+
+void WavePlayer::drain()
+{
+}
+
+void WavePlayer::stop()
+{
+ drain();
+ waveOutReset(_wave_out);
+ waveOutPause(_wave_out);
+ _paused = true;
+ reclaim();
+}
+
+bool WavePlayer::abort()
+{
+ return true;
+}
+
+uint32_t WavePlayer::get_delay_ms()
+{
+ return _in_use * WavePlaybackAbstract::FRAME_SIZE / _sampels_per_ms;
+}
+