Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 764 lines 21 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2005-2007 Miika Pekkarinen 11 * Copyright (C) 2007-2008 Nicolas Pennequin 12 * Copyright (C) 2011 Michael Sevakis 13 * 14 * This program is free software; you can redistribute it and/or 15 * modify it under the terms of the GNU General Public License 16 * as published by the Free Software Foundation; either version 2 17 * of the License, or (at your option) any later version. 18 * 19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20 * KIND, either express or implied. 21 * 22 ****************************************************************************/ 23 24#include "config.h" 25#include "system.h" 26#include "kernel.h" 27#include "codecs.h" 28#include "codec_thread.h" 29#include "pcm_mixer.h" 30#include "pcmbuf.h" 31#include "audio_thread.h" 32#include "playback.h" 33#include "buffering.h" 34#include "dsp_core.h" 35#include "metadata.h" 36#include "settings.h" 37 38/* Define LOGF_ENABLE to enable logf output in this file */ 39/*#define LOGF_ENABLE*/ 40#include "logf.h" 41 42/* macros to enable logf for queues 43 logging on SYS_TIMEOUT can be disabled */ 44#ifdef SIMULATOR 45/* Define this for logf output of all queuing except SYS_TIMEOUT */ 46#define PLAYBACK_LOGQUEUES 47/* Define this to logf SYS_TIMEOUT messages */ 48/*#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT*/ 49#endif 50 51#ifdef PLAYBACK_LOGQUEUES 52#define LOGFQUEUE logf 53#else 54#define LOGFQUEUE(...) 55#endif 56 57#ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT 58#define LOGFQUEUE_SYS_TIMEOUT logf 59#else 60#define LOGFQUEUE_SYS_TIMEOUT(...) 61#endif 62 63/* Variables are commented with the threads that use them: 64 * A=audio, C=codec 65 * - = reads only 66 * 67 * Unless otherwise noted, the extern variables are located 68 * in playback.c. 69 */ 70 71/* Q_LOAD_CODEC parameter data */ 72struct codec_load_info 73{ 74 int hid; /* audio handle id (specify < 0 to use afmt) */ 75 int afmt; /* codec specification (AFMT_*) */ 76}; 77 78 79/** --- Main state control --- **/ 80 81static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */ 82 83/* Private interfaces to main playback control */ 84extern void audio_codec_update_elapsed(unsigned long elapsed); 85extern void audio_codec_update_offset(size_t offset); 86extern void audio_codec_complete(int status); 87extern void audio_codec_seek_complete(void); 88extern struct codec_api ci; /* from codecs.c */ 89 90/* Codec thread */ 91static unsigned int codec_thread_id; /* For modifying thread priority later */ 92static struct event_queue codec_queue SHAREDBSS_ATTR; 93static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR; 94 95/* Workaround stack overflow in opus codec on highmem devices (see FS#13060). */ 96/* Fixed 2019-8-14 (see FS#13131) */ 97#if 0 /*!defined(CPU_COLDFIRE) && (MEMORYSIZE >= 8) && defined(IRAMSIZE) && IRAMSIZE > (32 * 1024)*/ 98#define WORKAROUND_FS13060 0x800 99#else 100#define WORKAROUND_FS13060 0 101#endif 102 103static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000 + WORKAROUND_FS13060)/sizeof(long)] IBSS_ATTR; 104static const char codec_thread_name[] = "codec"; 105 106static void unload_codec(void); 107 108/* Messages are only ever sent one at a time to the codec from the audio 109 thread. This is important for correct operation unless playback is 110 stopped. */ 111 112/* static routines */ 113static void codec_queue_ack(intptr_t ackme) 114{ 115 queue_reply(&codec_queue, ackme); 116} 117 118static intptr_t codec_queue_send(long id, intptr_t data) 119{ 120 return queue_send(&codec_queue, id, data); 121} 122 123/* Poll the state of the codec queue. Returns < 0 if the message is urgent 124 and any state should exit, > 0 if it's a run message (and it was 125 scrubbed), 0 if message was ignored. */ 126static int codec_check_queue__have_msg(void) 127{ 128 struct queue_event ev; 129 130 queue_peek(&codec_queue, &ev); 131 132 /* Seek, pause or stop? Just peek and return if so. Codec 133 must handle the command after returing. Inserts will not 134 be allowed until it complies. */ 135 switch (ev.id) 136 { 137 case Q_CODEC_SEEK: 138 LOGFQUEUE("codec - Q_CODEC_SEEK %ld", ev.id); 139 return -1; 140 case Q_CODEC_PAUSE: 141 LOGFQUEUE("codec - Q_CODEC_PAUSE %ld", ev.id); 142 return -1; 143 case Q_CODEC_STOP: 144 LOGFQUEUE("codec - Q_CODEC_STOP %ld", ev.id); 145 return -1; 146 } 147 148 /* This is in error in this context unless it's "go, go, go!" */ 149 queue_wait(&codec_queue, &ev); 150 151 if (ev.id == Q_CODEC_RUN) 152 { 153 logf("codec < Q_CODEC_RUN: already running!"); 154 codec_queue_ack(Q_CODEC_RUN); 155 return 1; 156 } 157 158 /* Ignore it */ 159 logf("codec < bad req %ld (%s)", ev.id, __func__); 160 codec_queue_ack(Q_NULL); 161 return 0; 162} 163 164/* Does the audio format type equal CODEC_TYPE_ENCODER? */ 165static inline bool type_is_encoder(int afmt) 166{ 167#ifdef AUDIO_HAVE_RECORDING 168 return (afmt & CODEC_TYPE_MASK) == CODEC_TYPE_ENCODER; 169#else 170 return false; 171 (void)afmt; 172#endif 173} 174 175/**************************************/ 176 177 178/** --- Miscellaneous external functions --- **/ 179const char * get_codec_filename(int cod_spec) 180{ 181 const char *fname; 182 183#ifdef HAVE_RECORDING 184 /* Can choose decoder or encoder if one available */ 185 int type = cod_spec & CODEC_TYPE_MASK; 186 int afmt = cod_spec & CODEC_AFMT_MASK; 187 int tmp_fmt = afmt; 188 if ((unsigned)afmt >= AFMT_NUM_CODECS) 189 { 190 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK); 191 tmp_fmt = AFMT_UNKNOWN; 192 } 193 fname = (type == CODEC_TYPE_ENCODER) ? 194 get_codec_enc_root_fn(tmp_fmt) : 195 audio_formats[tmp_fmt].codec_root_fn; 196 197 logf("%s: %d - %s", 198 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder", 199 afmt, fname ? fname : "<unknown>"); 200#else /* !HAVE_RECORDING */ 201 /* Always decoder */ 202 if ((unsigned)cod_spec >= AFMT_NUM_CODECS) 203 cod_spec = AFMT_UNKNOWN; 204 fname = audio_formats[cod_spec].codec_root_fn; 205 logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>"); 206#endif /* HAVE_RECORDING */ 207 208 return fname; 209} 210 211/* Borrow the codec thread and return the ID */ 212void codec_thread_do_callback(void (*fn)(void), unsigned int *id) 213{ 214 /* Set id before telling thread to call something; it may be 215 * needed before this function returns. */ 216 if (id != NULL) 217 *id = codec_thread_id; 218 219 /* Codec thread will signal just before entering callback */ 220 LOGFQUEUE("codec >| Q_CODEC_DO_CALLBACK"); 221 codec_queue_send(Q_CODEC_DO_CALLBACK, (intptr_t)fn); 222} 223 224 225/** --- codec API callbacks --- **/ 226 227static void codec_pcmbuf_insert_callback( 228 const void *ch1, const void *ch2, int count) 229{ 230 struct dsp_buffer src; 231 src.remcount = count; 232 src.pin[0] = ch1; 233 src.pin[1] = ch2; 234 src.proc_mask = 0; 235 236 while (LIKELY(queue_empty(&codec_queue)) || 237 codec_check_queue__have_msg() >= 0) 238 { 239 struct dsp_buffer dst; 240 dst.remcount = 0; 241 dst.bufcount = MAX(src.remcount, 1024); /* Arbitrary min request */ 242 243 if ((dst.p16out = pcmbuf_request_buffer(&dst.bufcount)) == NULL) 244 { 245 cancel_cpu_boost(); 246 247 /* It may be awhile before space is available but we want 248 "instant" response to any message */ 249 queue_wait_w_tmo(&codec_queue, NULL, HZ/20); 250 } 251 else 252 { 253 dsp_process(ci.dsp, &src, &dst, true); 254 255 if (dst.remcount > 0) 256 { 257 pcmbuf_write_complete(dst.remcount, ci.id3->elapsed, 258 ci.id3->offset); 259 } 260 else if (src.remcount <= 0) 261 { 262 return; /* No input remains and DSP purged */ 263 } 264 } 265 } 266} 267 268/* helper function, not a callback */ 269static bool codec_advance_buffer_counters(size_t amount) 270{ 271 if (bufadvance(ci.audio_hid, amount) < 0) 272 { 273 bufseek(ci.audio_hid, ci.filesize); 274 ci.curpos = ci.filesize; 275 return false; 276 } 277 278 ci.curpos += amount; 279 return true; 280} 281 282/* copy up-to size bytes into ptr and return the actual size copied */ 283static size_t codec_filebuf_callback(void *ptr, size_t size) 284{ 285 ssize_t copy_n = bufread(ci.audio_hid, size, ptr); 286 287 /* Nothing requested OR nothing left */ 288 if (copy_n <= 0) 289 return 0; 290 291 /* Update read and other position pointers */ 292 codec_advance_buffer_counters(copy_n); 293 294 /* Return the actual amount of data copied to the buffer */ 295 return copy_n; 296} 297 298static void * codec_request_buffer_callback(size_t *realsize, size_t reqsize) 299{ 300 size_t copy_n = reqsize; 301 ssize_t ret; 302 void *ptr; 303 304 ret = bufgetdata(ci.audio_hid, reqsize, &ptr); 305 if (ret >= 0) 306 copy_n = MIN((size_t)ret, reqsize); 307 else 308 copy_n = 0; 309 310 if (copy_n == 0) 311 ptr = NULL; 312 313 *realsize = copy_n; 314 return ptr; 315} 316 317static void codec_advance_buffer_callback(size_t amount) 318{ 319 if (!codec_advance_buffer_counters(amount)) 320 return; 321 322 audio_codec_update_offset(ci.curpos); 323} 324 325static bool codec_seek_buffer_callback(size_t newpos) 326{ 327 logf("codec_seek_buffer_callback"); 328 329 int ret = bufseek(ci.audio_hid, newpos); 330 if (ret == 0) 331 { 332 ci.curpos = newpos; 333 return true; 334 } 335 336 return false; 337} 338 339static void codec_seek_complete_callback(void) 340{ 341 logf("seek_complete"); 342 343 /* Clear DSP */ 344 dsp_configure(ci.dsp, DSP_FLUSH, 0); 345 346 /* Sync position */ 347 audio_codec_update_offset(ci.curpos); 348 349 /* Post notification to audio thread */ 350 audio_codec_seek_complete(); 351 352 /* Wait for urgent or go message */ 353 do 354 { 355 queue_wait(&codec_queue, NULL); 356 } 357 while (codec_check_queue__have_msg() == 0); 358} 359 360static void codec_configure_callback(int setting, intptr_t value) 361{ 362 dsp_configure(ci.dsp, setting, value); 363} 364 365static long codec_get_command_callback(intptr_t *param) 366{ 367 yield(); 368 369 if (LIKELY(queue_empty(&codec_queue))) 370 return CODEC_ACTION_NULL; /* As you were */ 371 372 /* Process the message - return requested action and data (if any should 373 be expected) */ 374 while (1) 375 { 376 long action = CODEC_ACTION_NULL; 377 struct queue_event ev; 378 379 queue_peek(&codec_queue, &ev); /* Find out what it is */ 380 381 intptr_t id = ev.id; 382 383 switch (id) 384 { 385 case Q_NULL: 386 LOGFQUEUE("codec < Q_NULL"); 387 break; 388 389 case Q_CODEC_RUN: /* Already running */ 390 LOGFQUEUE("codec < Q_CODEC_RUN"); 391 break; 392 393 case Q_CODEC_PAUSE: /* Stay here and wait */ 394 LOGFQUEUE("codec < Q_CODEC_PAUSE"); 395 queue_wait(&codec_queue, &ev); /* Remove message */ 396 codec_queue_ack(Q_CODEC_PAUSE); 397 queue_wait(&codec_queue, NULL); /* Wait for next (no remove) */ 398 continue; 399 400 case Q_CODEC_SEEK: /* Audio wants codec to seek */ 401 LOGFQUEUE("codec < Q_CODEC_SEEK %ld", ev.data); 402 *param = ev.data; 403 action = CODEC_ACTION_SEEK_TIME; 404 trigger_cpu_boost(); 405 break; 406 407 case Q_CODEC_STOP: /* Must only return 0 in main loop */ 408 LOGFQUEUE("codec < Q_CODEC_STOP: %ld", ev.data); 409#ifdef HAVE_RECORDING 410 if (type_is_encoder(codec_type)) 411 { 412 /* Stream finish request (soft stop)? */ 413 if (ev.data && param) 414 { 415 /* ev.data is pointer to size */ 416 *param = ev.data; 417 action = CODEC_ACTION_STREAM_FINISH; 418 break; 419 } 420 } 421 else 422#endif /* HAVE_RECORDING */ 423 { 424 dsp_configure(ci.dsp, DSP_FLUSH, 0); /* Discontinuity */ 425 } 426 427 return CODEC_ACTION_HALT; /* Leave in queue */ 428 429 default: /* This is in error in this context. */ 430 logf("codec bad req %ld (%s)", ev.id, __func__); 431 id = Q_NULL; 432 } 433 434 queue_wait(&codec_queue, &ev); /* Actually remove it */ 435 codec_queue_ack(id); 436 return action; 437 } 438} 439 440static bool codec_loop_track_callback(void) 441{ 442 return global_settings.repeat_mode == REPEAT_ONE; 443} 444 445void codec_strip_filesize_callback(off_t size) 446{ 447 if (bufstripsize(ci.audio_hid, size) >= 0) 448 ci.filesize = size; 449} 450 451/** --- CODEC THREAD --- **/ 452 453/* Handle Q_CODEC_LOAD */ 454static void load_codec(const struct codec_load_info *ev_data) 455{ 456 int status = CODEC_ERROR; 457 /* Save a local copy so we can let the audio thread go ASAP */ 458 struct codec_load_info data = *ev_data; 459 bool const encoder = type_is_encoder(data.afmt); 460 461 if (codec_type != AFMT_UNKNOWN) 462 { 463 /* Must have unloaded it first */ 464 logf("a codec is already loaded"); 465 if (data.hid >= 0) 466 bufclose(data.hid); 467 return; 468 } 469 470 trigger_cpu_boost(); 471 472 if (!encoder) 473 { 474 /* Do this now because codec may set some things up at load time */ 475 dsp_configure(ci.dsp, DSP_RESET, 0); 476 } 477 478#if defined(HAVE_CODEC_BUFFERING) 479 if (data.hid >= 0) 480 { 481 /* First try buffer load */ 482 status = codec_load_buf(data.hid, &ci); 483 bufclose(data.hid); 484 } 485#endif /* HAVE_CODEC_BUFFERING */ 486 487 if (status < 0) 488 { 489 /* Either not a valid handle or the buffer method failed */ 490 const char *codec_fn = get_codec_filename(data.afmt); 491 if (codec_fn) 492 status = codec_load_file(codec_fn, &ci); 493 } 494 495 /* Types must agree */ 496 if (status >= 0 && encoder == !!codec_get_enc_callback()) 497 { 498 codec_type = data.afmt; 499 codec_queue_ack(Q_CODEC_LOAD); 500 return; 501 } 502 503 /* Failed - get rid of it */ 504 unload_codec(); 505} 506 507/* Handle Q_CODEC_RUN */ 508static void run_codec(void) 509{ 510 bool const encoder = type_is_encoder(codec_type); 511 int status; 512 513 if (codec_type == AFMT_UNKNOWN) 514 { 515 logf("no codec to run"); 516 return; 517 } 518 519 codec_queue_ack(Q_CODEC_RUN); 520 521 trigger_cpu_boost(); 522 dsp_configure(ci.dsp, DSP_SET_OUT_FREQUENCY, mixer_get_frequency()); 523 524 if (!encoder) 525 { 526 /* This will be either the initial buffered offset or where it left off 527 if it remained buffered and we're skipping back to it and it is best 528 to have ci.curpos in sync with the handle's read position - it's the 529 codec's responsibility to ensure it has the correct positions - 530 playback is sorta dumb and only has a vague idea about what to 531 buffer based upon what metadata has to say */ 532 ci.curpos = bufftell(ci.audio_hid); 533 534 /* Pin the codec's audio data in place */ 535 buf_pin_handle(ci.audio_hid, true); 536 } 537 538 status = codec_run_proc(); 539 540 if (!encoder) 541 { 542 /* Codec is done with it - let it move */ 543 buf_pin_handle(ci.audio_hid, false); 544 545 /* Notify audio that we're done for better or worse - advise of the 546 status */ 547 audio_codec_complete(status); 548 } 549} 550 551/* Handle Q_CODEC_SEEK */ 552static void seek_codec(unsigned long time) 553{ 554 if (codec_type == AFMT_UNKNOWN) 555 { 556 logf("no codec to seek"); 557 codec_queue_ack(Q_CODEC_SEEK); 558 codec_seek_complete_callback(); 559 return; 560 } 561 562 /* Post it up one level */ 563 queue_post(&codec_queue, Q_CODEC_SEEK, time); 564 codec_queue_ack(Q_CODEC_SEEK); 565 566 /* Have to run it again */ 567 run_codec(); 568} 569 570/* Handle Q_CODEC_UNLOAD */ 571static void unload_codec(void) 572{ 573 /* Tell codec to clean up */ 574 codec_type = AFMT_UNKNOWN; 575 codec_close(); 576} 577 578/* Handle Q_CODEC_DO_CALLBACK */ 579static void do_callback(void (* callback)(void)) 580{ 581 codec_queue_ack(Q_CODEC_DO_CALLBACK); 582 583 if (callback) 584 { 585 commit_discard_idcache(); 586 callback(); 587 commit_dcache(); 588 } 589} 590 591/* Codec thread function */ 592static void NORETURN_ATTR codec_thread(void) 593{ 594 struct queue_event ev; 595 596 while (1) 597 { 598 cancel_cpu_boost(); 599 600 queue_wait(&codec_queue, &ev); 601 602 switch (ev.id) 603 { 604 case Q_CODEC_LOAD: 605 LOGFQUEUE("codec < Q_CODEC_LOAD"); 606 load_codec((const struct codec_load_info *)ev.data); 607 break; 608 609 case Q_CODEC_RUN: 610 LOGFQUEUE("codec < Q_CODEC_RUN"); 611 run_codec(); 612 break; 613 614 case Q_CODEC_PAUSE: 615 LOGFQUEUE("codec < Q_CODEC_PAUSE"); 616 break; 617 618 case Q_CODEC_SEEK: 619 LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data); 620 seek_codec(ev.data); 621 break; 622 623 case Q_CODEC_UNLOAD: 624 LOGFQUEUE("codec < Q_CODEC_UNLOAD"); 625 unload_codec(); 626 break; 627 628 case Q_CODEC_DO_CALLBACK: 629 LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK"); 630 do_callback((void (*)(void))ev.data); 631 break; 632 633 default: 634 LOGFQUEUE("codec < default : %ld", ev.id); 635 } 636 } 637} 638 639 640/** --- Miscellaneous external interfaces -- **/ 641 642/* Initialize playback's codec interface */ 643void INIT_ATTR codec_thread_init(void) 644{ 645 /* Init API */ 646 ci.dsp = dsp_get_config(CODEC_IDX_AUDIO); 647 ci.codec_get_buffer = codec_get_buffer_callback; 648 ci.pcmbuf_insert = codec_pcmbuf_insert_callback; 649 ci.set_elapsed = audio_codec_update_elapsed; 650 ci.read_filebuf = codec_filebuf_callback; 651 ci.request_buffer = codec_request_buffer_callback; 652 ci.advance_buffer = codec_advance_buffer_callback; 653 ci.seek_buffer = codec_seek_buffer_callback; 654 ci.seek_complete = codec_seek_complete_callback; 655 ci.set_offset = audio_codec_update_offset; 656 ci.configure = codec_configure_callback; 657 ci.get_command = codec_get_command_callback; 658 ci.loop_track = codec_loop_track_callback; 659 ci.strip_filesize = codec_strip_filesize_callback; 660 661 /* Init threading */ 662 queue_init(&codec_queue, false); 663 codec_thread_id = create_thread( 664 codec_thread, codec_stack, sizeof(codec_stack), 0, 665 codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK) 666 IF_COP(, CPU)); 667 queue_enable_queue_send(&codec_queue, &codec_queue_sender_list, 668 codec_thread_id); 669} 670 671#ifdef HAVE_PRIORITY_SCHEDULING 672/* Obtain codec thread's current priority */ 673int codec_thread_get_priority(void) 674{ 675 return thread_get_priority(codec_thread_id); 676} 677 678/* Set the codec thread's priority and return the old value */ 679int codec_thread_set_priority(int priority) 680{ 681 return thread_set_priority(codec_thread_id, priority); 682} 683#endif /* HAVE_PRIORITY_SCHEDULING */ 684 685 686/** --- Functions for audio thread use --- **/ 687 688/* Load a decoder or encoder and set the format type */ 689bool codec_load(int hid, int cod_spec) 690{ 691 struct codec_load_info parm = { hid, cod_spec }; 692 693 LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d, %d", hid, cod_spec); 694 return codec_queue_send(Q_CODEC_LOAD, (intptr_t)&parm) != 0; 695} 696 697/* Begin decoding the current file */ 698void codec_go(void) 699{ 700 LOGFQUEUE("audio >| codec Q_CODEC_RUN"); 701 codec_queue_send(Q_CODEC_RUN, 0); 702} 703 704/* Instruct the codec to seek to the specified time (should be properly 705 paused or stopped first to avoid possible buffering deadlock) */ 706void codec_seek(long time) 707{ 708 LOGFQUEUE("audio > codec Q_CODEC_SEEK: %ld", time); 709 codec_queue_send(Q_CODEC_SEEK, time); 710} 711 712/* Pause the codec and make it wait for further instructions inside the 713 command callback */ 714bool codec_pause(void) 715{ 716 LOGFQUEUE("audio >| codec Q_CODEC_PAUSE"); 717 return codec_queue_send(Q_CODEC_PAUSE, 0) != Q_NULL; 718} 719 720/* Stop codec if running - codec stays resident if loaded */ 721void codec_stop(void) 722{ 723 /* Wait until it's in the main loop */ 724 LOGFQUEUE("audio >| codec Q_CODEC_STOP: 0"); 725 while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL); 726} 727 728#ifdef HAVE_RECORDING 729/* Tells codec to take final encoding step and then exit - 730 Returns minimum buffer size required or 0 if complete */ 731size_t codec_finish_stream(void) 732{ 733 size_t size = 0; 734 735 LOGFQUEUE("audio >| codec Q_CODEC_STOP: &size"); 736 if (codec_queue_send(Q_CODEC_STOP, (intptr_t)&size) != Q_NULL) 737 { 738 /* Sync to keep size in scope and get response */ 739 LOGFQUEUE("audio >| codec Q_NULL"); 740 codec_queue_send(Q_NULL, 0); 741 742 if (size == 0) 743 codec_stop(); /* Replied with 0 size */ 744 } 745 /* else thread running in the main loop */ 746 747 return size; 748} 749#endif /* HAVE_RECORDING */ 750 751/* Call the codec's exit routine and close all references */ 752void codec_unload(void) 753{ 754 codec_stop(); 755 LOGFQUEUE("audio >| codec Q_CODEC_UNLOAD"); 756 codec_queue_send(Q_CODEC_UNLOAD, 0); 757} 758 759/* Return the afmt type of the loaded codec - sticks until calling 760 codec_unload unless initial load failed */ 761int codec_loaded(void) 762{ 763 return codec_type; 764}