Merge branch 't/utf8'
[paraslash.git] / oss_mix.c
1 /*
2 * Copyright (C) 1998-2013 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file oss_mix.c The OSS mixer plugin. */
8
9 #include <sys/soundcard.h>
10 #include <sys/ioctl.h>
11 #include <regex.h>
12
13 #include "para.h"
14 #include "error.h"
15 #include "mix.h"
16 #include "fd.h"
17 #include "string.h"
18
19 struct oss_mixer_channel {
20 const char *name;
21 int id;
22 };
23
24 static const struct oss_mixer_channel channels[] = {
25 {.name = "volume", .id = SOUND_MIXER_VOLUME},
26 {.name = "bass", .id = SOUND_MIXER_BASS},
27 {.name = "treble", .id = SOUND_MIXER_TREBLE},
28 {.name = "synth", .id = SOUND_MIXER_SYNTH},
29 {.name = "pcm", .id = SOUND_MIXER_PCM},
30 {.name = "speaker", .id = SOUND_MIXER_SPEAKER},
31 {.name = "line", .id = SOUND_MIXER_LINE},
32 {.name = "mic", .id = SOUND_MIXER_MIC},
33 {.name = "cd", .id = SOUND_MIXER_CD},
34 {.name = "imix", .id = SOUND_MIXER_IMIX},
35 {.name = "altpcm", .id = SOUND_MIXER_ALTPCM},
36 {.name = "reclev", .id = SOUND_MIXER_RECLEV},
37 {.name = "igain", .id = SOUND_MIXER_IGAIN},
38 {.name = "ogain", .id = SOUND_MIXER_OGAIN},
39 };
40
41 /** Iterate over all defined mixer channels. */
42 #define FOR_EACH_CHANNEL(i) for ((i) = 0; (i) < ARRAY_SIZE(channels); (i)++)
43
44 struct mixer_handle {
45 int fd;
46 int id;
47 };
48
49 static int oss_mix_open(const char *dev, struct mixer_handle **handle)
50 {
51 int ret;
52 struct mixer_handle *h;
53
54 *handle = NULL;
55 if (!dev)
56 dev = "/dev/mixer";
57 PARA_INFO_LOG("opening %s\n", dev);
58 ret = para_open(dev, O_RDWR, 42);
59 if (ret < 0)
60 return ret;
61 h = para_malloc(sizeof(*h));
62 h->fd = ret;
63 *handle = h;
64 return 1;
65 }
66
67 static char *oss_mix_get_channels(__a_unused struct mixer_handle *handle)
68 {
69 int i;
70 char *list = NULL;
71
72 FOR_EACH_CHANNEL(i) {
73 const char *name = channels[i].name;
74 char *tmp = list;
75 list = make_message("%s%s%s",
76 list? list : "",
77 list? ", " : "",
78 name);
79 free(tmp);
80 }
81 return list;
82 }
83
84 static int oss_mix_set_channel(struct mixer_handle *handle,
85 const char *mixer_channel)
86 {
87 int i;
88
89 if (!mixer_channel) {
90 handle->id = SOUND_MIXER_VOLUME; /* default */
91 return 0;
92 }
93 FOR_EACH_CHANNEL(i) {
94 if (strcasecmp(channels[i].name, mixer_channel))
95 continue;
96 handle->id = i;
97 return 1;
98 }
99 return -E_OSS_MIXER_CHANNEL;
100 }
101
102 static int oss_mix_get(struct mixer_handle *handle)
103 {
104 int val, fd = handle->fd, id = handle->id;
105
106 if (ioctl(fd, MIXER_READ(id), &val) < 0)
107 return -ERRNO_TO_PARA_ERROR(errno);
108 /* take the mean value of left and right */
109 return (val % 256 + (val >> 8)) / 2;
110 }
111
112 static int oss_mix_set(struct mixer_handle *handle, int val)
113 {
114 int fd = handle->fd, id = handle->id, tmp = (val << 8) + val;
115
116 if (ioctl(fd, MIXER_WRITE(id), &tmp) < 0)
117 return -ERRNO_TO_PARA_ERROR(errno);
118 return 1;
119 }
120
121 static void oss_mix_close(struct mixer_handle **handle)
122 {
123 struct mixer_handle *h;
124
125 if (!handle)
126 return;
127 h = *handle;
128 if (h) {
129 if (h->fd >= 0)
130 close(h->fd);
131 free(h);
132 }
133 *handle = NULL;
134 }
135
136 /**
137 * The init function of the OSS mixer.
138 *
139 * \param self The structure to initialize.
140 *
141 * \sa struct \ref mixer, \ref alsa_mix_init().
142 */
143 void oss_mix_init(struct mixer *self)
144 {
145 self->open = oss_mix_open;
146 self->get_channels = oss_mix_get_channels;
147 self->set_channel = oss_mix_set_channel;
148 self->get = oss_mix_get;
149 self->set = oss_mix_set;
150 self->close = oss_mix_close;
151 }