2 * Copyright (C) 2010 Red Hat, Inc.
4 * maintained by Gerd Hoffmann <kraxel@redhat.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 or
9 * (at your option) version 3 of the License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
21 #include "qemu/host-utils.h"
22 #include "qemu/module.h"
23 #include "qemu/error-report.h"
24 #include "qemu/timer.h"
25 #include "qapi/error.h"
26 #include "ui/qemu-spice.h"
28 #define AUDIO_CAP "spice"
30 #include "audio_int.h"
32 #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
33 #define LINE_OUT_SAMPLES (480 * 4)
35 #define LINE_OUT_SAMPLES (256 * 4)
38 #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
39 #define LINE_IN_SAMPLES (480 * 4)
41 #define LINE_IN_SAMPLES (256 * 4)
44 typedef struct SpiceVoiceOut
{
46 SpicePlaybackInstance sin
;
54 typedef struct SpiceVoiceIn
{
56 SpiceRecordInstance sin
;
61 static const SpicePlaybackInterface playback_sif
= {
62 .base
.type
= SPICE_INTERFACE_PLAYBACK
,
63 .base
.description
= "playback",
64 .base
.major_version
= SPICE_INTERFACE_PLAYBACK_MAJOR
,
65 .base
.minor_version
= SPICE_INTERFACE_PLAYBACK_MINOR
,
68 static const SpiceRecordInterface record_sif
= {
69 .base
.type
= SPICE_INTERFACE_RECORD
,
70 .base
.description
= "record",
71 .base
.major_version
= SPICE_INTERFACE_RECORD_MAJOR
,
72 .base
.minor_version
= SPICE_INTERFACE_RECORD_MINOR
,
75 static void *spice_audio_init(Audiodev
*dev
, Error
**errp
)
78 error_setg(errp
, "Cannot use spice audio without -spice");
82 return &spice_audio_init
;
85 static void spice_audio_fini (void *opaque
)
92 static int line_out_init(HWVoiceOut
*hw
, struct audsettings
*as
,
95 SpiceVoiceOut
*out
= container_of (hw
, SpiceVoiceOut
, hw
);
96 struct audsettings settings
;
98 #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
99 settings
.freq
= spice_server_get_best_playback_rate(NULL
);
101 settings
.freq
= SPICE_INTERFACE_PLAYBACK_FREQ
;
103 settings
.nchannels
= SPICE_INTERFACE_PLAYBACK_CHAN
;
104 settings
.fmt
= AUDIO_FORMAT_S16
;
105 settings
.endianness
= AUDIO_HOST_ENDIANNESS
;
107 audio_pcm_init_info (&hw
->info
, &settings
);
108 hw
->samples
= LINE_OUT_SAMPLES
;
111 out
->sin
.base
.sif
= &playback_sif
.base
;
112 qemu_spice
.add_interface(&out
->sin
.base
);
113 #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
114 spice_server_set_playback_rate(&out
->sin
, settings
.freq
);
119 static void line_out_fini (HWVoiceOut
*hw
)
121 SpiceVoiceOut
*out
= container_of (hw
, SpiceVoiceOut
, hw
);
123 spice_server_remove_interface (&out
->sin
.base
);
126 static size_t line_out_get_free(HWVoiceOut
*hw
)
128 SpiceVoiceOut
*out
= container_of(hw
, SpiceVoiceOut
, hw
);
130 return audio_rate_peek_bytes(&out
->rate
, &hw
->info
);
133 static void *line_out_get_buffer(HWVoiceOut
*hw
, size_t *size
)
135 SpiceVoiceOut
*out
= container_of(hw
, SpiceVoiceOut
, hw
);
138 spice_server_playback_get_buffer(&out
->sin
, &out
->frame
, &out
->fsize
);
143 *size
= MIN((out
->fsize
- out
->fpos
) << 2, *size
);
146 return out
->frame
+ out
->fpos
;
149 static size_t line_out_put_buffer(HWVoiceOut
*hw
, void *buf
, size_t size
)
151 SpiceVoiceOut
*out
= container_of(hw
, SpiceVoiceOut
, hw
);
153 audio_rate_add_bytes(&out
->rate
, size
);
156 assert(buf
== out
->frame
+ out
->fpos
&& out
->fpos
<= out
->fsize
);
157 out
->fpos
+= size
>> 2;
159 if (out
->fpos
== out
->fsize
) { /* buffer full */
160 spice_server_playback_put_samples(&out
->sin
, out
->frame
);
168 static void line_out_enable(HWVoiceOut
*hw
, bool enable
)
170 SpiceVoiceOut
*out
= container_of (hw
, SpiceVoiceOut
, hw
);
177 audio_rate_start(&out
->rate
);
178 spice_server_playback_start (&out
->sin
);
185 memset(out
->frame
+ out
->fpos
, 0, (out
->fsize
- out
->fpos
) << 2);
186 spice_server_playback_put_samples (&out
->sin
, out
->frame
);
189 spice_server_playback_stop (&out
->sin
);
193 #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
194 static void line_out_volume(HWVoiceOut
*hw
, Volume
*vol
)
196 SpiceVoiceOut
*out
= container_of(hw
, SpiceVoiceOut
, hw
);
199 assert(vol
->channels
== 2);
200 svol
[0] = vol
->vol
[0] * 257;
201 svol
[1] = vol
->vol
[1] * 257;
202 spice_server_playback_set_volume(&out
->sin
, 2, svol
);
203 spice_server_playback_set_mute(&out
->sin
, vol
->mute
);
209 static int line_in_init(HWVoiceIn
*hw
, struct audsettings
*as
, void *drv_opaque
)
211 SpiceVoiceIn
*in
= container_of (hw
, SpiceVoiceIn
, hw
);
212 struct audsettings settings
;
214 #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
215 settings
.freq
= spice_server_get_best_record_rate(NULL
);
217 settings
.freq
= SPICE_INTERFACE_RECORD_FREQ
;
219 settings
.nchannels
= SPICE_INTERFACE_RECORD_CHAN
;
220 settings
.fmt
= AUDIO_FORMAT_S16
;
221 settings
.endianness
= AUDIO_HOST_ENDIANNESS
;
223 audio_pcm_init_info (&hw
->info
, &settings
);
224 hw
->samples
= LINE_IN_SAMPLES
;
227 in
->sin
.base
.sif
= &record_sif
.base
;
228 qemu_spice
.add_interface(&in
->sin
.base
);
229 #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
230 spice_server_set_record_rate(&in
->sin
, settings
.freq
);
235 static void line_in_fini (HWVoiceIn
*hw
)
237 SpiceVoiceIn
*in
= container_of (hw
, SpiceVoiceIn
, hw
);
239 spice_server_remove_interface (&in
->sin
.base
);
242 static size_t line_in_read(HWVoiceIn
*hw
, void *buf
, size_t len
)
244 SpiceVoiceIn
*in
= container_of (hw
, SpiceVoiceIn
, hw
);
245 uint64_t to_read
= audio_rate_get_bytes(&in
->rate
, &hw
->info
, len
) >> 2;
246 size_t ready
= spice_server_record_get_samples(&in
->sin
, buf
, to_read
);
249 * If the client didn't send new frames, it most likely disconnected.
250 * Generate silence in this case to avoid a stalled audio stream.
253 memset(buf
, 0, to_read
<< 2);
260 static void line_in_enable(HWVoiceIn
*hw
, bool enable
)
262 SpiceVoiceIn
*in
= container_of (hw
, SpiceVoiceIn
, hw
);
269 audio_rate_start(&in
->rate
);
270 spice_server_record_start (&in
->sin
);
276 spice_server_record_stop (&in
->sin
);
280 #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
281 static void line_in_volume(HWVoiceIn
*hw
, Volume
*vol
)
283 SpiceVoiceIn
*in
= container_of(hw
, SpiceVoiceIn
, hw
);
286 assert(vol
->channels
== 2);
287 svol
[0] = vol
->vol
[0] * 257;
288 svol
[1] = vol
->vol
[1] * 257;
289 spice_server_record_set_volume(&in
->sin
, 2, svol
);
290 spice_server_record_set_mute(&in
->sin
, vol
->mute
);
294 static struct audio_pcm_ops audio_callbacks
= {
295 .init_out
= line_out_init
,
296 .fini_out
= line_out_fini
,
297 .write
= audio_generic_write
,
298 .buffer_get_free
= line_out_get_free
,
299 .get_buffer_out
= line_out_get_buffer
,
300 .put_buffer_out
= line_out_put_buffer
,
301 .enable_out
= line_out_enable
,
302 #if (SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && \
303 (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)
304 .volume_out
= line_out_volume
,
307 .init_in
= line_in_init
,
308 .fini_in
= line_in_fini
,
309 .read
= line_in_read
,
310 .run_buffer_in
= audio_generic_run_buffer_in
,
311 .enable_in
= line_in_enable
,
312 #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
313 .volume_in
= line_in_volume
,
317 static struct audio_driver spice_audio_driver
= {
319 .descr
= "spice audio driver",
320 .init
= spice_audio_init
,
321 .fini
= spice_audio_fini
,
322 .pcm_ops
= &audio_callbacks
,
325 .voice_size_out
= sizeof (SpiceVoiceOut
),
326 .voice_size_in
= sizeof (SpiceVoiceIn
),
329 static void register_audio_spice(void)
331 audio_driver_register(&spice_audio_driver
);
333 type_init(register_audio_spice
);
335 module_dep("ui-spice-core");