Add some screenshots for Sansa c200.
[Rockbox.git] / firmware / pcm_playback.c
blob1b16eb47db430c976b41167aa624bb6c6a2afe2c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "system.h"
20 #include "kernel.h"
21 #include "logf.h"
22 #include "audio.h"
23 #include "sound.h"
25 /**
26 * APIs implemented in the target-specific portion:
27 * Public -
28 * pcm_init
29 * pcm_get_bytes_waiting
30 * pcm_calculate_peaks
31 * Semi-private -
32 * pcm_play_dma_start
33 * pcm_play_dma_stop
34 * pcm_play_pause_pause
35 * pcm_play_pause_unpause
38 /** These items may be implemented target specifically or need to
39 be shared semi-privately **/
41 /* the registered callback function to ask for more mp3 data */
42 volatile pcm_more_callback_type pcm_callback_for_more = NULL;
43 volatile bool pcm_playing = false;
44 volatile bool pcm_paused = false;
46 void pcm_play_dma_start(const void *addr, size_t size);
47 void pcm_play_dma_stop(void);
48 void pcm_play_pause_pause(void);
49 void pcm_play_pause_unpause(void);
51 /** Functions that require targeted implementation **/
53 #if defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || (CONFIG_CPU == IMX31L)
54 /* Implemented in target/... */
55 #else
56 /* dummy functions for those not actually supporting all this yet */
57 void pcm_apply_settings(void)
60 /** **/
62 void pcm_mute(bool mute)
64 #if defined(HAVE_WM8975) || defined(HAVE_WM8758) \
65 || defined(HAVE_WM8731) || defined(HAVE_WM8721)
66 audiohw_mute(mute);
67 #endif
68 if (mute)
69 sleep(HZ/16);
71 #endif /* defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) */
73 #if defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || defined(CPU_PP)
74 /* Implemented in target/... */
75 #else
76 static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
78 unsigned short* p IBSS_ATTR;
79 size_t p_size IBSS_ATTR;
81 void pcm_play_dma_start(const void *addr, size_t size)
83 p = (unsigned short*)addr;
84 p_size = size;
86 pcm_playing = true;
89 void pcm_play_dma_stop(void)
91 pcm_playing = false;
92 if (!audio_status())
93 pcm_paused = false;
96 void pcm_play_pause_pause(void)
100 void pcm_play_pause_unpause(void)
104 void pcm_postinit(void)
106 audiohw_postinit();
109 void pcm_set_frequency(unsigned int frequency)
111 (void)frequency;
112 pcm_freq = HW_SAMPR_DEFAULT;
114 size_t pcm_get_bytes_waiting(void)
116 return p_size;
120 * This function goes directly into the DMA buffer to calculate the left and
121 * right peak values. To avoid missing peaks it tries to look forward two full
122 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
123 * the entire period will not be visible. To reduce CPU load it only looks at
124 * every third sample, and this can be reduced even further if needed (even
125 * every tenth sample would still be pretty accurate).
128 /* Check for a peak every PEAK_STRIDE samples */
129 #define PEAK_STRIDE 3
130 /* Up to 1/50th of a second of audio for peak calculation */
131 /* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
132 #define PEAK_SAMPLES (44100/50)
133 void pcm_calculate_peaks(int *left, int *right)
135 short *addr;
136 short *end;
138 size_t samples = p_size / 4;
139 addr = p;
141 if (samples > PEAK_SAMPLES)
142 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
143 else
144 samples -= MIN(PEAK_STRIDE - 1, samples);
146 end = &addr[samples * 2];
149 if (left && right) {
150 int left_peak = 0, right_peak = 0;
152 while (addr < end) {
153 int value;
154 if ((value = addr [0]) > left_peak)
155 left_peak = value;
156 else if (-value > left_peak)
157 left_peak = -value;
159 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
160 right_peak = value;
161 else if (-value > right_peak)
162 right_peak = -value;
164 addr = &addr[PEAK_STRIDE * 2];
167 *left = left_peak;
168 *right = right_peak;
170 else if (left || right) {
171 int peak_value = 0, value;
173 if (right)
174 addr += (PEAK_STRIDE | 1);
176 while (addr < end) {
177 if ((value = addr [0]) > peak_value)
178 peak_value = value;
179 else if (-value > peak_value)
180 peak_value = -value;
182 addr += PEAK_STRIDE * 2;
185 if (left)
186 *left = peak_value;
187 else
188 *right = peak_value;
191 #endif /* defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || defined(CPU_PP) */
193 /****************************************************************************
194 * Functions that do not require targeted implementation but only a targeted
195 * interface
198 /* Common code to pcm_play_data and pcm_play_pause
199 Returns true if DMA playback was started, else false. */
200 bool pcm_play_data_start(pcm_more_callback_type get_more,
201 unsigned char *start, size_t size)
203 if (!(start && size))
205 size = 0;
206 if (get_more)
207 get_more(&start, &size);
210 if (start && size)
212 pcm_play_dma_start(start, size);
213 return true;
216 return false;
219 void pcm_play_data(pcm_more_callback_type get_more,
220 unsigned char *start, size_t size)
222 pcm_callback_for_more = get_more;
224 if (pcm_play_data_start(get_more, start, size) && pcm_paused)
226 pcm_paused = false;
227 pcm_play_pause(false);
231 void pcm_play_pause(bool play)
233 bool needs_change = pcm_paused == play;
235 /* This needs to be done ahead of the rest to prevent infinite
236 recursion from pcm_play_data */
237 pcm_paused = !play;
239 if (pcm_playing && needs_change)
241 if (play)
243 if (pcm_get_bytes_waiting())
245 logf("unpause");
246 pcm_play_pause_unpause();
248 else
250 logf("unpause, no data waiting");
251 if (!pcm_play_data_start(pcm_callback_for_more, NULL, 0))
253 pcm_play_dma_stop();
254 logf("unpause attempted, no data");
258 else
260 logf("pause");
261 pcm_play_pause_pause();
263 } /* pcm_playing && needs_change */
266 void pcm_play_stop(void)
268 if (pcm_playing)
269 pcm_play_dma_stop();
272 bool pcm_is_playing(void)
274 return pcm_playing;
277 bool pcm_is_paused(void)
279 return pcm_paused;