source: roaraudio/plugins/alsavs/ctl_roar.c @ 5439:7950543cabbc

Last change on this file since 5439:7950543cabbc was 5381:430b1d26e12d, checked in by phi, 12 years ago

updated copyright years

File size: 8.8 KB
Line 
1//ctl_roar.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011-2012
5 *
6 *  This file is part of libroar a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  libroar is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 *  NOTE for everyone want's to change something and send patches:
25 *  read README and HACKING! There a addition information on
26 *  the license of this document you need to read before you send
27 *  any patches.
28 *
29 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
30 *  or libpulse*:
31 *  The libs libroaresd, libroararts and libroarpulse link this lib
32 *  and are therefore GPL. Because of this it may be illigal to use
33 *  them with any software that uses libesd, libartsc or libpulse*.
34 */
35
36//#define DEBUG
37#include "roar.h"
38#include <string.h>
39
40#ifdef DEBUG
41static struct roar_vio_calls DEBUG_FH;
42#endif
43
44static void roar_plugin_close(snd_ctl_ext_t * ext) {
45 struct roar_alsa_ctl * self = ext->private_data;
46
47 roar_disconnect(&(self->roar.con));
48 free(self);
49}
50
51static int roar_plugin_elem_count(snd_ctl_ext_t *ext) {
52 struct roar_alsa_ctl * self = ext->private_data;
53 int ret = 0;
54
55 for (; self->streams[ret].id != -1; ret++);
56
57 return ret;
58}
59
60static int roar_plugin_elem_list(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id) {
61 struct roar_alsa_ctl * self = ext->private_data;
62
63 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
64 snd_ctl_elem_id_set_name(id, self->streams[offset].name);
65
66 return 0;
67}
68
69// ALSA is strange here, this function makes keys (stream ID) from non-unique stream names...
70static snd_ctl_ext_key_t roar_plugin_find_elem(snd_ctl_ext_t *ext,
71                                               const snd_ctl_elem_id_t *id) {
72 struct roar_alsa_ctl * self = ext->private_data;
73// int numid = snd_ctl_elem_id_get_numid(id);
74 const char * name;
75 int i;
76
77// ROAR_DBG("roar_plugin_find_elem(ext=%p, id=%p): numid=%i", ext, id, numid);
78
79// return self->streams[numid].id;
80
81 name = snd_ctl_elem_id_get_name(id);
82
83 for (i = 0; i < MAX_STREAMS; i++) {
84  if ( !strcmp(self->streams[i].name, name) ) {
85   return self->streams[i].id;
86  }
87 }
88
89 return SND_CTL_EXT_KEY_NOT_FOUND;
90}
91
92static int roar_plugin_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
93                                     int *type, unsigned int *acc, unsigned int *count) {
94 struct roar_alsa_ctl * self = ext->private_data;
95 int i;
96
97 *type = SND_CTL_ELEM_TYPE_INTEGER;
98 *acc = SND_CTL_EXT_ACCESS_READWRITE;
99
100 for (i = 0; i < MAX_STREAMS && self->streams[i].id != (int)key; i++)
101
102 if ( i == MAX_STREAMS )
103  return -EIO;
104
105 *count = self->streams[i].channels;
106
107 return 0;
108}
109
110static int roar_plugin_get_integer_info(snd_ctl_ext_t *ext ATTRIBUTE_UNUSED,
111                                        snd_ctl_ext_key_t key ATTRIBUTE_UNUSED,
112                                        long *imin, long *imax, long *istep) {
113 *istep = 0;
114 *imin  = 0;
115 *imax  = 65535;
116 return 0;
117}
118
119static int roar_plugin_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) {
120 struct roar_alsa_ctl * self = ext->private_data;
121 struct roar_mixer_settings mixer;
122 int channels;
123 int i;
124
125 for (i = 0; i < MAX_STREAMS && self->streams[i].id != (int)key; i++)
126
127 if ( i == MAX_STREAMS )
128  return -EIO;
129
130 roar_err_clear_all();
131 if ( roar_get_vol(&(self->roar.con), key, &mixer, &channels) == -1 ) {
132  roar_err_update();
133  return -errno;
134 }
135
136 if ( channels != (int)self->streams[i].channels )
137  return -EIO;
138
139 for (i = 0; i < channels; i++)
140  value[i] = ((unsigned long int)mixer.mixer[i] * 65535LU) / mixer.scale;
141
142 return 0;
143}
144
145static int roar_plugin_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) {
146 struct roar_alsa_ctl * self = ext->private_data;
147 struct roar_mixer_settings mixer;
148 int sid;
149 int i;
150
151 ROAR_DBG("roar_plugin_write_integer(ext=%p, key=%i, value=%p) = ?", ext, (int)key, value);
152
153 for (sid = 0; sid < MAX_STREAMS && self->streams[sid].id != (int)key; sid++)
154
155 if ( sid == MAX_STREAMS )
156  return -EIO;
157
158 mixer.scale = 65535;
159 mixer.rpg_mul = 1;
160 mixer.rpg_div = 1;
161
162 for (i = 0; i < (int)self->streams[i].channels; i++)
163  mixer.mixer[i] = value[i];
164
165 roar_err_clear_all();
166 if ( roar_set_vol(&(self->roar.con), key, &mixer, self->streams[sid].channels, ROAR_SET_VOL_ALL) == -1 ) {
167  roar_err_update();
168  return -errno;
169 }
170
171 return 0;
172}
173
174static int roar_plugin_read_event(snd_ctl_ext_t *ext ATTRIBUTE_UNUSED,
175                                  snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED,
176                                  unsigned int *event_mask ATTRIBUTE_UNUSED) {
177 return -EAGAIN;
178}
179
180static void roar_plugin__handle_stream(struct roar_alsa_ctl * self, int id) {
181 struct roar_mixerstream * cs = NULL;
182 struct roar_stream s;
183 char name[1024];
184 int i;
185
186 if ( roar_get_stream(&(self->roar.con), &s, id) == -1 )
187  return;
188
189 switch (s.dir) {
190  case ROAR_DIR_OUTPUT:
191  case ROAR_DIR_MIXING:
192  case ROAR_DIR_BRIDGE:
193    if ( roar_stream_get_name(&(self->roar.con), &s, name, sizeof(name)) != 0 )
194     snprintf(name, sizeof(name), "Stream %i", id);
195   break;
196  default:
197    return;
198   break;
199 }
200
201 switch (s.dir) {
202  case ROAR_DIR_MIXING:
203  case ROAR_DIR_BRIDGE:
204    if ( strstr(name, "MIDI") != NULL || strstr(name, "Light") != NULL )
205     return;
206   break;
207 }
208
209 for (i = 0; cs == NULL && i < (int)(sizeof(self->streams)/sizeof(*self->streams)); i++)
210  if (self->streams[i].id == -1 )
211   cs = &(self->streams[i]);
212
213 if ( cs == NULL )
214  return;
215
216 cs->id = id;
217 cs->channels = s.info.channels;
218 strncpy(cs->name, name, sizeof(cs->name) - 1);
219}
220
221static int roar_plugin__find_streams(struct roar_alsa_ctl * self) {
222 int id[ROAR_STREAMS_MAX];
223 int num;
224 int i;
225
226 for (i = 0; i < (int)(sizeof(self->streams)/sizeof(*self->streams)); i++)
227  self->streams[i].id = -1;
228
229 roar_err_clear_all();
230
231 if ( (num = roar_list_streams(&(self->roar.con), id, ROAR_STREAMS_MAX)) == -1 ) {
232  roar_err_update();
233  return -errno;
234 }
235
236 for (i = 0; i < num; i++)
237  roar_plugin__handle_stream(self, i);
238
239 return 0;
240}
241
242static snd_ctl_ext_callback_t roar_ext_callback = {
243 .close            = roar_plugin_close,
244 .elem_count       = roar_plugin_elem_count,
245 .elem_list        = roar_plugin_elem_list,
246 .find_elem        = roar_plugin_find_elem,
247 .get_attribute    = roar_plugin_get_attribute,
248 .get_integer_info = roar_plugin_get_integer_info,
249 .read_integer     = roar_plugin_read_integer,
250 .write_integer    = roar_plugin_write_integer,
251 .read_event       = roar_plugin_read_event
252};
253
254SND_CTL_PLUGIN_DEFINE_FUNC(roar) {
255 struct roar_alsa_ctl * self;
256 snd_config_iterator_t i, next;
257 snd_config_t * n;
258 const char   * para;
259 const char   * server = NULL;
260 int error;
261
262 const char   * ext_id   = "ROAR";
263 const char   * ext_name = "RoarAudio";
264
265 (void)root;
266
267#ifdef DEBUG
268 roar_vio_open_dstr_simple(&DEBUG_FH, "/tmp/alsamixer-roar.log", O_WRONLY|O_CREAT|O_APPEND);
269 roar_debug_set_stderr_vio(&DEBUG_FH);
270 roar_debug_set_stderr_mode(ROAR_DEBUG_MODE_VIO);
271#endif
272
273 snd_config_for_each(i, next, conf) {
274  n = snd_config_iterator_entry(i);
275  if ( snd_config_get_id(n, &para) < 0 )
276   continue;
277
278  if ( !strcmp(para, "type") || !strcmp(para, "comment") || !strcmp(para, "hint") )
279   continue;
280
281  if ( !strcmp(para, "server") ) {
282   if (snd_config_get_string(n, &server) < 0) {
283    return -EINVAL;
284   }
285  } else {
286   return -EINVAL;
287  }
288 }
289
290 if ( (self = malloc(sizeof(struct roar_alsa_ctl))) == NULL )
291  return -errno;
292
293 memset(self, 0, sizeof(struct roar_alsa_ctl));
294
295 errno = ENOSYS;
296 if ( roar_simple_connect(&(self->roar.con), server, "ALSA Plugin") == -1 ) {
297  free(self);
298  return -errno;
299 }
300
301 if ( (error = roar_plugin__find_streams(self)) < 0 ) {
302  roar_disconnect(&(self->roar.con));
303  free(self);
304  return error;
305 }
306
307 self->ext.version = SND_CTL_EXT_VERSION;
308 self->ext.card_idx = 0; /* FIXME */
309 strncpy(self->ext.id, ext_id, sizeof(self->ext.id) - 1);
310 strcpy(self->ext.driver, "OSS-Emulation");
311 strncpy(self->ext.name, ext_name, sizeof(self->ext.name) - 1);
312 strncpy(self->ext.longname, ext_name, sizeof(self->ext.longname) - 1);
313 strncpy(self->ext.mixername, ext_name, sizeof(self->ext.mixername) - 1);
314 self->ext.poll_fd = -1;
315 self->ext.callback = &roar_ext_callback;
316 self->ext.private_data = self;
317
318 if ( (error = snd_ctl_ext_create(&(self->ext), name, mode)) < 0 ) {
319  roar_disconnect(&(self->roar.con));
320  free(self);
321  return error;
322 }
323
324 *handlep = self->ext.handle;
325 return 0;
326}
327
328SND_CTL_PLUGIN_SYMBOL(roar);
Note: See TracBrowser for help on using the repository browser.