diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-06-29 06:37:04 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-06-29 06:37:04 +0000 |
commit | a2b6703a369f6cdbfec1f150c408dadc877631fb (patch) | |
tree | 3145a8c1372c44711d38feefeba39c7d4098f139 /firmware | |
parent | 8411614b8a068a4f274c3841aa55aab1df1bc246 (diff) | |
download | rockbox-a2b6703a369f6cdbfec1f150c408dadc877631fb.tar.gz rockbox-a2b6703a369f6cdbfec1f150c408dadc877631fb.zip |
Commit FS#12150 - Fully-functional audio mixer - and finally whip old limitations about playback of voice and other sounds when paused. Channels are independent in state and amplitude. Fade on stop/pause is handled by the channel's volume control rather than global volume which means it now works from anywhere. Opens up the possibility of plugin sounds during music playback by merely adding an additional channel enum. If any PCM drivers were not properly modified, see one of the last comments in the task for a description of the simple change that is expected. Some params are tunable in firmware/export/pcm-mixer.h as well.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30097 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
28 files changed, 1462 insertions, 69 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES index 7c3a909f22..85d9a28f1b 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -311,6 +311,7 @@ sound.c | |||
311 | #ifndef BOOTLOADER | 311 | #ifndef BOOTLOADER |
312 | pcm_sampr.c | 312 | pcm_sampr.c |
313 | pcm.c | 313 | pcm.c |
314 | pcm_mixer.c | ||
314 | #ifdef HAVE_RECORDING | 315 | #ifdef HAVE_RECORDING |
315 | enc_base.c | 316 | enc_base.c |
316 | #endif /* HAVE_RECORDING */ | 317 | #endif /* HAVE_RECORDING */ |
diff --git a/firmware/export/config.h b/firmware/export/config.h index 2c7c6e89db..70047ff866 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h | |||
@@ -1048,4 +1048,9 @@ Lyre prototype 1 */ | |||
1048 | #define HAVE_IO_PRIORITY | 1048 | #define HAVE_IO_PRIORITY |
1049 | #endif | 1049 | #endif |
1050 | 1050 | ||
1051 | #if defined(CPU_COLDIRE) || CONFIG_CPU == IMX31L | ||
1052 | /* Can record and play simultaneously */ | ||
1053 | #define HAVE_PCM_FULL_DUPLEX | ||
1054 | #endif | ||
1055 | |||
1051 | #endif /* __CONFIG_H__ */ | 1056 | #endif /* __CONFIG_H__ */ |
diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h new file mode 100644 index 0000000000..d69138f534 --- /dev/null +++ b/firmware/export/pcm-internal.h | |||
@@ -0,0 +1,81 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 by Linus Nielsen Feltzing | ||
11 | * Copyright (C) 2011 by Michael Sevakis | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #ifndef PCM_INTERNAL_H | ||
23 | #define PCM_INTERNAL_H | ||
24 | |||
25 | /** The following are for internal use between pcm.c and target- | ||
26 | specific portion **/ | ||
27 | |||
28 | /* Called by the bottom layer ISR when more data is needed. Returns non- | ||
29 | * zero size if more data is to be played. Setting start to NULL | ||
30 | * forces stop. */ | ||
31 | void pcm_play_get_more_callback(void **start, size_t *size); | ||
32 | |||
33 | /* Called by the bottom layer ISR after next transfer has begun in order | ||
34 | to fill more data for next "get more" callback to implement double-buffered | ||
35 | callbacks - except for a couple ASM handlers, help drivers to implement | ||
36 | this functionality with minimal overhead */ | ||
37 | static FORCE_INLINE void pcm_play_dma_started_callback(void) | ||
38 | { | ||
39 | extern void (* pcm_play_dma_started)(void); | ||
40 | void (* callback)(void) = pcm_play_dma_started; | ||
41 | if (callback) | ||
42 | callback(); | ||
43 | } | ||
44 | |||
45 | extern unsigned long pcm_curr_sampr; | ||
46 | extern unsigned long pcm_sampr; | ||
47 | extern int pcm_fsel; | ||
48 | |||
49 | #ifdef HAVE_PCM_DMA_ADDRESS | ||
50 | void * pcm_dma_addr(void *addr); | ||
51 | #endif | ||
52 | |||
53 | extern volatile bool pcm_playing; | ||
54 | extern volatile bool pcm_paused; | ||
55 | |||
56 | void pcm_play_dma_lock(void); | ||
57 | void pcm_play_dma_unlock(void); | ||
58 | void pcm_play_dma_init(void) INIT_ATTR; | ||
59 | void pcm_play_dma_start(const void *addr, size_t size); | ||
60 | void pcm_play_dma_stop(void); | ||
61 | void pcm_play_dma_pause(bool pause); | ||
62 | const void * pcm_play_dma_get_peak_buffer(int *count); | ||
63 | |||
64 | void pcm_dma_apply_settings(void); | ||
65 | |||
66 | #ifdef HAVE_RECORDING | ||
67 | |||
68 | /* DMA transfer in is currently active */ | ||
69 | extern volatile bool pcm_recording; | ||
70 | |||
71 | /* APIs implemented in the target-specific portion */ | ||
72 | void pcm_rec_dma_init(void); | ||
73 | void pcm_rec_dma_close(void); | ||
74 | void pcm_rec_dma_start(void *addr, size_t size); | ||
75 | void pcm_rec_dma_record_more(void *start, size_t size); | ||
76 | void pcm_rec_dma_stop(void); | ||
77 | const void * pcm_rec_dma_get_peak_buffer(void); | ||
78 | |||
79 | #endif /* HAVE_RECORDING */ | ||
80 | |||
81 | #endif /* PCM_INTERNAL_H */ | ||
diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h index 80b5b09a79..22c5ef350e 100644 --- a/firmware/export/pcm.h +++ b/firmware/export/pcm.h | |||
@@ -49,7 +49,7 @@ | |||
49 | 49 | ||
50 | /** RAW PCM routines used with playback and recording **/ | 50 | /** RAW PCM routines used with playback and recording **/ |
51 | 51 | ||
52 | /* Typedef for registered callback */ | 52 | /* Typedef for registered callbacks */ |
53 | typedef void (*pcm_play_callback_type)(unsigned char **start, | 53 | typedef void (*pcm_play_callback_type)(unsigned char **start, |
54 | size_t *size); | 54 | size_t *size); |
55 | typedef void (*pcm_rec_callback_type)(int status, void **start, size_t *size); | 55 | typedef void (*pcm_rec_callback_type)(int status, void **start, size_t *size); |
@@ -90,34 +90,7 @@ void pcm_play_pause(bool play); | |||
90 | bool pcm_is_paused(void); | 90 | bool pcm_is_paused(void); |
91 | bool pcm_is_playing(void); | 91 | bool pcm_is_playing(void); |
92 | 92 | ||
93 | /** The following are for internal use between pcm.c and target- | 93 | void pcm_play_set_dma_started_callback(void (* callback)(void)); |
94 | specific portion **/ | ||
95 | |||
96 | /* Called by the bottom layer ISR when more data is needed. Returns non- | ||
97 | * zero size if more data is to be played. Setting start to NULL | ||
98 | * forces stop. */ | ||
99 | void pcm_play_get_more_callback(void **start, size_t *size); | ||
100 | |||
101 | extern unsigned long pcm_curr_sampr; | ||
102 | extern unsigned long pcm_sampr; | ||
103 | extern int pcm_fsel; | ||
104 | |||
105 | #ifdef HAVE_PCM_DMA_ADDRESS | ||
106 | void * pcm_dma_addr(void *addr); | ||
107 | #endif | ||
108 | |||
109 | extern volatile bool pcm_playing; | ||
110 | extern volatile bool pcm_paused; | ||
111 | |||
112 | void pcm_play_dma_lock(void); | ||
113 | void pcm_play_dma_unlock(void); | ||
114 | void pcm_play_dma_init(void) INIT_ATTR; | ||
115 | void pcm_play_dma_start(const void *addr, size_t size); | ||
116 | void pcm_play_dma_stop(void); | ||
117 | void pcm_play_dma_pause(bool pause); | ||
118 | const void * pcm_play_dma_get_peak_buffer(int *count); | ||
119 | |||
120 | void pcm_dma_apply_settings(void); | ||
121 | 94 | ||
122 | #ifdef HAVE_RECORDING | 95 | #ifdef HAVE_RECORDING |
123 | 96 | ||
@@ -148,19 +121,6 @@ void pcm_rec_more_ready_callback(int status, void **start, size_t *size); | |||
148 | 121 | ||
149 | void pcm_calculate_rec_peaks(int *left, int *right); | 122 | void pcm_calculate_rec_peaks(int *left, int *right); |
150 | 123 | ||
151 | /** The following are for internal use between pcm.c and target- | ||
152 | specific portion **/ | ||
153 | /* DMA transfer in is currently active */ | ||
154 | extern volatile bool pcm_recording; | ||
155 | |||
156 | /* APIs implemented in the target-specific portion */ | ||
157 | void pcm_rec_dma_init(void); | ||
158 | void pcm_rec_dma_close(void); | ||
159 | void pcm_rec_dma_start(void *addr, size_t size); | ||
160 | void pcm_rec_dma_record_more(void *start, size_t size); | ||
161 | void pcm_rec_dma_stop(void); | ||
162 | const void * pcm_rec_dma_get_peak_buffer(void); | ||
163 | |||
164 | #endif /* HAVE_RECORDING */ | 124 | #endif /* HAVE_RECORDING */ |
165 | 125 | ||
166 | #endif /* PCM_PLAYBACK_H */ | 126 | #endif /* PCM_PLAYBACK_H */ |
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h new file mode 100644 index 0000000000..3b420e1320 --- /dev/null +++ b/firmware/export/pcm_mixer.h | |||
@@ -0,0 +1,102 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by Michael Sevakis | ||
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 | #ifndef PCM_MIXER_H | ||
23 | #define PCM_MIXER_H | ||
24 | |||
25 | /** Simple config **/ | ||
26 | |||
27 | /* Length of PCM frames (always) */ | ||
28 | #if CONFIG_CPU == PP5002 | ||
29 | /* There's far less time to do mixing because HW FIFOs are short */ | ||
30 | #define MIX_FRAME_SAMPLES 64 | ||
31 | #else | ||
32 | /* Assume HW DMA engine is available or sufficient latency exists in the | ||
33 | PCM pathway */ | ||
34 | #define MIX_FRAME_SAMPLES 256 | ||
35 | #endif | ||
36 | |||
37 | #if defined(CPU_COLDFIRE) || defined(CPU_PP) | ||
38 | /* For Coldfire, it's just faster | ||
39 | For PortalPlayer, this also avoids more expensive cache coherency */ | ||
40 | #define DOWNMIX_BUF_IBSS IBSS_ATTR | ||
41 | #else | ||
42 | /* Otherwise can't DMA from IRAM, IRAM is pointless or worse */ | ||
43 | #define DOWNMIX_BUF_IBSS | ||
44 | #endif | ||
45 | |||
46 | |||
47 | /** Definitions **/ | ||
48 | |||
49 | /* Channels are preassigned for simplicity */ | ||
50 | enum pcm_mixer_channel | ||
51 | { | ||
52 | PCM_MIXER_CHAN_PLAYBACK = 0, | ||
53 | PCM_MIXER_CHAN_VOICE, | ||
54 | #ifndef HAVE_HARDWARE_BEEP | ||
55 | PCM_MIXER_CHAN_BEEP, | ||
56 | #endif | ||
57 | /* Add new channel indexes above this line */ | ||
58 | PCM_MIXER_NUM_CHANNELS, | ||
59 | }; | ||
60 | |||
61 | /* Channel playback states */ | ||
62 | enum channel_status | ||
63 | { | ||
64 | CHANNEL_STOPPED = 0, | ||
65 | CHANNEL_PLAYING, | ||
66 | CHANNEL_PAUSED, | ||
67 | }; | ||
68 | |||
69 | #define MIX_AMP_UNITY 0x00010000 | ||
70 | #define MIX_AMP_MUTE 0x00000000 | ||
71 | |||
72 | |||
73 | /** Public interfaces **/ | ||
74 | |||
75 | /* Start playback on a channel */ | ||
76 | void mixer_channel_play_data(enum pcm_mixer_channel channel, | ||
77 | pcm_play_callback_type get_more, | ||
78 | unsigned char *start, size_t size); | ||
79 | |||
80 | /* Pause or resume a channel (when started) */ | ||
81 | void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play); | ||
82 | |||
83 | /* Stop playback on a channel */ | ||
84 | void mixer_channel_stop(enum pcm_mixer_channel channel); | ||
85 | |||
86 | /* Set channel's amplitude factor */ | ||
87 | void mixer_channel_set_amplitude(enum pcm_mixer_channel channel, | ||
88 | unsigned int amplitude); | ||
89 | |||
90 | /* Return channel's playback status */ | ||
91 | enum channel_status mixer_channel_status(enum pcm_mixer_channel channel); | ||
92 | |||
93 | /* Returns amount data remaining in channel before next callback */ | ||
94 | size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel); | ||
95 | |||
96 | /* Return pointer to channel's playing audio data and the size remaining */ | ||
97 | void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count); | ||
98 | |||
99 | /* Stop ALL channels and PCM and reset state */ | ||
100 | void mixer_reset(void); | ||
101 | |||
102 | #endif /* PCM_MIXER_H */ | ||
diff --git a/firmware/pcm.c b/firmware/pcm.c index d15c129015..b7415f329b 100644 --- a/firmware/pcm.c +++ b/firmware/pcm.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include "audio.h" | 28 | #include "audio.h" |
29 | #include "sound.h" | 29 | #include "sound.h" |
30 | #include "general.h" | 30 | #include "general.h" |
31 | #include "pcm-internal.h" | ||
32 | #include "pcm_mixer.h" | ||
31 | 33 | ||
32 | /** | 34 | /** |
33 | * Aspects implemented in the target-specific portion: | 35 | * Aspects implemented in the target-specific portion: |
@@ -78,8 +80,8 @@ | |||
78 | */ | 80 | */ |
79 | 81 | ||
80 | /* the registered callback function to ask for more mp3 data */ | 82 | /* the registered callback function to ask for more mp3 data */ |
81 | static volatile pcm_play_callback_type pcm_callback_for_more | 83 | static pcm_play_callback_type pcm_callback_for_more SHAREDBSS_ATTR = NULL; |
82 | SHAREDBSS_ATTR = NULL; | 84 | void (* pcm_play_dma_started)(void) SHAREDBSS_ATTR = NULL; |
83 | /* PCM playback state */ | 85 | /* PCM playback state */ |
84 | volatile bool pcm_playing SHAREDBSS_ATTR = false; | 86 | volatile bool pcm_playing SHAREDBSS_ATTR = false; |
85 | /* PCM paused state. paused implies playing */ | 87 | /* PCM paused state. paused implies playing */ |
@@ -95,6 +97,7 @@ int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT; | |||
95 | static void pcm_play_stopped(void) | 97 | static void pcm_play_stopped(void) |
96 | { | 98 | { |
97 | pcm_callback_for_more = NULL; | 99 | pcm_callback_for_more = NULL; |
100 | pcm_play_dma_started = NULL; | ||
98 | pcm_paused = false; | 101 | pcm_paused = false; |
99 | pcm_playing = false; | 102 | pcm_playing = false; |
100 | } | 103 | } |
@@ -404,6 +407,12 @@ void pcm_apply_settings(void) | |||
404 | } | 407 | } |
405 | } | 408 | } |
406 | 409 | ||
410 | /* register callback to buffer more data */ | ||
411 | void pcm_play_set_dma_started_callback(void (* callback)(void)) | ||
412 | { | ||
413 | pcm_play_dma_started = callback; | ||
414 | } | ||
415 | |||
407 | #ifdef HAVE_RECORDING | 416 | #ifdef HAVE_RECORDING |
408 | /** Low level pcm recording apis **/ | 417 | /** Low level pcm recording apis **/ |
409 | 418 | ||
@@ -475,6 +484,11 @@ void pcm_init_recording(void) | |||
475 | { | 484 | { |
476 | logf("pcm_init_recording"); | 485 | logf("pcm_init_recording"); |
477 | 486 | ||
487 | #ifndef HAVE_PCM_FULL_DUPLEX | ||
488 | /* Stop the beasty before attempting recording */ | ||
489 | mixer_reset(); | ||
490 | #endif | ||
491 | |||
478 | /* Recording init is locked unlike general pcm init since this is not | 492 | /* Recording init is locked unlike general pcm init since this is not |
479 | * just a one-time event at startup and it should and must be safe by | 493 | * just a one-time event at startup and it should and must be safe by |
480 | * now. */ | 494 | * now. */ |
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c new file mode 100644 index 0000000000..cddd3f0f86 --- /dev/null +++ b/firmware/pcm_mixer.c | |||
@@ -0,0 +1,501 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by Michael Sevakis | ||
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 | #include "config.h" | ||
22 | #include "system.h" | ||
23 | #include "general.h" | ||
24 | #include "kernel.h" | ||
25 | #include "pcm.h" | ||
26 | #include "pcm_mixer.h" | ||
27 | #include "dsp.h" | ||
28 | |||
29 | /* Channels use standard-style PCM callback interface but a latency of one | ||
30 | frame by double-buffering is introduced in order to facilitate mixing and | ||
31 | keep the hardware fed. There must be sufficient time to perform operations | ||
32 | before the last samples are sent to the codec and so things are done in | ||
33 | parallel (as much as possible) with sending-out data. */ | ||
34 | |||
35 | /* Define this to nonzero to add a marker pulse at each frame start */ | ||
36 | #define FRAME_BOUNDARY_MARKERS 0 | ||
37 | |||
38 | /* Descriptor for each channel */ | ||
39 | struct mixer_channel | ||
40 | { | ||
41 | unsigned char *start; /* Buffer pointer */ | ||
42 | size_t size; /* Bytes remaining */ | ||
43 | size_t last_size; /* Size of consumed data in prev. cycle */ | ||
44 | pcm_play_callback_type get_more; /* Registered callback */ | ||
45 | enum channel_status status; /* Playback status */ | ||
46 | uint32_t amplitude; /* Amp. factor: 0x0000 = mute, 0x10000 = unity */ | ||
47 | }; | ||
48 | |||
49 | /* Forget about boost here for the moment */ | ||
50 | #define MIX_FRAME_SIZE (MIX_FRAME_SAMPLES*4) | ||
51 | |||
52 | /* Because of the double-buffering, playback is always from here, otherwise a | ||
53 | mechanism for the channel callbacks not to free buffers too early would be | ||
54 | needed (if we _really_ want it and it's worth it, we _can_ do that ;-) ) */ | ||
55 | static uint32_t downmix_buf[2][MIX_FRAME_SAMPLES] DOWNMIX_BUF_IBSS MEM_ALIGN_ATTR; | ||
56 | static int downmix_index = 0; /* Which downmix_buf? */ | ||
57 | static size_t next_size = 0; /* Size of buffer to play next time */ | ||
58 | |||
59 | /* Descriptors for all available channels */ | ||
60 | static struct mixer_channel channels[PCM_MIXER_NUM_CHANNELS] IBSS_ATTR; | ||
61 | |||
62 | /* Packed pointer array of all playing (active) channels in "channels" array */ | ||
63 | static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR; | ||
64 | |||
65 | /* Number of silence frames to play after all data has played */ | ||
66 | #define MAX_IDLE_FRAMES (NATIVE_FREQUENCY*3 / MIX_FRAME_SAMPLES) | ||
67 | static unsigned int idle_counter = 0; | ||
68 | |||
69 | /* Cheapo buffer align macro to align to the 16-16 PCM size */ | ||
70 | #define ALIGN_CHANNEL(start, size) \ | ||
71 | ({ start = (void *)(((uintptr_t)start + 3) & ~3); \ | ||
72 | size &= ~3; }) | ||
73 | |||
74 | /* Include any implemented CPU-optimized mixdown routines */ | ||
75 | #if defined(CPU_ARM) | ||
76 | #if ARM_ARCH >= 6 | ||
77 | #include "pcm-mixer-armv6.c" | ||
78 | #elif ARM_ARCH >= 5 | ||
79 | #include "pcm-mixer-armv5.c" | ||
80 | #else | ||
81 | #include "pcm-mixer-armv4.c" | ||
82 | #endif /* ARM_ARCH */ | ||
83 | #elif defined (CPU_COLDFIRE) | ||
84 | #include "pcm-mixer-coldfire.c" | ||
85 | #endif /* CPU_* */ | ||
86 | |||
87 | |||
88 | /** Generic mixing routines **/ | ||
89 | |||
90 | #ifndef MIXER_OPTIMIZED_MIX_SAMPLES | ||
91 | /* Clip sample to signed 16 bit range */ | ||
92 | static FORCE_INLINE int32_t clip_sample_16(int32_t sample) | ||
93 | { | ||
94 | if ((int16_t)sample != sample) | ||
95 | sample = 0x7fff ^ (sample >> 31); | ||
96 | return sample; | ||
97 | } | ||
98 | |||
99 | /* Mix channels' samples and apply gain factors */ | ||
100 | static FORCE_INLINE void mix_samples(uint32_t *out, | ||
101 | int16_t *src0, | ||
102 | int32_t src0_amp, | ||
103 | int16_t *src1, | ||
104 | int32_t src1_amp, | ||
105 | size_t size) | ||
106 | { | ||
107 | if (src0_amp == MIX_AMP_UNITY && src1_amp == MIX_AMP_UNITY) | ||
108 | { | ||
109 | /* Both are unity amplitude */ | ||
110 | do | ||
111 | { | ||
112 | int32_t l = *src0++ + *src1++; | ||
113 | int32_t h = *src0++ + *src1++; | ||
114 | *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16); | ||
115 | } | ||
116 | while ((size -= 4) > 0); | ||
117 | } | ||
118 | else if (src0_amp != MIX_AMP_UNITY && src1_amp != MIX_AMP_UNITY) | ||
119 | { | ||
120 | /* Neither are unity amplitude */ | ||
121 | do | ||
122 | { | ||
123 | int32_t l = (*src0++ * src0_amp >> 16) + (*src1++ * src1_amp >> 16); | ||
124 | int32_t h = (*src0++ * src0_amp >> 16) + (*src1++ * src1_amp >> 16); | ||
125 | *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16); | ||
126 | } | ||
127 | while ((size -= 4) > 0); | ||
128 | } | ||
129 | else | ||
130 | { | ||
131 | /* One is unity amplitude */ | ||
132 | if (src0_amp != MIX_AMP_UNITY) | ||
133 | { | ||
134 | /* Keep unity in src0, amp0 */ | ||
135 | int16_t *src_tmp = src0; | ||
136 | src0 = src1; | ||
137 | src1 = src_tmp; | ||
138 | src1_amp = src0_amp; | ||
139 | src0_amp = MIX_AMP_UNITY; | ||
140 | } | ||
141 | |||
142 | do | ||
143 | { | ||
144 | int32_t l = *src0++ + (*src1++ * src1_amp >> 16); | ||
145 | int32_t h = *src0++ + (*src1++ * src1_amp >> 16); | ||
146 | *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16); | ||
147 | } | ||
148 | while ((size -= 4) > 0); | ||
149 | } | ||
150 | } | ||
151 | #endif /* MIXER_OPTIMIZED_MIX_SAMPLES */ | ||
152 | |||
153 | #ifndef MIXER_OPTIMIZED_WRITE_SAMPLES | ||
154 | /* Write channel's samples and apply gain factor */ | ||
155 | static FORCE_INLINE void write_samples(uint32_t *out, | ||
156 | int16_t *src, | ||
157 | int32_t amp, | ||
158 | size_t size) | ||
159 | { | ||
160 | if (LIKELY(amp == MIX_AMP_UNITY)) | ||
161 | { | ||
162 | /* Channel is unity amplitude */ | ||
163 | memcpy(out, src, size); | ||
164 | } | ||
165 | else | ||
166 | { | ||
167 | /* Channel needs amplitude cut */ | ||
168 | do | ||
169 | { | ||
170 | int32_t l = *src++ * amp >> 16; | ||
171 | int32_t h = *src++ * amp & 0xffff0000; | ||
172 | *out++ = (uint16_t)l | h; | ||
173 | } | ||
174 | while ((size -= 4) > 0); | ||
175 | } | ||
176 | } | ||
177 | #endif /* MIXER_OPTIMIZED_WRITE_SAMPLES */ | ||
178 | |||
179 | |||
180 | /** Private generic routines **/ | ||
181 | |||
182 | /* Mark channel active to mix its data */ | ||
183 | static void mixer_activate_channel(struct mixer_channel *chan) | ||
184 | { | ||
185 | void **elem = find_array_ptr((void **)active_channels, chan); | ||
186 | |||
187 | if (!*elem) | ||
188 | { | ||
189 | idle_counter = 0; | ||
190 | *elem = chan; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /* Stop channel from mixing */ | ||
195 | static void mixer_deactivate_channel(struct mixer_channel *chan) | ||
196 | { | ||
197 | remove_array_ptr((void **)active_channels, chan); | ||
198 | } | ||
199 | |||
200 | /* Deactivate channel and change it to stopped state */ | ||
201 | static void channel_stopped(struct mixer_channel *chan) | ||
202 | { | ||
203 | mixer_deactivate_channel(chan); | ||
204 | chan->size = 0; | ||
205 | chan->start = NULL; | ||
206 | chan->status = CHANNEL_STOPPED; | ||
207 | } | ||
208 | |||
209 | /* Main PCM callback - sends the current prepared frame to play */ | ||
210 | static void mixer_pcm_callback(unsigned char **start, size_t *size) | ||
211 | { | ||
212 | *start = (unsigned char *)downmix_buf[downmix_index]; | ||
213 | *size = next_size; | ||
214 | } | ||
215 | |||
216 | /* Buffering callback - calls sub-callbacks and mixes the data for next | ||
217 | buffer to be sent from mixer_pcm_callback() */ | ||
218 | static void ICODE_ATTR mixer_buffer_callback(void) | ||
219 | { | ||
220 | downmix_index ^= 1; /* Next buffer */ | ||
221 | |||
222 | void *mixptr = downmix_buf[downmix_index]; | ||
223 | size_t mixsize = MIX_FRAME_SIZE; | ||
224 | struct mixer_channel **chan_p; | ||
225 | |||
226 | next_size = 0; | ||
227 | |||
228 | /* "Loop" back here if one round wasn't enough to fill a frame */ | ||
229 | fill_frame: | ||
230 | chan_p = active_channels; | ||
231 | |||
232 | while (*chan_p) | ||
233 | { | ||
234 | /* Find the active channel with the least data remaining and call any | ||
235 | callbacks for channels that ran out - stopping whichever report | ||
236 | "no more" */ | ||
237 | struct mixer_channel *chan = *chan_p; | ||
238 | chan->start += chan->last_size; | ||
239 | chan->size -= chan->last_size; | ||
240 | |||
241 | if (chan->size == 0) | ||
242 | { | ||
243 | if (chan->get_more) | ||
244 | { | ||
245 | chan->get_more(&chan->start, &chan->size); | ||
246 | ALIGN_CHANNEL(chan->start, chan->size); | ||
247 | } | ||
248 | |||
249 | if (!(chan->start && chan->size)) | ||
250 | { | ||
251 | /* Channel is stopping */ | ||
252 | channel_stopped(chan); | ||
253 | continue; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | /* Channel will play for at least part of this frame */ | ||
258 | |||
259 | /* Channel with least amount of data remaining determines the downmix | ||
260 | size */ | ||
261 | if (chan->size < mixsize) | ||
262 | mixsize = chan->size; | ||
263 | |||
264 | chan_p++; | ||
265 | } | ||
266 | |||
267 | /* Add all still-active channels to the downmix */ | ||
268 | chan_p = active_channels; | ||
269 | |||
270 | if (LIKELY(*chan_p)) | ||
271 | { | ||
272 | struct mixer_channel *chan = *chan_p++; | ||
273 | |||
274 | if (LIKELY(!*chan_p)) | ||
275 | { | ||
276 | write_samples(mixptr, (void *)chan->start, | ||
277 | chan->amplitude, mixsize); | ||
278 | } | ||
279 | else | ||
280 | { | ||
281 | void *src0, *src1; | ||
282 | unsigned int amp0, amp1; | ||
283 | |||
284 | /* Mix first two channels with each other as the downmix */ | ||
285 | src0 = chan->start; | ||
286 | amp0 = chan->amplitude; | ||
287 | chan->last_size = mixsize; | ||
288 | |||
289 | chan = *chan_p++; | ||
290 | src1 = chan->start; | ||
291 | amp1 = chan->amplitude; | ||
292 | |||
293 | while (1) | ||
294 | { | ||
295 | mix_samples(mixptr, src0, amp0, src1, amp1, mixsize); | ||
296 | |||
297 | if (!*chan_p) | ||
298 | break; | ||
299 | |||
300 | /* More channels to mix - mix each with existing downmix */ | ||
301 | chan->last_size = mixsize; | ||
302 | chan = *chan_p++; | ||
303 | src0 = mixptr; | ||
304 | amp0 = MIX_AMP_UNITY; | ||
305 | src1 = chan->start; | ||
306 | amp1 = chan->amplitude; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | chan->last_size = mixsize; | ||
311 | next_size += mixsize; | ||
312 | |||
313 | if (next_size < MIX_FRAME_SIZE) | ||
314 | { | ||
315 | /* There is still space remaining in this frame */ | ||
316 | mixptr += mixsize; | ||
317 | mixsize = MIX_FRAME_SIZE - next_size; | ||
318 | goto fill_frame; | ||
319 | } | ||
320 | } | ||
321 | else if (idle_counter++ < MAX_IDLE_FRAMES) | ||
322 | { | ||
323 | /* Pad incomplete frames with silence */ | ||
324 | if (idle_counter <= 3) | ||
325 | memset(mixptr, 0, MIX_FRAME_SIZE - next_size); | ||
326 | |||
327 | next_size = MIX_FRAME_SIZE; | ||
328 | } | ||
329 | /* else silence period ran out - go to sleep */ | ||
330 | |||
331 | #if FRAME_BOUNDARY_MARKERS != 0 | ||
332 | if (next_size) | ||
333 | *downmix_buf[downmix_index] = downmix_index ? 0x7fff7fff : 0x80008000; | ||
334 | #endif | ||
335 | } | ||
336 | |||
337 | /* Start PCM driver if it's not currently playing */ | ||
338 | static void mixer_start_pcm(void) | ||
339 | { | ||
340 | if (pcm_is_playing()) | ||
341 | return; | ||
342 | |||
343 | #if defined(HAVE_RECORDING) && !defined(HAVE_PCM_FULL_DUPLEX) | ||
344 | if (pcm_is_recording()) | ||
345 | return; | ||
346 | #endif | ||
347 | |||
348 | /* Prepare initial frames and set up the double buffer */ | ||
349 | mixer_buffer_callback(); | ||
350 | |||
351 | /* Save the previous call's output */ | ||
352 | void *start = downmix_buf[downmix_index]; | ||
353 | |||
354 | mixer_buffer_callback(); | ||
355 | |||
356 | pcm_play_set_dma_started_callback(mixer_buffer_callback); | ||
357 | pcm_play_data(mixer_pcm_callback, start, MIX_FRAME_SIZE); | ||
358 | } | ||
359 | |||
360 | /* Initialize the channel and start it if it has data */ | ||
361 | static void mixer_channel_play_start(struct mixer_channel *chan, | ||
362 | pcm_play_callback_type get_more, | ||
363 | unsigned char *start, size_t size) | ||
364 | { | ||
365 | pcm_play_unlock(); /* Allow playback while doing any callback */ | ||
366 | |||
367 | ALIGN_CHANNEL(start, size); | ||
368 | |||
369 | if (!(start && size)) | ||
370 | { | ||
371 | /* Initial buffer not passed - call the callback now */ | ||
372 | size = 0; | ||
373 | if (get_more) | ||
374 | { | ||
375 | get_more(&start, &size); | ||
376 | ALIGN_CHANNEL(start, size); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | if (start && size) | ||
381 | { | ||
382 | /* We have data - start the channel */ | ||
383 | chan->status = CHANNEL_PLAYING; | ||
384 | chan->start = start; | ||
385 | chan->size = size; | ||
386 | chan->last_size = 0; | ||
387 | chan->get_more = get_more; | ||
388 | |||
389 | pcm_play_lock(); | ||
390 | mixer_activate_channel(chan); | ||
391 | mixer_start_pcm(); | ||
392 | } | ||
393 | else | ||
394 | { | ||
395 | /* Never had anything - stop it now */ | ||
396 | pcm_play_lock(); | ||
397 | channel_stopped(chan); | ||
398 | } | ||
399 | } | ||
400 | |||
401 | |||
402 | /** Public interfaces **/ | ||
403 | |||
404 | /* Start playback on a channel */ | ||
405 | void mixer_channel_play_data(enum pcm_mixer_channel channel, | ||
406 | pcm_play_callback_type get_more, | ||
407 | unsigned char *start, size_t size) | ||
408 | { | ||
409 | struct mixer_channel *chan = &channels[channel]; | ||
410 | |||
411 | pcm_play_lock(); | ||
412 | mixer_deactivate_channel(chan); | ||
413 | mixer_channel_play_start(chan, get_more, start, size); | ||
414 | pcm_play_unlock(); | ||
415 | } | ||
416 | |||
417 | /* Pause or resume a channel (when started) */ | ||
418 | void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play) | ||
419 | { | ||
420 | struct mixer_channel *chan = &channels[channel]; | ||
421 | |||
422 | pcm_play_lock(); | ||
423 | |||
424 | if (play == (chan->status == CHANNEL_PAUSED) && | ||
425 | chan->status != CHANNEL_STOPPED) | ||
426 | { | ||
427 | if (play) | ||
428 | { | ||
429 | chan->status = CHANNEL_PLAYING; | ||
430 | mixer_activate_channel(chan); | ||
431 | mixer_start_pcm(); | ||
432 | } | ||
433 | else | ||
434 | { | ||
435 | mixer_deactivate_channel(chan); | ||
436 | chan->status = CHANNEL_PAUSED; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | pcm_play_unlock(); | ||
441 | } | ||
442 | |||
443 | /* Stop playback on a channel */ | ||
444 | void mixer_channel_stop(enum pcm_mixer_channel channel) | ||
445 | { | ||
446 | struct mixer_channel *chan = &channels[channel]; | ||
447 | |||
448 | pcm_play_lock(); | ||
449 | channel_stopped(chan); | ||
450 | pcm_play_unlock(); | ||
451 | } | ||
452 | |||
453 | /* Set channel's amplitude factor */ | ||
454 | void mixer_channel_set_amplitude(enum pcm_mixer_channel channel, | ||
455 | unsigned int amplitude) | ||
456 | { | ||
457 | channels[channel].amplitude = MIN(amplitude, MIX_AMP_UNITY); | ||
458 | } | ||
459 | |||
460 | /* Return channel's playback status */ | ||
461 | enum channel_status mixer_channel_status(enum pcm_mixer_channel channel) | ||
462 | { | ||
463 | return channels[channel].status; | ||
464 | } | ||
465 | |||
466 | /* Returns amount data remaining in channel before next callback */ | ||
467 | size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel) | ||
468 | { | ||
469 | return channels[channel].size; | ||
470 | } | ||
471 | |||
472 | /* Return pointer to channel's playing audio data and the size remaining */ | ||
473 | void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count) | ||
474 | { | ||
475 | struct mixer_channel *chan = &channels[channel]; | ||
476 | void * buf = *(unsigned char * volatile *)&chan->start; | ||
477 | size_t size = *(size_t volatile *)&chan->size; | ||
478 | void * buf2 = *(unsigned char * volatile *)&chan->start; | ||
479 | |||
480 | /* Still same buffer? */ | ||
481 | if (buf == buf2) | ||
482 | { | ||
483 | *count = size >> 2; | ||
484 | return buf; | ||
485 | } | ||
486 | /* else can't be sure buf and size are related */ | ||
487 | |||
488 | *count = 0; | ||
489 | return NULL; | ||
490 | } | ||
491 | |||
492 | /* Stop ALL channels and PCM and reset state */ | ||
493 | void mixer_reset(void) | ||
494 | { | ||
495 | pcm_play_stop(); | ||
496 | |||
497 | while (*active_channels) | ||
498 | channel_stopped(*active_channels); | ||
499 | |||
500 | idle_counter = 0; | ||
501 | } | ||
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c index 469833b05c..1b22d48f7f 100644 --- a/firmware/target/arm/as3525/pcm-as3525.c +++ b/firmware/target/arm/as3525/pcm-as3525.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "as3514.h" | 29 | #include "as3514.h" |
30 | #include "audiohw.h" | 30 | #include "audiohw.h" |
31 | #include "mmu-arm.h" | 31 | #include "mmu-arm.h" |
32 | #include "pcm-internal.h" | ||
32 | 33 | ||
33 | #define MAX_TRANSFER (4*((1<<11)-1)) /* maximum data we can transfer via DMA | 34 | #define MAX_TRANSFER (4*((1<<11)-1)) /* maximum data we can transfer via DMA |
34 | * i.e. 32 bits at once (size of I2SO_DATA) | 35 | * i.e. 32 bits at once (size of I2SO_DATA) |
@@ -104,9 +105,13 @@ static void dma_callback(void) | |||
104 | 105 | ||
105 | /* force writeback */ | 106 | /* force writeback */ |
106 | clean_dcache_range(dma_start_addr, dma_start_size); | 107 | clean_dcache_range(dma_start_addr, dma_start_size); |
108 | play_start_pcm(); | ||
109 | pcm_play_dma_started_callback(); | ||
110 | } | ||
111 | else | ||
112 | { | ||
113 | play_start_pcm(); | ||
107 | } | 114 | } |
108 | |||
109 | play_start_pcm(); | ||
110 | } | 115 | } |
111 | 116 | ||
112 | void pcm_play_dma_start(const void *addr, size_t size) | 117 | void pcm_play_dma_start(const void *addr, size_t size) |
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c index c8c1283d12..1f6eef435a 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "ccm-imx31.h" | 26 | #include "ccm-imx31.h" |
27 | #include "sdma-imx31.h" | 27 | #include "sdma-imx31.h" |
28 | #include "mmu-imx31.h" | 28 | #include "mmu-imx31.h" |
29 | #include "pcm-internal.h" | ||
29 | 30 | ||
30 | #define DMA_PLAY_CH_NUM 2 | 31 | #define DMA_PLAY_CH_NUM 2 |
31 | #define DMA_REC_CH_NUM 1 | 32 | #define DMA_REC_CH_NUM 1 |
@@ -105,6 +106,8 @@ static void play_dma_callback(void) | |||
105 | dma_play_bd.mode.command = TRANSFER_16BIT; | 106 | dma_play_bd.mode.command = TRANSFER_16BIT; |
106 | dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; | 107 | dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; |
107 | sdma_channel_run(DMA_PLAY_CH_NUM); | 108 | sdma_channel_run(DMA_PLAY_CH_NUM); |
109 | |||
110 | pcm_play_dma_started_callback(); | ||
108 | } | 111 | } |
109 | 112 | ||
110 | void pcm_play_lock(void) | 113 | void pcm_play_lock(void) |
diff --git a/firmware/target/arm/pcm-mixer-armv4.c b/firmware/target/arm/pcm-mixer-armv4.c new file mode 100644 index 0000000000..4818544d7b --- /dev/null +++ b/firmware/target/arm/pcm-mixer-armv4.c | |||
@@ -0,0 +1,182 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by Michael Sevakis | ||
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 | #define MIXER_OPTIMIZED_WRITE_SAMPLES | ||
23 | #define MIXER_OPTIMIZED_MIX_SAMPLES | ||
24 | |||
25 | /* Mix channels' samples and apply gain factors */ | ||
26 | static FORCE_INLINE void mix_samples(void *out, | ||
27 | void *src0, | ||
28 | int32_t src0_amp, | ||
29 | void *src1, | ||
30 | int32_t src1_amp, | ||
31 | size_t size) | ||
32 | { | ||
33 | if (src0_amp == MIX_AMP_UNITY && src1_amp == MIX_AMP_UNITY) | ||
34 | { | ||
35 | /* Both are unity amplitude */ | ||
36 | int32_t l0, l1, h0, h1; | ||
37 | asm volatile ( | ||
38 | "1: \n" | ||
39 | "ldrsh %4, [%1], #2 \n" | ||
40 | "ldrsh %5, [%2], #2 \n" | ||
41 | "ldrsh %6, [%1], #2 \n" | ||
42 | "ldrsh %7, [%2], #2 \n" | ||
43 | "add %4, %4, %5 \n" | ||
44 | "add %6, %6, %7 \n" | ||
45 | "mov %5, %4, asr #15 \n" | ||
46 | "teq %5, %5, asr #31 \n" | ||
47 | "eorne %4, %8, %4, asr #31 \n" | ||
48 | "mov %7, %6, asr #15 \n" | ||
49 | "teq %7, %7, asr #31 \n" | ||
50 | "eorne %6, %8, %6, asr #31 \n" | ||
51 | "subs %3, %3, #4 \n" | ||
52 | "and %4, %4, %8, lsr #16 \n" | ||
53 | "orr %6, %4, %6, lsl #16 \n" | ||
54 | "str %6, [%0], #4 \n" | ||
55 | "bhi 1b \n" | ||
56 | : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size), | ||
57 | "=&r"(l0), "=&r"(l1), "=&r"(h0), "=&r"(h1) | ||
58 | : "r"(0xffff7fff)); | ||
59 | } | ||
60 | else if (src0_amp != MIX_AMP_UNITY && src1_amp != MIX_AMP_UNITY) | ||
61 | { | ||
62 | /* Neither are unity amplitude */ | ||
63 | int32_t l0, l1, h0, h1; | ||
64 | asm volatile ( | ||
65 | "1: \n" | ||
66 | "ldrsh %4, [%1], #2 \n" | ||
67 | "ldrsh %5, [%2], #2 \n" | ||
68 | "ldrsh %6, [%1], #2 \n" | ||
69 | "ldrsh %7, [%2], #2 \n" | ||
70 | "mul %4, %8, %4 \n" | ||
71 | "mul %5, %9, %5 \n" | ||
72 | "mul %6, %8, %6 \n" | ||
73 | "mul %7, %9, %7 \n" | ||
74 | "mov %4, %4, asr #16 \n" | ||
75 | "add %4, %4, %5, asr #16 \n" | ||
76 | "mov %6, %6, asr #16 \n" | ||
77 | "add %6, %6, %7, asr #16 \n" | ||
78 | "mov %5, %4, asr #15 \n" | ||
79 | "teq %5, %5, asr #31 \n" | ||
80 | "eorne %4, %10, %4, asr #31 \n" | ||
81 | "mov %7, %6, asr #15 \n" | ||
82 | "teq %7, %7, asr #31 \n" | ||
83 | "eorne %6, %10, %6, asr #31 \n" | ||
84 | "subs %3, %3, #4 \n" | ||
85 | "and %4, %4, %10, lsr #16 \n" | ||
86 | "orr %6, %4, %6, lsl #16 \n" | ||
87 | "str %6, [%0], #4 \n" | ||
88 | "bhi 1b \n" | ||
89 | : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size), | ||
90 | "=&r"(l0), "=&r"(l1), "=&r"(h0), "=&r"(h1) | ||
91 | : "r"(src0_amp), "r"(src1_amp), "r"(0xffff7fff)); | ||
92 | } | ||
93 | else | ||
94 | { | ||
95 | /* One is unity amplitude */ | ||
96 | if (src0_amp != MIX_AMP_UNITY) | ||
97 | { | ||
98 | /* Keep unity in src0, amp0 */ | ||
99 | int16_t *src_tmp = src0; | ||
100 | src0 = src1; | ||
101 | src1 = src_tmp; | ||
102 | src1_amp = src0_amp; | ||
103 | src0_amp = MIX_AMP_UNITY; | ||
104 | } | ||
105 | |||
106 | int32_t l0, l1, h0, h1; | ||
107 | asm volatile ( | ||
108 | "1: \n" | ||
109 | "ldrsh %4, [%1], #2 \n" | ||
110 | "ldrsh %5, [%2], #2 \n" | ||
111 | "ldrsh %6, [%1], #2 \n" | ||
112 | "ldrsh %7, [%2], #2 \n" | ||
113 | "mul %5, %8, %5 \n" | ||
114 | "mul %7, %8, %7 \n" | ||
115 | "add %4, %4, %5, asr #16 \n" | ||
116 | "add %6, %6, %7, asr #16 \n" | ||
117 | "mov %5, %4, asr #15 \n" | ||
118 | "teq %5, %5, asr #31 \n" | ||
119 | "eorne %4, %9, %4, asr #31 \n" | ||
120 | "mov %7, %6, asr #15 \n" | ||
121 | "teq %7, %7, asr #31 \n" | ||
122 | "eorne %6, %9, %6, asr #31 \n" | ||
123 | "subs %3, %3, #4 \n" | ||
124 | "and %4, %4, %9, lsr #16 \n" | ||
125 | "orr %6, %4, %6, lsl #16 \n" | ||
126 | "str %6, [%0], #4 \n" | ||
127 | "bhi 1b \n" | ||
128 | : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size), | ||
129 | "=&r"(l0), "=&r"(l1), "=&r"(h0), "=&r"(h1) | ||
130 | : "r"(src1_amp), "r"(0xffff7fff)); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | /* Write channel's samples and apply gain factor */ | ||
135 | static FORCE_INLINE void write_samples(void *out, | ||
136 | void *src, | ||
137 | int32_t amp, | ||
138 | size_t size) | ||
139 | { | ||
140 | if (LIKELY(amp == MIX_AMP_UNITY)) | ||
141 | { | ||
142 | /* Channel is unity amplitude */ | ||
143 | asm volatile ( | ||
144 | "ands r1, %2, #0x1f \n" | ||
145 | "beq 2f \n" | ||
146 | "1: \n" | ||
147 | "ldr r0, [%1], #4 \n" | ||
148 | "subs r1, r1, #4 \n" | ||
149 | "str r0, [%0], #4 \n" | ||
150 | "bne 1b \n" | ||
151 | "bics %2, %2, #0x1f \n" | ||
152 | "beq 3f \n" | ||
153 | "2: \n" | ||
154 | "ldmia %1!, { r0-r7 } \n" | ||
155 | "subs %2, %2, #32 \n" | ||
156 | "stmia %0!, { r0-r7 } \n" | ||
157 | "bhi 2b \n" | ||
158 | "3: \n" | ||
159 | : "+r"(out), "+r"(src), "+r"(size) | ||
160 | : | ||
161 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"); | ||
162 | } | ||
163 | else | ||
164 | { | ||
165 | /* Channel needs amplitude cut */ | ||
166 | uint32_t l, h; | ||
167 | asm volatile ( | ||
168 | "1: \n" | ||
169 | "ldrsh %3, [%1], #2 \n" | ||
170 | "ldrsh %4, [%1], #2 \n" | ||
171 | "subs %2, %2, #4 \n" | ||
172 | "mul %3, %5, %3 \n" | ||
173 | "mul %4, %5, %4 \n" | ||
174 | "and %4, %4, %6, lsl #16 \n" | ||
175 | "orr %4, %4, %3, lsr #16 \n" | ||
176 | "str %4, [%0], #4 \n" | ||
177 | "bhi 1b \n" | ||
178 | : "+r"(out), "+r"(src), "+r"(size), | ||
179 | "=&r"(l), "=&r"(h) | ||
180 | : "r"(amp), "r"(0xffffffffu)); | ||
181 | } | ||
182 | } | ||
diff --git a/firmware/target/arm/pcm-mixer-armv5.c b/firmware/target/arm/pcm-mixer-armv5.c new file mode 100644 index 0000000000..64f2c86f52 --- /dev/null +++ b/firmware/target/arm/pcm-mixer-armv5.c | |||
@@ -0,0 +1,106 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by Michael Sevakis | ||
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 | #define MIXER_OPTIMIZED_WRITE_SAMPLES | ||
23 | #define MIXER_OPTIMIZED_MIX_SAMPLES | ||
24 | |||
25 | /* Mix channels' samples and apply gain factors */ | ||
26 | static FORCE_INLINE void mix_samples(void *out, | ||
27 | void *src0, | ||
28 | int32_t src0_amp, | ||
29 | void *src1, | ||
30 | int32_t src1_amp, | ||
31 | size_t size) | ||
32 | { | ||
33 | int32_t s0, s1, tmp; | ||
34 | asm volatile ( | ||
35 | "1: \n" | ||
36 | "ldr %4, [%1], #4 \n" | ||
37 | "ldr %5, [%2], #4 \n" | ||
38 | "smulwb %6, %7, %4 \n" | ||
39 | "smulwt %4, %7, %4 \n" | ||
40 | "smlawb %6, %8, %5, %6 \n" | ||
41 | "smlawt %4, %8, %5, %4 \n" | ||
42 | "mov %5, %6, asr #15 \n" | ||
43 | "teq %5, %5, asr #31 \n" | ||
44 | "eorne %6, %9, %6, asr #31 \n" | ||
45 | "mov %5, %4, asr #15 \n" | ||
46 | "teq %5, %5, asr #31 \n" | ||
47 | "eorne %4, %9, %4, asr #31 \n" | ||
48 | "subs %3, %3, #4 \n" | ||
49 | "and %6, %6, %9, lsr #16 \n" | ||
50 | "orr %6, %6, %4, lsl #16 \n" | ||
51 | "str %6, [%0], #4 \n" | ||
52 | "bhi 1b \n" | ||
53 | : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size), | ||
54 | "=&r"(s0), "=&r"(s1), "=&r"(tmp) | ||
55 | : "r"(src0_amp), "r"(src1_amp), "r"(0xffff7fff)); | ||
56 | } | ||
57 | |||
58 | /* Write channel's samples and apply gain factor */ | ||
59 | static FORCE_INLINE void write_samples(void *out, | ||
60 | void *src, | ||
61 | int32_t amp, | ||
62 | size_t size) | ||
63 | { | ||
64 | if (LIKELY(amp == MIX_AMP_UNITY)) | ||
65 | { | ||
66 | /* Channel is unity amplitude */ | ||
67 | asm volatile ( | ||
68 | "ands r1, %2, #0x1f \n" | ||
69 | "beq 2f \n" | ||
70 | "1: \n" | ||
71 | "ldr r0, [%1], #4 \n" | ||
72 | "subs r1, r1, #4 \n" | ||
73 | "str r0, [%0], #4 \n" | ||
74 | "bne 1b \n" | ||
75 | "bics %2, %2, #0x1f \n" | ||
76 | "beq 3f \n" | ||
77 | "2: \n" | ||
78 | "ldmia %1!, { r0-r7 } \n" | ||
79 | "subs %2, %2, #32 \n" | ||
80 | "stmia %0!, { r0-r7 } \n" | ||
81 | "bhi 2b \n" | ||
82 | "3: \n" | ||
83 | : "+r"(out), "+r"(src), "+r"(size) | ||
84 | : | ||
85 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"); | ||
86 | } | ||
87 | else | ||
88 | { | ||
89 | /* Channel needs amplitude cut */ | ||
90 | uint32_t l, h; | ||
91 | asm volatile ( | ||
92 | "1: \n" | ||
93 | "ldr %3, [%1], #4 \n" | ||
94 | "subs %2, %2, #4 \n" | ||
95 | "smulwt %4, %5, %3 \n" | ||
96 | "smulwb %3, %5, %3 \n" | ||
97 | "mov %4, %4, lsl #16 \n" | ||
98 | "mov %3, %3, lsl #16 \n" | ||
99 | "orr %4, %4, %3, lsr #16 \n" | ||
100 | "str %4, [%0], #4 \n" | ||
101 | "bhi 1b \n" | ||
102 | : "+r"(out), "+r"(src), "+r"(size), | ||
103 | "=&r"(l), "=&r"(h) | ||
104 | : "r"(amp)); | ||
105 | } | ||
106 | } | ||
diff --git a/firmware/target/arm/pcm-mixer-armv6.c b/firmware/target/arm/pcm-mixer-armv6.c new file mode 100644 index 0000000000..94eecd0f90 --- /dev/null +++ b/firmware/target/arm/pcm-mixer-armv6.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by Michael Sevakis | ||
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 | #define MIXER_OPTIMIZED_MIX_SAMPLES | ||
22 | #define MIXER_OPTIMIZED_WRITE_SAMPLES | ||
23 | |||
24 | /* Mix channels' samples and apply gain factors */ | ||
25 | static FORCE_INLINE void mix_samples(void *out, | ||
26 | void *src0, | ||
27 | int32_t src0_amp, | ||
28 | void *src1, | ||
29 | int32_t src1_amp, | ||
30 | size_t size) | ||
31 | { | ||
32 | uint32_t s0, s1; | ||
33 | |||
34 | if (src0_amp == MIX_AMP_UNITY && src1_amp == MIX_AMP_UNITY) | ||
35 | { | ||
36 | /* Both are unity amplitude */ | ||
37 | asm volatile ( | ||
38 | "1: \n" | ||
39 | "ldr %4, [%1], #4 \n" | ||
40 | "ldr %5, [%2], #4 \n" | ||
41 | "subs %3, %3, #4 \n" | ||
42 | "qadd16 %5, %5, %4 \n" | ||
43 | "str %5, [%0], #4 \n" | ||
44 | "bhi 1b \n" | ||
45 | : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size), | ||
46 | "=&r"(s0), "=&r"(s1)); | ||
47 | } | ||
48 | else | ||
49 | { | ||
50 | /* One or neither are unity amplitude */ | ||
51 | uint32_t tmp; | ||
52 | asm volatile ( | ||
53 | "1: \n" | ||
54 | "ldr %4, [%1], #4 \n" | ||
55 | "ldr %5, [%2], #4 \n" | ||
56 | "subs %3, %3, #4 \n" | ||
57 | "smulwb %6, %7, %4 \n" | ||
58 | "smulwt %4, %7, %4 \n" | ||
59 | "smlawb %6, %8, %5, %6 \n" | ||
60 | "smlawt %4, %8, %5, %4 \n" | ||
61 | "ssat %6, #16, %6 \n" | ||
62 | "ssat %4, #16, %4 \n" | ||
63 | "pkhbt %6, %6, %4, asl #16 \n" | ||
64 | "str %6, [%0], #4 \n" | ||
65 | "bhi 1b \n" | ||
66 | : "+r"(out), "+r"(src0), "+r"(src1), "+r"(size), | ||
67 | "=&r"(s0), "=&r"(s1), "=&r"(tmp) | ||
68 | : "r"(src0_amp), "r"(src1_amp)); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /* Write channel's samples and apply gain factor */ | ||
73 | static FORCE_INLINE void write_samples(void *out, | ||
74 | void *src, | ||
75 | int32_t amp, | ||
76 | size_t size) | ||
77 | { | ||
78 | if (LIKELY(amp == MIX_AMP_UNITY)) | ||
79 | { | ||
80 | /* Channel is unity amplitude */ | ||
81 | asm volatile ( | ||
82 | "ands r1, %2, #0x1f \n" | ||
83 | "beq 2f \n" | ||
84 | "1: \n" | ||
85 | "ldr r0, [%1], #4 \n" | ||
86 | "subs r1, r1, #4 \n" | ||
87 | "str r0, [%0], #4 \n" | ||
88 | "bne 1b \n" | ||
89 | "bics %2, %2, #0x1f \n" | ||
90 | "beq 3f \n" | ||
91 | "2: \n" | ||
92 | "ldmia %1!, { r0-r7 } \n" | ||
93 | "subs %2, %2, #32 \n" | ||
94 | "stmia %0!, { r0-r7 } \n" | ||
95 | "bhi 2b \n" | ||
96 | "3: \n" | ||
97 | : "+r"(out), "+r"(src), "+r"(size) | ||
98 | : | ||
99 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"); | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | /* Channel needs amplitude cut */ | ||
104 | uint32_t s, tmp; | ||
105 | asm volatile( | ||
106 | "1: \n" | ||
107 | "ldr %3, [%1], #4 \n" | ||
108 | "subs %2, %2, #4 \n" | ||
109 | "smulwt %4, %5, %3 \n" | ||
110 | "smulwb %3, %5, %3 \n" | ||
111 | "pkhbt %4, %3, %4, asl #16 \n" | ||
112 | "str %4, [%0], #4 \n" | ||
113 | "bhi 1b \n" | ||
114 | : "+r"(out), "+r"(src), "+r"(size), | ||
115 | "=&r"(s), "=&r"(tmp) | ||
116 | : "r"(amp)); | ||
117 | } | ||
118 | } | ||
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c index c446f98fcf..704296d407 100644 --- a/firmware/target/arm/pcm-pp.c +++ b/firmware/target/arm/pcm-pp.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "sound.h" | 26 | #include "sound.h" |
27 | #include "pcm.h" | 27 | #include "pcm.h" |
28 | #include "pcm_sampr.h" | 28 | #include "pcm_sampr.h" |
29 | #include "pcm-internal.h" | ||
29 | 30 | ||
30 | /** DMA **/ | 31 | /** DMA **/ |
31 | 32 | ||
@@ -115,6 +116,7 @@ void pcm_dma_apply_settings(void) | |||
115 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ | 116 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ |
116 | void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) | 117 | void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) |
117 | { | 118 | { |
119 | bool new_buffer = false; | ||
118 | register size_t size; | 120 | register size_t size; |
119 | 121 | ||
120 | DMA0_STATUS; /* Clear any pending interrupt */ | 122 | DMA0_STATUS; /* Clear any pending interrupt */ |
@@ -136,9 +138,14 @@ void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) | |||
136 | /* Set the new DMA values and activate channel */ | 138 | /* Set the new DMA values and activate channel */ |
137 | DMA0_RAM_ADDR = dma_play_data.addr; | 139 | DMA0_RAM_ADDR = dma_play_data.addr; |
138 | DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START; | 140 | DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START; |
141 | |||
142 | if (new_buffer) | ||
143 | pcm_play_dma_started_callback(); | ||
139 | return; | 144 | return; |
140 | } | 145 | } |
141 | 146 | ||
147 | new_buffer = true; | ||
148 | |||
142 | /* Buffer empty. Try to get more. */ | 149 | /* Buffer empty. Try to get more. */ |
143 | pcm_play_get_more_callback((void **)&dma_play_data.addr, | 150 | pcm_play_get_more_callback((void **)&dma_play_data.addr, |
144 | &dma_play_data.size); | 151 | &dma_play_data.size); |
@@ -181,8 +188,9 @@ void fiq_playback(void) | |||
181 | * r0-r3 and r12 is a working register. | 188 | * r0-r3 and r12 is a working register. |
182 | */ | 189 | */ |
183 | asm volatile ( | 190 | asm volatile ( |
184 | "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ | 191 | "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ |
185 | 192 | ||
193 | "mov r4, #0 \n" /* Was the callback called? */ | ||
186 | #if CONFIG_CPU == PP5002 | 194 | #if CONFIG_CPU == PP5002 |
187 | "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ | 195 | "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ |
188 | "ldr r12, [r12] \n" | 196 | "ldr r12, [r12] \n" |
@@ -212,16 +220,13 @@ void fiq_playback(void) | |||
212 | "tst r1, #1 \n" /* two samples (one word) left? */ | 220 | "tst r1, #1 \n" /* two samples (one word) left? */ |
213 | "ldrne r12, [r8], #4 \n" /* load two samples */ | 221 | "ldrne r12, [r8], #4 \n" /* load two samples */ |
214 | "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ | 222 | "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ |
215 | |||
216 | "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */ | ||
217 | "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */ | ||
218 | #elif SAMPLE_SIZE == 32 | 223 | #elif SAMPLE_SIZE == 32 |
219 | ".check_fifo: \n" | 224 | ".check_fifo: \n" |
220 | "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ | 225 | "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ |
221 | "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */ | 226 | "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */ |
222 | 227 | ||
223 | "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */ | 228 | "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */ |
224 | "beq .exit \n" /* no complete pair? -> exit */ | 229 | "beq .fifo_fill_complete \n" /* no complete pair? -> exit */ |
225 | "cmp r1, r9, lsr #2 \n" /* number of words from source */ | 230 | "cmp r1, r9, lsr #2 \n" /* number of words from source */ |
226 | "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ | 231 | "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ |
227 | "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ | 232 | "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ |
@@ -234,11 +239,23 @@ void fiq_playback(void) | |||
234 | "subs r1, r1, #1 \n" /* one more loop? */ | 239 | "subs r1, r1, #1 \n" /* one more loop? */ |
235 | "bgt .fifo_loop \n" /* yes, continue */ | 240 | "bgt .fifo_loop \n" /* yes, continue */ |
236 | 241 | ||
242 | ".fifo_fill_complete: \n" | ||
243 | #endif | ||
244 | "cmp r4, #0 \n" /* If fill came after get_more... */ | ||
245 | "beq .still_old_buffer \n" | ||
246 | "mov r4, #0 \n" | ||
247 | "ldr r2, =pcm_play_dma_started \n" | ||
248 | "ldrne r2, [r2] \n" | ||
249 | "cmp r2, #0 \n" | ||
250 | "movne lr, pc \n" | ||
251 | "bxne r2 \n" | ||
252 | |||
253 | ".still_old_buffer: \n" | ||
237 | "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */ | 254 | "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */ |
238 | "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */ | 255 | "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */ |
239 | #endif | ||
240 | 256 | ||
241 | ".more_data: \n" | 257 | ".more_data: \n" |
258 | "mov r4, #1 \n" /* Remember we did this */ | ||
242 | "ldr r2, =pcm_play_get_more_callback \n" | 259 | "ldr r2, =pcm_play_get_more_callback \n" |
243 | "mov r0, r11 \n" /* r0 = &p */ | 260 | "mov r0, r11 \n" /* r0 = &p */ |
244 | "add r1, r11, #4 \n" /* r1 = &size */ | 261 | "add r1, r11, #4 \n" /* r1 = &size */ |
@@ -250,7 +267,7 @@ void fiq_playback(void) | |||
250 | 267 | ||
251 | ".exit: \n" /* (r9=0 if stopping, look above) */ | 268 | ".exit: \n" /* (r9=0 if stopping, look above) */ |
252 | "stmia r11, { r8-r9 } \n" /* save p and size */ | 269 | "stmia r11, { r8-r9 } \n" /* save p and size */ |
253 | "ldmfd sp!, { r0-r3, lr } \n" | 270 | "ldmfd sp!, { r0-r4, lr } \n" |
254 | "subs pc, lr, #4 \n" /* FIQ specific return sequence */ | 271 | "subs pc, lr, #4 \n" /* FIQ specific return sequence */ |
255 | ".ltorg \n" | 272 | ".ltorg \n" |
256 | : /* These must only be integers! No regs */ | 273 | : /* These must only be integers! No regs */ |
@@ -264,6 +281,8 @@ void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR; | |||
264 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ | 281 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ |
265 | void fiq_playback(void) | 282 | void fiq_playback(void) |
266 | { | 283 | { |
284 | bool new_buffer = false; | ||
285 | |||
267 | #if CONFIG_CPU == PP5002 | 286 | #if CONFIG_CPU == PP5002 |
268 | inl(0xcf001040); | 287 | inl(0xcf001040); |
269 | #endif | 288 | #endif |
@@ -271,6 +290,10 @@ void fiq_playback(void) | |||
271 | do { | 290 | do { |
272 | while (dma_play_data.size > 0) { | 291 | while (dma_play_data.size > 0) { |
273 | if (IIS_TX_FREE_COUNT < 2) { | 292 | if (IIS_TX_FREE_COUNT < 2) { |
293 | if (new_buffer) { | ||
294 | new_buffer = false; | ||
295 | pcm_play_dma_started_callback(); | ||
296 | } | ||
274 | return; | 297 | return; |
275 | } | 298 | } |
276 | #if SAMPLE_SIZE == 16 | 299 | #if SAMPLE_SIZE == 16 |
@@ -282,9 +305,15 @@ void fiq_playback(void) | |||
282 | dma_play_data.size -= 4; | 305 | dma_play_data.size -= 4; |
283 | } | 306 | } |
284 | 307 | ||
308 | if (new_buffer) { | ||
309 | new_buffer = false; | ||
310 | pcm_play_dma_started_callback(); | ||
311 | } | ||
312 | |||
285 | /* p is empty, get some more data */ | 313 | /* p is empty, get some more data */ |
286 | pcm_play_get_more_callback((void **)&dma_play_data.addr, | 314 | pcm_play_get_more_callback((void **)&dma_play_data.addr, |
287 | &dma_play_data.size); | 315 | &dma_play_data.size); |
316 | new_buffer = true; | ||
288 | } while (dma_play_data.size); | 317 | } while (dma_play_data.size); |
289 | 318 | ||
290 | /* No more data */ | 319 | /* No more data */ |
diff --git a/firmware/target/arm/pcm-telechips.c b/firmware/target/arm/pcm-telechips.c index 851ebee7de..aff43171f6 100644 --- a/firmware/target/arm/pcm-telechips.c +++ b/firmware/target/arm/pcm-telechips.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "sound.h" | 27 | #include "sound.h" |
28 | #include "i2s.h" | 28 | #include "i2s.h" |
29 | #include "pcm.h" | 29 | #include "pcm.h" |
30 | #include "pcm-internal.h" | ||
30 | 31 | ||
31 | struct dma_data | 32 | struct dma_data |
32 | { | 33 | { |
@@ -247,6 +248,8 @@ void fiq_handler(void) | |||
247 | * r0-r3 and r12 is a working register. | 248 | * r0-r3 and r12 is a working register. |
248 | */ | 249 | */ |
249 | asm volatile ( | 250 | asm volatile ( |
251 | "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ | ||
252 | "mov r4, #0 \n" /* Was the callback called? */ | ||
250 | #if defined(CPU_TCC780X) | 253 | #if defined(CPU_TCC780X) |
251 | "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */ | 254 | "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */ |
252 | "ldr r9, =0xf3001004 \n" /* CREQ */ | 255 | "ldr r9, =0xf3001004 \n" /* CREQ */ |
@@ -279,33 +282,41 @@ void fiq_handler(void) | |||
279 | "sub r9, r9, #0x10 \n" /* 4 words written */ | 282 | "sub r9, r9, #0x10 \n" /* 4 words written */ |
280 | "stmia r11, { r8-r9 } \n" /* save p and size */ | 283 | "stmia r11, { r8-r9 } \n" /* save p and size */ |
281 | 284 | ||
285 | "cmp r4, #0 \n" /* Callback called? */ | ||
286 | "beq .exit \n" | ||
287 | /* "mov r4, #0 \n" If get_more could be called multiple times! */ | ||
288 | "ldr r2, =pcm_play_dma_started\n" | ||
289 | "ldr r2, [r2] \n" | ||
290 | "cmp r2, #0 \n" | ||
291 | "blxne r2 \n" | ||
292 | |||
282 | ".exit: \n" | 293 | ".exit: \n" |
294 | "ldmfd sp!, { r0-r4, lr } \n" | ||
283 | "subs pc, lr, #4 \n" /* FIQ specific return sequence */ | 295 | "subs pc, lr, #4 \n" /* FIQ specific return sequence */ |
284 | 296 | ||
285 | ".more_data: \n" | 297 | ".more_data: \n" |
286 | "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */ | 298 | "mov r4, #1 \n" /* Remember we got more data in this FIQ */ |
287 | "ldr r2, =pcm_play_get_more_callback \n" | 299 | "ldr r2, =pcm_play_get_more_callback \n" |
288 | "mov r0, r11 \n" /* r0 = &p */ | 300 | "mov r0, r11 \n" /* r0 = &p */ |
289 | "add r1, r11, #4 \n" /* r1 = &size */ | 301 | "add r1, r11, #4 \n" /* r1 = &size */ |
290 | "blx r2 \n" /* call pcm_play_get_more_callback */ | 302 | "blx r2 \n" /* call pcm_play_get_more_callback */ |
291 | "ldmia r11, { r8-r9 } \n" /* load new p and size */ | 303 | "ldmia r11, { r8-r9 } \n" /* load new p and size */ |
292 | "cmp r9, #0x10 \n" /* did we actually get enough data? */ | 304 | "cmp r9, #0x10 \n" /* did we actually get enough data? */ |
293 | "ldmfd sp!, { r0-r3, lr } \n" | ||
294 | "bpl .fill_fifo \n" /* not stop and enough? refill */ | 305 | "bpl .fill_fifo \n" /* not stop and enough? refill */ |
295 | "b .exit \n" | 306 | "b .exit \n" |
296 | ".ltorg \n" | 307 | ".ltorg \n" |
297 | ); | 308 | ); |
298 | } | 309 | } |
299 | #else /* C version for reference */ | 310 | #else /* C version for reference */ |
300 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | 311 | void fiq_handler(void) ICODE_ATTR; |
301 | void fiq_handler(void) | 312 | void fiq_handler(void) |
302 | { | 313 | { |
303 | asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */ | 314 | register bool new_buffer = false; |
304 | "sub sp, sp, #8 \n"); /* Reserve stack */ | ||
305 | 315 | ||
306 | if (dma_play_data.size < 16) | 316 | if (dma_play_data.size < 16) |
307 | { | 317 | { |
308 | /* p is empty, get some more data */ | 318 | /* p is empty, get some more data */ |
319 | new_buffer = true; | ||
309 | pcm_play_get_more_callback((void**)&dma_play_data.p, | 320 | pcm_play_get_more_callback((void**)&dma_play_data.p, |
310 | &dma_play_data.size); | 321 | &dma_play_data.size); |
311 | } | 322 | } |
@@ -327,9 +338,8 @@ void fiq_handler(void) | |||
327 | /* Clear FIQ status */ | 338 | /* Clear FIQ status */ |
328 | CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; | 339 | CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; |
329 | 340 | ||
330 | asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */ | 341 | if (new_buffer) |
331 | "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ | 342 | pcm_play_dma_started_callback(); |
332 | "subs pc, lr, #4 \n"); /* Return from FIQ */ | ||
333 | } | 343 | } |
334 | #endif | 344 | #endif |
335 | 345 | ||
diff --git a/firmware/target/arm/pnx0101/pcm-pnx0101.c b/firmware/target/arm/pnx0101/pcm-pnx0101.c index 9d4ffbd773..d4c17454ed 100644 --- a/firmware/target/arm/pnx0101/pcm-pnx0101.c +++ b/firmware/target/arm/pnx0101/pcm-pnx0101.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "system.h" | 21 | #include "system.h" |
22 | #include "audio.h" | 22 | #include "audio.h" |
23 | #include "string.h" | 23 | #include "string.h" |
24 | #include "pcm-internal.h" | ||
24 | 25 | ||
25 | #define DMA_BUF_SAMPLES 0x100 | 26 | #define DMA_BUF_SAMPLES 0x100 |
26 | 27 | ||
@@ -63,6 +64,8 @@ static inline void fill_dma_buf(int offset) | |||
63 | 64 | ||
64 | if (pcm_playing && !pcm_paused) | 65 | if (pcm_playing && !pcm_paused) |
65 | { | 66 | { |
67 | bool new_buffer =false; | ||
68 | |||
66 | do | 69 | do |
67 | { | 70 | { |
68 | int count; | 71 | int count; |
@@ -102,10 +105,20 @@ static inline void fill_dma_buf(int offset) | |||
102 | count--; | 105 | count--; |
103 | } | 106 | } |
104 | p = tmp_p; | 107 | p = tmp_p; |
108 | |||
109 | if (new_buffer) | ||
110 | { | ||
111 | new_buffer = false; | ||
112 | pcm_play_dma_started_callback(); | ||
113 | } | ||
114 | |||
105 | if (l >= lend) | 115 | if (l >= lend) |
106 | return; | 116 | return; |
107 | 117 | ||
108 | pcm_play_get_more_callback((void**)&p, &p_size); | 118 | pcm_play_get_more_callback((void**)&p, &p_size); |
119 | |||
120 | if (p_size) | ||
121 | new_buffer = true; | ||
109 | } | 122 | } |
110 | while (p_size); | 123 | while (p_size); |
111 | } | 124 | } |
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c index c1c9017fbb..33194ae5d9 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include "audio.h" | 25 | #include "audio.h" |
26 | #include "sound.h" | 26 | #include "sound.h" |
27 | #include "file.h" | 27 | #include "file.h" |
28 | #include "pcm-internal.h" | ||
28 | 29 | ||
29 | /* PCM interrupt routine lockout */ | 30 | /* PCM interrupt routine lockout */ |
30 | static struct | 31 | static struct |
@@ -235,6 +236,8 @@ void fiq_handler(void) | |||
235 | 236 | ||
236 | /* Re-Activate the channel */ | 237 | /* Re-Activate the channel */ |
237 | DMASKTRIG2 = 0x2; | 238 | DMASKTRIG2 = 0x2; |
239 | |||
240 | pcm_play_dma_started_callback(); | ||
238 | } | 241 | } |
239 | 242 | ||
240 | size_t pcm_get_bytes_waiting(void) | 243 | size_t pcm_get_bytes_waiting(void) |
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c index 01b177da6c..eea4c58e4b 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c | |||
@@ -99,14 +99,15 @@ void TIMER3(void) | |||
99 | INTPND = TIMER3_MASK; | 99 | INTPND = TIMER3_MASK; |
100 | } | 100 | } |
101 | 101 | ||
102 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) | 102 | void beep_play(unsigned int frequency, unsigned int duration, |
103 | unsigned int amplitude) | ||
103 | { | 104 | { |
104 | #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE) | 105 | #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE) |
105 | 106 | ||
106 | unsigned long tcnt, tcmp; | 107 | unsigned long tcnt, tcmp; |
107 | int oldstatus; | 108 | int oldstatus; |
108 | 109 | ||
109 | if (amplitude <= 0) | 110 | if (frequency == 0 || duration == 0 || amplitude == 0) |
110 | { | 111 | { |
111 | beep_stop(); /* won't hear it anyway */ | 112 | beep_stop(); /* won't hear it anyway */ |
112 | return; | 113 | return; |
diff --git a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c index 8a6b62f31f..0c69c1e6d3 100644 --- a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c +++ b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "audio.h" | 26 | #include "audio.h" |
27 | #include "sound.h" | 27 | #include "sound.h" |
28 | #include "file.h" | 28 | #include "file.h" |
29 | #include "pcm-internal.h" | ||
29 | 30 | ||
30 | /* PCM interrupt routine lockout */ | 31 | /* PCM interrupt routine lockout */ |
31 | static struct | 32 | static struct |
@@ -275,6 +276,8 @@ void fiq_handler(void) | |||
275 | 276 | ||
276 | /* Re-Activate the channel */ | 277 | /* Re-Activate the channel */ |
277 | DMASKTRIG2 = 0x2; | 278 | DMASKTRIG2 = 0x2; |
279 | |||
280 | pcm_play_dma_started_callback(); | ||
278 | } | 281 | } |
279 | 282 | ||
280 | size_t pcm_get_bytes_waiting(void) | 283 | size_t pcm_get_bytes_waiting(void) |
diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c index 08086c37d8..14c515ec47 100644 --- a/firmware/target/arm/s5l8700/pcm-s5l8700.c +++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "panic.h" | 27 | #include "panic.h" |
28 | #include "audiohw.h" | 28 | #include "audiohw.h" |
29 | #include "pcm.h" | 29 | #include "pcm.h" |
30 | #include "pcm-internal.h" | ||
30 | #include "pcm_sampr.h" | 31 | #include "pcm_sampr.h" |
31 | #include "dma-target.h" | 32 | #include "dma-target.h" |
32 | #include "mmu-arm.h" | 33 | #include "mmu-arm.h" |
@@ -100,6 +101,7 @@ void pcm_play_unlock(void) | |||
100 | void INT_DMA(void) ICODE_ATTR; | 101 | void INT_DMA(void) ICODE_ATTR; |
101 | void INT_DMA(void) | 102 | void INT_DMA(void) |
102 | { | 103 | { |
104 | bool new_buffer = false; | ||
103 | DMACOM0 = 7; | 105 | DMACOM0 = 7; |
104 | while (!(DMACON0 & (1 << 18))) | 106 | while (!(DMACON0 & (1 << 18))) |
105 | { | 107 | { |
@@ -112,8 +114,12 @@ void INT_DMA(void) | |||
112 | } | 114 | } |
113 | else | 115 | else |
114 | { | 116 | { |
115 | if (!nextsize) pcm_play_get_more_callback((void**)&nextbuf, &nextsize); | 117 | if (!nextsize) |
116 | if (!nextsize) break; | 118 | { |
119 | pcm_play_get_more_callback((void**)&nextbuf, &nextsize); | ||
120 | if (!nextsize) break; | ||
121 | new_buffer = true; | ||
122 | } | ||
117 | queuedsize = MIN(sizeof(dblbuf), nextsize / 2); | 123 | queuedsize = MIN(sizeof(dblbuf), nextsize / 2); |
118 | nextsize -= queuedsize; | 124 | nextsize -= queuedsize; |
119 | queuedbuf = nextbuf + nextsize; | 125 | queuedbuf = nextbuf + nextsize; |
@@ -124,7 +130,14 @@ void INT_DMA(void) | |||
124 | clean_dcache(); | 130 | clean_dcache(); |
125 | DMACOM0 = 4; | 131 | DMACOM0 = 4; |
126 | DMACOM0 = 7; | 132 | DMACOM0 = 7; |
133 | |||
134 | if (new_buffer) | ||
135 | { | ||
136 | pcm_play_dma_started_callback(); | ||
137 | new_buffer = false; | ||
138 | } | ||
127 | } | 139 | } |
140 | |||
128 | } | 141 | } |
129 | 142 | ||
130 | void pcm_play_dma_start(const void* addr, size_t size) | 143 | void pcm_play_dma_start(const void* addr, size_t size) |
diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c index c0498a9ce2..dbadf3bac0 100644 --- a/firmware/target/arm/s5l8702/pcm-s5l8702.c +++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "panic.h" | 27 | #include "panic.h" |
28 | #include "audiohw.h" | 28 | #include "audiohw.h" |
29 | #include "pcm.h" | 29 | #include "pcm.h" |
30 | #include "pcm-internal.h" | ||
30 | #include "pcm_sampr.h" | 31 | #include "pcm_sampr.h" |
31 | #include "mmu-arm.h" | 32 | #include "mmu-arm.h" |
32 | #include "pcm-target.h" | 33 | #include "pcm-target.h" |
@@ -113,6 +114,8 @@ void INT_DMAC0C0(void) | |||
113 | DMAC0C0CONFIG = 0x8a81; | 114 | DMAC0C0CONFIG = 0x8a81; |
114 | } | 115 | } |
115 | else DMAC0C0NEXTLLI = pcm_lli; | 116 | else DMAC0C0NEXTLLI = pcm_lli; |
117 | |||
118 | pcm_play_dma_started_callback(); | ||
116 | } | 119 | } |
117 | 120 | ||
118 | void pcm_play_dma_start(const void* addr, size_t size) | 121 | void pcm_play_dma_start(const void* addr, size_t size) |
diff --git a/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c b/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c index 3c54ce81fb..5ec62cf876 100644 --- a/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c +++ b/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "dm320.h" | 27 | #include "dm320.h" |
28 | #include "audiohw.h" | 28 | #include "audiohw.h" |
29 | #include "dsp-target.h" | 29 | #include "dsp-target.h" |
30 | #include "pcm-internal.h" | ||
30 | 31 | ||
31 | void pcm_play_dma_init(void) | 32 | void pcm_play_dma_init(void) |
32 | { | 33 | { |
diff --git a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c index fb94adae71..90c342e868 100644 --- a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c +++ b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "dsp-target.h" | 28 | #include "dsp-target.h" |
29 | #include "dsp/ipc.h" | 29 | #include "dsp/ipc.h" |
30 | #include "mmu-arm.h" | 30 | #include "mmu-arm.h" |
31 | #include "pcm-internal.h" | ||
31 | 32 | ||
32 | /* This is global to save some latency when pcm_play_dma_get_peak_buffer is | 33 | /* This is global to save some latency when pcm_play_dma_get_peak_buffer is |
33 | * called. | 34 | * called. |
@@ -178,6 +179,8 @@ void DSPHINT(void) | |||
178 | 179 | ||
179 | DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", | 180 | DEBUGF("pcm_sdram at 0x%08lx, sdem_addr 0x%08lx", |
180 | (unsigned long)start, (unsigned long)sdem_addr); | 181 | (unsigned long)start, (unsigned long)sdem_addr); |
182 | |||
183 | pcm_play_dma_started_callback(); | ||
181 | } | 184 | } |
182 | 185 | ||
183 | break; | 186 | break; |
diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c index a06542c31f..85eeaec815 100644 --- a/firmware/target/coldfire/pcm-coldfire.c +++ b/firmware/target/coldfire/pcm-coldfire.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT) | 28 | #if defined(HAVE_SPDIF_REC) || defined(HAVE_SPDIF_OUT) |
29 | #include "spdif.h" | 29 | #include "spdif.h" |
30 | #endif | 30 | #endif |
31 | #include "pcm-internal.h" | ||
31 | 32 | ||
32 | #define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ | 33 | #define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ |
33 | (IIS_PLAY & (7 << 8)) | \ | 34 | (IIS_PLAY & (7 << 8)) | \ |
@@ -318,6 +319,9 @@ void DMA0(void) | |||
318 | SAR0 = (unsigned long)start; /* Source address */ | 319 | SAR0 = (unsigned long)start; /* Source address */ |
319 | BCR0 = size; /* Bytes to transfer */ | 320 | BCR0 = size; /* Bytes to transfer */ |
320 | or_l(DMA_EEXT | DMA_INT, &DCR0); /* per request and int ON */ | 321 | or_l(DMA_EEXT | DMA_INT, &DCR0); /* per request and int ON */ |
322 | |||
323 | /* Call buffer callback */ | ||
324 | pcm_play_dma_started_callback(); | ||
321 | } | 325 | } |
322 | /* else inished playing */ | 326 | /* else inished playing */ |
323 | } /* DMA0 */ | 327 | } /* DMA0 */ |
diff --git a/firmware/target/coldfire/pcm-mixer-coldfire.c b/firmware/target/coldfire/pcm-mixer-coldfire.c new file mode 100644 index 0000000000..d8318fffaf --- /dev/null +++ b/firmware/target/coldfire/pcm-mixer-coldfire.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by Michael Sevakis | ||
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 | #define MIXER_OPTIMIZED_MIX_SAMPLES | ||
23 | #define MIXER_OPTIMIZED_WRITE_SAMPLES | ||
24 | static struct emac_context | ||
25 | { | ||
26 | unsigned long r[4]; | ||
27 | } emac_context IBSS_ATTR; | ||
28 | |||
29 | /* Save emac context affected in ISR */ | ||
30 | static FORCE_INLINE void save_emac_context(void) | ||
31 | { | ||
32 | asm volatile ( | ||
33 | "move.l %%macsr, %%d0 \n" | ||
34 | "move.l %%accext01, %%d1 \n" | ||
35 | "movclr.l %%acc0, %%a0 \n" | ||
36 | "movclr.l %%acc1, %%a1 \n" | ||
37 | "movem.l %%d0-%%d1/%%a0-%%a1, (%0) \n" | ||
38 | : | ||
39 | : "a"(&emac_context) | ||
40 | : "d0", "d1", "a0", "a1"); | ||
41 | } | ||
42 | |||
43 | /* Restore emac context affected in ISR */ | ||
44 | static FORCE_INLINE void restore_emac_context(void) | ||
45 | { | ||
46 | asm volatile ( | ||
47 | "movem.l (%0), %%d0-%%d1/%%a0-%%a1 \n" | ||
48 | "move.l %%a1, %%acc1 \n" | ||
49 | "move.l %%a0, %%acc0 \n" | ||
50 | "move.l %%d1, %%accext01 \n" | ||
51 | "move.l %%d0, %%macsr \n" | ||
52 | : | ||
53 | : "a"(&emac_context) | ||
54 | : "d0", "d1", "a0", "a1"); | ||
55 | } | ||
56 | |||
57 | /* Mix channels' samples and apply gain factors */ | ||
58 | static FORCE_INLINE void mix_samples(void *out, | ||
59 | void *src0, | ||
60 | int32_t src0_amp, | ||
61 | void *src1, | ||
62 | int32_t src1_amp, | ||
63 | size_t size) | ||
64 | { | ||
65 | uint32_t s0, s1, s2, s3; | ||
66 | save_emac_context(); | ||
67 | coldfire_set_macsr(EMAC_ROUND | EMAC_SATURATE); | ||
68 | |||
69 | asm volatile ( | ||
70 | "move.l (%1)+, %5 \n" | ||
71 | "1: \n" | ||
72 | "movea.w %5, %4 \n" | ||
73 | "asr.l %10, %5 \n" | ||
74 | "mac.l %4, %8, %%acc0 \n" | ||
75 | "mac.l %5, %8, (%2)+, %5, %%acc1 \n" | ||
76 | "movea.w %5, %4 \n" | ||
77 | "asr.l %10, %5 \n" | ||
78 | "mac.l %4, %9, %%acc0 \n" | ||
79 | "mac.l %5, %9, (%1)+, %5, %%acc1 \n" | ||
80 | "movclr.l %%acc0, %6 \n" | ||
81 | "movclr.l %%acc1, %7 \n" | ||
82 | "swap.w %6 \n" | ||
83 | "move.w %6, %7 \n" | ||
84 | "move.l %7, (%0)+ \n" | ||
85 | "subq.l #4, %3 \n" | ||
86 | "bhi.b 1b \n" | ||
87 | : "+a"(out), "+a"(src0), "+a"(src1), "+d"(size), | ||
88 | "=&a"(s0), "=&d"(s1), "=&d"(s2), "=&d"(s3) | ||
89 | : "r"(src0_amp), "r"(src1_amp), "d"(16) | ||
90 | ); | ||
91 | |||
92 | restore_emac_context(); | ||
93 | } | ||
94 | |||
95 | /* Write channel's samples and apply gain factor */ | ||
96 | static FORCE_INLINE void write_samples(void *out, | ||
97 | void *src, | ||
98 | int32_t amp, | ||
99 | size_t size) | ||
100 | { | ||
101 | if (LIKELY(amp == MIX_AMP_UNITY)) | ||
102 | { | ||
103 | /* Channel is unity amplitude */ | ||
104 | memcpy(out, src, size); | ||
105 | } | ||
106 | else | ||
107 | { | ||
108 | /* Channel needs amplitude cut */ | ||
109 | uint32_t s0, s1, s2, s3; | ||
110 | save_emac_context(); | ||
111 | coldfire_set_macsr(EMAC_ROUND | EMAC_SATURATE); | ||
112 | |||
113 | asm volatile ( | ||
114 | "move.l (%1)+, %4 \n" | ||
115 | "1: \n" | ||
116 | "movea.w %4, %3 \n" | ||
117 | "asr.l %8, %4 \n" | ||
118 | "mac.l %3, %7, %%acc0 \n" | ||
119 | "mac.l %4, %7, (%1)+, %4, %%acc1 \n" | ||
120 | "movclr.l %%acc0, %5 \n" | ||
121 | "movclr.l %%acc1, %6 \n" | ||
122 | "swap.w %5 \n" | ||
123 | "move.w %5, %6 \n" | ||
124 | "move.l %6, (%0)+ \n" | ||
125 | "subq.l #4, %2 \n" | ||
126 | "bhi.b 1b \n" | ||
127 | : "+a"(out), "+a"(src), "+d"(size), | ||
128 | "=&a"(s0), "=&d"(s1), "=&d"(s2), "=&d"(s3) | ||
129 | : "r"(amp), "d"(16) | ||
130 | ); | ||
131 | |||
132 | restore_emac_context(); | ||
133 | } | ||
134 | } | ||
diff --git a/firmware/target/hosted/android/pcm-android.c b/firmware/target/hosted/android/pcm-android.c index 88792cd76f..cbd6cb3228 100644 --- a/firmware/target/hosted/android/pcm-android.c +++ b/firmware/target/hosted/android/pcm-android.c | |||
@@ -23,14 +23,18 @@ | |||
23 | #include <stdbool.h> | 23 | #include <stdbool.h> |
24 | #define _SYSTEM_WITH_JNI /* for getJavaEnvironment */ | 24 | #define _SYSTEM_WITH_JNI /* for getJavaEnvironment */ |
25 | #include <system.h> | 25 | #include <system.h> |
26 | #include <pthread.h> | ||
26 | #include "debug.h" | 27 | #include "debug.h" |
27 | #include "pcm.h" | 28 | #include "pcm.h" |
29 | #include "pcm-internal.h" | ||
28 | 30 | ||
29 | extern JNIEnv *env_ptr; | 31 | extern JNIEnv *env_ptr; |
30 | 32 | ||
31 | /* infos about our pcm chunks */ | 33 | /* infos about our pcm chunks */ |
32 | static size_t pcm_data_size; | 34 | static size_t pcm_data_size; |
33 | static char *pcm_data_start; | 35 | static char *pcm_data_start; |
36 | static int audio_locked = 0; | ||
37 | static pthread_mutex_t audio_lock_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
34 | 38 | ||
35 | /* cache frequently called methods */ | 39 | /* cache frequently called methods */ |
36 | static jmethodID play_pause_method; | 40 | static jmethodID play_pause_method; |
@@ -42,6 +46,20 @@ static jobject RockboxPCM_instance; | |||
42 | 46 | ||
43 | 47 | ||
44 | /* | 48 | /* |
49 | * mutex lock/unlock wrappers neatness' sake | ||
50 | */ | ||
51 | static inline void lock_audio(void) | ||
52 | { | ||
53 | pthread_mutex_lock(&audio_lock_mutex); | ||
54 | } | ||
55 | |||
56 | static inline void unlock_audio(void) | ||
57 | { | ||
58 | pthread_mutex_unlock(&audio_lock_mutex); | ||
59 | } | ||
60 | |||
61 | |||
62 | /* | ||
45 | * write pcm samples to the hardware. Calls AudioTrack.write directly (which | 63 | * write pcm samples to the hardware. Calls AudioTrack.write directly (which |
46 | * is usually a blocking call) | 64 | * is usually a blocking call) |
47 | * | 65 | * |
@@ -54,10 +72,17 @@ JNIEXPORT jint JNICALL | |||
54 | Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this, | 72 | Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this, |
55 | jbyteArray temp_array, jint max_size) | 73 | jbyteArray temp_array, jint max_size) |
56 | { | 74 | { |
75 | bool new_buffer = false; | ||
76 | |||
77 | lock_audio(); | ||
78 | |||
57 | jint left = max_size; | 79 | jint left = max_size; |
58 | 80 | ||
59 | if (!pcm_data_size) /* get some initial data */ | 81 | if (!pcm_data_size) /* get some initial data */ |
82 | { | ||
83 | new_buffer = true; | ||
60 | pcm_play_get_more_callback((void**) &pcm_data_start, &pcm_data_size); | 84 | pcm_play_get_more_callback((void**) &pcm_data_start, &pcm_data_size); |
85 | } | ||
61 | 86 | ||
62 | while(left > 0 && pcm_data_size) | 87 | while(left > 0 && pcm_data_size) |
63 | { | 88 | { |
@@ -70,23 +95,49 @@ Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this, | |||
70 | 95 | ||
71 | ret = (*env)->CallIntMethod(env, this, write_method, | 96 | ret = (*env)->CallIntMethod(env, this, write_method, |
72 | temp_array, 0, transfer_size); | 97 | temp_array, 0, transfer_size); |
98 | |||
99 | if (new_buffer) | ||
100 | { | ||
101 | new_buffer = false; | ||
102 | pcm_play_dma_started_callback(); | ||
103 | |||
104 | /* NOTE: might need to release the mutex and sleep here if the | ||
105 | buffer is shorter than the required buffer (like pcm-sdl.c) to | ||
106 | have the mixer clocked at a regular interval */ | ||
107 | } | ||
108 | |||
73 | if (ret < 0) | 109 | if (ret < 0) |
110 | { | ||
111 | unlock_audio(); | ||
74 | return ret; | 112 | return ret; |
113 | } | ||
75 | 114 | ||
76 | if (pcm_data_size == 0) /* need new data */ | 115 | if (pcm_data_size == 0) /* need new data */ |
116 | { | ||
117 | new_buffer = true; | ||
77 | pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size); | 118 | pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size); |
119 | } | ||
78 | else /* increment data pointer and feed more */ | 120 | else /* increment data pointer and feed more */ |
79 | pcm_data_start += transfer_size; | 121 | pcm_data_start += transfer_size; |
80 | } | 122 | } |
123 | |||
124 | if (new_buffer && pcm_data_size) | ||
125 | pcm_play_dma_started_callback(); | ||
126 | |||
127 | unlock_audio(); | ||
81 | return max_size - left; | 128 | return max_size - left; |
82 | } | 129 | } |
83 | 130 | ||
84 | void pcm_play_lock(void) | 131 | void pcm_play_lock(void) |
85 | { | 132 | { |
133 | if (++audio_locked == 1) | ||
134 | lock_audio(); | ||
86 | } | 135 | } |
87 | 136 | ||
88 | void pcm_play_unlock(void) | 137 | void pcm_play_unlock(void) |
89 | { | 138 | { |
139 | if (--audio_locked == 0) | ||
140 | unlock_audio(); | ||
90 | } | 141 | } |
91 | 142 | ||
92 | void pcm_dma_apply_settings(void) | 143 | void pcm_dma_apply_settings(void) |
@@ -153,8 +204,6 @@ void pcm_play_dma_init(void) | |||
153 | set_volume_method = e->GetMethodID(env_ptr, RockboxPCM_class, "set_volume", "(I)V"); | 204 | set_volume_method = e->GetMethodID(env_ptr, RockboxPCM_class, "set_volume", "(I)V"); |
154 | stop_method = e->GetMethodID(env_ptr, RockboxPCM_class, "stop", "()V"); | 205 | stop_method = e->GetMethodID(env_ptr, RockboxPCM_class, "stop", "()V"); |
155 | write_method = e->GetMethodID(env_ptr, RockboxPCM_class, "write", "([BII)I"); | 206 | write_method = e->GetMethodID(env_ptr, RockboxPCM_class, "write", "([BII)I"); |
156 | /* get initial pcm data, if any */ | ||
157 | pcm_play_get_more_callback((void*)&pcm_data_start, &pcm_data_size); | ||
158 | } | 207 | } |
159 | 208 | ||
160 | void pcm_postinit(void) | 209 | void pcm_postinit(void) |
@@ -173,6 +222,7 @@ void pcm_shutdown(void) | |||
173 | JNIEnv e = *env_ptr; | 222 | JNIEnv e = *env_ptr; |
174 | jmethodID release = e->GetMethodID(env_ptr, RockboxPCM_class, "release", "()V"); | 223 | jmethodID release = e->GetMethodID(env_ptr, RockboxPCM_class, "release", "()V"); |
175 | e->CallVoidMethod(env_ptr, RockboxPCM_instance, release); | 224 | e->CallVoidMethod(env_ptr, RockboxPCM_instance, release); |
225 | pthread_mutex_destroy(&audio_lock_mutex); | ||
176 | } | 226 | } |
177 | 227 | ||
178 | /* Due to limitations of default_event_handler(), parameters gets swallowed when | 228 | /* Due to limitations of default_event_handler(), parameters gets swallowed when |
diff --git a/firmware/target/hosted/maemo/pcm-gstreamer.c b/firmware/target/hosted/maemo/pcm-gstreamer.c index e3e40f0619..6069801fba 100644 --- a/firmware/target/hosted/maemo/pcm-gstreamer.c +++ b/firmware/target/hosted/maemo/pcm-gstreamer.c | |||
@@ -54,6 +54,7 @@ | |||
54 | #endif | 54 | #endif |
55 | 55 | ||
56 | #include "pcm.h" | 56 | #include "pcm.h" |
57 | #include "pcm-internal.h" | ||
57 | #include "pcm_sampr.h" | 58 | #include "pcm_sampr.h" |
58 | 59 | ||
59 | /*#define LOGF_ENABLE*/ | 60 | /*#define LOGF_ENABLE*/ |
@@ -182,6 +183,8 @@ static void feed_data(GstElement * appsrc, guint size_hint, void *unused) | |||
182 | 183 | ||
183 | if (ret != 0) | 184 | if (ret != 0) |
184 | DEBUGF("push-buffer error result: %d\n", ret); | 185 | DEBUGF("push-buffer error result: %d\n", ret); |
186 | |||
187 | pcm_play_dma_started_callback(); | ||
185 | } else | 188 | } else |
186 | { | 189 | { |
187 | DEBUGF("feed_data: No Data.\n"); | 190 | DEBUGF("feed_data: No Data.\n"); |
diff --git a/firmware/target/hosted/sdl/pcm-sdl.c b/firmware/target/hosted/sdl/pcm-sdl.c index 7780083b99..dfdd90f29b 100644 --- a/firmware/target/hosted/sdl/pcm-sdl.c +++ b/firmware/target/hosted/sdl/pcm-sdl.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "sound.h" | 30 | #include "sound.h" |
31 | #include "audiohw.h" | 31 | #include "audiohw.h" |
32 | #include "system.h" | 32 | #include "system.h" |
33 | #include "panic.h" | ||
33 | 34 | ||
34 | #ifdef HAVE_RECORDING | 35 | #ifdef HAVE_RECORDING |
35 | #include "audiohw.h" | 36 | #include "audiohw.h" |
@@ -39,6 +40,7 @@ | |||
39 | #endif | 40 | #endif |
40 | 41 | ||
41 | #include "pcm.h" | 42 | #include "pcm.h" |
43 | #include "pcm-internal.h" | ||
42 | #include "pcm_sampr.h" | 44 | #include "pcm_sampr.h" |
43 | 45 | ||
44 | /*#define LOGF_ENABLE*/ | 46 | /*#define LOGF_ENABLE*/ |
@@ -71,15 +73,19 @@ static struct pcm_udata | |||
71 | 73 | ||
72 | static SDL_AudioSpec obtained; | 74 | static SDL_AudioSpec obtained; |
73 | static SDL_AudioCVT cvt; | 75 | static SDL_AudioCVT cvt; |
76 | static int audio_locked = 0; | ||
77 | static SDL_mutex *audio_lock; | ||
74 | 78 | ||
75 | void pcm_play_lock(void) | 79 | void pcm_play_lock(void) |
76 | { | 80 | { |
77 | SDL_LockAudio(); | 81 | if (++audio_locked == 1) |
82 | SDL_LockMutex(audio_lock); | ||
78 | } | 83 | } |
79 | 84 | ||
80 | void pcm_play_unlock(void) | 85 | void pcm_play_unlock(void) |
81 | { | 86 | { |
82 | SDL_UnlockAudio(); | 87 | if (--audio_locked == 0) |
88 | SDL_UnlockMutex(audio_lock); | ||
83 | } | 89 | } |
84 | 90 | ||
85 | static void pcm_dma_apply_settings_nolock(void) | 91 | static void pcm_dma_apply_settings_nolock(void) |
@@ -227,14 +233,19 @@ static void write_to_soundcard(struct pcm_udata *udata) | |||
227 | static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) | 233 | static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) |
228 | { | 234 | { |
229 | logf("sdl_audio_callback: len %d, pcm %d\n", len, pcm_data_size); | 235 | logf("sdl_audio_callback: len %d, pcm %d\n", len, pcm_data_size); |
236 | |||
237 | bool new_buffer = false; | ||
230 | udata->stream = stream; | 238 | udata->stream = stream; |
231 | 239 | ||
240 | SDL_LockMutex(audio_lock); | ||
241 | |||
232 | /* Write what we have in the PCM buffer */ | 242 | /* Write what we have in the PCM buffer */ |
233 | if (pcm_data_size > 0) | 243 | if (pcm_data_size > 0) |
234 | goto start; | 244 | goto start; |
235 | 245 | ||
236 | /* Audio card wants more? Get some more then. */ | 246 | /* Audio card wants more? Get some more then. */ |
237 | while (len > 0) { | 247 | while (len > 0) { |
248 | new_buffer = true; | ||
238 | pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size); | 249 | pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size); |
239 | start: | 250 | start: |
240 | if (pcm_data_size != 0) { | 251 | if (pcm_data_size != 0) { |
@@ -246,6 +257,28 @@ static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) | |||
246 | udata->num_in *= pcm_sample_bytes; | 257 | udata->num_in *= pcm_sample_bytes; |
247 | udata->num_out *= pcm_sample_bytes; | 258 | udata->num_out *= pcm_sample_bytes; |
248 | 259 | ||
260 | |||
261 | if (new_buffer) | ||
262 | { | ||
263 | new_buffer = false; | ||
264 | pcm_play_dma_started_callback(); | ||
265 | |||
266 | if ((size_t)len > udata->num_out) | ||
267 | { | ||
268 | int delay = pcm_data_size*250 / pcm_sampr - 1; | ||
269 | |||
270 | if (delay > 0) | ||
271 | { | ||
272 | SDL_UnlockMutex(audio_lock); | ||
273 | SDL_Delay(delay); | ||
274 | SDL_LockMutex(audio_lock); | ||
275 | |||
276 | if (!pcm_is_playing()) | ||
277 | break; | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
249 | pcm_data += udata->num_in; | 282 | pcm_data += udata->num_in; |
250 | pcm_data_size -= udata->num_in; | 283 | pcm_data_size -= udata->num_in; |
251 | udata->stream += udata->num_out; | 284 | udata->stream += udata->num_out; |
@@ -255,6 +288,8 @@ static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) | |||
255 | break; | 288 | break; |
256 | } | 289 | } |
257 | } | 290 | } |
291 | |||
292 | SDL_UnlockMutex(audio_lock); | ||
258 | } | 293 | } |
259 | 294 | ||
260 | const void * pcm_play_dma_get_peak_buffer(int *count) | 295 | const void * pcm_play_dma_get_peak_buffer(int *count) |
@@ -320,6 +355,14 @@ void pcm_play_dma_init(void) | |||
320 | return; | 355 | return; |
321 | } | 356 | } |
322 | 357 | ||
358 | audio_lock = SDL_CreateMutex(); | ||
359 | |||
360 | if (!audio_lock) | ||
361 | { | ||
362 | panicf("Could not create audio_lock\n"); | ||
363 | return; | ||
364 | } | ||
365 | |||
323 | SDL_AudioSpec wanted_spec; | 366 | SDL_AudioSpec wanted_spec; |
324 | #ifdef DEBUG | 367 | #ifdef DEBUG |
325 | udata.debug = NULL; | 368 | udata.debug = NULL; |
diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c index 5cd9c33e18..cfc3c9ef8e 100644 --- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include "audio.h" | 25 | #include "audio.h" |
26 | #include "sound.h" | 26 | #include "sound.h" |
27 | #include "pcm.h" | 27 | #include "pcm.h" |
28 | #include "pcm-internal.h" | ||
28 | #include "jz4740.h" | 29 | #include "jz4740.h" |
29 | 30 | ||
30 | 31 | ||
@@ -109,6 +110,7 @@ static inline void play_dma_callback(void) | |||
109 | { | 110 | { |
110 | set_dma(start, size); | 111 | set_dma(start, size); |
111 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; | 112 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; |
113 | pcm_play_dma_started_callback(); | ||
112 | } | 114 | } |
113 | } | 115 | } |
114 | 116 | ||