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