Fix typo.
[Rockbox.git] / firmware / pcm_playback.c
blobd317b3708e5d02fa0b92a69ad3f3c9c0bdff3385
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 || (CONFIG_CPU == DM320)
55 /* Implemented in target/... */
56 #else
57 /* dummy functions for those not actually supporting all this yet */
58 void pcm_apply_settings(void)
61 /** **/
63 void pcm_mute(bool mute)
65 #if defined(HAVE_WM8975) || defined(HAVE_WM8758) \
66 || defined(HAVE_WM8731) || defined(HAVE_WM8721)
67 audiohw_mute(mute);
68 #endif
69 if (mute)
70 sleep(HZ/16);
72 #endif /* defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) */
74 #if defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || defined(CPU_PP) \
75 || (CONFIG_CPU == IMX31L) || (CONFIG_CPU == DM320)
76 /* Implemented in target/... */
77 #else
78 static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
80 unsigned short* p IBSS_ATTR;
81 size_t p_size IBSS_ATTR;
83 void pcm_play_dma_start(const void *addr, size_t size)
85 p = (unsigned short*)addr;
86 p_size = size;
88 pcm_playing = true;
91 void pcm_play_dma_stop(void)
93 pcm_playing = false;
94 if (!audio_status())
95 pcm_paused = false;
98 void pcm_play_pause_pause(void)
102 void pcm_play_pause_unpause(void)
106 void pcm_postinit(void)
108 audiohw_postinit();
111 void pcm_set_frequency(unsigned int frequency)
113 (void)frequency;
114 pcm_freq = HW_SAMPR_DEFAULT;
116 size_t pcm_get_bytes_waiting(void)
118 return p_size;
122 * This function goes directly into the DMA buffer to calculate the left and
123 * right peak values. To avoid missing peaks it tries to look forward two full
124 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
125 * the entire period will not be visible. To reduce CPU load it only looks at
126 * every third sample, and this can be reduced even further if needed (even
127 * every tenth sample would still be pretty accurate).
130 /* Check for a peak every PEAK_STRIDE samples */
131 #define PEAK_STRIDE 3
132 /* Up to 1/50th of a second of audio for peak calculation */
133 /* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
134 #define PEAK_SAMPLES (44100/50)
135 void pcm_calculate_peaks(int *left, int *right)
137 short *addr;
138 short *end;
140 size_t samples = p_size / 4;
141 addr = p;
143 if (samples > PEAK_SAMPLES)
144 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
145 else
146 samples -= MIN(PEAK_STRIDE - 1, samples);
148 end = &addr[samples * 2];
151 if (left && right) {
152 int left_peak = 0, right_peak = 0;
154 while (addr < end) {
155 int value;
156 if ((value = addr [0]) > left_peak)
157 left_peak = value;
158 else if (-value > left_peak)
159 left_peak = -value;
161 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
162 right_peak = value;
163 else if (-value > right_peak)
164 right_peak = -value;
166 addr = &addr[PEAK_STRIDE * 2];
169 *left = left_peak;
170 *right = right_peak;
172 else if (left || right) {
173 int peak_value = 0, value;
175 if (right)
176 addr += (PEAK_STRIDE | 1);
178 while (addr < end) {
179 if ((value = addr [0]) > peak_value)
180 peak_value = value;
181 else if (-value > peak_value)
182 peak_value = -value;
184 addr += PEAK_STRIDE * 2;
187 if (left)
188 *left = peak_value;
189 else
190 *right = peak_value;
193 #endif /* defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || defined(CPU_PP) */
195 /****************************************************************************
196 * Functions that do not require targeted implementation but only a targeted
197 * interface
200 /* Common code to pcm_play_data and pcm_play_pause
201 Returns true if DMA playback was started, else false. */
202 bool pcm_play_data_start(pcm_more_callback_type get_more,
203 unsigned char *start, size_t size)
205 if (!(start && size))
207 size = 0;
208 if (get_more)
209 get_more(&start, &size);
212 if (start && size)
214 pcm_play_dma_start(start, size);
215 return true;
218 return false;
221 void pcm_play_data(pcm_more_callback_type get_more,
222 unsigned char *start, size_t size)
224 pcm_callback_for_more = get_more;
226 if (pcm_play_data_start(get_more, start, size) && pcm_paused)
228 pcm_paused = false;
229 pcm_play_pause(false);
233 void pcm_play_pause(bool play)
235 bool needs_change = pcm_paused == play;
237 /* This needs to be done ahead of the rest to prevent infinite
238 recursion from pcm_play_data */
239 pcm_paused = !play;
241 if (pcm_playing && needs_change)
243 if (play)
245 if (pcm_get_bytes_waiting())
247 logf("unpause");
248 pcm_play_pause_unpause();
250 else
252 logf("unpause, no data waiting");
253 if (!pcm_play_data_start(pcm_callback_for_more, NULL, 0))
255 pcm_play_dma_stop();
256 logf("unpause attempted, no data");
260 else
262 logf("pause");
263 pcm_play_pause_pause();
265 } /* pcm_playing && needs_change */
268 void pcm_play_stop(void)
270 if (pcm_playing)
271 pcm_play_dma_stop();
274 bool pcm_is_playing(void)
276 return pcm_playing;
279 bool pcm_is_paused(void)
281 return pcm_paused;