~ahs3/+junk/cq-qemu

« back to all changes in this revision

Viewing changes to audio/spiceaudio.c

  • Committer: Al Stone
  • Date: 2012-02-09 01:17:20 UTC
  • Revision ID: albert.stone@canonical.com-20120209011720-tztl7ik3qayz80p4
first commit to bzr for qemu

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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_ns (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_ns (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]);
 
272
 
 
273
    if (len[1]) {
 
274
        hw->conv (hw->conv_buf, samples + len[0], len[1]);
 
275
    }
 
276
 
 
277
    hw->wpos = (hw->wpos + num_samples) % hw->samples;
 
278
 
 
279
    return num_samples;
 
280
}
 
281
 
 
282
static int line_in_read (SWVoiceIn *sw, void *buf, int size)
 
283
{
 
284
    return audio_pcm_sw_read (sw, buf, size);
 
285
}
 
286
 
 
287
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
 
288
{
 
289
    SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
 
290
 
 
291
    switch (cmd) {
 
292
    case VOICE_ENABLE:
 
293
        if (in->active) {
 
294
            break;
 
295
        }
 
296
        in->active = 1;
 
297
        rate_start (&in->rate);
 
298
        spice_server_record_start (&in->sin);
 
299
        break;
 
300
    case VOICE_DISABLE:
 
301
        if (!in->active) {
 
302
            break;
 
303
        }
 
304
        in->active = 0;
 
305
        spice_server_record_stop (&in->sin);
 
306
        break;
 
307
    }
 
308
    return 0;
 
309
}
 
310
 
 
311
static struct audio_option audio_options[] = {
 
312
    { /* end of list */ },
 
313
};
 
314
 
 
315
static struct audio_pcm_ops audio_callbacks = {
 
316
    .init_out = line_out_init,
 
317
    .fini_out = line_out_fini,
 
318
    .run_out  = line_out_run,
 
319
    .write    = line_out_write,
 
320
    .ctl_out  = line_out_ctl,
 
321
 
 
322
    .init_in  = line_in_init,
 
323
    .fini_in  = line_in_fini,
 
324
    .run_in   = line_in_run,
 
325
    .read     = line_in_read,
 
326
    .ctl_in   = line_in_ctl,
 
327
};
 
328
 
 
329
struct audio_driver spice_audio_driver = {
 
330
    .name           = "spice",
 
331
    .descr          = "spice audio driver",
 
332
    .options        = audio_options,
 
333
    .init           = spice_audio_init,
 
334
    .fini           = spice_audio_fini,
 
335
    .pcm_ops        = &audio_callbacks,
 
336
    .max_voices_out = 1,
 
337
    .max_voices_in  = 1,
 
338
    .voice_size_out = sizeof (SpiceVoiceOut),
 
339
    .voice_size_in  = sizeof (SpiceVoiceIn),
 
340
};
 
341
 
 
342
void qemu_spice_audio_init (void)
 
343
{
 
344
    spice_audio_driver.can_be_default = 1;
 
345
}