Get rid of sys/time.h
[helenos.git] / uspace / srv / audio / hound / audio_device.c
blob7df2bd0ab134f79438e2e7160d2cacd7e9cb5bb4
1 /*
2 * Copyright (c) 2012 Jan Vesely
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /**
30 * @addtogroup audio
31 * @brief HelenOS sound server.
32 * @{
34 /** @file
37 #include <assert.h>
38 #include <async.h>
39 #include <errno.h>
40 #include <inttypes.h>
41 #include <loc.h>
42 #include <stdbool.h>
43 #include <str.h>
44 #include <str_error.h>
45 #include <as.h>
48 #include "audio_device.h"
49 #include "log.h"
51 /* hardwired to provide ~21ms per fragment */
52 #define BUFFER_PARTS 16
54 static errno_t device_sink_connection_callback(audio_sink_t *sink, bool new);
55 static errno_t device_source_connection_callback(audio_source_t *source, bool new);
56 static void device_event_callback(ipc_call_t *icall, void *arg);
57 static errno_t device_check_format(audio_sink_t *sink);
58 static errno_t get_buffer(audio_device_t *dev);
59 static errno_t release_buffer(audio_device_t *dev);
60 static void advance_buffer(audio_device_t *dev, size_t size);
61 static inline bool is_running(audio_device_t *dev)
63 assert(dev);
64 /* we release buffer on stop so this should be enough */
65 return dev->buffer.base != NULL;
68 /**
69 * Initialize audio device structure.
70 * @param dev The structure to initialize.
71 * @param id Location service id of the device driver.
72 * @param name Name of the device.
73 * @return Error code.
75 errno_t audio_device_init(audio_device_t *dev, service_id_t id, const char *name)
77 assert(dev);
78 link_initialize(&dev->link);
79 dev->id = id;
80 dev->name = str_dup(name);
81 dev->sess = audio_pcm_open_service(id);
82 if (!dev->sess) {
83 log_debug("Failed to connect to device \"%s\"", name);
84 return ENOMEM;
87 audio_sink_init(&dev->sink, name, dev, device_sink_connection_callback,
88 device_check_format, NULL, &AUDIO_FORMAT_ANY);
89 audio_source_init(&dev->source, name, dev,
90 device_source_connection_callback, NULL, &AUDIO_FORMAT_ANY);
92 /* Init buffer members */
93 dev->buffer.base = NULL;
94 dev->buffer.position = NULL;
95 dev->buffer.size = 0;
96 dev->buffer.fragment_size = 0;
98 log_verbose("Initialized device (%p) '%s' with id %" PRIun ".",
99 dev, dev->name, dev->id);
101 return EOK;
105 * Restore resource cplaimed during initialization.
106 * @param dev The device to release.
108 * NOT IMPLEMENTED
110 void audio_device_fini(audio_device_t *dev)
112 //TODO implement;
116 * Get device provided audio source.
117 * @param dev Th device.
118 * @return pointer to aa audio source structure, NULL if the device is not
119 * capable of capturing audio.
121 audio_source_t *audio_device_get_source(audio_device_t *dev)
123 assert(dev);
124 sysarg_t val;
125 errno_t rc = audio_pcm_query_cap(dev->sess, AUDIO_CAP_CAPTURE, &val);
126 if (rc == EOK && val)
127 return &dev->source;
128 return NULL;
132 * Get device provided audio sink.
133 * @param dev Th device.
134 * @return pointer to aa audio source structure, NULL if the device is not
135 * capable of audio playback.
137 audio_sink_t *audio_device_get_sink(audio_device_t *dev)
139 assert(dev);
140 sysarg_t val;
141 errno_t rc = audio_pcm_query_cap(dev->sess, AUDIO_CAP_PLAYBACK, &val);
142 if (rc == EOK && val)
143 return &dev->sink;
144 return NULL;
148 * Handle connection addition and removal.
149 * @param sink audio sink that is connected or disconnected.
150 * @param new True of a connection was added, false otherwise.
151 * @return Error code.
153 * Starts playback on first connection. Stops playback when there are no
154 * connections.
156 static errno_t device_sink_connection_callback(audio_sink_t *sink, bool new)
158 assert(sink);
159 audio_device_t *dev = sink->private_data;
160 if (new && list_count(&sink->connections) == 1) {
161 log_verbose("First connection on device sink '%s'", sink->name);
163 errno_t ret = get_buffer(dev);
164 if (ret != EOK) {
165 log_error("Failed to get device buffer: %s",
166 str_error(ret));
167 return ret;
169 audio_pcm_register_event_callback(dev->sess,
170 device_event_callback, dev);
173 * Fill the buffer first. Fill the first two fragments,
174 * so that we stay one fragment ahead
176 pcm_format_silence(dev->buffer.base, dev->buffer.size,
177 &dev->sink.format);
178 //TODO add underrun detection.
179 const size_t size = dev->buffer.fragment_size * 2;
180 /* We never cross the end of the buffer here */
181 audio_sink_mix_inputs(&dev->sink, dev->buffer.position, size);
182 advance_buffer(dev, size);
184 const unsigned frames = dev->buffer.fragment_size /
185 pcm_format_frame_size(&dev->sink.format);
186 log_verbose("Fragment frame count %u", frames);
187 ret = audio_pcm_start_playback_fragment(dev->sess, frames,
188 dev->sink.format.channels, dev->sink.format.sampling_rate,
189 dev->sink.format.sample_format);
190 if (ret != EOK) {
191 log_error("Failed to start playback: %s",
192 str_error(ret));
193 release_buffer(dev);
194 return ret;
197 if (list_count(&sink->connections) == 0) {
198 assert(!new);
199 log_verbose("Removed last connection on device sink '%s'",
200 sink->name);
201 errno_t ret = audio_pcm_stop_playback(dev->sess);
202 if (ret != EOK) {
203 log_error("Failed to stop playback: %s",
204 str_error(ret));
205 return ret;
208 return EOK;
212 * Handle connection addition and removal.
213 * @param source audio source that is connected or disconnected.
214 * @param new True of a connection was added, false otherwise.
215 * @return Error code.
217 * Starts capture on first connection. Stops capture when there are no
218 * connections.
220 static errno_t device_source_connection_callback(audio_source_t *source, bool new)
222 assert(source);
223 audio_device_t *dev = source->private_data;
224 if (new && list_count(&source->connections) == 1) {
225 errno_t ret = get_buffer(dev);
226 if (ret != EOK) {
227 log_error("Failed to get device buffer: %s",
228 str_error(ret));
229 return ret;
232 //TODO set and test format
234 const unsigned frames = dev->buffer.fragment_size /
235 pcm_format_frame_size(&dev->sink.format);
236 ret = audio_pcm_start_capture_fragment(dev->sess, frames,
237 dev->source.format.channels,
238 dev->source.format.sampling_rate,
239 dev->source.format.sample_format);
240 if (ret != EOK) {
241 log_error("Failed to start recording: %s",
242 str_error(ret));
243 release_buffer(dev);
244 return ret;
247 if (list_count(&source->connections) == 0) { /* Disconnected */
248 assert(!new);
249 errno_t ret = audio_pcm_stop_capture_immediate(dev->sess);
250 if (ret != EOK) {
251 log_error("Failed to start recording: %s",
252 str_error(ret));
253 return ret;
257 return EOK;
260 /** Audio device event handler.
262 * @param icall Initial call structure.
263 * @param arg (unused)
266 static void device_event_callback(ipc_call_t *icall, void *arg)
268 struct timespec time1;
269 errno_t ret;
271 /* Answer initial request */
272 async_answer_0(icall, EOK);
273 audio_device_t *dev = arg;
274 assert(dev);
275 while (true) {
276 ipc_call_t call;
277 async_get_call(&call);
278 async_answer_0(&call, EOK);
280 switch (IPC_GET_IMETHOD(call)) {
281 case PCM_EVENT_FRAMES_PLAYED:
282 getuptime(&time1);
283 //TODO add underrun detection.
284 /* We never cross the end of the buffer here */
285 audio_sink_mix_inputs(&dev->sink, dev->buffer.position,
286 dev->buffer.fragment_size);
287 advance_buffer(dev, dev->buffer.fragment_size);
288 struct timespec time2;
289 getuptime(&time2);
290 log_verbose("Time to mix sources: %lld\n",
291 NSEC2USEC(ts_sub_diff(&time2, &time1)));
292 break;
293 case PCM_EVENT_CAPTURE_TERMINATED:
294 log_verbose("Capture terminated");
295 dev->source.format = AUDIO_FORMAT_ANY;
296 ret = release_buffer(dev);
297 if (ret != EOK) {
298 log_error("Failed to release buffer: %s",
299 str_error(ret));
301 audio_pcm_unregister_event_callback(dev->sess);
302 break;
303 case PCM_EVENT_PLAYBACK_TERMINATED:
304 log_verbose("Playback Terminated");
305 dev->sink.format = AUDIO_FORMAT_ANY;
306 ret = release_buffer(dev);
307 if (ret != EOK) {
308 log_error("Failed to release buffer: %s",
309 str_error(ret));
311 audio_pcm_unregister_event_callback(dev->sess);
312 break;
313 case PCM_EVENT_FRAMES_CAPTURED:
314 ret = audio_source_push_data(&dev->source,
315 dev->buffer.position, dev->buffer.fragment_size);
316 advance_buffer(dev, dev->buffer.fragment_size);
317 if (ret != EOK)
318 log_warning("Failed to push recorded data");
319 break;
320 case 0:
321 log_info("Device event callback hangup");
322 return;
328 * Test format against hardware limits.
329 * @param sink audio playback device.
330 * @return Error code.
332 static errno_t device_check_format(audio_sink_t *sink)
334 assert(sink);
335 audio_device_t *dev = sink->private_data;
336 assert(dev);
337 /* Check whether we are running */
338 if (is_running(dev))
339 return EBUSY;
340 log_verbose("Checking format on sink %s", sink->name);
341 return audio_pcm_test_format(dev->sess, &sink->format.channels,
342 &sink->format.sampling_rate, &sink->format.sample_format);
346 * Get access to device buffer.
347 * @param dev Audio device.
348 * @return Error code.
350 static errno_t get_buffer(audio_device_t *dev)
352 assert(dev);
353 if (!dev->sess) {
354 log_debug("No connection to device");
355 return EIO;
357 if (dev->buffer.base) {
358 log_debug("We already have a buffer");
359 return EBUSY;
362 /* Ask for largest buffer possible */
363 size_t preferred_size = 0;
365 const errno_t ret = audio_pcm_get_buffer(dev->sess, &dev->buffer.base,
366 &preferred_size);
367 if (ret == EOK) {
368 dev->buffer.size = preferred_size;
369 dev->buffer.fragment_size = dev->buffer.size / BUFFER_PARTS;
370 dev->buffer.position = dev->buffer.base;
372 return ret;
377 * Surrender access to device buffer.
378 * @param dev Audio device.
379 * @return Error code.
381 static errno_t release_buffer(audio_device_t *dev)
383 assert(dev);
384 assert(dev->buffer.base);
386 const errno_t ret = audio_pcm_release_buffer(dev->sess);
387 if (ret == EOK) {
388 as_area_destroy(dev->buffer.base);
389 dev->buffer.base = NULL;
390 dev->buffer.size = 0;
391 dev->buffer.position = NULL;
392 } else {
393 log_warning("Failed to release buffer: %s", str_error(ret));
395 return ret;
399 * Move buffer position pointer.
400 * @param dev Audio device.
401 * @param size number of bytes to move forward
403 static void advance_buffer(audio_device_t *dev, size_t size)
405 assert(dev);
406 assert(dev->buffer.position >= dev->buffer.base);
407 assert(dev->buffer.position < (dev->buffer.base + dev->buffer.size));
408 dev->buffer.position += size;
409 if (dev->buffer.position == (dev->buffer.base + dev->buffer.size))
410 dev->buffer.position = dev->buffer.base;
413 * @}