2 * Remix ALSA Player: ALSA audio output
4 * Govindaraju SM <govi.sm@samsung.com>, October 2011
5 * Prince Kumar Dubey <prince.dubey@samsung.com>, October 2011
10 #include <remix/remix.h>
11 #include <alsa/asoundlib.h>
13 #ifdef HAVE_LIBSNDFILE
17 #define ALSA_PLAYER_BUFFERLEN 2048
19 typedef struct _Alsa_Player_Data Alsa_Player_Data;
20 typedef short PLAYER_PCM;
22 struct _Alsa_Player_Data
24 RemixPCM databuffer[ALSA_PLAYER_BUFFERLEN];
28 unsigned int frequency;
31 static int _log_dom = -1;
32 static int init_count = 0;
37 #define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
41 /* Optimisation dependencies: none */
42 static RemixBase *alsa_player_optimise(RemixEnv *env, RemixBase *base);
45 alsa_open(int channels, unsigned int samplerate, unsigned int *real_samplerate)
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;
55 alsa_buffer_frames = ALSA_PLAYER_BUFFERLEN;
56 alsa_period_size = ALSA_PLAYER_BUFFERLEN / 4;
58 if ((err = snd_pcm_open(&alsa_dev, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
60 WRN("cannot open alsa playback stream (%s)", snd_strerror(err));
63 snd_pcm_hw_params_alloca(&hw_params);
64 if ((err = snd_pcm_hw_params_any(alsa_dev, hw_params)) < 0)
66 WRN("cannot initialize snd hw params (%s)", snd_strerror(err));
69 if ((err = snd_pcm_hw_params_set_access(alsa_dev, hw_params,
70 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
72 WRN("cannot set interleaved access (%s)", snd_strerror(err));
75 if ((err = snd_pcm_hw_params_set_format(alsa_dev, hw_params,
76 SND_PCM_FORMAT_FLOAT)) < 0)
78 WRN("cannot set float sample format (%s)", snd_strerror(err));
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)
86 WRN("cannot set sample rate (%s)", snd_strerror(err));
90 if ((err = snd_pcm_hw_params_set_rate_near(alsa_dev, hw_params,
93 WRN("cannot set sample rate (%s)", snd_strerror(err));
97 if ((err = snd_pcm_hw_params_set_channels(alsa_dev, hw_params, channels)) < 0)
99 WRN("cannot set channel count (%s)", snd_strerror(err));
102 if ((err = snd_pcm_hw_params_set_buffer_size_near(alsa_dev, hw_params,
103 &alsa_buffer_frames)) < 0)
105 WRN("cannot set buffer size (%s)", snd_strerror(err));
108 if ((err = snd_pcm_hw_params_set_period_size_near(alsa_dev, hw_params,
109 &alsa_period_size, 0)) < 0)
111 WRN("cannot set period size (%s)", snd_strerror(err));
114 if ((err = snd_pcm_hw_params(alsa_dev, hw_params)) < 0)
116 WRN("cannot set parameters (%s)", snd_strerror(err));
119 if ((err = snd_pcm_hw_params_get_rate(hw_params, &samplerate_ret, 0)) < 0)
121 WRN("cannot get samplerate (%s)", snd_strerror(err));
124 if ((err = snd_pcm_prepare(alsa_dev)) < 0)
126 WRN("cannot prepare audio for use (%s)", snd_strerror(err));
129 if (real_samplerate) *real_samplerate = samplerate_ret;
132 if ((err < 0) && (alsa_dev != NULL))
134 snd_pcm_close(alsa_dev);
141 alsa_player_reset_device(RemixEnv *env, RemixBase *base)
143 Alsa_Player_Data *player_data = remix_base_get_instance_data(env, base);
144 unsigned int real_samplerate = 0;
146 if (player_data->alsa_dev)
148 snd_pcm_drain(player_data->alsa_dev);
149 snd_pcm_close(player_data->alsa_dev);
151 player_data->alsa_dev = alsa_open(player_data->channels,
152 player_data->frequency,
154 if (!player_data->alsa_dev)
156 remix_set_error(env, REMIX_ERROR_SYSTEM);
159 // printf("%i != %i\n", real_samplerate, player_data->frequency);
160 if (real_samplerate != player_data->frequency)
162 player_data->frequency = real_samplerate;
163 remix_set_samplerate(env, player_data->frequency);
169 alsa_player_init(RemixEnv *env, RemixBase *base, CDSet *parameters __UNUSED__)
172 Alsa_Player_Data *player_data = calloc(1, sizeof(Alsa_Player_Data));
176 remix_set_error(env, REMIX_ERROR_SYSTEM);
184 _log_dom = eina_log_domain_register("remix-alsa", EINA_COLOR_CYAN);
187 remix_base_set_instance_data(env, base, player_data);
188 channels = remix_get_channels(env);
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;
194 player_data->frequency = remix_get_samplerate(env);
195 alsa_player_reset_device(env, base);
196 base = alsa_player_optimise(env, base);
201 alsa_player_clone(RemixEnv *env, RemixBase *base __UNUSED__)
203 RemixBase *new_player = remix_base_new(env);
204 alsa_player_init(env, new_player, NULL);
209 alsa_player_destroy(RemixEnv *env, RemixBase *base)
211 Alsa_Player_Data *player_data = remix_base_get_instance_data(env, base);
213 if (player_data->alsa_dev)
215 snd_pcm_drain(player_data->alsa_dev);
216 snd_pcm_close(player_data->alsa_dev);
222 eina_log_domain_unregister(_log_dom);
230 alsa_player_ready(RemixEnv *env, RemixBase *base)
232 Alsa_Player_Data *player_data = remix_base_get_instance_data(env, base);
233 RemixCount nr_channels;
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))));
246 alsa_player_prepare(RemixEnv *env, RemixBase *base)
248 alsa_player_reset_device(env, base);
253 alsa_player_playbuffer(RemixEnv *env __UNUSED__, Alsa_Player_Data *player, RemixPCM *data, RemixCount count)
257 static int total = 0;
258 static SNDFILE *sfile = NULL;
259 static SF_INFO sfinfo;
264 sfinfo.samplerate = player->frequency;
266 sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
269 sfile = sf_open("out.wav", SFM_WRITE, &sfinfo);
274 sf_writef_float(sfile, data, count);
279 return snd_pcm_writei(player->alsa_dev, data, count);
283 alsa_player_chunk(RemixEnv *env, RemixChunk *chunk, RemixCount offset, RemixCount count, int channelname __UNUSED__, void *data)
285 Alsa_Player_Data *player = data;
286 RemixCount remaining = count, written = 0, n, playcount;
289 while (remaining > 0)
291 playcount = MIN(remaining, ALSA_PLAYER_BUFFERLEN);
293 d = &chunk->data[offset];
294 n = alsa_player_playbuffer(env, player, d, playcount);
296 if (n == -1) return -1;
297 else n /= sizeof(PLAYER_PCM);
307 alsa_player_process(RemixEnv *env, RemixBase *base, RemixCount count, RemixStream *input, RemixStream *output __UNUSED__)
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;
313 if ((nr_channels == 1) && (player_data->stereo == 0))
315 return remix_stream_chunkfuncify(env, input, count,
316 alsa_player_chunk, player_data);
318 else if ((nr_channels == 2) && (player_data->stereo == 1))
320 while (remaining > 0)
322 n = MIN(remaining, ALSA_PLAYER_BUFFERLEN / 2);
323 n = remix_stream_interleave_2(env, input,
326 player_data->databuffer, n);
327 nn = alsa_player_playbuffer(env, player_data,
328 player_data->databuffer, n);
334 WRN("[alsa_player_process] unsupported stream/output channel "
335 "combination %ld / %d", nr_channels, player_data->stereo ? 2 : 1);
340 alsa_player_length(RemixEnv *env __UNUSED__, RemixBase *base __UNUSED__)
342 return REMIX_COUNT_INFINITE;
346 alsa_player_seek(RemixEnv *env __UNUSED__, RemixBase *base __UNUSED__, RemixCount count __UNUSED__)
352 alsa_player_flush(RemixEnv *env, RemixBase *base)
354 alsa_player_reset_device(env, base);
358 static struct _RemixMethods _alsa_player_methods =
371 alsa_player_optimise(RemixEnv *env, RemixBase *base)
373 remix_base_set_methods(env, base, &_alsa_player_methods);
377 static struct _RemixMetaText alsa_player_metatext =
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"),
387 static struct _RemixPlugin alsa_player_plugin =
389 &alsa_player_metatext,
391 CD_EMPTY_SET, /* init scheme */
393 CD_EMPTY_SET, /* process scheme */
395 NULL, /* plugin data */
400 remix_load(RemixEnv *env)
402 CDList *plugins = cd_list_new(env);
403 plugins = cd_list_prepend(env, plugins, CD_POINTER(&alsa_player_plugin));