uploaded original spice-server-0.12.4 and celt-0.5.1.3
[sdk/emulator/libs/spice-server.git] / client / x11 / playback.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 "playback.h"
22 #include "utils.h"
23 #include "debug.h"
24
25 #define REING_SIZE_MS 300
26
27 WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels)
28     : _pcm (NULL)
29     , _hw_params (NULL)
30     , _sw_params (NULL)
31 {
32     if (!init(sampels_per_sec, bits_per_sample, channels)) {
33         cleanup();
34         THROW("failed");
35     }
36 }
37
38 void WavePlayer::cleanup()
39 {
40     if (_pcm) {
41         snd_pcm_close(_pcm);
42     }
43
44     if (_hw_params) {
45         snd_pcm_hw_params_free(_hw_params);
46     }
47
48     if (_sw_params) {
49         snd_pcm_sw_params_free(_sw_params);
50     }
51 }
52
53 WavePlayer::~WavePlayer()
54 {
55     cleanup();
56 }
57
58 bool WavePlayer::init(uint32_t sampels_per_sec,
59                       uint32_t bits_per_sample,
60                       uint32_t channels)
61 {
62     const int frame_size = WavePlaybackAbstract::FRAME_SIZE;
63     const char* pcm_device = "default";
64     snd_pcm_format_t format;
65     int err;
66
67     switch (bits_per_sample) {
68     case 8:
69         format = SND_PCM_FORMAT_S8;
70         break;
71     case 16:
72         format = SND_PCM_FORMAT_S16_LE;
73         break;
74     default:
75         return false;
76     }
77     _sampels_per_ms = sampels_per_sec / 1000;
78
79     if ((err = snd_pcm_open(&_pcm, pcm_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
80         LOG_ERROR("cannot open audio playback device %s %s", pcm_device, snd_strerror(err));
81         return false;
82     }
83
84     if ((err = snd_pcm_hw_params_malloc(&_hw_params)) < 0) {
85         LOG_ERROR("cannot allocate hardware parameter structure %s", snd_strerror(err));
86         return false;
87     }
88
89     if ((err = snd_pcm_sw_params_malloc(&_sw_params)) < 0) {
90         LOG_ERROR("cannot allocate software parameter structure %s", snd_strerror(err));
91         return false;
92     }
93
94     if ((err = snd_pcm_hw_params_any(_pcm, _hw_params)) < 0) {
95         LOG_ERROR("cannot initialize hardware parameter structure %s", snd_strerror(err));
96         return false;
97     }
98
99     if ((err = snd_pcm_hw_params_set_rate_resample(_pcm, _hw_params, 1)) < 0) {
100         LOG_ERROR("cannot set rate resample %s", snd_strerror(err));
101         return false;
102     }
103
104     if ((err = snd_pcm_hw_params_set_access(_pcm, _hw_params,
105                                             SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
106         LOG_ERROR("cannot set access type %s", snd_strerror(err));
107         return false;
108     }
109
110     if ((err = snd_pcm_hw_params_set_rate(_pcm, _hw_params, sampels_per_sec, 0)) < 0) {
111         LOG_ERROR("cannot set sample rate %s", snd_strerror(err));
112         return false;
113     }
114
115     if ((err = snd_pcm_hw_params_set_channels(_pcm, _hw_params, channels)) < 0) {
116         LOG_ERROR("cannot set channel count %s", snd_strerror(err));
117         return false;
118     }
119
120     if ((err = snd_pcm_hw_params_set_format(_pcm, _hw_params, format)) < 0) {
121         LOG_ERROR("cannot set sample format %s", snd_strerror(err));
122         return false;
123     }
124
125     snd_pcm_uframes_t buffer_size;
126     buffer_size = (sampels_per_sec * REING_SIZE_MS / 1000) / frame_size * frame_size;
127
128     if ((err = snd_pcm_hw_params_set_buffer_size_near(_pcm, _hw_params, &buffer_size)) < 0) {
129         LOG_ERROR("cannot set buffer size %s", snd_strerror(err));
130         return false;
131     }
132
133     int direction = 1;
134     snd_pcm_uframes_t period_size = (sampels_per_sec * 20 / 1000) / frame_size * frame_size;
135     if ((err = snd_pcm_hw_params_set_period_size_near(_pcm, _hw_params, &period_size,
136                                                       &direction)) < 0) {
137         LOG_ERROR("cannot set period size %s", snd_strerror(err));
138         return false;
139     }
140
141     if ((err = snd_pcm_hw_params(_pcm, _hw_params)) < 0) {
142         LOG_ERROR("cannot set parameters %s", snd_strerror(err));
143         return false;
144     }
145
146     if ((err = snd_pcm_sw_params_current(_pcm, _sw_params)) < 0) {
147         LOG_ERROR("cannot obtain sw parameters %s", snd_strerror(err));
148         return false;
149     }
150
151     err = snd_pcm_hw_params_get_buffer_size(_hw_params, &buffer_size);
152     if (err < 0) {
153         LOG_ERROR("unable to get buffer size for playback: %s", snd_strerror(err));
154         return false;
155     }
156
157     direction = 0;
158     err = snd_pcm_hw_params_get_period_size(_hw_params, &period_size, &direction);
159     if (err < 0) {
160         LOG_ERROR("unable to get period size for playback: %s", snd_strerror(err));
161         return false;
162     }
163
164     err = snd_pcm_sw_params_set_start_threshold(_pcm, _sw_params,
165                                                 (buffer_size / period_size) * period_size);
166     if (err < 0) {
167         LOG_ERROR("unable to set start threshold mode for playback: %s", snd_strerror(err));
168         return false;
169     }
170
171     if ((err = snd_pcm_sw_params(_pcm, _sw_params)) < 0) {
172         LOG_ERROR("cannot set software parameters %s", snd_strerror(err));
173         return false;
174     }
175
176     if ((err = snd_pcm_prepare(_pcm)) < 0) {
177         LOG_ERROR("cannot prepare pcm device %s", snd_strerror(err));
178         return false;
179     }
180
181     return true;
182 }
183
184 bool WavePlayer::write(uint8_t* frame)
185 {
186     snd_pcm_sframes_t ret = snd_pcm_writei(_pcm, frame, WavePlaybackAbstract::FRAME_SIZE);
187     if (ret < 0) {
188         if (ret == -EAGAIN) {
189             return false;
190         }
191         DBG(0, "err %s", snd_strerror(-ret));
192         if (snd_pcm_recover(_pcm, ret, 1) == 0) {
193             snd_pcm_writei(_pcm, frame, WavePlaybackAbstract::FRAME_SIZE);
194         }
195     }
196     return true;
197 }
198
199 void WavePlayer::stop()
200 {
201     snd_pcm_drain(_pcm);
202     snd_pcm_prepare(_pcm);
203 }
204
205 bool WavePlayer::abort()
206 {
207     return true;
208 }
209
210 uint32_t WavePlayer::get_delay_ms()
211 {
212     ASSERT(_pcm);
213
214     snd_pcm_sframes_t delay;
215
216     if (snd_pcm_delay(_pcm, &delay) < 0) {
217         return 0;
218     }
219     return delay / _sampels_per_ms;
220 }