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 "ui/qemu-spice.h"
27 #define AUDIO_CAP "spice"
29 #include "audio_int.h"
31 #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
32 #define LINE_OUT_SAMPLES (480 * 4)
34 #define LINE_OUT_SAMPLES (256 * 4)
37 #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
38 #define LINE_IN_SAMPLES (480 * 4)
40 #define LINE_IN_SAMPLES (256 * 4)
43 typedef struct SpiceRateCtl
{
48 typedef struct SpiceVoiceOut
{
50 SpicePlaybackInstance sin
;
58 typedef struct SpiceVoiceIn
{
60 SpiceRecordInstance sin
;
65 static const SpicePlaybackInterface playback_sif
= {
66 .base
.type
= SPICE_INTERFACE_PLAYBACK
,
67 .base
.description
= "playback",
68 .base
.major_version
= SPICE_INTERFACE_PLAYBACK_MAJOR
,
69 .base
.minor_version
= SPICE_INTERFACE_PLAYBACK_MINOR
,
72 static const SpiceRecordInterface record_sif
= {
73 .base
.type
= SPICE_INTERFACE_RECORD
,
74 .base
.description
= "record",
75 .base
.major_version
= SPICE_INTERFACE_RECORD_MAJOR
,
76 .base
.minor_version
= SPICE_INTERFACE_RECORD_MINOR
,
79 static void *spice_audio_init(Audiodev
*dev
)
84 return &spice_audio_init
;
87 static void spice_audio_fini (void *opaque
)
92 static void rate_start (SpiceRateCtl
*rate
)
94 memset (rate
, 0, sizeof (*rate
));
95 rate
->start_ticks
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
98 static int rate_get_samples (struct audio_pcm_info
*info
, SpiceRateCtl
*rate
)
105 now
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
106 ticks
= now
- rate
->start_ticks
;
107 bytes
= muldiv64(ticks
, info
->bytes_per_second
, NANOSECONDS_PER_SECOND
);
108 samples
= (bytes
- rate
->bytes_sent
) >> info
->shift
;
109 if (samples
< 0 || samples
> 65536) {
110 error_report("Resetting rate control (%" PRId64
" samples)", samples
);
114 rate
->bytes_sent
+= samples
<< info
->shift
;
120 static int line_out_init(HWVoiceOut
*hw
, struct audsettings
*as
,
123 SpiceVoiceOut
*out
= container_of (hw
, SpiceVoiceOut
, hw
);
124 struct audsettings settings
;
126 #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
127 settings
.freq
= spice_server_get_best_playback_rate(NULL
);
129 settings
.freq
= SPICE_INTERFACE_PLAYBACK_FREQ
;
131 settings
.nchannels
= SPICE_INTERFACE_PLAYBACK_CHAN
;
132 settings
.fmt
= AUDIO_FORMAT_S16
;
133 settings
.endianness
= AUDIO_HOST_ENDIANNESS
;
135 audio_pcm_init_info (&hw
->info
, &settings
);
136 hw
->samples
= LINE_OUT_SAMPLES
;
139 out
->sin
.base
.sif
= &playback_sif
.base
;
140 qemu_spice_add_interface (&out
->sin
.base
);
141 #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
142 spice_server_set_playback_rate(&out
->sin
, settings
.freq
);
147 static void line_out_fini (HWVoiceOut
*hw
)
149 SpiceVoiceOut
*out
= container_of (hw
, SpiceVoiceOut
, hw
);
151 spice_server_remove_interface (&out
->sin
.base
);
154 static void *line_out_get_buffer(HWVoiceOut
*hw
, size_t *size
)
156 SpiceVoiceOut
*out
= container_of(hw
, SpiceVoiceOut
, hw
);
160 spice_server_playback_get_buffer(&out
->sin
, &out
->frame
, &out
->fsize
);
165 decr
= rate_get_samples(&hw
->info
, &out
->rate
);
166 decr
= MIN(out
->fsize
- out
->fpos
, decr
);
168 *size
= decr
<< hw
->info
.shift
;
170 rate_start(&out
->rate
);
172 return out
->frame
+ out
->fpos
;
175 static size_t line_out_put_buffer(HWVoiceOut
*hw
, void *buf
, size_t size
)
177 SpiceVoiceOut
*out
= container_of(hw
, SpiceVoiceOut
, hw
);
179 assert(buf
== out
->frame
+ out
->fpos
&& out
->fpos
<= out
->fsize
);
180 out
->fpos
+= size
>> 2;
182 if (out
->fpos
== out
->fsize
) { /* buffer full */
183 spice_server_playback_put_samples(&out
->sin
, out
->frame
);
190 static int line_out_ctl (HWVoiceOut
*hw
, int cmd
, ...)
192 SpiceVoiceOut
*out
= container_of (hw
, SpiceVoiceOut
, hw
);
200 rate_start (&out
->rate
);
201 spice_server_playback_start (&out
->sin
);
209 memset(out
->frame
+ out
->fpos
, 0, (out
->fsize
- out
->fpos
) << 2);
210 spice_server_playback_put_samples (&out
->sin
, out
->frame
);
213 spice_server_playback_stop (&out
->sin
);
217 #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
223 sw
= va_arg (ap
, SWVoiceOut
*);
226 vol
[0] = sw
->vol
.l
/ ((1ULL << 16) + 1);
227 vol
[1] = sw
->vol
.r
/ ((1ULL << 16) + 1);
228 spice_server_playback_set_volume (&out
->sin
, 2, vol
);
229 spice_server_playback_set_mute (&out
->sin
, sw
->vol
.mute
);
240 static int line_in_init(HWVoiceIn
*hw
, struct audsettings
*as
, void *drv_opaque
)
242 SpiceVoiceIn
*in
= container_of (hw
, SpiceVoiceIn
, hw
);
243 struct audsettings settings
;
245 #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
246 settings
.freq
= spice_server_get_best_record_rate(NULL
);
248 settings
.freq
= SPICE_INTERFACE_RECORD_FREQ
;
250 settings
.nchannels
= SPICE_INTERFACE_RECORD_CHAN
;
251 settings
.fmt
= AUDIO_FORMAT_S16
;
252 settings
.endianness
= AUDIO_HOST_ENDIANNESS
;
254 audio_pcm_init_info (&hw
->info
, &settings
);
255 hw
->samples
= LINE_IN_SAMPLES
;
258 in
->sin
.base
.sif
= &record_sif
.base
;
259 qemu_spice_add_interface (&in
->sin
.base
);
260 #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
261 spice_server_set_record_rate(&in
->sin
, settings
.freq
);
266 static void line_in_fini (HWVoiceIn
*hw
)
268 SpiceVoiceIn
*in
= container_of (hw
, SpiceVoiceIn
, hw
);
270 spice_server_remove_interface (&in
->sin
.base
);
273 static size_t line_in_read(HWVoiceIn
*hw
, void *buf
, size_t len
)
275 SpiceVoiceIn
*in
= container_of (hw
, SpiceVoiceIn
, hw
);
276 uint64_t delta_samp
= rate_get_samples(&hw
->info
, &in
->rate
);
277 uint64_t to_read
= MIN(len
>> 2, delta_samp
);
278 size_t ready
= spice_server_record_get_samples(&in
->sin
, buf
, to_read
);
280 /* XXX: do we need this? */
282 memset(buf
, 0, to_read
<< 2);
289 static int line_in_ctl (HWVoiceIn
*hw
, int cmd
, ...)
291 SpiceVoiceIn
*in
= container_of (hw
, SpiceVoiceIn
, hw
);
299 rate_start (&in
->rate
);
300 spice_server_record_start (&in
->sin
);
307 spice_server_record_stop (&in
->sin
);
311 #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
317 sw
= va_arg (ap
, SWVoiceIn
*);
320 vol
[0] = sw
->vol
.l
/ ((1ULL << 16) + 1);
321 vol
[1] = sw
->vol
.r
/ ((1ULL << 16) + 1);
322 spice_server_record_set_volume (&in
->sin
, 2, vol
);
323 spice_server_record_set_mute (&in
->sin
, sw
->vol
.mute
);
332 static struct audio_pcm_ops audio_callbacks
= {
333 .init_out
= line_out_init
,
334 .fini_out
= line_out_fini
,
335 .write
= audio_generic_write
,
336 .get_buffer_out
= line_out_get_buffer
,
337 .put_buffer_out
= line_out_put_buffer
,
338 .ctl_out
= line_out_ctl
,
340 .init_in
= line_in_init
,
341 .fini_in
= line_in_fini
,
342 .read
= line_in_read
,
343 .ctl_in
= line_in_ctl
,
346 static struct audio_driver spice_audio_driver
= {
348 .descr
= "spice audio driver",
349 .init
= spice_audio_init
,
350 .fini
= spice_audio_fini
,
351 .pcm_ops
= &audio_callbacks
,
354 .voice_size_out
= sizeof (SpiceVoiceOut
),
355 .voice_size_in
= sizeof (SpiceVoiceIn
),
356 #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
357 .ctl_caps
= VOICE_VOLUME_CAP
361 void qemu_spice_audio_init (void)
363 spice_audio_driver
.can_be_default
= 1;
366 static void register_audio_spice(void)
368 audio_driver_register(&spice_audio_driver
);
370 type_init(register_audio_spice
);