source: mainline/uspace/srv/audio/hound/audio_device.c@ fa91c0f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fa91c0f was d86c9736, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

Rename record → capture.

  • Property mode set to 100644
File size: 7.3 KB
RevLine 
[737b4c0]1/*
2 * Copyright (c) 2012 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @addtogroup audio
31 * @brief HelenOS sound server.
32 * @{
33 */
34/** @file
35 */
36
37#include <assert.h>
38#include <async.h>
39#include <errno.h>
40#include <loc.h>
41#include <str.h>
42#include <str_error.h>
43
[992ef56]44
[737b4c0]45#include "audio_device.h"
46#include "log.h"
47
[57e8b3b]48#define BUFFER_PARTS 2
[992ef56]49
[13df13c8]50static int device_sink_connection_callback(audio_sink_t *sink, bool new);
[1df3018a]51static int device_source_connection_callback(audio_source_t *source);
[992ef56]52static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void *arg);
[389ef25]53static int device_check_format(audio_sink_t* sink);
[992ef56]54static int get_buffer(audio_device_t *dev);
55static int release_buffer(audio_device_t *dev);
[737b4c0]56
57
58int audio_device_init(audio_device_t *dev, service_id_t id, const char *name)
59{
60 assert(dev);
61 link_initialize(&dev->link);
62 dev->id = id;
63 dev->name = str_dup(name);
[2cc5c835]64 dev->sess = audio_pcm_open_service(id);
[737b4c0]65 if (!dev->sess) {
66 log_debug("Failed to connect to device \"%s\"", name);
67 return ENOMEM;
68 }
69
[1df3018a]70 audio_sink_init(&dev->sink, name, dev, device_sink_connection_callback,
[389ef25]71 device_check_format, &AUDIO_FORMAT_ANY);
[1df3018a]72 audio_source_init(&dev->source, name, dev,
73 device_source_connection_callback, NULL, &AUDIO_FORMAT_ANY);
[737b4c0]74
[992ef56]75 /* Init buffer members */
76 fibril_mutex_initialize(&dev->buffer.guard);
77 fibril_condvar_initialize(&dev->buffer.wc);
78 dev->buffer.base = NULL;
79 dev->buffer.position = NULL;
80 dev->buffer.size = 0;
81
[737b4c0]82 log_verbose("Initialized device (%p) '%s' with id %u.",
83 dev, dev->name, dev->id);
84
85 return EOK;
86}
[1df3018a]87void audio_device_fini(audio_device_t *dev)
88{
89 //TODO implement;
90}
[737b4c0]91
[13df13c8]92static int device_sink_connection_callback(audio_sink_t* sink, bool new)
[737b4c0]93{
[1df3018a]94 assert(sink);
95 audio_device_t *dev = sink->private_data;
[13df13c8]96 if (new && list_count(&sink->sources) == 1) {
[f3fced0]97 log_verbose("First connection on device sink '%s'", sink->name);
98
[992ef56]99 int ret = get_buffer(dev);
[737b4c0]100 if (ret != EOK) {
101 log_error("Failed to get device buffer: %s",
102 str_error(ret));
103 return ret;
104 }
[f3fced0]105
[2cc5c835]106 /* Fill the buffer first */
107 audio_sink_mix_inputs(&dev->sink,
108 dev->buffer.base, dev->buffer.size);
109
[57e8b3b]110 const unsigned frames = dev->buffer.size /
[ea6c838]111 (BUFFER_PARTS * pcm_format_frame_size(&dev->sink.format));
[57e8b3b]112 ret = audio_pcm_start_playback(dev->sess, frames,
[2cc5c835]113 dev->sink.format.channels, dev->sink.format.sampling_rate,
114 dev->sink.format.sample_format);
[737b4c0]115 if (ret != EOK) {
116 log_error("Failed to start playback: %s",
117 str_error(ret));
[992ef56]118 release_buffer(dev);
[737b4c0]119 return ret;
120 }
121 }
[1df3018a]122 if (list_count(&sink->sources) == 0) {
[13df13c8]123 assert(!new);
[f3fced0]124 log_verbose("No connections on device sink '%s'", sink->name);
[2cc5c835]125 int ret = audio_pcm_stop_playback(dev->sess);
[737b4c0]126 if (ret != EOK) {
127 log_error("Failed to start playback: %s",
128 str_error(ret));
129 return ret;
130 }
131 dev->sink.format = AUDIO_FORMAT_ANY;
[992ef56]132 ret = release_buffer(dev);
[737b4c0]133 if (ret != EOK) {
134 log_error("Failed to release buffer: %s",
135 str_error(ret));
136 return ret;
137 }
138 }
139 return EOK;
140}
141
[1df3018a]142static int device_source_connection_callback(audio_source_t *source)
[737b4c0]143{
[1df3018a]144 assert(source);
145 audio_device_t *dev = source->private_data;
146 if (source->connected_sink) {
[992ef56]147 int ret = get_buffer(dev);
[737b4c0]148 if (ret != EOK) {
149 log_error("Failed to get device buffer: %s",
150 str_error(ret));
151 return ret;
152 }
[57e8b3b]153 const unsigned frames = dev->buffer.size /
[ea6c838]154 (BUFFER_PARTS * pcm_format_frame_size(&dev->sink.format));
[d86c9736]155 ret = audio_pcm_start_capture(dev->sess, frames,
[2cc5c835]156 dev->sink.format.channels, dev->sink.format.sampling_rate,
157 dev->sink.format.sample_format);
[737b4c0]158 if (ret != EOK) {
159 log_error("Failed to start recording: %s",
160 str_error(ret));
[992ef56]161 release_buffer(dev);
[737b4c0]162 return ret;
163 }
[1df3018a]164 } else { /* Disconnected */
[d86c9736]165 int ret = audio_pcm_stop_capture(dev->sess);
[737b4c0]166 if (ret != EOK) {
167 log_error("Failed to start recording: %s",
168 str_error(ret));
169 return ret;
170 }
[1df3018a]171 source->format = AUDIO_FORMAT_ANY;
[992ef56]172 ret = release_buffer(dev);
[737b4c0]173 if (ret != EOK) {
174 log_error("Failed to release buffer: %s",
175 str_error(ret));
176 return ret;
177 }
178 }
179
180 return EOK;
181}
182
[992ef56]183static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void *arg)
184{
185 /* Answer initial request */
186 async_answer_0(iid, EOK);
187 audio_device_t *dev = arg;
188 assert(dev);
189 while (1) {
190 ipc_call_t call;
191 ipc_callid_t callid = async_get_call(&call);
192 async_answer_0(callid, EOK);
[fd5c0b7]193 switch(IPC_GET_IMETHOD(call)) {
[57e8b3b]194 case PCM_EVENT_FRAMES_PLAYED: {
195 //TODO add underrun protection.
[fd5c0b7]196 if (dev->buffer.position) {
197 dev->buffer.position +=
[57e8b3b]198 (dev->buffer.size / BUFFER_PARTS);
[fd5c0b7]199 }
[ab07cf0]200 if ((!dev->buffer.position) ||
201 (dev->buffer.position >=
202 (dev->buffer.base + dev->buffer.size)))
[fd5c0b7]203 {
204 dev->buffer.position = dev->buffer.base;
205 }
206 audio_sink_mix_inputs(&dev->sink, dev->buffer.position,
[57e8b3b]207 dev->buffer.size / BUFFER_PARTS);
[fd5c0b7]208 break;
[992ef56]209 }
[d86c9736]210 case PCM_EVENT_PLAYBACK_TERMINATED:
[fd5c0b7]211 log_verbose("Playback terminated!");
212 return;
[d86c9736]213 case PCM_EVENT_FRAMES_CAPTURED:
[ab07cf0]214 //TODO implement
[fd5c0b7]215 break;
[d86c9736]216 case PCM_EVENT_CAPTURE_TERMINATED:
[fd5c0b7]217 log_verbose("Recording terminated!");
[ab07cf0]218 return;
[992ef56]219 }
220
221 }
222}
[389ef25]223static int device_check_format(audio_sink_t* sink)
224{
225 assert(sink);
226 audio_device_t *dev = sink->private_data;
227 assert(dev);
228 log_verbose("Checking format on sink %s", sink->name);
229 return audio_pcm_test_format(dev->sess, &sink->format.channels,
230 &sink->format.sampling_rate, &sink->format.sample_format);
231}
[992ef56]232
233static int get_buffer(audio_device_t *dev)
234{
235 assert(dev);
236 if (!dev->sess) {
237 log_debug("No connection to device");
238 return EIO;
239 }
240 if (dev->buffer.base) {
241 log_debug("We already have a buffer");
242 return EBUSY;
243 }
244
245 dev->buffer.size = 0;
246
[2cc5c835]247 return audio_pcm_get_buffer(dev->sess, &dev->buffer.base,
[b497018]248 &dev->buffer.size, device_event_callback, dev);
[992ef56]249}
250
251static int release_buffer(audio_device_t *dev)
252{
[2cc5c835]253 assert(dev);
254 assert(dev->buffer.base);
[992ef56]255
[2cc5c835]256 const int ret = audio_pcm_release_buffer(dev->sess);
[992ef56]257 if (ret == EOK) {
258 dev->buffer.base = NULL;
259 dev->buffer.size = 0;
260 dev->buffer.position = NULL;
[2cc5c835]261 } else {
262 log_debug("Failed to release buffer: %s", str_error(ret));
[992ef56]263 }
264 return ret;
265}
[737b4c0]266/**
267 * @}
268 */
Note: See TracBrowser for help on using the repository browser.