2 * Copyright (c) 2012 Jan Vesely
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
31 * @brief HelenOS sound server.
44 #include <str_error.h>
48 #include "audio_device.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
)
64 /* we release buffer on stop so this should be enough */
65 return dev
->buffer
.base
!= NULL
;
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.
75 errno_t
audio_device_init(audio_device_t
*dev
, service_id_t id
, const char *name
)
78 link_initialize(&dev
->link
);
80 dev
->name
= str_dup(name
);
81 dev
->sess
= audio_pcm_open_service(id
);
83 log_debug("Failed to connect to device \"%s\"", name
);
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
;
96 dev
->buffer
.fragment_size
= 0;
98 log_verbose("Initialized device (%p) '%s' with id %" PRIun
".",
99 dev
, dev
->name
, dev
->id
);
105 * Restore resource cplaimed during initialization.
106 * @param dev The device to release.
110 void audio_device_fini(audio_device_t
*dev
)
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
)
125 errno_t rc
= audio_pcm_query_cap(dev
->sess
, AUDIO_CAP_CAPTURE
, &val
);
126 if (rc
== EOK
&& val
)
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
)
141 errno_t rc
= audio_pcm_query_cap(dev
->sess
, AUDIO_CAP_PLAYBACK
, &val
);
142 if (rc
== EOK
&& val
)
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
156 static errno_t
device_sink_connection_callback(audio_sink_t
*sink
, bool new)
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
);
165 log_error("Failed to get device buffer: %s",
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
,
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
);
191 log_error("Failed to start playback: %s",
197 if (list_count(&sink
->connections
) == 0) {
199 log_verbose("Removed last connection on device sink '%s'",
201 errno_t ret
= audio_pcm_stop_playback(dev
->sess
);
203 log_error("Failed to stop playback: %s",
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
220 static errno_t
device_source_connection_callback(audio_source_t
*source
, bool new)
223 audio_device_t
*dev
= source
->private_data
;
224 if (new && list_count(&source
->connections
) == 1) {
225 errno_t ret
= get_buffer(dev
);
227 log_error("Failed to get device buffer: %s",
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
);
241 log_error("Failed to start recording: %s",
247 if (list_count(&source
->connections
) == 0) { /* Disconnected */
249 errno_t ret
= audio_pcm_stop_capture_immediate(dev
->sess
);
251 log_error("Failed to start recording: %s",
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
;
271 /* Answer initial request */
272 async_answer_0(icall
, EOK
);
273 audio_device_t
*dev
= arg
;
277 async_get_call(&call
);
278 async_answer_0(&call
, EOK
);
280 switch (IPC_GET_IMETHOD(call
)) {
281 case PCM_EVENT_FRAMES_PLAYED
:
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
;
290 log_verbose("Time to mix sources: %lld\n",
291 NSEC2USEC(ts_sub_diff(&time2
, &time1
)));
293 case PCM_EVENT_CAPTURE_TERMINATED
:
294 log_verbose("Capture terminated");
295 dev
->source
.format
= AUDIO_FORMAT_ANY
;
296 ret
= release_buffer(dev
);
298 log_error("Failed to release buffer: %s",
301 audio_pcm_unregister_event_callback(dev
->sess
);
303 case PCM_EVENT_PLAYBACK_TERMINATED
:
304 log_verbose("Playback Terminated");
305 dev
->sink
.format
= AUDIO_FORMAT_ANY
;
306 ret
= release_buffer(dev
);
308 log_error("Failed to release buffer: %s",
311 audio_pcm_unregister_event_callback(dev
->sess
);
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
);
318 log_warning("Failed to push recorded data");
321 log_info("Device event callback hangup");
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
)
335 audio_device_t
*dev
= sink
->private_data
;
337 /* Check whether we are running */
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
)
354 log_debug("No connection to device");
357 if (dev
->buffer
.base
) {
358 log_debug("We already have a buffer");
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
,
368 dev
->buffer
.size
= preferred_size
;
369 dev
->buffer
.fragment_size
= dev
->buffer
.size
/ BUFFER_PARTS
;
370 dev
->buffer
.position
= dev
->buffer
.base
;
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
)
384 assert(dev
->buffer
.base
);
386 const errno_t ret
= audio_pcm_release_buffer(dev
->sess
);
388 as_area_destroy(dev
->buffer
.base
);
389 dev
->buffer
.base
= NULL
;
390 dev
->buffer
.size
= 0;
391 dev
->buffer
.position
= NULL
;
393 log_warning("Failed to release buffer: %s", str_error(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
)
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
;