diff options
Diffstat (limited to 'firmware/target/hosted/android/dx50/pcm-dx50.c')
-rw-r--r-- | firmware/target/hosted/android/dx50/pcm-dx50.c | 364 |
1 files changed, 0 insertions, 364 deletions
diff --git a/firmware/target/hosted/android/dx50/pcm-dx50.c b/firmware/target/hosted/android/dx50/pcm-dx50.c deleted file mode 100644 index e7695873a0..0000000000 --- a/firmware/target/hosted/android/dx50/pcm-dx50.c +++ /dev/null | |||
@@ -1,364 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Thomas Martitz | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | |||
23 | /* | ||
24 | * Based, but heavily modified, on the example given at | ||
25 | * http://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html | ||
26 | * | ||
27 | * This driver uses the so-called unsafe async callback method and hardcoded device | ||
28 | * names. It fails when the audio device is busy by other apps. | ||
29 | * | ||
30 | * To make the async callback safer, an alternative stack is installed, since | ||
31 | * it's run from a signal hanlder (which otherwise uses the user stack). If | ||
32 | * tick tasks are run from a signal handler too, please install | ||
33 | * an alternative stack for it too. | ||
34 | * | ||
35 | * TODO: Rewrite this to do it properly with multithreading | ||
36 | * | ||
37 | * Alternatively, a version using polling in a tick task is provided. While | ||
38 | * supposedly safer, it appears to use more CPU (however I didn't measure it | ||
39 | * accurately, only looked at htop). At least, in this mode the "default" | ||
40 | * device works which doesnt break with other apps running. | ||
41 | */ | ||
42 | |||
43 | |||
44 | #include "autoconf.h" | ||
45 | |||
46 | #include <stdlib.h> | ||
47 | #include <stdbool.h> | ||
48 | #include <stdio.h> | ||
49 | #include <errno.h> | ||
50 | #include "tinyalsa/asoundlib.h" | ||
51 | #include "tinyalsa/asound.h" | ||
52 | #include "system.h" | ||
53 | #include "debug.h" | ||
54 | #include "kernel.h" | ||
55 | |||
56 | #include "pcm.h" | ||
57 | #include "pcm-internal.h" | ||
58 | #include "pcm_mixer.h" | ||
59 | #include "pcm_sampr.h" | ||
60 | #include "audiohw.h" | ||
61 | |||
62 | #include <pthread.h> | ||
63 | #include <signal.h> | ||
64 | |||
65 | static const snd_pcm_format_t format = PCM_FORMAT_S16_LE; /* sample format */ | ||
66 | static const int channels = 2; /* count of channels */ | ||
67 | static unsigned int rate = 44100; /* stream rate */ | ||
68 | |||
69 | typedef struct pcm snd_pcm_t; | ||
70 | static snd_pcm_t *handle; | ||
71 | struct pcm_config config; | ||
72 | |||
73 | static snd_pcm_sframes_t period_size = 512; /* if set to >= 1024, all timers become even slower */ | ||
74 | static char *frames; | ||
75 | |||
76 | static const void *pcm_data = 0; | ||
77 | static size_t pcm_size = 0; | ||
78 | |||
79 | static int recursion; | ||
80 | |||
81 | static int set_hwparams(snd_pcm_t *handle) | ||
82 | { | ||
83 | int err; | ||
84 | if (!frames) | ||
85 | frames = malloc(pcm_frames_to_bytes(handle, pcm_get_buffer_size(handle))); | ||
86 | err = 0; /* success */ | ||
87 | return err; | ||
88 | } | ||
89 | |||
90 | |||
91 | /* copy pcm samples to a spare buffer, suitable for snd_pcm_writei() */ | ||
92 | static bool fill_frames(void) | ||
93 | { | ||
94 | size_t copy_n, frames_left = period_size; | ||
95 | bool new_buffer = false; | ||
96 | |||
97 | while (frames_left > 0) | ||
98 | { | ||
99 | if (!pcm_size) | ||
100 | { | ||
101 | new_buffer = true; | ||
102 | if (!pcm_play_dma_complete_callback(PCM_DMAST_OK, &pcm_data, | ||
103 | &pcm_size)) | ||
104 | { | ||
105 | return false; | ||
106 | } | ||
107 | } | ||
108 | copy_n = MIN((size_t)pcm_size, pcm_frames_to_bytes(handle, frames_left)); | ||
109 | memcpy(&frames[pcm_frames_to_bytes(handle, period_size-frames_left)], pcm_data, copy_n); | ||
110 | |||
111 | pcm_data += copy_n; | ||
112 | pcm_size -= copy_n; | ||
113 | frames_left -= pcm_bytes_to_frames(handle, copy_n); | ||
114 | |||
115 | if (new_buffer) | ||
116 | { | ||
117 | new_buffer = false; | ||
118 | pcm_play_dma_status_callback(PCM_DMAST_STARTED); | ||
119 | } | ||
120 | } | ||
121 | return true; | ||
122 | } | ||
123 | |||
124 | |||
125 | static void pcm_tick(void) | ||
126 | { | ||
127 | if (fill_frames()) | ||
128 | { | ||
129 | if (pcm_write(handle, frames, pcm_frames_to_bytes(handle, period_size))) { | ||
130 | printf("Error playing sample\n"); | ||
131 | return;//break; | ||
132 | } | ||
133 | |||
134 | } | ||
135 | else | ||
136 | { | ||
137 | DEBUGF("%s: No Data.\n", __func__); | ||
138 | return;//break; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | static int async_rw(snd_pcm_t *handle) | ||
143 | { | ||
144 | int err; | ||
145 | snd_pcm_sframes_t sample_size; | ||
146 | char *samples; | ||
147 | |||
148 | /* fill buffer with silence to initiate playback without noisy click */ | ||
149 | sample_size = pcm_frames_to_bytes(handle, pcm_get_buffer_size(handle)); | ||
150 | samples = malloc(sample_size); | ||
151 | |||
152 | memset(samples, 0, sample_size); | ||
153 | |||
154 | err = pcm_write(handle, samples, sample_size); | ||
155 | free(samples); | ||
156 | |||
157 | if (err != 0) | ||
158 | { | ||
159 | DEBUGF("Initial write error: %d\n", err); | ||
160 | return err; | ||
161 | } | ||
162 | if (pcm_state(handle) == PCM_STATE_PREPARED) | ||
163 | { | ||
164 | err = pcm_start(handle); | ||
165 | if (err < 0) | ||
166 | { | ||
167 | DEBUGF("Start error: %d\n", err); | ||
168 | return err; | ||
169 | } | ||
170 | } | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | void cleanup(void) | ||
175 | { | ||
176 | free(frames); | ||
177 | frames = NULL; | ||
178 | pcm_close(handle); | ||
179 | } | ||
180 | |||
181 | void pcm_play_dma_init(void) | ||
182 | { | ||
183 | config.channels = channels; | ||
184 | config.rate = rate; | ||
185 | config.period_size = period_size; | ||
186 | config.period_count = 4; | ||
187 | config.format = format; | ||
188 | config.start_threshold = 0; | ||
189 | config.stop_threshold = 0; | ||
190 | config.silence_threshold = 0; | ||
191 | |||
192 | |||
193 | handle = pcm_open(0, 0, PCM_OUT, &config); | ||
194 | if (!handle || !pcm_is_ready(handle)) { | ||
195 | printf("Unable to open PCM device: %s\n", pcm_get_error(handle)); | ||
196 | return; | ||
197 | } | ||
198 | |||
199 | pcm_dma_apply_settings(); | ||
200 | |||
201 | tick_add_task(pcm_tick); | ||
202 | |||
203 | atexit(cleanup); | ||
204 | return; | ||
205 | } | ||
206 | |||
207 | |||
208 | void pcm_play_lock(void) | ||
209 | { | ||
210 | if (recursion++ == 0) | ||
211 | tick_remove_task(pcm_tick); | ||
212 | } | ||
213 | |||
214 | void pcm_play_unlock(void) | ||
215 | { | ||
216 | if (--recursion == 0) | ||
217 | tick_add_task(pcm_tick); | ||
218 | } | ||
219 | |||
220 | static void pcm_dma_apply_settings_nolock(void) | ||
221 | { | ||
222 | set_hwparams(handle); | ||
223 | } | ||
224 | |||
225 | void pcm_dma_apply_settings(void) | ||
226 | { | ||
227 | pcm_play_lock(); | ||
228 | pcm_dma_apply_settings_nolock(); | ||
229 | pcm_play_unlock(); | ||
230 | } | ||
231 | |||
232 | |||
233 | void pcm_play_dma_pause(bool pause) | ||
234 | { | ||
235 | (void)pause; | ||
236 | } | ||
237 | |||
238 | |||
239 | void pcm_play_dma_stop(void) | ||
240 | { | ||
241 | pcm_stop(handle); | ||
242 | } | ||
243 | |||
244 | void pcm_play_dma_start(const void *addr, size_t size) | ||
245 | { | ||
246 | #if defined(DX50) || defined(DX90) | ||
247 | /* headphone output relay: if this is done at startup already, a loud click is audible on headphones. Here, some time later, | ||
248 | the output caps are charged a bit and the click is much softer */ | ||
249 | system("/system/bin/muteopen"); | ||
250 | #endif | ||
251 | pcm_dma_apply_settings_nolock(); | ||
252 | |||
253 | pcm_data = addr; | ||
254 | pcm_size = size; | ||
255 | |||
256 | while (1) | ||
257 | { | ||
258 | snd_pcm_state_t state = pcm_state(handle); | ||
259 | switch (state) | ||
260 | { | ||
261 | case PCM_STATE_RUNNING: | ||
262 | return; | ||
263 | case PCM_STATE_XRUN: | ||
264 | { | ||
265 | printf("No handler for STATE_XRUN!\n"); | ||
266 | continue; | ||
267 | } | ||
268 | case PCM_STATE_PREPARED: | ||
269 | { /* prepared state, we need to fill the buffer with silence before | ||
270 | * starting */ | ||
271 | int err = async_rw(handle); | ||
272 | if (err < 0) | ||
273 | printf("Start error: %d\n", err); | ||
274 | return; | ||
275 | } | ||
276 | case PCM_STATE_PAUSED: | ||
277 | { /* paused, simply resume */ | ||
278 | pcm_play_dma_pause(0); | ||
279 | return; | ||
280 | } | ||
281 | case PCM_STATE_DRAINING: | ||
282 | /* run until drained */ | ||
283 | continue; | ||
284 | default: | ||
285 | DEBUGF("Unhandled state: %d\n", state); | ||
286 | return; | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | size_t pcm_get_bytes_waiting(void) | ||
292 | { | ||
293 | return pcm_size; | ||
294 | } | ||
295 | |||
296 | const void * pcm_play_dma_get_peak_buffer(int *count) | ||
297 | { | ||
298 | uintptr_t addr = (uintptr_t)pcm_data; | ||
299 | *count = pcm_size / 4; | ||
300 | return (void *)((addr + 3) & ~3); | ||
301 | } | ||
302 | |||
303 | void pcm_play_dma_postinit(void) | ||
304 | { | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | void pcm_set_mixer_volume(int volume) | ||
309 | { | ||
310 | #if defined(DX50) || defined(DX90) | ||
311 | /* -990 to 0 -> 0 to 255 */ | ||
312 | int val = (volume+990)*255/990; | ||
313 | #if defined(DX50) | ||
314 | FILE *f = fopen("/dev/codec_volume", "w"); | ||
315 | #else /* DX90 */ | ||
316 | FILE *f = fopen("/sys/class/codec/es9018_volume", "w"); | ||
317 | #endif /* DX50 */ | ||
318 | fprintf(f, "%d", val); | ||
319 | fclose(f); | ||
320 | #else | ||
321 | (void)volume; | ||
322 | #endif /* DX50 || DX90 */ | ||
323 | } | ||
324 | |||
325 | #ifdef HAVE_RECORDING | ||
326 | void pcm_rec_lock(void) | ||
327 | { | ||
328 | } | ||
329 | |||
330 | void pcm_rec_unlock(void) | ||
331 | { | ||
332 | } | ||
333 | |||
334 | void pcm_rec_dma_init(void) | ||
335 | { | ||
336 | } | ||
337 | |||
338 | void pcm_rec_dma_close(void) | ||
339 | { | ||
340 | } | ||
341 | |||
342 | void pcm_rec_dma_start(void *start, size_t size) | ||
343 | { | ||
344 | (void)start; | ||
345 | (void)size; | ||
346 | } | ||
347 | |||
348 | void pcm_rec_dma_stop(void) | ||
349 | { | ||
350 | } | ||
351 | |||
352 | const void * pcm_rec_dma_get_peak_buffer(void) | ||
353 | { | ||
354 | return NULL; | ||
355 | } | ||
356 | |||
357 | void audiohw_set_recvol(int left, int right, int type) | ||
358 | { | ||
359 | (void)left; | ||
360 | (void)right; | ||
361 | (void)type; | ||
362 | } | ||
363 | |||
364 | #endif /* HAVE_RECORDING */ | ||