Statistics
| Branch: | Revision:

root / audio / spiceaudio.c @ cf2c1839

History | View | Annotate | Download (9 kB)

1
/*
2
 * Copyright (C) 2010 Red Hat, Inc.
3
 *
4
 * maintained by Gerd Hoffmann <kraxel@redhat.com>
5
 *
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.
10
 *
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.
15
 *
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/>.
18
 */
19

    
20
#include "hw/hw.h"
21
#include "qemu-timer.h"
22
#include "ui/qemu-spice.h"
23

    
24
#define AUDIO_CAP "spice"
25
#include "audio.h"
26
#include "audio_int.h"
27

    
28
#define LINE_IN_SAMPLES 1024
29
#define LINE_OUT_SAMPLES 1024
30

    
31
typedef struct SpiceRateCtl {
32
    int64_t               start_ticks;
33
    int64_t               bytes_sent;
34
} SpiceRateCtl;
35

    
36
typedef struct SpiceVoiceOut {
37
    HWVoiceOut            hw;
38
    SpicePlaybackInstance sin;
39
    SpiceRateCtl          rate;
40
    int                   active;
41
    uint32_t              *frame;
42
    uint32_t              *fpos;
43
    uint32_t              fsize;
44
} SpiceVoiceOut;
45

    
46
typedef struct SpiceVoiceIn {
47
    HWVoiceIn             hw;
48
    SpiceRecordInstance   sin;
49
    SpiceRateCtl          rate;
50
    int                   active;
51
    uint32_t              samples[LINE_IN_SAMPLES];
52
} SpiceVoiceIn;
53

    
54
static const SpicePlaybackInterface playback_sif = {
55
    .base.type          = SPICE_INTERFACE_PLAYBACK,
56
    .base.description   = "playback",
57
    .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR,
58
    .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR,
59
};
60

    
61
static const SpiceRecordInterface record_sif = {
62
    .base.type          = SPICE_INTERFACE_RECORD,
63
    .base.description   = "record",
64
    .base.major_version = SPICE_INTERFACE_RECORD_MAJOR,
65
    .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
66
};
67

    
68
static void *spice_audio_init (void)
69
{
70
    if (!using_spice) {
71
        return NULL;
72
    }
73
    return &spice_audio_init;
74
}
75

    
76
static void spice_audio_fini (void *opaque)
77
{
78
    /* nothing */
79
}
80

    
81
static void rate_start (SpiceRateCtl *rate)
82
{
83
    memset (rate, 0, sizeof (*rate));
84
    rate->start_ticks = qemu_get_clock (vm_clock);
85
}
86

    
87
static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
88
{
89
    int64_t now;
90
    int64_t ticks;
91
    int64_t bytes;
92
    int64_t samples;
93

    
94
    now = qemu_get_clock (vm_clock);
95
    ticks = now - rate->start_ticks;
96
    bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
97
    samples = (bytes - rate->bytes_sent) >> info->shift;
98
    if (samples < 0 || samples > 65536) {
99
        fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples);
100
        rate_start (rate);
101
        samples = 0;
102
    }
103
    rate->bytes_sent += samples << info->shift;
104
    return samples;
105
}
106

    
107
/* playback */
108

    
109
static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
110
{
111
    SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
112
    struct audsettings settings;
113

    
114
    settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ;
115
    settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN;
116
    settings.fmt        = AUD_FMT_S16;
117
    settings.endianness = AUDIO_HOST_ENDIANNESS;
118

    
119
    audio_pcm_init_info (&hw->info, &settings);
120
    hw->samples = LINE_OUT_SAMPLES;
121
    out->active = 0;
122

    
123
    out->sin.base.sif = &playback_sif.base;
124
    qemu_spice_add_interface (&out->sin.base);
125
    return 0;
126
}
127

    
128
static void line_out_fini (HWVoiceOut *hw)
129
{
130
    SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
131

    
132
    spice_server_remove_interface (&out->sin.base);
133
}
134

    
135
static int line_out_run (HWVoiceOut *hw, int live)
136
{
137
    SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
138
    int rpos, decr;
139
    int samples;
140

    
141
    if (!live) {
142
        return 0;
143
    }
144

    
145
    decr = rate_get_samples (&hw->info, &out->rate);
146
    decr = audio_MIN (live, decr);
147

    
148
    samples = decr;
149
    rpos = hw->rpos;
150
    while (samples) {
151
        int left_till_end_samples = hw->samples - rpos;
152
        int len = audio_MIN (samples, left_till_end_samples);
153

    
154
        if (!out->frame) {
155
            spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
156
            out->fpos = out->frame;
157
        }
158
        if (out->frame) {
159
            len = audio_MIN (len, out->fsize);
160
            hw->clip (out->fpos, hw->mix_buf + rpos, len);
161
            out->fsize -= len;
162
            out->fpos  += len;
163
            if (out->fsize == 0) {
164
                spice_server_playback_put_samples (&out->sin, out->frame);
165
                out->frame = out->fpos = NULL;
166
            }
167
        }
168
        rpos = (rpos + len) % hw->samples;
169
        samples -= len;
170
    }
171
    hw->rpos = rpos;
172
    return decr;
173
}
174

    
175
static int line_out_write (SWVoiceOut *sw, void *buf, int len)
176
{
177
    return audio_pcm_sw_write (sw, buf, len);
178
}
179

    
180
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
181
{
182
    SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
183

    
184
    switch (cmd) {
185
    case VOICE_ENABLE:
186
        if (out->active) {
187
            break;
188
        }
189
        out->active = 1;
190
        rate_start (&out->rate);
191
        spice_server_playback_start (&out->sin);
192
        break;
193
    case VOICE_DISABLE:
194
        if (!out->active) {
195
            break;
196
        }
197
        out->active = 0;
198
        if (out->frame) {
199
            memset (out->fpos, 0, out->fsize << 2);
200
            spice_server_playback_put_samples (&out->sin, out->frame);
201
            out->frame = out->fpos = NULL;
202
        }
203
        spice_server_playback_stop (&out->sin);
204
        break;
205
    }
206
    return 0;
207
}
208

    
209
/* record */
210

    
211
static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
212
{
213
    SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
214
    struct audsettings settings;
215

    
216
    settings.freq       = SPICE_INTERFACE_RECORD_FREQ;
217
    settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN;
218
    settings.fmt        = AUD_FMT_S16;
219
    settings.endianness = AUDIO_HOST_ENDIANNESS;
220

    
221
    audio_pcm_init_info (&hw->info, &settings);
222
    hw->samples = LINE_IN_SAMPLES;
223
    in->active = 0;
224

    
225
    in->sin.base.sif = &record_sif.base;
226
    qemu_spice_add_interface (&in->sin.base);
227
    return 0;
228
}
229

    
230
static void line_in_fini (HWVoiceIn *hw)
231
{
232
    SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
233

    
234
    spice_server_remove_interface (&in->sin.base);
235
}
236

    
237
static int line_in_run (HWVoiceIn *hw)
238
{
239
    SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
240
    int num_samples;
241
    int ready;
242
    int len[2];
243
    uint64_t delta_samp;
244
    const uint32_t *samples;
245

    
246
    if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
247
        return 0;
248
    }
249

    
250
    delta_samp = rate_get_samples (&hw->info, &in->rate);
251
    num_samples = audio_MIN (num_samples, delta_samp);
252

    
253
    ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
254
    samples = in->samples;
255
    if (ready == 0) {
256
        static const uint32_t silence[LINE_IN_SAMPLES];
257
        samples = silence;
258
        ready = LINE_IN_SAMPLES;
259
    }
260

    
261
    num_samples = audio_MIN (ready, num_samples);
262

    
263
    if (hw->wpos + num_samples > hw->samples) {
264
        len[0] = hw->samples - hw->wpos;
265
        len[1] = num_samples - len[0];
266
    } else {
267
        len[0] = num_samples;
268
        len[1] = 0;
269
    }
270

    
271
    hw->conv (hw->conv_buf + hw->wpos, samples, len[0], &nominal_volume);
272

    
273
    if (len[1]) {
274
        hw->conv (hw->conv_buf, samples + len[0], len[1],
275
                  &nominal_volume);
276
    }
277

    
278
    hw->wpos = (hw->wpos + num_samples) % hw->samples;
279

    
280
    return num_samples;
281
}
282

    
283
static int line_in_read (SWVoiceIn *sw, void *buf, int size)
284
{
285
    return audio_pcm_sw_read (sw, buf, size);
286
}
287

    
288
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
289
{
290
    SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
291

    
292
    switch (cmd) {
293
    case VOICE_ENABLE:
294
        if (in->active) {
295
            break;
296
        }
297
        in->active = 1;
298
        rate_start (&in->rate);
299
        spice_server_record_start (&in->sin);
300
        break;
301
    case VOICE_DISABLE:
302
        if (!in->active) {
303
            break;
304
        }
305
        in->active = 0;
306
        spice_server_record_stop (&in->sin);
307
        break;
308
    }
309
    return 0;
310
}
311

    
312
static struct audio_option audio_options[] = {
313
    { /* end of list */ },
314
};
315

    
316
static struct audio_pcm_ops audio_callbacks = {
317
    .init_out = line_out_init,
318
    .fini_out = line_out_fini,
319
    .run_out  = line_out_run,
320
    .write    = line_out_write,
321
    .ctl_out  = line_out_ctl,
322

    
323
    .init_in  = line_in_init,
324
    .fini_in  = line_in_fini,
325
    .run_in   = line_in_run,
326
    .read     = line_in_read,
327
    .ctl_in   = line_in_ctl,
328
};
329

    
330
struct audio_driver spice_audio_driver = {
331
    .name           = "spice",
332
    .descr          = "spice audio driver",
333
    .options        = audio_options,
334
    .init           = spice_audio_init,
335
    .fini           = spice_audio_fini,
336
    .pcm_ops        = &audio_callbacks,
337
    .max_voices_out = 1,
338
    .max_voices_in  = 1,
339
    .voice_size_out = sizeof (SpiceVoiceOut),
340
    .voice_size_in  = sizeof (SpiceVoiceIn),
341
};
342

    
343
void qemu_spice_audio_init (void)
344
{
345
    spice_audio_driver.can_be_default = 1;
346
}