A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 480 lines 14 kB view raw
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-internal.h" 27#include "pcm_mixer.h" 28#include "pcm_sampr.h" 29 30/* Channels use standard-style PCM callback interface but a latency of one 31 frame by double-buffering is introduced in order to facilitate mixing and 32 keep the hardware fed. There must be sufficient time to perform operations 33 before the last samples are sent to the codec and so things are done in 34 parallel (as much as possible) with sending-out data. */ 35 36static unsigned int mixer_sampr = HW_SAMPR_DEFAULT; 37static unsigned int mix_frame_size = MIX_FRAME_SAMPLES*4; 38 39/* Define this to nonzero to add a marker pulse at each frame start */ 40#define FRAME_BOUNDARY_MARKERS 0 41 42/* Descriptor for each channel */ 43struct mixer_channel 44{ 45 const void *start; /* Buffer pointer */ 46 size_t size; /* Bytes remaining */ 47 size_t last_size; /* Size of consumed data in prev. cycle */ 48 pcm_play_callback_type get_more; /* Registered callback */ 49 enum channel_status status; /* Playback status */ 50 uint32_t amplitude; /* Amp. factor: 0x0000 = mute, 0x10000 = unity */ 51 chan_buffer_hook_fn_type buffer_hook; /* Callback for new buffer */ 52}; 53 54#if (defined(HW_HAVE_192) || defined(HW_HAVE_176)) 55#define FRAME_SIZE_MULT 4 56#elif (defined(HW_HAVE_96) || defined(HW_HAVE_88)) 57#define FRAME_SIZE_MULT 2 58#else 59#define FRAME_SIZE_MULT 1 60#endif 61 62#define MAX_MIX_FRAME_SAMPLES (MIX_FRAME_SAMPLES * FRAME_SIZE_MULT) 63 64/* Because of the double-buffering, playback is always from here, otherwise a 65 mechanism for the channel callbacks not to free buffers too early would be 66 needed (if we _really_ want it and it's worth it, we _can_ do that ;-) ) */ 67static uint32_t downmix_buf[2][MAX_MIX_FRAME_SAMPLES] DOWNMIX_BUF_IBSS MEM_ALIGN_ATTR; 68static int downmix_index = 0; /* Which downmix_buf? */ 69static size_t next_size = 0; /* Size of buffer to play next time */ 70 71/* Descriptors for all available channels */ 72static struct mixer_channel channels[PCM_MIXER_NUM_CHANNELS] IBSS_ATTR; 73 74/* Packed pointer array of all playing (active) channels in "channels" array */ 75static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR; 76 77/* Number of silence frames to play after all data has played */ 78#define MAX_IDLE_FRAMES (mixer_sampr*3 / (mix_frame_size / 4)) 79static unsigned int idle_counter = 0; 80 81/** Mixing routines, CPU optmized **/ 82#include "asm/pcm-mixer.c" 83 84/** Private generic routines **/ 85 86/* Mark channel active to mix its data */ 87static void mixer_activate_channel(struct mixer_channel *chan) 88{ 89 void **elem = find_array_ptr((void **)active_channels, chan); 90 91 if (!*elem) 92 { 93 idle_counter = 0; 94 *elem = chan; 95 } 96} 97 98/* Stop channel from mixing */ 99static void mixer_deactivate_channel(struct mixer_channel *chan) 100{ 101 remove_array_ptr((void **)active_channels, chan); 102} 103 104/* Deactivate channel and change it to stopped state */ 105static void channel_stopped(struct mixer_channel *chan) 106{ 107 mixer_deactivate_channel(chan); 108 chan->size = 0; 109 chan->start = NULL; 110 chan->status = CHANNEL_STOPPED; 111} 112 113/* Main PCM callback - sends the current prepared frame to play */ 114static void mixer_pcm_callback(const void **addr, size_t *size) 115{ 116 *addr = downmix_buf[downmix_index]; 117 *size = next_size; 118} 119 120static inline void chan_call_buffer_hook(struct mixer_channel *chan) 121{ 122 if (UNLIKELY(chan->buffer_hook)) 123 chan->buffer_hook(chan->start, chan->size); 124} 125 126/* Buffering callback - calls sub-callbacks and mixes the data for next 127 buffer to be sent from mixer_pcm_callback() */ 128static enum pcm_dma_status MIXER_CALLBACK_ICODE 129mixer_buffer_callback(enum pcm_dma_status status) 130{ 131 if (status != PCM_DMAST_STARTED) 132 return status; 133 134 downmix_index ^= 1; /* Next buffer */ 135 136 void *mixptr = downmix_buf[downmix_index]; 137 size_t mixsize = mix_frame_size; 138 struct mixer_channel **chan_p; 139 140 next_size = 0; 141 142 /* "Loop" back here if one round wasn't enough to fill a frame */ 143fill_frame: 144 chan_p = active_channels; 145 146 while (*chan_p) 147 { 148 /* Find the active channel with the least data remaining and call any 149 callbacks for channels that ran out - stopping whichever report 150 "no more" */ 151 struct mixer_channel *chan = *chan_p; 152 chan->start += chan->last_size; 153 chan->size -= chan->last_size; 154 155 if (chan->size == 0) 156 { 157 if (chan->get_more) 158 { 159 chan->get_more(&chan->start, &chan->size); 160 ALIGN_AUDIOBUF(chan->start, chan->size); 161 } 162 163 if (!(chan->start && chan->size)) 164 { 165 /* Channel is stopping */ 166 channel_stopped(chan); 167 continue; 168 } 169 170 chan_call_buffer_hook(chan); 171 } 172 173 /* Channel will play for at least part of this frame */ 174 175 /* Channel with least amount of data remaining determines the downmix 176 size */ 177 if (chan->size < mixsize) 178 mixsize = chan->size; 179 180 chan_p++; 181 } 182 183 /* Add all still-active channels to the downmix */ 184 chan_p = active_channels; 185 186 if (LIKELY(*chan_p)) 187 { 188 struct mixer_channel *chan = *chan_p++; 189 190 if (LIKELY(!*chan_p)) 191 { 192 write_samples(mixptr, chan->start, chan->amplitude, mixsize); 193 } 194 else 195 { 196 const void *src0, *src1; 197 unsigned int amp0, amp1; 198 199 /* Mix first two channels with each other as the downmix */ 200 src0 = chan->start; 201 amp0 = chan->amplitude; 202 chan->last_size = mixsize; 203 204 chan = *chan_p++; 205 src1 = chan->start; 206 amp1 = chan->amplitude; 207 208 while (1) 209 { 210 mix_samples(mixptr, src0, amp0, src1, amp1, mixsize); 211 212 if (!*chan_p) 213 break; 214 215 /* More channels to mix - mix each with existing downmix */ 216 chan->last_size = mixsize; 217 chan = *chan_p++; 218 src0 = mixptr; 219 amp0 = MIX_AMP_UNITY; 220 src1 = chan->start; 221 amp1 = chan->amplitude; 222 } 223 } 224 225 chan->last_size = mixsize; 226 next_size += mixsize; 227 228 if (next_size < mix_frame_size) 229 { 230 /* There is still space remaining in this frame */ 231 mixptr += mixsize; 232 mixsize = mix_frame_size - next_size; 233 goto fill_frame; 234 } 235 } 236 else if (idle_counter++ < MAX_IDLE_FRAMES) 237 { 238 /* Pad incomplete frames with silence */ 239 if (idle_counter <= 3) 240 memset(mixptr, 0, mix_frame_size - next_size); 241 242 next_size = mix_frame_size; 243 } 244 /* else silence period ran out - go to sleep */ 245 246#if FRAME_BOUNDARY_MARKERS != 0 247 if (next_size) 248 *downmix_buf[downmix_index] = downmix_index ? 0x7fff7fff : 0x80008000; 249#endif 250 251 /* Certain SoC's have to do cleanup */ 252 mixer_buffer_callback_exit(); 253 254 return PCM_DMAST_OK; 255} 256 257/* Start PCM driver if it's not currently playing */ 258static void mixer_start_pcm(void) 259{ 260 if (pcm_is_playing()) 261 return; 262 263#if defined(HAVE_RECORDING) 264 if (pcm_is_recording()) 265 return; 266#endif 267 268 /* Requires a shared global sample rate for all channels */ 269 pcm_set_frequency(mixer_sampr); 270 271 /* Prepare initial frames and set up the double buffer */ 272 mixer_buffer_callback(PCM_DMAST_STARTED); 273 274 /* Save the previous call's output */ 275 void *start = downmix_buf[downmix_index]; 276 277 mixer_buffer_callback(PCM_DMAST_STARTED); 278 279 pcm_play_data(mixer_pcm_callback, mixer_buffer_callback, 280 start, mix_frame_size); 281} 282 283/** Public interfaces **/ 284 285/* Start playback on a channel */ 286void mixer_channel_play_data(enum pcm_mixer_channel channel, 287 pcm_play_callback_type get_more, 288 const void *start, size_t size) 289{ 290 struct mixer_channel *chan = &channels[channel]; 291 292 ALIGN_AUDIOBUF(start, size); 293 294 if (!(start && size) && get_more) 295 { 296 /* Initial buffer not passed - call the callback now */ 297 pcm_play_lock(); 298 mixer_deactivate_channel(chan); /* Protect chan struct if active; 299 may also be same callback which 300 must not be reentered */ 301 pcm_play_unlock(); /* Allow playback while doing callback */ 302 303 size = 0; 304 get_more(&start, &size); 305 ALIGN_AUDIOBUF(start, size); 306 } 307 308 pcm_play_lock(); 309 310 if (start && size) 311 { 312 /* We have data - start the channel */ 313 chan->status = CHANNEL_PLAYING; 314 chan->start = start; 315 chan->size = size; 316 chan->last_size = 0; 317 chan->get_more = get_more; 318 319 mixer_activate_channel(chan); 320 chan_call_buffer_hook(chan); 321 mixer_start_pcm(); 322 } 323 else 324 { 325 /* Never had anything - stop it now */ 326 channel_stopped(chan); 327 } 328 329 pcm_play_unlock(); 330} 331 332/* Pause or resume a channel (when started) */ 333void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play) 334{ 335 struct mixer_channel *chan = &channels[channel]; 336 337 pcm_play_lock(); 338 339 if (play == (chan->status == CHANNEL_PAUSED) && 340 chan->status != CHANNEL_STOPPED) 341 { 342 if (play) 343 { 344 chan->status = CHANNEL_PLAYING; 345 mixer_activate_channel(chan); 346 mixer_start_pcm(); 347 } 348 else 349 { 350 mixer_deactivate_channel(chan); 351 chan->status = CHANNEL_PAUSED; 352 } 353 } 354 355 pcm_play_unlock(); 356} 357 358/* Stop playback on a channel */ 359void mixer_channel_stop(enum pcm_mixer_channel channel) 360{ 361 struct mixer_channel *chan = &channels[channel]; 362 363 pcm_play_lock(); 364 channel_stopped(chan); 365 pcm_play_unlock(); 366} 367 368/* Set channel's amplitude factor */ 369void mixer_channel_set_amplitude(enum pcm_mixer_channel channel, 370 unsigned int amplitude) 371{ 372 channels[channel].amplitude = MIN(amplitude, MIX_AMP_UNITY); 373} 374 375/* Return channel's playback status */ 376enum channel_status mixer_channel_status(enum pcm_mixer_channel channel) 377{ 378 return channels[channel].status; 379} 380 381/* Returns amount data remaining in channel before next callback */ 382size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel) 383{ 384 return channels[channel].size; 385} 386 387/* Return pointer to channel's playing audio data and the size remaining */ 388const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count) 389{ 390 struct mixer_channel *chan = &channels[channel]; 391 const void * buf = *(const void * volatile *)&chan->start; 392 size_t size = *(size_t volatile *)&chan->size; 393 const void * buf2 = *(const void * volatile *)&chan->start; 394 395 /* Still same buffer? */ 396 if (buf == buf2) 397 { 398 *count = size >> 2; 399 return buf; 400 } 401 /* else can't be sure buf and size are related */ 402 403 *count = 0; 404 return NULL; 405} 406 407/* Calculate peak values for channel */ 408void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel, 409 struct pcm_peaks *peaks) 410{ 411 int count; 412 const void *addr = mixer_channel_get_buffer(channel, &count); 413 414 pcm_do_peak_calculation(peaks, 415 channels[channel].status == CHANNEL_PLAYING, 416 addr, count); 417} 418 419/* Adjust channel pointer by a given offset to support movable buffers */ 420void mixer_adjust_channel_address(enum pcm_mixer_channel channel, 421 off_t offset) 422{ 423 pcm_play_lock(); 424 /* Makes no difference if it's stopped */ 425 channels[channel].start += offset; 426 pcm_play_unlock(); 427} 428 429/* Set a hook that is called upon getting a new source buffer for a channel 430 NOTE: Called for each buffer, not each mixer chunk */ 431void mixer_channel_set_buffer_hook(enum pcm_mixer_channel channel, 432 chan_buffer_hook_fn_type fn) 433{ 434 struct mixer_channel *chan = &channels[channel]; 435 436 pcm_play_lock(); 437 chan->buffer_hook = fn; 438 pcm_play_unlock(); 439} 440 441/* Stop ALL channels and PCM and reset state */ 442void mixer_reset(void) 443{ 444 pcm_play_stop(); 445 446 while (*active_channels) 447 channel_stopped(*active_channels); 448 449 idle_counter = 0; 450} 451 452/* Set output samplerate */ 453void mixer_set_frequency(unsigned int samplerate) 454{ 455 pcm_set_frequency(samplerate); 456 samplerate = pcm_get_frequency(); 457 458 if (samplerate == mixer_sampr) 459 return; 460 461 /* All data is now invalid */ 462 mixer_reset(); 463 mixer_sampr = samplerate; 464 465 /* Work out how much space we really need */ 466 if (samplerate > SAMPR_96) 467 mix_frame_size = 4; 468 else if (samplerate > SAMPR_48) 469 mix_frame_size = 2; 470 else 471 mix_frame_size = 1; 472 473 mix_frame_size *= MIX_FRAME_SAMPLES * 4; 474} 475 476/* Get output samplerate */ 477unsigned int mixer_get_frequency(void) 478{ 479 return mixer_sampr; 480}