diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-08-28 07:45:35 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-08-28 07:45:35 +0000 |
commit | 7ad2cad173ffa094bb285112582afee1c9aea4e5 (patch) | |
tree | ce23e816cfdffb1767ebe44f4f960c304d8a5fb9 /apps/pcmbuf.c | |
parent | 463b3ed8b2630d1b9d656dd2a52bbcbd429b4c08 (diff) | |
download | rockbox-7ad2cad173ffa094bb285112582afee1c9aea4e5.tar.gz rockbox-7ad2cad173ffa094bb285112582afee1c9aea4e5.zip |
Commit work started in FS#12153 to put timing/position information in PCM
buffer chunks.
* Samples and position indication is closely associated with audio data
instead of compensating by a latency constant. Alleviates problems with
using the elapsed as a track indicator where it could be off by several
steps.
* Timing is accurate throughout track even if resampling for pitch shift,
whereas before it updated during transition latency at the normal 1:1 rate.
* Simpler PCM buffer with a constant chunk size, no linked lists.
In converting crossfade, a minor change was made to not change the WPS until
the fade-in of the incoming track, whereas before it would change upon the
start of the fade-out of the outgoing track possibly having the WPS change
with far too much lead time.
Codec changes are to set elapsed times *before* writing next PCM frame because
time and position data last set are saved in the next committed PCM chunk.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30366 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r-- | apps/pcmbuf.c | 1415 |
1 files changed, 762 insertions, 653 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 8736fe2ae2..f57021d237 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2005 by Miika Pekkarinen | 10 | * Copyright (C) 2005 by Miika Pekkarinen |
11 | * Copyright (C) 2011 by Michael Sevakis | ||
11 | * | 12 | * |
12 | * This program is free software; you can redistribute it and/or | 13 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 14 | * modify it under the terms of the GNU General Public License |
@@ -35,59 +36,69 @@ | |||
35 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 36 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
36 | #include "cpu.h" | 37 | #include "cpu.h" |
37 | #endif | 38 | #endif |
38 | #include <string.h> | ||
39 | #include "settings.h" | 39 | #include "settings.h" |
40 | #include "audio.h" | 40 | #include "audio.h" |
41 | #include "voice_thread.h" | 41 | #include "voice_thread.h" |
42 | #include "dsp.h" | 42 | #include "dsp.h" |
43 | 43 | ||
44 | #define PCMBUF_TARGET_CHUNK 32768 /* This is the target fill size of chunks | 44 | /* This is the target fill size of chunks on the pcm buffer |
45 | on the pcm buffer */ | 45 | Can be any number of samples but power of two sizes make for faster and |
46 | #define PCMBUF_MINAVG_CHUNK 24576 /* This is the minimum average size of | 46 | smaller math - must be < 65536 bytes */ |
47 | chunks on the pcm buffer (or we run out | 47 | #define PCMBUF_CHUNK_SIZE 8192u |
48 | of buffer descriptors, which is | 48 | #define PCMBUF_GUARD_SIZE 1024u |
49 | non-fatal) */ | 49 | |
50 | #define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than | 50 | /* Mnemonics for common data commit thresholds */ |
51 | this to the DMA */ | 51 | #define COMMIT_CHUNKS PCMBUF_CHUNK_SIZE |
52 | #define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */ | 52 | #define COMMIT_ALL_DATA 1u |
53 | 53 | ||
54 | /* number of bytes played per second (sample rate * 2 channels * 2 bytes/sample) */ | 54 | /* Size of the crossfade buffer where codec data is written to be faded |
55 | on commit */ | ||
56 | #define CROSSFADE_BUFSIZE 8192u | ||
57 | |||
58 | /* Number of bytes played per second: | ||
59 | (sample rate * 2 channels * 2 bytes/sample) */ | ||
55 | #define BYTERATE (NATIVE_FREQUENCY * 4) | 60 | #define BYTERATE (NATIVE_FREQUENCY * 4) |
56 | 61 | ||
57 | #if MEMORYSIZE > 2 | 62 | #if MEMORYSIZE > 2 |
58 | /* Keep watermark high for iPods at least (2s) */ | 63 | /* Keep watermark high for large memory target - at least (2s) */ |
59 | #define PCMBUF_WATERMARK (BYTERATE * 2) | 64 | #define PCMBUF_WATERMARK (BYTERATE * 2) |
65 | #define MIN_BUFFER_SIZE (BYTERATE * 3) | ||
60 | #else | 66 | #else |
61 | #define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */ | 67 | #define PCMBUF_WATERMARK (BYTERATE / 4) /* 0.25 seconds */ |
68 | #define MIN_BUFFER_SIZE (BYTERATE * 1) | ||
62 | #endif | 69 | #endif |
63 | 70 | ||
64 | /* Structure we can use to queue pcm chunks in memory to be played | 71 | /* Describes each audio packet - keep it small since there are many of them */ |
65 | * by the driver code. */ | ||
66 | struct chunkdesc | 72 | struct chunkdesc |
67 | { | 73 | { |
68 | unsigned char *addr; | 74 | uint16_t size; /* Actual size (0 < size <= PCMBUF_CHUNK_SIZE) */ |
69 | size_t size; | 75 | uint8_t is_end; /* Flag indicating end of track */ |
70 | struct chunkdesc* link; | 76 | uint8_t pos_key; /* Who put the position info in (0 = undefined) */ |
71 | /* true if last chunk in the track */ | 77 | unsigned long elapsed; /* Elapsed time to use */ |
72 | bool end_of_track; | 78 | off_t offset; /* Offset to use */ |
73 | }; | 79 | }; |
74 | 80 | ||
75 | #define NUM_CHUNK_DESCS(bufsize) \ | 81 | /* General PCM buffer data */ |
76 | ((bufsize) / PCMBUF_MINAVG_CHUNK) | 82 | #define INVALID_BUF_INDEX ((size_t)0 - (size_t)1) |
83 | |||
84 | static unsigned char *pcmbuf_buffer; | ||
85 | static unsigned char *pcmbuf_guardbuf; | ||
86 | static size_t pcmbuf_size; | ||
87 | static struct chunkdesc *pcmbuf_descriptors; | ||
88 | static unsigned int pcmbuf_desc_count; | ||
89 | static unsigned int position_key = 1; | ||
90 | |||
91 | static size_t chunk_ridx; | ||
92 | static size_t chunk_widx; | ||
93 | |||
94 | static size_t pcmbuf_bytes_waiting; | ||
77 | 95 | ||
78 | /* Size of the PCM buffer. */ | 96 | static size_t pcmbuf_watermark; |
79 | static size_t pcmbuf_size = 0; | 97 | static struct chunkdesc *current_desc; |
80 | static char *pcmbuf_bufend; | ||
81 | static char *pcmbuffer; | ||
82 | /* Current PCM buffer write index. */ | ||
83 | static size_t pcmbuffer_pos; | ||
84 | /* Amount pcmbuffer_pos will be increased.*/ | ||
85 | static size_t pcmbuffer_fillpos; | ||
86 | 98 | ||
87 | static struct chunkdesc *first_desc; | 99 | static bool low_latency_mode = false; |
88 | 100 | ||
89 | /* Gapless playback */ | 101 | static bool pcmbuf_sync_position = false; |
90 | static bool track_transition; | ||
91 | 102 | ||
92 | /* Fade effect */ | 103 | /* Fade effect */ |
93 | static unsigned int fade_vol = MIX_AMP_UNITY; | 104 | static unsigned int fade_vol = MIX_AMP_UNITY; |
@@ -104,184 +115,179 @@ static bool soft_mode = false; | |||
104 | 115 | ||
105 | #ifdef HAVE_CROSSFADE | 116 | #ifdef HAVE_CROSSFADE |
106 | /* Crossfade buffer */ | 117 | /* Crossfade buffer */ |
107 | static char *fadebuf; | 118 | static unsigned char *crossfade_buffer; |
108 | 119 | ||
109 | /* Crossfade related state */ | 120 | /* Crossfade related state */ |
110 | static bool crossfade_enabled; | 121 | static int crossfade_setting; |
111 | static bool crossfade_enable_request; | 122 | static int crossfade_enable_request; |
112 | static bool crossfade_mixmode; | 123 | static bool crossfade_mixmode; |
113 | static bool crossfade_auto_skip; | 124 | static bool crossfade_auto_skip; |
114 | static bool crossfade_active; | 125 | |
115 | static bool crossfade_track_change_started; | 126 | static enum |
127 | { | ||
128 | CROSSFADE_INACTIVE = 0, | ||
129 | CROSSFADE_TRACK_CHANGE_STARTED, | ||
130 | CROSSFADE_ACTIVE, | ||
131 | } crossfade_status = CROSSFADE_INACTIVE; | ||
116 | 132 | ||
117 | /* Track the current location for processing crossfade */ | 133 | /* Track the current location for processing crossfade */ |
118 | static struct chunkdesc *crossfade_chunk; | 134 | static size_t crossfade_index; |
119 | static size_t crossfade_sample; | ||
120 | 135 | ||
121 | /* Counters for fading in new data */ | 136 | /* Counters for fading in new data */ |
122 | static size_t crossfade_fade_in_total; | 137 | static size_t crossfade_fade_in_total; |
123 | static size_t crossfade_fade_in_rem; | 138 | static size_t crossfade_fade_in_rem; |
124 | #endif | ||
125 | 139 | ||
126 | static struct chunkdesc *read_chunk; | 140 | /* Defines for operations on position info when mixing/fading - |
127 | static struct chunkdesc *read_end_chunk; | 141 | passed in offset parameter */ |
128 | static struct chunkdesc *write_chunk; | 142 | enum |
129 | static struct chunkdesc *write_end_chunk; | 143 | { |
130 | static size_t last_chunksize; | 144 | MIXFADE_KEEP_POS = -1, /* Keep position info in chunk */ |
131 | 145 | MIXFADE_NULLIFY_POS = -2, /* Ignore position info in chunk */ | |
132 | static size_t pcmbuf_unplayed_bytes; | 146 | /* Positive values cause stamping/restamping */ |
133 | static size_t pcmbuf_watermark; | 147 | }; |
134 | 148 | ||
135 | static bool low_latency_mode = false; | 149 | static void crossfade_start(void); |
136 | static bool flush_pcmbuf = false; | 150 | static void write_to_crossfade(size_t size, unsigned long elapsed, |
151 | off_t offset); | ||
152 | static void pcmbuf_finish_crossfade_enable(void); | ||
153 | #endif /* HAVE_CROSSFADE */ | ||
137 | 154 | ||
155 | /* Thread */ | ||
138 | #ifdef HAVE_PRIORITY_SCHEDULING | 156 | #ifdef HAVE_PRIORITY_SCHEDULING |
139 | static int codec_thread_priority = PRIORITY_PLAYBACK; | 157 | static int codec_thread_priority = PRIORITY_PLAYBACK; |
140 | #endif | 158 | #endif |
141 | 159 | ||
142 | /* Helpful macros for use in conditionals this assumes some of the above | 160 | /* Helpful macros for use in conditionals this assumes some of the above |
143 | * static variable names */ | 161 | * static variable names */ |
144 | #define COMMIT_IF_NEEDED if(pcmbuffer_fillpos > PCMBUF_TARGET_CHUNK || \ | 162 | #define DATA_LEVEL(quarter_secs) (NATIVE_FREQUENCY * (quarter_secs)) |
145 | (pcmbuffer_pos + pcmbuffer_fillpos) >= pcmbuf_size) commit_chunk(false) | ||
146 | #define LOW_DATA(quarter_secs) \ | ||
147 | (pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs) | ||
148 | |||
149 | #ifdef HAVE_CROSSFADE | ||
150 | static void crossfade_start(void); | ||
151 | static void write_to_crossfade(size_t length); | ||
152 | static void pcmbuf_finish_crossfade_enable(void); | ||
153 | #endif | ||
154 | 163 | ||
155 | /* Callbacks into playback.c */ | 164 | /* Callbacks into playback.c */ |
156 | extern void audio_pcmbuf_position_callback(unsigned int time); | 165 | extern void audio_pcmbuf_position_callback(unsigned long elapsed, |
166 | off_t offset, unsigned int key); | ||
157 | extern void audio_pcmbuf_track_change(bool pcmbuf); | 167 | extern void audio_pcmbuf_track_change(bool pcmbuf); |
158 | extern bool audio_pcmbuf_may_play(void); | 168 | extern bool audio_pcmbuf_may_play(void); |
169 | extern void audio_pcmbuf_sync_position(void); | ||
159 | 170 | ||
160 | 171 | ||
161 | /**************************************/ | 172 | /**************************************/ |
162 | 173 | ||
163 | /* define this to show detailed chunkdesc usage information on the sim console */ | 174 | /* Return number of commited bytes in buffer (committed chunks count as |
164 | /*#define DESC_DEBUG*/ | 175 | a full chunk even if only partially filled) */ |
176 | static size_t pcmbuf_unplayed_bytes(void) | ||
177 | { | ||
178 | size_t ridx = chunk_ridx; | ||
179 | size_t widx = chunk_widx; | ||
180 | |||
181 | if (ridx > widx) | ||
182 | widx += pcmbuf_size; | ||
165 | 183 | ||
166 | #ifndef SIMULATOR | 184 | return widx - ridx; |
167 | #undef DESC_DEBUG | 185 | } |
168 | #endif | 186 | |
187 | /* Return the next PCM chunk in the PCM buffer given a byte index into it */ | ||
188 | static size_t index_next(size_t index) | ||
189 | { | ||
190 | index = ALIGN_DOWN(index + PCMBUF_CHUNK_SIZE, PCMBUF_CHUNK_SIZE); | ||
191 | |||
192 | if (index >= pcmbuf_size) | ||
193 | index -= pcmbuf_size; | ||
194 | |||
195 | return index; | ||
196 | } | ||
169 | 197 | ||
170 | #ifdef DESC_DEBUG | 198 | /* Convert a byte offset in the PCM buffer into a pointer in the buffer */ |
171 | #define DISPLAY_DESC(caller) while(!show_desc(caller)) | 199 | static FORCE_INLINE void * index_buffer(size_t index) |
172 | #define DESC_IDX(desc) (desc ? desc - first_desc : -1) | ||
173 | #define SHOW_DESC(desc) if(DESC_IDX(desc)==-1) DEBUGF("--"); \ | ||
174 | else DEBUGF("%02d", DESC_IDX(desc)) | ||
175 | #define SHOW_DESC_LINK(desc) if(desc){SHOW_DESC(desc->link);DEBUGF(" ");} \ | ||
176 | else DEBUGF("-- ") | ||
177 | #define SHOW_DETAIL(desc) DEBUGF(":");SHOW_DESC(desc); DEBUGF(">"); \ | ||
178 | SHOW_DESC_LINK(desc) | ||
179 | #define SHOW_POINT(tag,desc) DEBUGF("%s",tag);SHOW_DETAIL(desc) | ||
180 | #define SHOW_NUM(num,desc) DEBUGF("%02d>",num);SHOW_DESC_LINK(desc) | ||
181 | |||
182 | static bool show_desc(char *caller) | ||
183 | { | 200 | { |
184 | if (show_desc_in_use) return false; | 201 | return pcmbuf_buffer + index; |
185 | show_desc_in_use = true; | 202 | } |
186 | DEBUGF("%-14s\t", caller); | 203 | |
187 | SHOW_POINT("r", read_chunk); | 204 | /* Convert a pointer in the buffer into an index offset */ |
188 | SHOW_POINT("re", read_end_chunk); | 205 | static FORCE_INLINE size_t buffer_index(void *p) |
189 | DEBUGF(" "); | 206 | { |
190 | SHOW_POINT("w", write_chunk); | 207 | return (uintptr_t)p - (uintptr_t)pcmbuf_buffer; |
191 | SHOW_POINT("we", write_end_chunk); | 208 | } |
192 | DEBUGF("\n"); | 209 | |
193 | int i; | 210 | /* Return a chunk descriptor for a byte index in the buffer */ |
194 | for (i = 0; i < pcmbuf_descs(); i++) | 211 | static struct chunkdesc * index_chunkdesc(size_t index) |
212 | { | ||
213 | return &pcmbuf_descriptors[index / PCMBUF_CHUNK_SIZE]; | ||
214 | } | ||
215 | |||
216 | /* Return a chunk descriptor for a byte index in the buffer, offset by 'offset' | ||
217 | chunks */ | ||
218 | static struct chunkdesc * index_chunkdesc_offs(size_t index, int offset) | ||
219 | { | ||
220 | int i = index / PCMBUF_CHUNK_SIZE; | ||
221 | |||
222 | if (offset != 0) | ||
195 | { | 223 | { |
196 | SHOW_NUM(i, (first_desc + i)); | 224 | i = (i + offset) % pcmbuf_desc_count; |
197 | if (i%10 == 9) DEBUGF("\n"); | 225 | |
226 | /* remainder => modulus */ | ||
227 | if (i < 0) | ||
228 | i += pcmbuf_desc_count; | ||
198 | } | 229 | } |
199 | DEBUGF("\n\n"); | 230 | |
200 | show_desc_in_use = false; | 231 | return &pcmbuf_descriptors[i]; |
201 | return true; | ||
202 | } | 232 | } |
203 | #else | ||
204 | #define DISPLAY_DESC(caller) do{}while(0) | ||
205 | #endif | ||
206 | 233 | ||
207 | 234 | ||
208 | /** Accept new PCM data */ | 235 | /** Accept new PCM data */ |
209 | 236 | ||
210 | /* Commit PCM buffer samples as a new chunk for playback */ | 237 | /* Split the uncommitted data as needed into chunks, stopping when uncommitted |
211 | static void commit_chunk(bool flush_next_time) | 238 | data is below the threshold */ |
239 | static void commit_chunks(size_t threshold) | ||
212 | { | 240 | { |
213 | if (!pcmbuffer_fillpos) | 241 | size_t index = chunk_widx; |
214 | return; | 242 | size_t end_index = index + pcmbuf_bytes_waiting; |
215 | |||
216 | /* Never use the last buffer descriptor */ | ||
217 | while (write_chunk == write_end_chunk) { | ||
218 | /* If this happens, something is being stupid */ | ||
219 | if (!pcm_is_playing()) { | ||
220 | logf("commit_chunk error"); | ||
221 | pcmbuf_play_start(); | ||
222 | } | ||
223 | /* Let approximately one chunk of data playback */ | ||
224 | sleep(HZ * PCMBUF_TARGET_CHUNK / BYTERATE); | ||
225 | } | ||
226 | 243 | ||
227 | /* commit the chunk */ | 244 | /* Copy to the beginning of the buffer all data that must wrap */ |
245 | if (end_index > pcmbuf_size) | ||
246 | memcpy(pcmbuf_buffer, pcmbuf_guardbuf, end_index - pcmbuf_size); | ||
228 | 247 | ||
229 | register size_t size = pcmbuffer_fillpos; | 248 | struct chunkdesc *desc = index_chunkdesc(index); |
230 | /* Grab the next description to write, and change the write pointer */ | ||
231 | register struct chunkdesc *pcmbuf_current = write_chunk; | ||
232 | write_chunk = pcmbuf_current->link; | ||
233 | /* Fill in the values in the new buffer chunk */ | ||
234 | pcmbuf_current->addr = &pcmbuffer[pcmbuffer_pos]; | ||
235 | pcmbuf_current->size = size; | ||
236 | pcmbuf_current->end_of_track = false; | ||
237 | pcmbuf_current->link = NULL; | ||
238 | 249 | ||
239 | if (read_chunk != NULL) | 250 | do |
240 | { | 251 | { |
241 | if (flush_pcmbuf) | 252 | size_t size = MIN(pcmbuf_bytes_waiting, PCMBUF_CHUNK_SIZE); |
242 | { | 253 | pcmbuf_bytes_waiting -= size; |
243 | /* Flush! Discard all data after the currently playing chunk, | ||
244 | and make the current chunk play next */ | ||
245 | logf("commit_chunk: flush"); | ||
246 | pcm_play_lock(); | ||
247 | write_end_chunk->link = read_chunk->link; | ||
248 | read_chunk->link = pcmbuf_current; | ||
249 | while (write_end_chunk->link) | ||
250 | { | ||
251 | write_end_chunk = write_end_chunk->link; | ||
252 | pcmbuf_unplayed_bytes -= write_end_chunk->size; | ||
253 | } | ||
254 | 254 | ||
255 | read_chunk->end_of_track = track_transition; | 255 | /* Fill in the values in the new buffer chunk */ |
256 | pcm_play_unlock(); | 256 | desc->size = (uint16_t)size; |
257 | } | ||
258 | /* If there is already a read buffer setup, add to it */ | ||
259 | else | ||
260 | read_end_chunk->link = pcmbuf_current; | ||
261 | } | ||
262 | else | ||
263 | { | ||
264 | /* Otherwise create the buffer */ | ||
265 | read_chunk = pcmbuf_current; | ||
266 | } | ||
267 | 257 | ||
268 | /* If flush_next_time is true, then the current chunk will be thrown out | 258 | /* Advance the current write chunk and make it available to the |
269 | * and the next chunk to be committed will be the next to be played. | 259 | PCM callback */ |
270 | * This is used to empty the PCM buffer for a track change. */ | 260 | chunk_widx = index = index_next(index); |
271 | flush_pcmbuf = flush_next_time; | 261 | desc = index_chunkdesc(index); |
272 | 262 | ||
273 | /* This is now the last buffer to read */ | 263 | /* Reset it before using it */ |
274 | read_end_chunk = pcmbuf_current; | 264 | desc->is_end = 0; |
265 | desc->pos_key = 0; | ||
266 | } | ||
267 | while (pcmbuf_bytes_waiting >= threshold); | ||
268 | } | ||
275 | 269 | ||
276 | /* Update bytes counters */ | 270 | /* If uncommitted data count is above or equal to the threshold, commit it */ |
277 | pcmbuf_unplayed_bytes += size; | 271 | static FORCE_INLINE void commit_if_needed(size_t threshold) |
272 | { | ||
273 | if (pcmbuf_bytes_waiting >= threshold) | ||
274 | commit_chunks(threshold); | ||
275 | } | ||
278 | 276 | ||
279 | pcmbuffer_pos += size; | 277 | /* Place positioning information in the chunk */ |
280 | if (pcmbuffer_pos >= pcmbuf_size) | 278 | static void stamp_chunk(struct chunkdesc *desc, unsigned long elapsed, |
281 | pcmbuffer_pos -= pcmbuf_size; | 279 | off_t offset) |
280 | { | ||
281 | /* One-time stamping of a given chunk by the same track - new track may | ||
282 | overwrite */ | ||
283 | unsigned int key = position_key; | ||
282 | 284 | ||
283 | pcmbuffer_fillpos = 0; | 285 | if (desc->pos_key != key) |
284 | DISPLAY_DESC("commit_chunk"); | 286 | { |
287 | desc->pos_key = key; | ||
288 | desc->elapsed = elapsed; | ||
289 | desc->offset = offset; | ||
290 | } | ||
285 | } | 291 | } |
286 | 292 | ||
287 | /* Set priority of the codec thread */ | 293 | /* Set priority of the codec thread */ |
@@ -290,7 +296,8 @@ static void commit_chunk(bool flush_next_time) | |||
290 | * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */ | 296 | * expects pcm_fill_state in tenth-% units (e.g. full pcm buffer is 10) */ |
291 | static void boost_codec_thread(int pcm_fill_state) | 297 | static void boost_codec_thread(int pcm_fill_state) |
292 | { | 298 | { |
293 | static const int prios[11] = { | 299 | static const int8_t prios[11] = |
300 | { | ||
294 | PRIORITY_PLAYBACK_MAX, /* 0 - 10% */ | 301 | PRIORITY_PLAYBACK_MAX, /* 0 - 10% */ |
295 | PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */ | 302 | PRIORITY_PLAYBACK_MAX+1, /* 10 - 20% */ |
296 | PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */ | 303 | PRIORITY_PLAYBACK_MAX+3, /* 20 - 30% */ |
@@ -298,12 +305,13 @@ static void boost_codec_thread(int pcm_fill_state) | |||
298 | PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */ | 305 | PRIORITY_PLAYBACK_MAX+7, /* 40 - 50% */ |
299 | PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */ | 306 | PRIORITY_PLAYBACK_MAX+8, /* 50 - 60% */ |
300 | PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */ | 307 | PRIORITY_PLAYBACK_MAX+9, /* 60 - 70% */ |
301 | /* raiseing priority above 70% shouldn't be needed */ | 308 | /* raising priority above 70% shouldn't be needed */ |
302 | PRIORITY_PLAYBACK, /* 70 - 80% */ | 309 | PRIORITY_PLAYBACK, /* 70 - 80% */ |
303 | PRIORITY_PLAYBACK, /* 80 - 90% */ | 310 | PRIORITY_PLAYBACK, /* 80 - 90% */ |
304 | PRIORITY_PLAYBACK, /* 90 -100% */ | 311 | PRIORITY_PLAYBACK, /* 90 -100% */ |
305 | PRIORITY_PLAYBACK, /* 100% */ | 312 | PRIORITY_PLAYBACK, /* 100% */ |
306 | }; | 313 | }; |
314 | |||
307 | int new_prio = prios[pcm_fill_state]; | 315 | int new_prio = prios[pcm_fill_state]; |
308 | 316 | ||
309 | /* Keep voice and codec threads at the same priority or else voice | 317 | /* Keep voice and codec threads at the same priority or else voice |
@@ -319,37 +327,86 @@ static void boost_codec_thread(int pcm_fill_state) | |||
319 | #define boost_codec_thread(pcm_fill_state) do{}while(0) | 327 | #define boost_codec_thread(pcm_fill_state) do{}while(0) |
320 | #endif /* HAVE_PRIORITY_SCHEDULING */ | 328 | #endif /* HAVE_PRIORITY_SCHEDULING */ |
321 | 329 | ||
322 | /* Return true if the PCM buffer is able to receive new data. | 330 | /* Get the next available buffer and size - assumes adequate space exists */ |
323 | * Also maintain buffer level above the watermark. */ | 331 | static void * get_write_buffer(size_t *size) |
324 | static bool prepare_insert(size_t length) | ||
325 | { | 332 | { |
326 | bool playing = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED; | 333 | /* Obtain current chunk fill address */ |
334 | size_t index = chunk_widx + pcmbuf_bytes_waiting; | ||
335 | size_t index_end = pcmbuf_size + PCMBUF_GUARD_SIZE; | ||
327 | 336 | ||
328 | if (low_latency_mode) | 337 | /* Get count to the end of the buffer where a wrap will happen + |
329 | { | 338 | the guard */ |
330 | /* 1/4s latency. */ | 339 | size_t endsize = index_end - index; |
331 | if (!LOW_DATA(1) && playing) | ||
332 | return false; | ||
333 | } | ||
334 | 340 | ||
335 | /* Need to save PCMBUF_MIN_CHUNK to prevent wrapping overwriting */ | 341 | /* Return available unwrapped space */ |
336 | if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK) | 342 | *size = MIN(*size, endsize); |
337 | return false; | 343 | |
344 | return index_buffer(index); | ||
345 | } | ||
346 | |||
347 | /* Commit outstanding data leaving less than a chunk size remaining and | ||
348 | write position info to the first chunk */ | ||
349 | static void commit_write_buffer(size_t size, unsigned long elapsed, off_t offset) | ||
350 | { | ||
351 | struct chunkdesc *desc = index_chunkdesc(chunk_widx); | ||
352 | stamp_chunk(desc, elapsed, offset); | ||
353 | |||
354 | /* Add this data and commit if one or more chunks are ready */ | ||
355 | pcmbuf_bytes_waiting += size; | ||
356 | |||
357 | commit_if_needed(COMMIT_CHUNKS); | ||
358 | } | ||
359 | |||
360 | /* Request space in the buffer for writing output samples */ | ||
361 | void * pcmbuf_request_buffer(int *count) | ||
362 | { | ||
363 | size_t size = *count * 4; | ||
364 | |||
365 | #ifdef HAVE_CROSSFADE | ||
366 | /* We're going to crossfade to a new track, which is now on its way */ | ||
367 | if (crossfade_status == CROSSFADE_TRACK_CHANGE_STARTED) | ||
368 | crossfade_start(); | ||
369 | |||
370 | /* If crossfade has begun, put the new track samples in crossfade_buffer */ | ||
371 | if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE) | ||
372 | size = CROSSFADE_BUFSIZE; | ||
373 | #endif | ||
374 | |||
375 | enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK); | ||
376 | size_t remaining = pcmbuf_unplayed_bytes(); | ||
377 | |||
378 | /* Need to have length bytes to prevent wrapping overwriting - leave one | ||
379 | descriptor free to guard so that 0 != full in ring buffer */ | ||
380 | size_t freespace = pcmbuf_free(); | ||
381 | |||
382 | if (pcmbuf_sync_position) | ||
383 | audio_pcmbuf_sync_position(); | ||
384 | |||
385 | if (freespace < size + PCMBUF_CHUNK_SIZE) | ||
386 | return NULL; | ||
338 | 387 | ||
339 | /* Maintain the buffer level above the watermark */ | 388 | /* Maintain the buffer level above the watermark */ |
340 | if (playing) | 389 | if (status != CHANNEL_STOPPED) |
341 | { | 390 | { |
342 | /* boost cpu if necessary */ | 391 | if (low_latency_mode) |
343 | if (pcmbuf_unplayed_bytes < pcmbuf_watermark) | 392 | { |
393 | /* 1/4s latency. */ | ||
394 | if (remaining > DATA_LEVEL(1)) | ||
395 | return NULL; | ||
396 | } | ||
397 | |||
398 | /* Boost CPU if necessary */ | ||
399 | size_t realrem = pcmbuf_size - freespace; | ||
400 | |||
401 | if (realrem < pcmbuf_watermark) | ||
344 | trigger_cpu_boost(); | 402 | trigger_cpu_boost(); |
345 | boost_codec_thread(pcmbuf_unplayed_bytes*10/pcmbuf_size); | 403 | |
404 | boost_codec_thread(realrem*10 / pcmbuf_size); | ||
346 | 405 | ||
347 | #ifdef HAVE_CROSSFADE | 406 | #ifdef HAVE_CROSSFADE |
348 | /* Disable crossfade if < .5s of audio */ | 407 | /* Disable crossfade if < .5s of audio */ |
349 | if (LOW_DATA(2)) | 408 | if (remaining < DATA_LEVEL(2)) |
350 | { | 409 | crossfade_status = CROSSFADE_INACTIVE; |
351 | crossfade_active = false; | ||
352 | } | ||
353 | #endif | 410 | #endif |
354 | } | 411 | } |
355 | else /* !playing */ | 412 | else /* !playing */ |
@@ -359,378 +416,344 @@ static bool prepare_insert(size_t length) | |||
359 | 416 | ||
360 | /* If pre-buffered to the watermark, start playback */ | 417 | /* If pre-buffered to the watermark, start playback */ |
361 | #if MEMORYSIZE > 2 | 418 | #if MEMORYSIZE > 2 |
362 | if (!LOW_DATA(4)) | 419 | if (remaining > DATA_LEVEL(4)) |
363 | #else | 420 | #else |
364 | if (pcmbuf_unplayed_bytes > pcmbuf_watermark) | 421 | if (remaining > pcmbuf_watermark) |
365 | #endif | 422 | #endif |
366 | { | 423 | { |
367 | logf("pcm starting"); | ||
368 | if (audio_pcmbuf_may_play()) | 424 | if (audio_pcmbuf_may_play()) |
369 | pcmbuf_play_start(); | 425 | pcmbuf_play_start(); |
370 | } | 426 | } |
371 | } | 427 | } |
372 | 428 | ||
373 | return true; | 429 | void *buf = |
374 | } | ||
375 | |||
376 | /* Request space in the buffer for writing output samples */ | ||
377 | void *pcmbuf_request_buffer(int *count) | ||
378 | { | ||
379 | #ifdef HAVE_CROSSFADE | 430 | #ifdef HAVE_CROSSFADE |
380 | /* we're going to crossfade to a new track, which is now on its way */ | 431 | crossfade_status != CROSSFADE_INACTIVE ? crossfade_buffer : |
381 | if (crossfade_track_change_started) | ||
382 | crossfade_start(); | ||
383 | |||
384 | /* crossfade has begun, put the new track samples in fadebuf */ | ||
385 | if (crossfade_active) | ||
386 | { | ||
387 | int cnt = MIN(*count, CROSSFADE_BUFSIZE/4); | ||
388 | if (prepare_insert(cnt << 2)) | ||
389 | { | ||
390 | *count = cnt; | ||
391 | return fadebuf; | ||
392 | } | ||
393 | } | ||
394 | else | ||
395 | #endif | 432 | #endif |
396 | /* if possible, reserve room in the PCM buffer for new samples */ | 433 | get_write_buffer(&size); |
397 | { | 434 | |
398 | if(prepare_insert(*count << 2)) | 435 | *count = size / 4; |
399 | { | 436 | return buf; |
400 | size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos; | ||
401 | if (pcmbuf_size - pcmbuffer_index >= PCMBUF_MIN_CHUNK) | ||
402 | { | ||
403 | /* Usual case, there's space here */ | ||
404 | return &pcmbuffer[pcmbuffer_index]; | ||
405 | } | ||
406 | else | ||
407 | { | ||
408 | /* Wrap the buffer, the new samples go at the beginning */ | ||
409 | commit_chunk(false); | ||
410 | pcmbuffer_pos = 0; | ||
411 | return &pcmbuffer[0]; | ||
412 | } | ||
413 | } | ||
414 | } | ||
415 | /* PCM buffer not ready to receive new data yet */ | ||
416 | return NULL; | ||
417 | } | 437 | } |
418 | 438 | ||
419 | /* Handle new samples to the buffer */ | 439 | /* Handle new samples to the buffer */ |
420 | void pcmbuf_write_complete(int count) | 440 | void pcmbuf_write_complete(int count, unsigned long elapsed, off_t offset) |
421 | { | 441 | { |
422 | size_t length = (size_t)(unsigned int)count << 2; | 442 | size_t size = count * 4; |
443 | |||
423 | #ifdef HAVE_CROSSFADE | 444 | #ifdef HAVE_CROSSFADE |
424 | if (crossfade_active) | 445 | if (crossfade_status != CROSSFADE_INACTIVE) |
425 | write_to_crossfade(length); | 446 | { |
447 | write_to_crossfade(size, elapsed, offset); | ||
448 | } | ||
426 | else | 449 | else |
427 | #endif | 450 | #endif |
428 | { | 451 | { |
429 | pcmbuffer_fillpos += length; | 452 | commit_write_buffer(size, elapsed, offset); |
430 | COMMIT_IF_NEEDED; | ||
431 | } | 453 | } |
454 | |||
455 | /* Revert to position updates by PCM */ | ||
456 | pcmbuf_sync_position = false; | ||
432 | } | 457 | } |
433 | 458 | ||
434 | 459 | ||
435 | /** Init */ | 460 | /** Init */ |
436 | 461 | static unsigned int get_next_required_pcmbuf_chunks(void) | |
437 | static inline void init_pcmbuffers(void) | ||
438 | { | ||
439 | first_desc = write_chunk; | ||
440 | struct chunkdesc *next = write_chunk; | ||
441 | next++; | ||
442 | write_end_chunk = write_chunk; | ||
443 | while ((void *)next < (void *)pcmbuf_bufend) { | ||
444 | write_end_chunk->link=next; | ||
445 | write_end_chunk=next; | ||
446 | next++; | ||
447 | } | ||
448 | DISPLAY_DESC("init"); | ||
449 | } | ||
450 | |||
451 | static size_t get_next_required_pcmbuf_size(void) | ||
452 | { | 462 | { |
453 | size_t seconds = 1; | 463 | size_t size = MIN_BUFFER_SIZE; |
454 | 464 | ||
455 | #ifdef HAVE_CROSSFADE | 465 | #ifdef HAVE_CROSSFADE |
456 | if (crossfade_enable_request) | 466 | if (crossfade_enable_request != CROSSFADE_ENABLE_OFF) |
457 | seconds += global_settings.crossfade_fade_out_delay + | 467 | { |
458 | global_settings.crossfade_fade_out_duration; | 468 | size_t seconds = global_settings.crossfade_fade_out_delay + |
469 | global_settings.crossfade_fade_out_duration; | ||
470 | size += seconds * BYTERATE; | ||
471 | } | ||
459 | #endif | 472 | #endif |
460 | 473 | ||
461 | #if MEMORYSIZE > 2 | 474 | logf("pcmbuf len: %lu", (unsigned long)(size / BYTERATE)); |
462 | /* Buffer has to be at least 2s long. */ | 475 | return size / PCMBUF_CHUNK_SIZE; |
463 | seconds += 2; | 476 | } |
464 | #endif | 477 | |
465 | logf("pcmbuf len: %ld", (long)seconds); | 478 | /* Initialize the ringbuffer state */ |
466 | return seconds * BYTERATE; | 479 | static void init_buffer_state(void) |
480 | { | ||
481 | /* Reset counters */ | ||
482 | chunk_ridx = chunk_widx = 0; | ||
483 | pcmbuf_bytes_waiting = 0; | ||
484 | |||
485 | /* Reset first descriptor */ | ||
486 | struct chunkdesc *desc = pcmbuf_descriptors; | ||
487 | desc->is_end = 0; | ||
488 | desc->pos_key = 0; | ||
467 | } | 489 | } |
468 | 490 | ||
469 | /* Initialize the pcmbuffer the structure looks like this: | 491 | /* Initialize the PCM buffer. The structure looks like this: |
470 | * ...|---------PCMBUF---------[|FADEBUF]|DESCS|... */ | 492 | * ...[|FADEBUF]|---------PCMBUF---------|GUARDBUF|DESCS| */ |
471 | size_t pcmbuf_init(unsigned char *bufend) | 493 | size_t pcmbuf_init(unsigned char *bufend) |
472 | { | 494 | { |
473 | pcmbuf_bufend = bufend; | 495 | unsigned char *bufstart; |
474 | pcmbuf_size = get_next_required_pcmbuf_size(); | ||
475 | write_chunk = (struct chunkdesc *)pcmbuf_bufend - | ||
476 | NUM_CHUNK_DESCS(pcmbuf_size); | ||
477 | 496 | ||
478 | #ifdef HAVE_CROSSFADE | 497 | /* Set up the buffers */ |
479 | fadebuf = (unsigned char *)write_chunk - | 498 | pcmbuf_desc_count = get_next_required_pcmbuf_chunks(); |
480 | (crossfade_enable_request ? CROSSFADE_BUFSIZE : 0); | 499 | pcmbuf_size = pcmbuf_desc_count * PCMBUF_CHUNK_SIZE; |
481 | pcmbuffer = fadebuf - pcmbuf_size; | 500 | pcmbuf_descriptors = (struct chunkdesc *)bufend - pcmbuf_desc_count; |
482 | #else | 501 | |
483 | pcmbuffer = (unsigned char *)write_chunk - pcmbuf_size; | 502 | pcmbuf_buffer = (void *)pcmbuf_descriptors - |
484 | #endif | 503 | pcmbuf_size - PCMBUF_GUARD_SIZE; |
485 | 504 | ||
486 | init_pcmbuffers(); | 505 | /* Mem-align buffer chunks for more efficient handling in lower layers */ |
506 | pcmbuf_buffer = ALIGN_DOWN(pcmbuf_buffer, (uintptr_t)MEM_ALIGN_SIZE); | ||
507 | |||
508 | pcmbuf_guardbuf = pcmbuf_buffer + pcmbuf_size; | ||
509 | bufstart = pcmbuf_buffer; | ||
487 | 510 | ||
488 | #ifdef HAVE_CROSSFADE | 511 | #ifdef HAVE_CROSSFADE |
512 | /* Allocate FADEBUF if it will be needed */ | ||
513 | if (crossfade_enable_request != CROSSFADE_ENABLE_OFF) | ||
514 | { | ||
515 | bufstart -= CROSSFADE_BUFSIZE; | ||
516 | crossfade_buffer = bufstart; | ||
517 | } | ||
518 | |||
489 | pcmbuf_finish_crossfade_enable(); | 519 | pcmbuf_finish_crossfade_enable(); |
490 | #else | 520 | #else /* !HAVE_CROSSFADE */ |
491 | pcmbuf_watermark = PCMBUF_WATERMARK; | 521 | pcmbuf_watermark = PCMBUF_WATERMARK; |
492 | #endif | 522 | #endif /* HAVE_CROSSFADE */ |
493 | 523 | ||
494 | pcmbuf_play_stop(); | 524 | init_buffer_state(); |
495 | 525 | ||
496 | pcmbuf_soft_mode(false); | 526 | pcmbuf_soft_mode(false); |
497 | 527 | ||
498 | return pcmbuf_bufend - pcmbuffer; | 528 | return bufend - bufstart; |
499 | } | 529 | } |
500 | 530 | ||
501 | 531 | ||
502 | /** Track change */ | 532 | /** Track change */ |
503 | void pcmbuf_monitor_track_change(bool monitor) | ||
504 | { | ||
505 | pcm_play_lock(); | ||
506 | 533 | ||
507 | if (last_chunksize != 0) | 534 | /* Place a track change notification in a specific descriptor or post it |
535 | immediately if the buffer is empty or the index is invalid */ | ||
536 | static void pcmbuf_monitor_track_change_ex(size_t index, int offset) | ||
537 | { | ||
538 | if (chunk_ridx != chunk_widx && index != INVALID_BUF_INDEX) | ||
508 | { | 539 | { |
509 | /* If monitoring, wait until this track runs out. Place in | 540 | /* If monitoring, set flag in specified chunk */ |
510 | currently playing chunk. If not, cancel notification. */ | 541 | index_chunkdesc_offs(index, offset)->is_end = 1; |
511 | track_transition = monitor; | ||
512 | read_end_chunk->end_of_track = monitor; | ||
513 | if (!monitor) | ||
514 | { | ||
515 | /* Clear all notifications */ | ||
516 | struct chunkdesc *desc = first_desc; | ||
517 | struct chunkdesc *end = desc + pcmbuf_descs(); | ||
518 | while (desc < end) | ||
519 | desc++->end_of_track = false; | ||
520 | } | ||
521 | } | 542 | } |
522 | else | 543 | else |
523 | { | 544 | { |
524 | /* Post now if PCM stopped and last buffer was sent. */ | 545 | /* Post now if no outstanding buffers exist */ |
525 | track_transition = false; | 546 | audio_pcmbuf_track_change(false); |
526 | if (monitor) | ||
527 | audio_pcmbuf_track_change(false); | ||
528 | } | 547 | } |
548 | } | ||
549 | |||
550 | /* Clear end of track and optionally the positioning info for all data */ | ||
551 | static void pcmbuf_cancel_track_change(bool position) | ||
552 | { | ||
553 | size_t index = chunk_ridx; | ||
554 | |||
555 | while (1) | ||
556 | { | ||
557 | struct chunkdesc *desc = index_chunkdesc(index); | ||
558 | |||
559 | desc->is_end = 0; | ||
560 | |||
561 | if (position) | ||
562 | desc->pos_key = 0; | ||
563 | |||
564 | if (index == chunk_widx) | ||
565 | break; | ||
566 | |||
567 | index = index_next(index); | ||
568 | } | ||
569 | } | ||
570 | |||
571 | /* Place a track change notification at the end of the buffer or post it | ||
572 | immediately if the buffer is empty */ | ||
573 | void pcmbuf_monitor_track_change(bool monitor) | ||
574 | { | ||
575 | pcm_play_lock(); | ||
576 | |||
577 | if (monitor) | ||
578 | pcmbuf_monitor_track_change_ex(chunk_widx, -1); | ||
579 | else | ||
580 | pcmbuf_cancel_track_change(false); | ||
529 | 581 | ||
530 | pcm_play_unlock(); | 582 | pcm_play_unlock(); |
531 | } | 583 | } |
532 | 584 | ||
533 | bool pcmbuf_start_track_change(bool auto_skip) | 585 | void pcmbuf_start_track_change(enum pcm_track_change_type type) |
534 | { | 586 | { |
535 | bool crossfade = false; | ||
536 | #ifdef HAVE_CROSSFADE | 587 | #ifdef HAVE_CROSSFADE |
537 | /* Determine whether this track change needs to crossfade */ | 588 | bool crossfade = false; |
538 | if(crossfade_enabled && !pcmbuf_is_crossfade_active()) | ||
539 | { | ||
540 | switch(global_settings.crossfade) | ||
541 | { | ||
542 | case CROSSFADE_ENABLE_AUTOSKIP: | ||
543 | crossfade = auto_skip; | ||
544 | break; | ||
545 | case CROSSFADE_ENABLE_MANSKIP: | ||
546 | crossfade = !auto_skip; | ||
547 | break; | ||
548 | case CROSSFADE_ENABLE_SHUFFLE: | ||
549 | crossfade = global_settings.playlist_shuffle; | ||
550 | break; | ||
551 | case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP: | ||
552 | crossfade = global_settings.playlist_shuffle || !auto_skip; | ||
553 | break; | ||
554 | case CROSSFADE_ENABLE_ALWAYS: | ||
555 | crossfade = true; | ||
556 | break; | ||
557 | } | ||
558 | } | ||
559 | #endif | 589 | #endif |
590 | bool auto_skip = type != TRACK_CHANGE_MANUAL; | ||
560 | 591 | ||
561 | if (!auto_skip || crossfade) | 592 | /* Commit all outstanding data before starting next track - tracks don't |
562 | /* manual skip or crossfade */ | 593 | comingle inside a single buffer chunk */ |
563 | { | 594 | commit_if_needed(COMMIT_ALL_DATA); |
564 | if (crossfade) | ||
565 | { logf(" crossfade track change"); } | ||
566 | else | ||
567 | { logf(" manual track change"); } | ||
568 | 595 | ||
569 | pcm_play_lock(); | 596 | /* Update position key so that: |
597 | 1) Positions are keyed to the track to which they belong for sync | ||
598 | purposes | ||
570 | 599 | ||
571 | /* Cancel any pending automatic gapless transition */ | 600 | 2) Buffers stamped with the outgoing track's positions are restamped |
572 | pcmbuf_monitor_track_change(false); | 601 | to the incoming track's positions when crossfading |
602 | */ | ||
603 | if (++position_key > UINT8_MAX) | ||
604 | position_key = 1; | ||
573 | 605 | ||
574 | /* Can't do two crossfades at once and, no fade if pcm is off now */ | 606 | if (type == TRACK_CHANGE_END_OF_DATA) |
575 | if ( | 607 | { |
608 | /* If end of all data, force playback */ | ||
609 | if (audio_pcmbuf_may_play()) | ||
610 | pcmbuf_play_start(); | ||
611 | } | ||
576 | #ifdef HAVE_CROSSFADE | 612 | #ifdef HAVE_CROSSFADE |
577 | pcmbuf_is_crossfade_active() || | 613 | /* Determine whether this track change needs to crossfaded and how */ |
578 | #endif | 614 | else if (crossfade_setting != CROSSFADE_ENABLE_OFF && |
579 | mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED) | 615 | !pcmbuf_is_crossfade_active() && |
616 | pcmbuf_unplayed_bytes() >= DATA_LEVEL(2) && | ||
617 | !low_latency_mode) | ||
618 | { | ||
619 | switch (crossfade_setting) | ||
580 | { | 620 | { |
581 | pcmbuf_play_stop(); | 621 | case CROSSFADE_ENABLE_AUTOSKIP: |
582 | pcm_play_unlock(); | 622 | crossfade = auto_skip; |
583 | /* Notify playback that the track change starts now */ | 623 | break; |
584 | return true; | 624 | case CROSSFADE_ENABLE_MANSKIP: |
625 | crossfade = !auto_skip; | ||
626 | break; | ||
627 | case CROSSFADE_ENABLE_SHUFFLE: | ||
628 | crossfade = global_settings.playlist_shuffle; | ||
629 | break; | ||
630 | case CROSSFADE_ENABLE_SHUFFLE_OR_MANSKIP: | ||
631 | crossfade = global_settings.playlist_shuffle || !auto_skip; | ||
632 | break; | ||
633 | case CROSSFADE_ENABLE_ALWAYS: | ||
634 | crossfade = true; | ||
635 | break; | ||
585 | } | 636 | } |
637 | } | ||
638 | /* else crossfade is off, crossfade is already active, not enough data, | ||
639 | * pcm is off now (implying low data), not crossfading or low latency mode | ||
640 | */ | ||
586 | 641 | ||
587 | /* Not enough data, or not crossfading, flush the old data instead */ | 642 | if (crossfade) |
588 | if (LOW_DATA(2) || !crossfade || low_latency_mode) | 643 | { |
589 | { | 644 | logf("crossfade track change"); |
590 | commit_chunk(true); | ||
591 | } | ||
592 | #ifdef HAVE_CROSSFADE | ||
593 | else | ||
594 | { | ||
595 | /* Don't enable mix mode when skipping tracks manually. */ | ||
596 | crossfade_mixmode = auto_skip && | ||
597 | global_settings.crossfade_fade_out_mixmode; | ||
598 | 645 | ||
599 | crossfade_auto_skip = auto_skip; | 646 | /* Don't enable mix mode when skipping tracks manually */ |
647 | crossfade_mixmode = auto_skip && | ||
648 | global_settings.crossfade_fade_out_mixmode; | ||
600 | 649 | ||
601 | crossfade_track_change_started = crossfade; | 650 | crossfade_auto_skip = auto_skip; |
602 | } | 651 | |
603 | #endif | 652 | crossfade_status = CROSSFADE_TRACK_CHANGE_STARTED; |
604 | pcm_play_unlock(); | ||
605 | 653 | ||
606 | /* Keep trigger outside the play lock or HW FIFO underruns can happen | ||
607 | since frequency scaling is *not* always fast */ | ||
608 | trigger_cpu_boost(); | 654 | trigger_cpu_boost(); |
609 | 655 | ||
610 | /* Notify playback that the track change starts now */ | 656 | /* Cancel any pending automatic gapless transition and if a manual |
611 | return true; | 657 | skip, stop position updates */ |
658 | pcm_play_lock(); | ||
659 | pcmbuf_cancel_track_change(!auto_skip); | ||
660 | pcm_play_unlock(); | ||
612 | } | 661 | } |
613 | else /* automatic and not crossfading, so do gapless track change */ | 662 | else |
663 | #endif /* HAVE_CROSSFADE */ | ||
664 | if (auto_skip) | ||
614 | { | 665 | { |
615 | /* The codec is moving on to the next track, but the current track will | 666 | /* The codec is moving on to the next track, but the current track will |
616 | * continue to play. Set a flag to make sure the elapsed time of the | 667 | * continue to play, so mark the last write chunk as the last one in |
617 | * current track will be updated properly, and mark the current chunk | 668 | * the track */ |
618 | * as the last one in the track. */ | 669 | logf("gapless track change"); |
619 | logf(" gapless track change"); | 670 | #ifdef HAVE_CROSSFADE |
671 | if (crossfade_status != CROSSFADE_INACTIVE) | ||
672 | { | ||
673 | /* Crossfade is still active but crossfade is not happening - for | ||
674 | * now, chicken-out and clear out the buffer (just like before) to | ||
675 | * avoid fade pile-up on short tracks fading-in over long ones */ | ||
676 | pcmbuf_play_stop(); | ||
677 | } | ||
678 | #endif | ||
620 | pcmbuf_monitor_track_change(true); | 679 | pcmbuf_monitor_track_change(true); |
621 | return false; | 680 | } |
681 | else | ||
682 | { | ||
683 | /* Discard old data; caller needs no transition notification */ | ||
684 | logf("manual track change"); | ||
685 | pcmbuf_play_stop(); | ||
622 | } | 686 | } |
623 | } | 687 | } |
624 | 688 | ||
625 | 689 | ||
626 | /** Playback */ | 690 | /** Playback */ |
627 | 691 | ||
628 | /* PCM driver callback | 692 | /* PCM driver callback */ |
629 | * This function has 3 major logical parts (separated by brackets both for | 693 | static void pcmbuf_pcm_callback(unsigned char **start, size_t *size) |
630 | * readability and variable scoping). The first part performs the | ||
631 | * operations related to finishing off the last chunk we fed to the DMA. | ||
632 | * The second part detects the end of playlist condition when the PCM | ||
633 | * buffer is empty except for uncommitted samples. Then they are committed | ||
634 | * and sent to the PCM driver for playback. The third part performs the | ||
635 | * operations involved in sending a new chunk to the DMA. */ | ||
636 | static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) | ||
637 | { | 694 | { |
638 | struct chunkdesc *pcmbuf_current = read_chunk; | 695 | /*- Process the chunk that just finished -*/ |
696 | size_t index = chunk_ridx; | ||
697 | struct chunkdesc *desc = current_desc; | ||
639 | 698 | ||
699 | if (desc) | ||
640 | { | 700 | { |
641 | /* Take the finished chunk out of circulation */ | 701 | /* If last chunk in the track, notify of track change */ |
642 | read_chunk = pcmbuf_current->link; | 702 | if (desc->is_end != 0) |
643 | |||
644 | /* if during a track transition, update the elapsed time in ms */ | ||
645 | if (track_transition) | ||
646 | audio_pcmbuf_position_callback(last_chunksize * 1000 / BYTERATE); | ||
647 | |||
648 | /* if last chunk in the track, stop updates and notify audio thread */ | ||
649 | if (pcmbuf_current->end_of_track) | ||
650 | { | ||
651 | track_transition = false; | ||
652 | audio_pcmbuf_track_change(true); | 703 | audio_pcmbuf_track_change(true); |
653 | } | ||
654 | |||
655 | /* Put the finished chunk back into circulation */ | ||
656 | write_end_chunk->link = pcmbuf_current; | ||
657 | write_end_chunk = pcmbuf_current; | ||
658 | 704 | ||
659 | #ifdef HAVE_CROSSFADE | 705 | /* Free it for reuse */ |
660 | /* If we've read over the crossfade chunk while it's still fading */ | 706 | chunk_ridx = index = index_next(index); |
661 | if (pcmbuf_current == crossfade_chunk) | ||
662 | crossfade_chunk = read_chunk; | ||
663 | #endif | ||
664 | } | 707 | } |
665 | 708 | ||
709 | /*- Process the new one -*/ | ||
710 | if (index != chunk_widx && !fade_out_complete) | ||
666 | { | 711 | { |
667 | /* Commit last samples at end of playlist */ | 712 | current_desc = desc = index_chunkdesc(index); |
668 | if (pcmbuffer_fillpos && !pcmbuf_current) | ||
669 | { | ||
670 | logf("pcmbuf_pcm_callback: commit last samples"); | ||
671 | commit_chunk(false); | ||
672 | } | ||
673 | } | ||
674 | 713 | ||
675 | /* Stop at this frame */ | 714 | *start = index_buffer(index); |
676 | pcmbuf_current = fade_out_complete ? NULL : read_chunk; | 715 | *size = desc->size; |
677 | 716 | ||
678 | { | 717 | if (desc->pos_key != 0) |
679 | /* Send the new chunk to the DMA */ | ||
680 | if(pcmbuf_current) | ||
681 | { | ||
682 | last_chunksize = pcmbuf_current->size; | ||
683 | pcmbuf_unplayed_bytes -= last_chunksize; | ||
684 | *size = last_chunksize; | ||
685 | *start = pcmbuf_current->addr; | ||
686 | } | ||
687 | else | ||
688 | { | 718 | { |
689 | /* No more chunks or pause indicated */ | 719 | /* Positioning chunk - notify playback */ |
690 | logf("pcmbuf_pcm_callback: no more chunks"); | 720 | audio_pcmbuf_position_callback(desc->elapsed, desc->offset, |
691 | last_chunksize = 0; | 721 | desc->pos_key); |
692 | *size = 0; | ||
693 | *start = NULL; | ||
694 | } | 722 | } |
695 | } | 723 | } |
696 | DISPLAY_DESC("callback"); | ||
697 | } | 724 | } |
698 | 725 | ||
699 | /* Force playback */ | 726 | /* Force playback */ |
700 | void pcmbuf_play_start(void) | 727 | void pcmbuf_play_start(void) |
701 | { | 728 | { |
729 | logf("pcmbuf_play_start"); | ||
730 | |||
702 | if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED && | 731 | if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED && |
703 | pcmbuf_unplayed_bytes && read_chunk != NULL) | 732 | chunk_widx != chunk_ridx) |
704 | { | 733 | { |
705 | logf("pcmbuf_play_start"); | 734 | current_desc = NULL; |
706 | last_chunksize = read_chunk->size; | ||
707 | pcmbuf_unplayed_bytes -= last_chunksize; | ||
708 | mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback, | 735 | mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback, |
709 | read_chunk->addr, last_chunksize); | 736 | NULL, 0); |
710 | } | 737 | } |
711 | } | 738 | } |
712 | 739 | ||
740 | /* Stop channel, empty and reset buffer */ | ||
713 | void pcmbuf_play_stop(void) | 741 | void pcmbuf_play_stop(void) |
714 | { | 742 | { |
715 | logf("pcmbuf_play_stop"); | 743 | logf("pcmbuf_play_stop"); |
744 | |||
745 | /* Reset channel */ | ||
716 | mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); | 746 | mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); |
717 | 747 | ||
718 | pcmbuf_unplayed_bytes = 0; | 748 | /* Reset buffer */ |
719 | if (read_chunk) { | 749 | init_buffer_state(); |
720 | write_end_chunk->link = read_chunk; | 750 | |
721 | write_end_chunk = read_end_chunk; | 751 | /* Revert to position updates by PCM */ |
722 | read_chunk = read_end_chunk = NULL; | 752 | pcmbuf_sync_position = false; |
723 | } | 753 | |
724 | last_chunksize = 0; | ||
725 | pcmbuffer_pos = 0; | ||
726 | pcmbuffer_fillpos = 0; | ||
727 | #ifdef HAVE_CROSSFADE | 754 | #ifdef HAVE_CROSSFADE |
728 | crossfade_track_change_started = false; | 755 | crossfade_status = CROSSFADE_INACTIVE; |
729 | crossfade_active = false; | ||
730 | #endif | 756 | #endif |
731 | track_transition = false; | ||
732 | flush_pcmbuf = false; | ||
733 | DISPLAY_DESC("play_stop"); | ||
734 | 757 | ||
735 | /* Can unboost the codec thread here no matter who's calling, | 758 | /* Can unboost the codec thread here no matter who's calling, |
736 | * pretend full pcm buffer to unboost */ | 759 | * pretend full pcm buffer to unboost */ |
@@ -750,74 +773,153 @@ void pcmbuf_pause(bool pause) | |||
750 | 773 | ||
751 | /** Crossfade */ | 774 | /** Crossfade */ |
752 | 775 | ||
753 | /* Clip sample to signed 16 bit range */ | 776 | #ifdef HAVE_CROSSFADE |
754 | static inline int32_t clip_sample_16(int32_t sample) | 777 | /* Find the buffer index that's 'size' bytes away from 'index' */ |
778 | static size_t crossfade_find_index(size_t index, size_t size) | ||
755 | { | 779 | { |
756 | if ((int16_t)sample != sample) | 780 | if (index != INVALID_BUF_INDEX) |
757 | sample = 0x7fff ^ (sample >> 31); | 781 | { |
758 | return sample; | 782 | size_t i = ALIGN_DOWN(index, PCMBUF_CHUNK_SIZE); |
783 | size += index - i; | ||
784 | |||
785 | while (i != chunk_widx) | ||
786 | { | ||
787 | size_t desc_size = index_chunkdesc(i)->size; | ||
788 | |||
789 | if (size < desc_size) | ||
790 | return i + size; | ||
791 | |||
792 | size -= desc_size; | ||
793 | i = index_next(i); | ||
794 | } | ||
795 | } | ||
796 | |||
797 | return INVALID_BUF_INDEX; | ||
759 | } | 798 | } |
760 | 799 | ||
761 | #ifdef HAVE_CROSSFADE | 800 | /* Align the needed buffer area up to the end of existing data */ |
762 | /* Find the chunk that's (length) deep in the list. Return the position within | 801 | static size_t crossfade_find_buftail(size_t buffer_rem, size_t buffer_need) |
763 | * the chunk, and leave the chunkdesc pointer pointing to the chunk. */ | ||
764 | static size_t find_chunk(size_t length, struct chunkdesc **chunk) | ||
765 | { | 802 | { |
766 | while (*chunk && length >= (*chunk)->size) | 803 | crossfade_index = chunk_ridx; |
804 | |||
805 | if (buffer_rem > buffer_need) | ||
767 | { | 806 | { |
768 | length -= (*chunk)->size; | 807 | size_t distance; |
769 | *chunk = (*chunk)->link; | 808 | |
809 | if (crossfade_auto_skip) | ||
810 | { | ||
811 | /* Automatic track changes only modify the last part of the buffer, | ||
812 | * so find the right chunk and sample to start the crossfade */ | ||
813 | distance = buffer_rem - buffer_need; | ||
814 | buffer_rem = buffer_need; | ||
815 | } | ||
816 | else | ||
817 | { | ||
818 | /* Manual skips occur immediately, but give 1/5s to process */ | ||
819 | distance = BYTERATE / 5; | ||
820 | buffer_rem -= BYTERATE / 5; | ||
821 | } | ||
822 | |||
823 | crossfade_index = crossfade_find_index(crossfade_index, distance); | ||
770 | } | 824 | } |
771 | return length; | 825 | |
826 | return buffer_rem; | ||
827 | } | ||
828 | |||
829 | /* Clip sample to signed 16 bit range */ | ||
830 | static FORCE_INLINE int32_t clip_sample_16(int32_t sample) | ||
831 | { | ||
832 | if ((int16_t)sample != sample) | ||
833 | sample = 0x7fff ^ (sample >> 31); | ||
834 | return sample; | ||
772 | } | 835 | } |
773 | 836 | ||
774 | /* Returns the number of bytes _NOT_ mixed/faded */ | 837 | /* Returns the number of bytes _NOT_ mixed/faded */ |
775 | static size_t crossfade_mix_fade(int factor, size_t length, const char *buf, | 838 | static int crossfade_mix_fade(int factor, size_t size, void *buf, size_t *out_index, |
776 | size_t *out_sample, struct chunkdesc **out_chunk) | 839 | unsigned long elapsed, off_t offset) |
777 | { | 840 | { |
778 | if (length == 0) | 841 | if (size == 0) |
779 | return 0; | 842 | return 0; |
780 | 843 | ||
781 | const int16_t *input_buf = (const int16_t *)buf; | 844 | size_t index = *out_index; |
782 | int16_t *output_buf = (int16_t *)((*out_chunk)->addr); | 845 | |
783 | int16_t *chunk_end = SKIPBYTES(output_buf, (*out_chunk)->size); | 846 | if (index == INVALID_BUF_INDEX) |
784 | output_buf = &output_buf[*out_sample]; | 847 | return size; |
785 | int32_t sample; | 848 | |
849 | const int16_t *input_buf = buf; | ||
850 | int16_t *output_buf = (int16_t *)index_buffer(index); | ||
786 | 851 | ||
787 | while (length) | 852 | while (size) |
788 | { | 853 | { |
789 | /* fade left and right channel at once to keep buffer alignment */ | 854 | struct chunkdesc *desc = index_chunkdesc(index); |
790 | int i; | 855 | |
791 | for (i = 0; i < 2; i++) | 856 | switch (offset) |
792 | { | 857 | { |
858 | case MIXFADE_NULLIFY_POS: | ||
859 | /* Stop position updates for the chunk */ | ||
860 | desc->pos_key = 0; | ||
861 | break; | ||
862 | case MIXFADE_KEEP_POS: | ||
863 | /* Keep position info as it is */ | ||
864 | break; | ||
865 | default: | ||
866 | /* Replace position info */ | ||
867 | stamp_chunk(desc, elapsed, offset); | ||
868 | } | ||
869 | |||
870 | size_t rem = desc->size - (index % PCMBUF_CHUNK_SIZE); | ||
871 | int16_t *chunk_end = SKIPBYTES(output_buf, rem); | ||
872 | |||
873 | if (size < rem) | ||
874 | rem = size; | ||
875 | |||
876 | size -= rem; | ||
877 | |||
878 | do | ||
879 | { | ||
880 | /* fade left and right channel at once to keep buffer alignment */ | ||
881 | int32_t left = output_buf[0]; | ||
882 | int32_t right = output_buf[1]; | ||
883 | |||
793 | if (input_buf) | 884 | if (input_buf) |
794 | /* fade the input buffer and mix into the chunk */ | ||
795 | { | 885 | { |
796 | sample = *input_buf++; | 886 | /* fade the input buffer and mix into the chunk */ |
797 | sample = ((sample * factor) >> 8) + *output_buf; | 887 | left += *input_buf++ * factor >> 8; |
798 | *output_buf++ = clip_sample_16(sample); | 888 | right += *input_buf++ * factor >> 8; |
889 | left = clip_sample_16(left); | ||
890 | right = clip_sample_16(right); | ||
799 | } | 891 | } |
800 | else | 892 | else |
801 | /* fade the chunk only */ | ||
802 | { | 893 | { |
803 | sample = *output_buf; | 894 | /* fade the chunk only */ |
804 | *output_buf++ = (sample * factor) >> 8; | 895 | left = left * factor >> 8; |
896 | right = right * factor >> 8; | ||
805 | } | 897 | } |
806 | } | ||
807 | 898 | ||
808 | length -= 4; /* 2 samples, each 16 bit -> 4 bytes */ | 899 | *output_buf++ = left; |
900 | *output_buf++ = right; | ||
901 | |||
902 | rem -= 4; | ||
903 | } | ||
904 | while (rem); | ||
809 | 905 | ||
810 | /* move to next chunk as needed */ | 906 | /* move to next chunk as needed */ |
811 | if (output_buf >= chunk_end) | 907 | if (output_buf >= chunk_end) |
812 | { | 908 | { |
813 | *out_chunk = (*out_chunk)->link; | 909 | index = index_next(index); |
814 | if (!(*out_chunk)) | 910 | |
815 | return length; | 911 | if (index == chunk_widx) |
816 | output_buf = (int16_t *)((*out_chunk)->addr); | 912 | { |
817 | chunk_end = SKIPBYTES(output_buf, (*out_chunk)->size); | 913 | /* End of existing data */ |
914 | *out_index = INVALID_BUF_INDEX; | ||
915 | return size; | ||
916 | } | ||
917 | |||
918 | output_buf = (int16_t *)index_buffer(index); | ||
818 | } | 919 | } |
819 | } | 920 | } |
820 | *out_sample = output_buf - (int16_t *)((*out_chunk)->addr); | 921 | |
922 | *out_index = buffer_index(output_buf); | ||
821 | return 0; | 923 | return 0; |
822 | } | 924 | } |
823 | 925 | ||
@@ -825,184 +927,209 @@ static size_t crossfade_mix_fade(int factor, size_t length, const char *buf, | |||
825 | * fade-out with the PCM buffer. */ | 927 | * fade-out with the PCM buffer. */ |
826 | static void crossfade_start(void) | 928 | static void crossfade_start(void) |
827 | { | 929 | { |
828 | size_t crossfade_rem; | ||
829 | size_t crossfade_need; | ||
830 | size_t fade_out_rem; | ||
831 | size_t fade_out_delay; | ||
832 | size_t fade_in_delay; | ||
833 | |||
834 | crossfade_track_change_started = false; | ||
835 | /* Reject crossfade if less than .5s of data */ | ||
836 | if (LOW_DATA(2)) { | ||
837 | logf("crossfade rejected"); | ||
838 | pcmbuf_play_stop(); | ||
839 | return ; | ||
840 | } | ||
841 | |||
842 | logf("crossfade_start"); | 930 | logf("crossfade_start"); |
843 | commit_chunk(false); | 931 | |
844 | crossfade_active = true; | 932 | pcm_play_lock(); |
845 | 933 | ||
846 | /* Initialize the crossfade buffer size to all of the buffered data that | 934 | /* Initialize the crossfade buffer size to all of the buffered data that |
847 | * has not yet been sent to the DMA */ | 935 | * has not yet been sent to the DMA */ |
848 | crossfade_rem = pcmbuf_unplayed_bytes; | 936 | size_t unplayed = pcmbuf_unplayed_bytes(); |
849 | crossfade_chunk = read_chunk->link; | ||
850 | crossfade_sample = 0; | ||
851 | |||
852 | /* Get fade out info from settings. */ | ||
853 | fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE; | ||
854 | fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE; | ||
855 | 937 | ||
856 | crossfade_need = fade_out_delay + fade_out_rem; | 938 | /* Reject crossfade if less than .5s of data */ |
857 | if (crossfade_rem > crossfade_need) | 939 | if (unplayed < DATA_LEVEL(2)) |
858 | { | 940 | { |
941 | logf("crossfade rejected"); | ||
942 | |||
943 | crossfade_status = CROSSFADE_INACTIVE; | ||
944 | |||
859 | if (crossfade_auto_skip) | 945 | if (crossfade_auto_skip) |
860 | /* Automatic track changes only modify the last part of the buffer, | 946 | pcmbuf_monitor_track_change(true); |
861 | * so find the right chunk and sample to start the crossfade */ | 947 | |
862 | { | 948 | pcm_play_unlock(); |
863 | crossfade_sample = find_chunk(crossfade_rem - crossfade_need, | 949 | return; |
864 | &crossfade_chunk) / 2; | ||
865 | crossfade_rem = crossfade_need; | ||
866 | } | ||
867 | else | ||
868 | /* Manual skips occur immediately, but give time to process */ | ||
869 | { | ||
870 | crossfade_rem -= crossfade_chunk->size; | ||
871 | crossfade_chunk = crossfade_chunk->link; | ||
872 | } | ||
873 | } | 950 | } |
874 | /* Truncate fade out duration if necessary. */ | 951 | |
875 | if (crossfade_rem < crossfade_need) | 952 | /* Fading will happen */ |
953 | crossfade_status = CROSSFADE_ACTIVE; | ||
954 | |||
955 | /* Get fade info from settings. */ | ||
956 | size_t fade_out_delay = global_settings.crossfade_fade_out_delay * BYTERATE; | ||
957 | size_t fade_out_rem = global_settings.crossfade_fade_out_duration * BYTERATE; | ||
958 | size_t fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE; | ||
959 | size_t fade_in_duration = global_settings.crossfade_fade_in_duration * BYTERATE; | ||
960 | |||
961 | if (!crossfade_auto_skip) | ||
876 | { | 962 | { |
877 | size_t crossfade_short = crossfade_need - crossfade_rem; | 963 | /* Forego fade-in delay on manual skip - do the best to preserve auto skip |
878 | if (fade_out_rem >= crossfade_short) | 964 | relationship */ |
879 | fade_out_rem -= crossfade_short; | 965 | if (fade_out_delay > fade_in_delay) |
966 | fade_out_delay -= fade_in_delay; | ||
880 | else | 967 | else |
881 | { | 968 | fade_out_delay = 0; |
882 | fade_out_delay -= crossfade_short - fade_out_rem; | 969 | |
883 | fade_out_rem = 0; | 970 | fade_in_delay = 0; |
884 | } | ||
885 | } | 971 | } |
886 | crossfade_rem -= fade_out_delay + fade_out_rem; | ||
887 | 972 | ||
888 | /* Completely process the crossfade fade-out effect with current PCM buffer */ | 973 | size_t fade_out_need = fade_out_delay + fade_out_rem; |
974 | |||
889 | if (!crossfade_mixmode) | 975 | if (!crossfade_mixmode) |
890 | { | 976 | { |
977 | size_t buffer_rem = crossfade_find_buftail(unplayed, fade_out_need); | ||
978 | |||
979 | pcm_play_unlock(); | ||
980 | |||
981 | if (buffer_rem < fade_out_need) | ||
982 | { | ||
983 | /* Existing buffers are short */ | ||
984 | size_t fade_out_short = fade_out_need - buffer_rem; | ||
985 | |||
986 | if (fade_out_rem >= fade_out_short) | ||
987 | { | ||
988 | /* Truncate fade-out duration */ | ||
989 | fade_out_rem -= fade_out_short; | ||
990 | } | ||
991 | else | ||
992 | { | ||
993 | /* Truncate fade-out and fade-out delay */ | ||
994 | fade_out_delay = fade_out_rem; | ||
995 | fade_out_rem = 0; | ||
996 | } | ||
997 | } | ||
998 | |||
999 | /* Completely process the crossfade fade-out effect with current PCM buffer */ | ||
1000 | |||
891 | /* Fade out the specified amount of the already processed audio */ | 1001 | /* Fade out the specified amount of the already processed audio */ |
892 | size_t total_fade_out = fade_out_rem; | 1002 | size_t fade_out_total = fade_out_rem; |
893 | size_t fade_out_sample; | ||
894 | struct chunkdesc *fade_out_chunk = crossfade_chunk; | ||
895 | 1003 | ||
896 | /* Find the right chunk and sample to start fading out */ | 1004 | /* Find the right chunk and sample to start fading out */ |
897 | fade_out_delay += crossfade_sample * 2; | 1005 | size_t fade_out_index = crossfade_find_index(crossfade_index, fade_out_delay); |
898 | fade_out_sample = find_chunk(fade_out_delay, &fade_out_chunk) / 2; | ||
899 | 1006 | ||
900 | while (fade_out_rem > 0) | 1007 | while (fade_out_rem > 0) |
901 | { | 1008 | { |
902 | /* Each 1/10 second of audio will have the same fade applied */ | 1009 | /* Each 1/20 second of audio will have the same fade applied */ |
903 | size_t block_rem = MIN(BYTERATE / 10, fade_out_rem); | 1010 | size_t block_rem = MIN(BYTERATE / 20, fade_out_rem); |
904 | int factor = (fade_out_rem << 8) / total_fade_out; | 1011 | int factor = (fade_out_rem << 8) / fade_out_total; |
905 | 1012 | ||
906 | fade_out_rem -= block_rem; | 1013 | fade_out_rem -= block_rem; |
907 | 1014 | ||
908 | crossfade_mix_fade(factor, block_rem, NULL, | 1015 | crossfade_mix_fade(factor, block_rem, NULL, &fade_out_index, |
909 | &fade_out_sample, &fade_out_chunk); | 1016 | 0, MIXFADE_KEEP_POS); |
910 | } | 1017 | } |
911 | 1018 | ||
912 | /* zero out the rest of the buffer */ | 1019 | /* zero out the rest of the buffer */ |
913 | crossfade_mix_fade(0, crossfade_rem, NULL, | 1020 | crossfade_mix_fade(0, INT_MAX, NULL, &fade_out_index, |
914 | &fade_out_sample, &fade_out_chunk); | 1021 | 0, MIXFADE_NULLIFY_POS); |
1022 | |||
1023 | pcm_play_lock(); | ||
915 | } | 1024 | } |
916 | 1025 | ||
917 | /* Initialize fade-in counters */ | 1026 | /* Initialize fade-in counters */ |
918 | crossfade_fade_in_total = global_settings.crossfade_fade_in_duration * BYTERATE; | 1027 | crossfade_fade_in_total = fade_in_duration; |
919 | crossfade_fade_in_rem = crossfade_fade_in_total; | 1028 | crossfade_fade_in_rem = fade_in_duration; |
920 | 1029 | ||
921 | fade_in_delay = global_settings.crossfade_fade_in_delay * BYTERATE; | 1030 | /* Find the right chunk and sample to start fading in - redo from read |
1031 | chunk in case original position were/was overrun in callback - the | ||
1032 | track change event _must not_ ever fail to happen */ | ||
1033 | unplayed = pcmbuf_unplayed_bytes() + fade_in_delay; | ||
1034 | |||
1035 | crossfade_find_buftail(unplayed, fade_out_need); | ||
1036 | |||
1037 | if (crossfade_auto_skip) | ||
1038 | pcmbuf_monitor_track_change_ex(crossfade_index, 0); | ||
1039 | |||
1040 | pcm_play_unlock(); | ||
922 | 1041 | ||
923 | /* Find the right chunk and sample to start fading in */ | ||
924 | fade_in_delay += crossfade_sample * 2; | ||
925 | crossfade_sample = find_chunk(fade_in_delay, &crossfade_chunk) / 2; | ||
926 | logf("crossfade_start done!"); | 1042 | logf("crossfade_start done!"); |
927 | } | 1043 | } |
928 | 1044 | ||
929 | /* Perform fade-in of new track */ | 1045 | /* Perform fade-in of new track */ |
930 | static void write_to_crossfade(size_t length) | 1046 | static void write_to_crossfade(size_t size, unsigned long elapsed, off_t offset) |
931 | { | 1047 | { |
932 | if (length) | 1048 | unsigned char *buf = crossfade_buffer; |
933 | { | ||
934 | char *buf = fadebuf; | ||
935 | if (crossfade_fade_in_rem) | ||
936 | { | ||
937 | size_t samples; | ||
938 | int16_t *input_buf; | ||
939 | 1049 | ||
940 | /* Fade factor for this packet */ | 1050 | if (crossfade_fade_in_rem) |
941 | int factor = | 1051 | { |
942 | ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) / | 1052 | /* Fade factor for this packet */ |
943 | crossfade_fade_in_total; | 1053 | int factor = |
944 | /* Bytes to fade */ | 1054 | ((crossfade_fade_in_total - crossfade_fade_in_rem) << 8) / |
945 | size_t fade_rem = MIN(length, crossfade_fade_in_rem); | 1055 | crossfade_fade_in_total; |
946 | 1056 | /* Bytes to fade */ | |
947 | /* We _will_ fade this many bytes */ | 1057 | size_t fade_rem = MIN(size, crossfade_fade_in_rem); |
948 | crossfade_fade_in_rem -= fade_rem; | ||
949 | 1058 | ||
950 | if (crossfade_chunk) | 1059 | /* We _will_ fade this many bytes */ |
951 | { | 1060 | crossfade_fade_in_rem -= fade_rem; |
952 | /* Mix the data */ | ||
953 | size_t fade_total = fade_rem; | ||
954 | fade_rem = crossfade_mix_fade(factor, fade_rem, buf, | ||
955 | &crossfade_sample, &crossfade_chunk); | ||
956 | length -= fade_total - fade_rem; | ||
957 | buf += fade_total - fade_rem; | ||
958 | if (!length) | ||
959 | return; | ||
960 | } | ||
961 | 1061 | ||
962 | samples = fade_rem / 2; | 1062 | if (crossfade_index != INVALID_BUF_INDEX) |
963 | input_buf = (int16_t *)buf; | ||
964 | /* Fade remaining samples in place */ | ||
965 | while (samples--) | ||
966 | { | ||
967 | int32_t sample = *input_buf; | ||
968 | *input_buf++ = (sample * factor) >> 8; | ||
969 | } | ||
970 | } | ||
971 | |||
972 | if (crossfade_chunk) | ||
973 | { | 1063 | { |
974 | /* Mix the data */ | 1064 | /* Mix the data */ |
975 | size_t mix_total = length; | 1065 | size_t fade_total = fade_rem; |
976 | /* A factor of 256 means mix only, no fading */ | 1066 | fade_rem = crossfade_mix_fade(factor, fade_rem, buf, &crossfade_index, |
977 | length = crossfade_mix_fade(256, length, buf, | 1067 | elapsed, offset); |
978 | &crossfade_sample, &crossfade_chunk); | 1068 | fade_total -= fade_rem; |
979 | buf += mix_total - length; | 1069 | size -= fade_total; |
980 | if (!length) | 1070 | buf += fade_total; |
1071 | |||
1072 | if (!size) | ||
981 | return; | 1073 | return; |
982 | } | 1074 | } |
983 | 1075 | ||
984 | while (length > 0) | 1076 | /* Fade remaining samples in place */ |
1077 | int samples = fade_rem / 4; | ||
1078 | int16_t *input_buf = (int16_t *)buf; | ||
1079 | |||
1080 | while (samples--) | ||
985 | { | 1081 | { |
986 | COMMIT_IF_NEEDED; | 1082 | int32_t left = input_buf[0]; |
987 | size_t pcmbuffer_index = pcmbuffer_pos + pcmbuffer_fillpos; | 1083 | int32_t right = input_buf[1]; |
988 | size_t copy_n = MIN(length, pcmbuf_size - pcmbuffer_index); | 1084 | *input_buf++ = left * factor >> 8; |
989 | memcpy(&pcmbuffer[pcmbuffer_index], buf, copy_n); | 1085 | *input_buf++ = right * factor >> 8; |
990 | buf += copy_n; | ||
991 | pcmbuffer_fillpos += copy_n; | ||
992 | length -= copy_n; | ||
993 | } | 1086 | } |
994 | } | 1087 | } |
1088 | |||
1089 | if (crossfade_index != INVALID_BUF_INDEX) | ||
1090 | { | ||
1091 | /* Mix the data */ | ||
1092 | size_t mix_total = size; | ||
1093 | |||
1094 | /* A factor of 256 means mix only, no fading */ | ||
1095 | size = crossfade_mix_fade(256, size, buf, &crossfade_index, | ||
1096 | elapsed, offset); | ||
1097 | buf += mix_total - size; | ||
1098 | |||
1099 | if (!size) | ||
1100 | return; | ||
1101 | } | ||
1102 | |||
1103 | /* Data might remain in the fade buffer yet the fade-in has run its | ||
1104 | course - finish it off as normal chunks */ | ||
1105 | while (size > 0) | ||
1106 | { | ||
1107 | size_t copy_n = size; | ||
1108 | unsigned char *outbuf = get_write_buffer(©_n); | ||
1109 | memcpy(outbuf, buf, copy_n); | ||
1110 | commit_write_buffer(copy_n, elapsed, offset); | ||
1111 | buf += copy_n; | ||
1112 | size -= copy_n; | ||
1113 | } | ||
1114 | |||
995 | /* if no more fading-in to do, stop the crossfade */ | 1115 | /* if no more fading-in to do, stop the crossfade */ |
996 | if (!(crossfade_fade_in_rem || crossfade_chunk)) | 1116 | #if 0 |
997 | crossfade_active = false; | 1117 | /* This way (the previous way) can cause a sudden volume jump if mixable |
1118 | data is used up before the fade-in completes and that just sounds wrong | ||
1119 | -- jethead71 */ | ||
1120 | if (!crossfade_fade_in_rem || crossfade_index == INVALID_BUF_INDEX) | ||
1121 | #endif | ||
1122 | /* Let fade-in complete even if not fully overlapping the existing data */ | ||
1123 | if (!crossfade_fade_in_rem) | ||
1124 | crossfade_status = CROSSFADE_INACTIVE; | ||
998 | } | 1125 | } |
999 | 1126 | ||
1000 | static void pcmbuf_finish_crossfade_enable(void) | 1127 | static void pcmbuf_finish_crossfade_enable(void) |
1001 | { | 1128 | { |
1002 | /* Copy the pending setting over now */ | 1129 | /* Copy the pending setting over now */ |
1003 | crossfade_enabled = crossfade_enable_request; | 1130 | crossfade_setting = crossfade_enable_request; |
1004 | 1131 | ||
1005 | pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ? | 1132 | pcmbuf_watermark = (crossfade_setting != CROSSFADE_ENABLE_OFF && pcmbuf_size) ? |
1006 | /* If crossfading, try to keep the buffer full other than 1 second */ | 1133 | /* If crossfading, try to keep the buffer full other than 1 second */ |
1007 | (pcmbuf_size - BYTERATE) : | 1134 | (pcmbuf_size - BYTERATE) : |
1008 | /* Otherwise, just use the default */ | 1135 | /* Otherwise, just use the default */ |
@@ -1011,20 +1138,20 @@ static void pcmbuf_finish_crossfade_enable(void) | |||
1011 | 1138 | ||
1012 | bool pcmbuf_is_crossfade_active(void) | 1139 | bool pcmbuf_is_crossfade_active(void) |
1013 | { | 1140 | { |
1014 | return crossfade_active || crossfade_track_change_started; | 1141 | return crossfade_status != CROSSFADE_INACTIVE; |
1015 | } | 1142 | } |
1016 | 1143 | ||
1017 | void pcmbuf_request_crossfade_enable(bool on_off) | 1144 | void pcmbuf_request_crossfade_enable(int setting) |
1018 | { | 1145 | { |
1019 | /* Next setting to be used, not applied now */ | 1146 | /* Next setting to be used, not applied now */ |
1020 | crossfade_enable_request = on_off; | 1147 | crossfade_enable_request = setting; |
1021 | } | 1148 | } |
1022 | 1149 | ||
1023 | bool pcmbuf_is_same_size(void) | 1150 | bool pcmbuf_is_same_size(void) |
1024 | { | 1151 | { |
1025 | /* if pcmbuffer is NULL, then not set up yet even once so always */ | 1152 | /* if pcmbuf_buffer is NULL, then not set up yet even once so always */ |
1026 | bool same_size = pcmbuffer ? | 1153 | bool same_size = pcmbuf_buffer ? |
1027 | (get_next_required_pcmbuf_size() == pcmbuf_size) : true; | 1154 | (get_next_required_pcmbuf_chunks() == pcmbuf_desc_count) : true; |
1028 | 1155 | ||
1029 | /* no buffer change needed, so finish crossfade setup now */ | 1156 | /* no buffer change needed, so finish crossfade setup now */ |
1030 | if (same_size) | 1157 | if (same_size) |
@@ -1037,49 +1164,29 @@ bool pcmbuf_is_same_size(void) | |||
1037 | 1164 | ||
1038 | /** Debug menu, other metrics */ | 1165 | /** Debug menu, other metrics */ |
1039 | 1166 | ||
1040 | /* Amount of bytes left in the buffer. */ | 1167 | /* Amount of bytes left in the buffer, accounting for uncommitted bytes */ |
1041 | size_t pcmbuf_free(void) | 1168 | size_t pcmbuf_free(void) |
1042 | { | 1169 | { |
1043 | if (read_chunk != NULL) | 1170 | return pcmbuf_size - pcmbuf_unplayed_bytes() - pcmbuf_bytes_waiting; |
1044 | { | ||
1045 | void *read = (void *)read_chunk->addr; | ||
1046 | void *write = &pcmbuffer[pcmbuffer_pos + pcmbuffer_fillpos]; | ||
1047 | if (read < write) | ||
1048 | return (size_t)(read - write) + pcmbuf_size; | ||
1049 | else | ||
1050 | return (size_t) (read - write); | ||
1051 | } | ||
1052 | return pcmbuf_size - pcmbuffer_fillpos; | ||
1053 | } | 1171 | } |
1054 | 1172 | ||
1173 | /* Data bytes allocated for buffer */ | ||
1055 | size_t pcmbuf_get_bufsize(void) | 1174 | size_t pcmbuf_get_bufsize(void) |
1056 | { | 1175 | { |
1057 | return pcmbuf_size; | 1176 | return pcmbuf_size; |
1058 | } | 1177 | } |
1059 | 1178 | ||
1179 | /* Number of committed descriptors */ | ||
1060 | int pcmbuf_used_descs(void) | 1180 | int pcmbuf_used_descs(void) |
1061 | { | 1181 | { |
1062 | struct chunkdesc *temp = read_chunk; | 1182 | return pcmbuf_unplayed_bytes() / PCMBUF_CHUNK_SIZE; |
1063 | unsigned int i = 0; | ||
1064 | while (temp) { | ||
1065 | temp = temp->link; | ||
1066 | i++; | ||
1067 | } | ||
1068 | return i; | ||
1069 | } | 1183 | } |
1070 | 1184 | ||
1185 | /* Total number of descriptors allocated */ | ||
1071 | int pcmbuf_descs(void) | 1186 | int pcmbuf_descs(void) |
1072 | { | 1187 | { |
1073 | return NUM_CHUNK_DESCS(pcmbuf_size); | 1188 | return pcmbuf_desc_count; |
1074 | } | ||
1075 | |||
1076 | #ifdef ROCKBOX_HAS_LOGF | ||
1077 | unsigned char *pcmbuf_get_meminfo(size_t *length) | ||
1078 | { | ||
1079 | *length = pcmbuf_bufend - pcmbuffer; | ||
1080 | return pcmbuffer; | ||
1081 | } | 1189 | } |
1082 | #endif | ||
1083 | 1190 | ||
1084 | 1191 | ||
1085 | /** Fading and channel volume control */ | 1192 | /** Fading and channel volume control */ |
@@ -1112,7 +1219,6 @@ static void pcmbuf_fade_tick(void) | |||
1112 | { | 1219 | { |
1113 | /* Fade is complete */ | 1220 | /* Fade is complete */ |
1114 | tick_remove_task(pcmbuf_fade_tick); | 1221 | tick_remove_task(pcmbuf_fade_tick); |
1115 | |||
1116 | if (fade_state == PCM_FADING_OUT) | 1222 | if (fade_state == PCM_FADING_OUT) |
1117 | { | 1223 | { |
1118 | /* Tell PCM to stop at its earliest convenience */ | 1224 | /* Tell PCM to stop at its earliest convenience */ |
@@ -1177,19 +1283,15 @@ void pcmbuf_soft_mode(bool shhh) | |||
1177 | 1283 | ||
1178 | bool pcmbuf_is_lowdata(void) | 1284 | bool pcmbuf_is_lowdata(void) |
1179 | { | 1285 | { |
1180 | if (!pcm_is_playing() || pcm_is_paused() | 1286 | if (!audio_pcmbuf_may_play() || pcmbuf_is_crossfade_active()) |
1181 | #ifdef HAVE_CROSSFADE | ||
1182 | || pcmbuf_is_crossfade_active() | ||
1183 | #endif | ||
1184 | ) | ||
1185 | return false; | 1287 | return false; |
1186 | 1288 | ||
1187 | #if MEMORYSIZE > 2 | 1289 | #if MEMORYSIZE > 2 |
1188 | /* 1 seconds of buffer is low data */ | 1290 | /* 1 seconds of buffer is low data */ |
1189 | return LOW_DATA(4); | 1291 | return pcmbuf_unplayed_bytes() < DATA_LEVEL(4); |
1190 | #else | 1292 | #else |
1191 | /* under watermark is low data */ | 1293 | /* under watermark is low data */ |
1192 | return (pcmbuf_unplayed_bytes < pcmbuf_watermark); | 1294 | return pcmbuf_unplayed_bytes() < pcmbuf_watermark; |
1193 | #endif | 1295 | #endif |
1194 | } | 1296 | } |
1195 | 1297 | ||
@@ -1198,8 +1300,15 @@ void pcmbuf_set_low_latency(bool state) | |||
1198 | low_latency_mode = state; | 1300 | low_latency_mode = state; |
1199 | } | 1301 | } |
1200 | 1302 | ||
1201 | unsigned long pcmbuf_get_latency(void) | 1303 | /* Return the current position key value */ |
1304 | unsigned int pcmbuf_get_position_key(void) | ||
1305 | { | ||
1306 | return position_key; | ||
1307 | } | ||
1308 | |||
1309 | /* Set position updates to be synchronous and immediate in addition to during | ||
1310 | PCM frames - cancelled upon first codec insert or upon stopping */ | ||
1311 | void pcmbuf_sync_position_update(void) | ||
1202 | { | 1312 | { |
1203 | return (pcmbuf_unplayed_bytes + | 1313 | pcmbuf_sync_position = true; |
1204 | mixer_channel_get_bytes_waiting(PCM_MIXER_CHAN_PLAYBACK)) * 1000 / BYTERATE; | ||
1205 | } | 1314 | } |