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/export | |
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/export')
-rw-r--r-- | firmware/export/config.h | 5 | ||||
-rw-r--r-- | firmware/export/pcm-internal.h | 81 | ||||
-rw-r--r-- | firmware/export/pcm.h | 44 | ||||
-rw-r--r-- | firmware/export/pcm_mixer.h | 102 |
4 files changed, 190 insertions, 42 deletions
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 */ | ||