aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/edje/src/modules/alsa_snd_player/alsa_snd_player.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/edje/src/modules/alsa_snd_player/alsa_snd_player.c')
-rw-r--r--libraries/edje/src/modules/alsa_snd_player/alsa_snd_player.c405
1 files changed, 405 insertions, 0 deletions
diff --git a/libraries/edje/src/modules/alsa_snd_player/alsa_snd_player.c b/libraries/edje/src/modules/alsa_snd_player/alsa_snd_player.c
new file mode 100644
index 0000000..f397255
--- /dev/null
+++ b/libraries/edje/src/modules/alsa_snd_player/alsa_snd_player.c
@@ -0,0 +1,405 @@
1/*
2 * Remix ALSA Player: ALSA audio output
3 *
4 * Govindaraju SM <govi.sm@samsung.com>, October 2011
5 * Prince Kumar Dubey <prince.dubey@samsung.com>, October 2011
6 */
7
8#include "config.h"
9#include <stdio.h>
10#include <remix/remix.h>
11#include <alsa/asoundlib.h>
12#include <Eina.h>
13#ifdef HAVE_LIBSNDFILE
14#include <sndfile.h>
15#endif
16
17#define ALSA_PLAYER_BUFFERLEN 2048
18
19typedef struct _Alsa_Player_Data Alsa_Player_Data;
20typedef short PLAYER_PCM;
21
22struct _Alsa_Player_Data
23{
24 RemixPCM databuffer[ALSA_PLAYER_BUFFERLEN];
25 snd_pcm_t *alsa_dev;
26 unsigned int stereo;
27 unsigned channels;
28 unsigned int frequency;
29};
30
31static int _log_dom = -1;
32static int init_count = 0;
33
34#ifdef WRN
35# undef WRN
36#endif
37#define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
38
39//#define MIXDBG 1
40
41/* Optimisation dependencies: none */
42static RemixBase *alsa_player_optimise(RemixEnv *env, RemixBase *base);
43
44static snd_pcm_t *
45alsa_open(int channels, unsigned int samplerate, unsigned int *real_samplerate)
46{
47 const char *device = "default";
48 snd_pcm_t *alsa_dev = NULL;
49 snd_pcm_hw_params_t *hw_params;
50 snd_pcm_uframes_t alsa_buffer_frames;
51 snd_pcm_uframes_t alsa_period_size;
52 unsigned int samplerate_ret = 0;
53 int err;
54
55 alsa_buffer_frames = ALSA_PLAYER_BUFFERLEN;
56 alsa_period_size = ALSA_PLAYER_BUFFERLEN / 4;
57
58 if ((err = snd_pcm_open(&alsa_dev, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
59 {
60 WRN("cannot open alsa playback stream (%s)", snd_strerror(err));
61 goto catch_error;
62 }
63 snd_pcm_hw_params_alloca(&hw_params);
64 if ((err = snd_pcm_hw_params_any(alsa_dev, hw_params)) < 0)
65 {
66 WRN("cannot initialize snd hw params (%s)", snd_strerror(err));
67 goto catch_error;
68 }
69 if ((err = snd_pcm_hw_params_set_access(alsa_dev, hw_params,
70 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
71 {
72 WRN("cannot set interleaved access (%s)", snd_strerror(err));
73 goto catch_error;
74 }
75 if ((err = snd_pcm_hw_params_set_format(alsa_dev, hw_params,
76 SND_PCM_FORMAT_FLOAT)) < 0)
77 {
78 WRN("cannot set float sample format (%s)", snd_strerror(err));
79 goto catch_error;
80 }
81#ifdef MIXDBG // testing/debugging by making output samplerate be 48khz
82 samplerate_ret = 48000;
83 if ((err = snd_pcm_hw_params_set_rate_near(alsa_dev, hw_params,
84 &samplerate_ret, 0)) < 0)
85 {
86 WRN("cannot set sample rate (%s)", snd_strerror(err));
87 goto catch_error;
88 }
89#else
90 if ((err = snd_pcm_hw_params_set_rate_near(alsa_dev, hw_params,
91 &samplerate, 0)) < 0)
92 {
93 WRN("cannot set sample rate (%s)", snd_strerror(err));
94 goto catch_error;
95 }
96#endif
97 if ((err = snd_pcm_hw_params_set_channels(alsa_dev, hw_params, channels)) < 0)
98 {
99 WRN("cannot set channel count (%s)", snd_strerror(err));
100 goto catch_error;
101 }
102 if ((err = snd_pcm_hw_params_set_buffer_size_near(alsa_dev, hw_params,
103 &alsa_buffer_frames)) < 0)
104 {
105 WRN("cannot set buffer size (%s)", snd_strerror(err));
106 goto catch_error;
107 }
108 if ((err = snd_pcm_hw_params_set_period_size_near(alsa_dev, hw_params,
109 &alsa_period_size, 0)) < 0)
110 {
111 WRN("cannot set period size (%s)", snd_strerror(err));
112 goto catch_error;
113 }
114 if ((err = snd_pcm_hw_params(alsa_dev, hw_params)) < 0)
115 {
116 WRN("cannot set parameters (%s)", snd_strerror(err));
117 goto catch_error;
118 }
119 if ((err = snd_pcm_hw_params_get_rate(hw_params, &samplerate_ret, 0)) < 0)
120 {
121 WRN("cannot get samplerate (%s)", snd_strerror(err));
122 goto catch_error;
123 }
124 if ((err = snd_pcm_prepare(alsa_dev)) < 0)
125 {
126 WRN("cannot prepare audio for use (%s)", snd_strerror(err));
127 goto catch_error;
128 }
129 if (real_samplerate) *real_samplerate = samplerate_ret;
130
131catch_error:
132 if ((err < 0) && (alsa_dev != NULL))
133 {
134 snd_pcm_close(alsa_dev);
135 return NULL;
136 }
137 return alsa_dev;
138}
139
140static RemixBase *
141alsa_player_reset_device(RemixEnv *env, RemixBase *base)
142{
143 Alsa_Player_Data *player_data = remix_base_get_instance_data(env, base);
144 unsigned int real_samplerate = 0;
145
146 if (player_data->alsa_dev)
147 {
148 snd_pcm_drain(player_data->alsa_dev);
149 snd_pcm_close(player_data->alsa_dev);
150 }
151 player_data->alsa_dev = alsa_open(player_data->channels,
152 player_data->frequency,
153 &real_samplerate);
154 if (!player_data->alsa_dev)
155 {
156 remix_set_error(env, REMIX_ERROR_SYSTEM);
157 return RemixNone;
158 }
159// printf("%i != %i\n", real_samplerate, player_data->frequency);
160 if (real_samplerate != player_data->frequency)
161 {
162 player_data->frequency = real_samplerate;
163 remix_set_samplerate(env, player_data->frequency);
164 }
165 return base;
166}
167
168static RemixBase *
169alsa_player_init(RemixEnv *env, RemixBase *base, CDSet *parameters __UNUSED__)
170{
171 CDSet *channels;
172 Alsa_Player_Data *player_data = calloc(1, sizeof(Alsa_Player_Data));
173
174 if (!player_data)
175 {
176 remix_set_error(env, REMIX_ERROR_SYSTEM);
177 return RemixNone;
178 }
179
180 init_count++;
181 if (init_count == 1)
182 {
183 eina_init();
184 _log_dom = eina_log_domain_register("remix-alsa", EINA_COLOR_CYAN);
185 }
186
187 remix_base_set_instance_data(env, base, player_data);
188 channels = remix_get_channels(env);
189
190 player_data->channels = cd_set_size(env, channels);
191 if (player_data->channels == 1) player_data->stereo = 0;
192 else if (player_data->channels == 2) player_data->stereo = 1;
193
194 player_data->frequency = remix_get_samplerate(env);
195 alsa_player_reset_device(env, base);
196 base = alsa_player_optimise(env, base);
197 return base;
198}
199
200static RemixBase *
201alsa_player_clone(RemixEnv *env, RemixBase *base __UNUSED__)
202{
203 RemixBase *new_player = remix_base_new(env);
204 alsa_player_init(env, new_player, NULL);
205 return new_player;
206}
207
208static int
209alsa_player_destroy(RemixEnv *env, RemixBase *base)
210{
211 Alsa_Player_Data *player_data = remix_base_get_instance_data(env, base);
212
213 if (player_data->alsa_dev)
214 {
215 snd_pcm_drain(player_data->alsa_dev);
216 snd_pcm_close(player_data->alsa_dev);
217 }
218 free(player_data);
219 init_count--;
220 if (init_count == 0)
221 {
222 eina_log_domain_unregister(_log_dom);
223 _log_dom = -1;
224 eina_shutdown();
225 }
226 return 0;
227}
228
229static int
230alsa_player_ready(RemixEnv *env, RemixBase *base)
231{
232 Alsa_Player_Data *player_data = remix_base_get_instance_data(env, base);
233 RemixCount nr_channels;
234 CDSet *channels;
235 int samplerate;
236
237 channels = remix_get_channels(env);
238 samplerate = (int)remix_get_samplerate(env);
239 nr_channels = cd_set_size(env, channels);
240 return ((samplerate == (int)player_data->frequency) &&
241 (((nr_channels == 1) && (player_data->stereo == 0)) ||
242 ((nr_channels > 1) && (player_data->stereo == 1))));
243}
244
245static RemixBase *
246alsa_player_prepare(RemixEnv *env, RemixBase *base)
247{
248 alsa_player_reset_device(env, base);
249 return base;
250}
251
252static RemixCount
253alsa_player_playbuffer(RemixEnv *env __UNUSED__, Alsa_Player_Data *player, RemixPCM *data, RemixCount count)
254{
255#ifdef MIXDBG
256 {
257 static int total = 0;
258 static SNDFILE *sfile = NULL;
259 static SF_INFO sfinfo;
260
261 if (total == 0)
262 {
263 sfinfo.frames = 0;
264 sfinfo.samplerate = player->frequency;
265 sfinfo.channels = 2;
266 sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
267 sfinfo.sections = 0;
268 sfinfo.seekable = 0;
269 sfile = sf_open("out.wav", SFM_WRITE, &sfinfo);
270 }
271
272 if (sfile)
273 {
274 sf_writef_float(sfile, data, count);
275 total += count;
276 }
277 }
278#endif
279 return snd_pcm_writei(player->alsa_dev, data, count);
280}
281
282static RemixCount
283alsa_player_chunk(RemixEnv *env, RemixChunk *chunk, RemixCount offset, RemixCount count, int channelname __UNUSED__, void *data)
284{
285 Alsa_Player_Data *player = data;
286 RemixCount remaining = count, written = 0, n, playcount;
287 RemixPCM *d;
288
289 while (remaining > 0)
290 {
291 playcount = MIN(remaining, ALSA_PLAYER_BUFFERLEN);
292
293 d = &chunk->data[offset];
294 n = alsa_player_playbuffer(env, player, d, playcount);
295
296 if (n == -1) return -1;
297 else n /= sizeof(PLAYER_PCM);
298
299 offset += n;
300 written += n;
301 remaining -= n;
302 }
303 return written;
304}
305
306static RemixCount
307alsa_player_process(RemixEnv *env, RemixBase *base, RemixCount count, RemixStream *input, RemixStream *output __UNUSED__)
308{
309 Alsa_Player_Data *player_data = remix_base_get_instance_data(env, base);
310 RemixCount nr_channels = remix_stream_nr_channels(env, input);
311 RemixCount remaining = count, processed = 0, n, nn;
312
313 if ((nr_channels == 1) && (player_data->stereo == 0))
314 { /*MONO*/
315 return remix_stream_chunkfuncify(env, input, count,
316 alsa_player_chunk, player_data);
317 }
318 else if ((nr_channels == 2) && (player_data->stereo == 1))
319 { /*STEREO*/
320 while (remaining > 0)
321 {
322 n = MIN(remaining, ALSA_PLAYER_BUFFERLEN / 2);
323 n = remix_stream_interleave_2(env, input,
324 REMIX_CHANNEL_LEFT,
325 REMIX_CHANNEL_RIGHT,
326 player_data->databuffer, n);
327 nn = alsa_player_playbuffer(env, player_data,
328 player_data->databuffer, n);
329 processed += n;
330 remaining -= n;
331 }
332 return processed;
333 }
334 WRN("[alsa_player_process] unsupported stream/output channel "
335 "combination %ld / %d", nr_channels, player_data->stereo ? 2 : 1);
336 return -1;
337}
338
339static RemixCount
340alsa_player_length(RemixEnv *env __UNUSED__, RemixBase *base __UNUSED__)
341{
342 return REMIX_COUNT_INFINITE;
343}
344
345static RemixCount
346alsa_player_seek(RemixEnv *env __UNUSED__, RemixBase *base __UNUSED__, RemixCount count __UNUSED__)
347{
348 return count;
349}
350
351static int
352alsa_player_flush(RemixEnv *env, RemixBase *base)
353{
354 alsa_player_reset_device(env, base);
355 return 0;
356}
357
358static struct _RemixMethods _alsa_player_methods =
359{
360 alsa_player_clone,
361 alsa_player_destroy,
362 alsa_player_ready,
363 alsa_player_prepare,
364 alsa_player_process,
365 alsa_player_length,
366 alsa_player_seek,
367 alsa_player_flush,
368};
369
370static RemixBase *
371alsa_player_optimise(RemixEnv *env, RemixBase *base)
372{
373 remix_base_set_methods(env, base, &_alsa_player_methods);
374 return base;
375}
376
377static struct _RemixMetaText alsa_player_metatext =
378{
379 "alsa_snd_player",
380 "ALSA sound player for Remix",
381 "Output the audio stream into ALSA Driver",
382 "Copyright (C) 2011, Samsung Electronics Co., Ltd.",
383 "http://www.samsung.com",
384 REMIX_ONE_AUTHOR("Govindaraju SM", "prince.dubey@samsung.com"),
385};
386
387static struct _RemixPlugin alsa_player_plugin =
388{
389 &alsa_player_metatext,
390 REMIX_FLAGS_NONE,
391 CD_EMPTY_SET, /* init scheme */
392 alsa_player_init,
393 CD_EMPTY_SET, /* process scheme */
394 NULL, /* suggests */
395 NULL, /* plugin data */
396 NULL /* destroy */
397};
398
399EAPI CDList *
400remix_load(RemixEnv *env)
401{
402 CDList *plugins = cd_list_new(env);
403 plugins = cd_list_prepend(env, plugins, CD_POINTER(&alsa_player_plugin));
404 return plugins;
405}