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
Line 
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
44
45#include "audio_device.h"
46#include "log.h"
47
48#define BUFFER_PARTS 2
49
50static int device_sink_connection_callback(audio_sink_t *sink, bool new);
51static int device_source_connection_callback(audio_source_t *source);
52static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void *arg);
53static int device_check_format(audio_sink_t* sink);
54static int get_buffer(audio_device_t *dev);
55static int release_buffer(audio_device_t *dev);
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);
64 dev->sess = audio_pcm_open_service(id);
65 if (!dev->sess) {
66 log_debug("Failed to connect to device \"%s\"", name);
67 return ENOMEM;
68 }
69
70 audio_sink_init(&dev->sink, name, dev, device_sink_connection_callback,
71 device_check_format, &AUDIO_FORMAT_ANY);
72 audio_source_init(&dev->source, name, dev,
73 device_source_connection_callback, NULL, &AUDIO_FORMAT_ANY);
74
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
82 log_verbose("Initialized device (%p) '%s' with id %u.",
83 dev, dev->name, dev->id);
84
85 return EOK;
86}
87void audio_device_fini(audio_device_t *dev)
88{
89 //TODO implement;
90}
91
92static int device_sink_connection_callback(audio_sink_t* sink, bool new)
93{
94 assert(sink);
95 audio_device_t *dev = sink->private_data;
96 if (new && list_count(&sink->sources) == 1) {
97 log_verbose("First connection on device sink '%s'", sink->name);
98
99 int ret = get_buffer(dev);
100 if (ret != EOK) {
101 log_error("Failed to get device buffer: %s",
102 str_error(ret));
103 return ret;
104 }
105
106 /* Fill the buffer first */
107 audio_sink_mix_inputs(&dev->sink,
108 dev->buffer.base, dev->buffer.size);
109
110 const unsigned frames = dev->buffer.size /
111 (BUFFER_PARTS * pcm_format_frame_size(&dev->sink.format));
112 ret = audio_pcm_start_playback(dev->sess, frames,
113 dev->sink.format.channels, dev->sink.format.sampling_rate,
114 dev->sink.format.sample_format);
115 if (ret != EOK) {
116 log_error("Failed to start playback: %s",
117 str_error(ret));
118 release_buffer(dev);
119 return ret;
120 }
121 }
122 if (list_count(&sink->sources) == 0) {
123 assert(!new);
124 log_verbose("No connections on device sink '%s'", sink->name);
125 int ret = audio_pcm_stop_playback(dev->sess);
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;
132 ret = release_buffer(dev);
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
142static int device_source_connection_callback(audio_source_t *source)
143{
144 assert(source);
145 audio_device_t *dev = source->private_data;
146 if (source->connected_sink) {
147 int ret = get_buffer(dev);
148 if (ret != EOK) {
149 log_error("Failed to get device buffer: %s",
150 str_error(ret));
151 return ret;
152 }
153 const unsigned frames = dev->buffer.size /
154 (BUFFER_PARTS * pcm_format_frame_size(&dev->sink.format));
155 ret = audio_pcm_start_capture(dev->sess, frames,
156 dev->sink.format.channels, dev->sink.format.sampling_rate,
157 dev->sink.format.sample_format);
158 if (ret != EOK) {
159 log_error("Failed to start recording: %s",
160 str_error(ret));
161 release_buffer(dev);
162 return ret;
163 }
164 } else { /* Disconnected */
165 int ret = audio_pcm_stop_capture(dev->sess);
166 if (ret != EOK) {
167 log_error("Failed to start recording: %s",
168 str_error(ret));
169 return ret;
170 }
171 source->format = AUDIO_FORMAT_ANY;
172 ret = release_buffer(dev);
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
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);
193 switch(IPC_GET_IMETHOD(call)) {
194 case PCM_EVENT_FRAMES_PLAYED: {
195 //TODO add underrun protection.
196 if (dev->buffer.position) {
197 dev->buffer.position +=
198 (dev->buffer.size / BUFFER_PARTS);
199 }
200 if ((!dev->buffer.position) ||
201 (dev->buffer.position >=
202 (dev->buffer.base + dev->buffer.size)))
203 {
204 dev->buffer.position = dev->buffer.base;
205 }
206 audio_sink_mix_inputs(&dev->sink, dev->buffer.position,
207 dev->buffer.size / BUFFER_PARTS);
208 break;
209 }
210 case PCM_EVENT_PLAYBACK_TERMINATED:
211 log_verbose("Playback terminated!");
212 return;
213 case PCM_EVENT_FRAMES_CAPTURED:
214 //TODO implement
215 break;
216 case PCM_EVENT_CAPTURE_TERMINATED:
217 log_verbose("Recording terminated!");
218 return;
219 }
220
221 }
222}
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}
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
247 return audio_pcm_get_buffer(dev->sess, &dev->buffer.base,
248 &dev->buffer.size, device_event_callback, dev);
249}
250
251static int release_buffer(audio_device_t *dev)
252{
253 assert(dev);
254 assert(dev->buffer.base);
255
256 const int ret = audio_pcm_release_buffer(dev->sess);
257 if (ret == EOK) {
258 dev->buffer.base = NULL;
259 dev->buffer.size = 0;
260 dev->buffer.position = NULL;
261 } else {
262 log_debug("Failed to release buffer: %s", str_error(ret));
263 }
264 return ret;
265}
266/**
267 * @}
268 */
Note: See TracBrowser for help on using the repository browser.