/*************************************************************************** * * Copyright 2015-2019 BES. * All rights reserved. All unpublished rights reserved. * * No part of this work may be used or reproduced in any form or by any * means, or stored in a database or retrieval system, without prior written * permission of BES. * * Use of this work is governed by a license granted by BES. * This work contains confidential and proprietary information of * BES. which is protected by copyright, trade secret, * trademark and other intellectual property rights. * ****************************************************************************/ /* rbplay source */ /* playback control & rockbox codec porting & codec thread */ #ifdef MBED #include "mbed.h" #include "rtos.h" #endif #include "SDFileSystem.h" #include "app_audio.h" #include "audioflinger.h" #include "cqueue.h" #include "hal_timer.h" #include "hal_trace.h" #include "hal_uart.h" #include #include #include #include #include #include #include #include #include "audiohw.h" #include "channel_mode.h" #include "codec_platform.h" #include "codeclib.h" #include "codecs.h" #include "compressor.h" #include "dsp_core.h" #include "eq.h" #include "metadata.h" #include "metadata_parsers.h" #include "pga.h" #include "app_key.h" #include "app_overlay.h" #include "app_thread.h" #include "app_utils.h" #include "hal_overlay.h" #include "rbpcmbuf.h" #include "rbplay.h" #include "rb_ctl.h" #include "rbplaysd.h" /* Internals */ void rb_ctl_wait_lock_thread(bool lock); void app_rbcodec_ctl_set_play_status(bool st); extern bool app_rbcodec_check_hfp_active(void); #include "SDFileSystem.h" extern "C" void hal_sysfreq_print(void); #define _LOG_TAG "[rb_ctl] " #undef __attribute__(a) #define RBPLAY_DEBUG 1 #if RBPLAY_DEBUG #define _LOG_DBG(str, ...) TRACE(0, _LOG_TAG "" str, ##__VA_ARGS__) #define _LOG_ERR(str, ...) \ TRACE(0, \ _LOG_TAG "err:" \ "" str, \ ##__VA_ARGS__) #else #define _LOG_DBG(str, ...) #define _LOG_ERR(str, ...) \ TRACE(0, \ _LOG_TAG "err:" \ "" str, \ ##__VA_ARGS__) #endif #include "rb_ctl.h" void rb_thread_send_resume(void); typedef struct { uint32_t evt; uint32_t arg; } RBCTL_MSG_BLOCK; static osThreadId rb_ctl_tid; static void rb_ctl_thread(void const *argument); static int rb_ctl_default_priority; osThreadDef(rb_ctl_thread, osPriorityHigh, 1, 1024 * 4, "rb_ctl"); #define RBCTL_MAILBOX_MAX (20) osMailQDef(rb_ctl_mailbox, RBCTL_MAILBOX_MAX, RBCTL_MSG_BLOCK); static osMailQId rb_ctl_mailbox = NULL; int rb_ctl_mailbox_put(RBCTL_MSG_BLOCK *msg_src); // playlist extern playlist_struct sd_playlist; rb_ctl_struct rb_ctl_context; extern void rb_thread_set_decode_vars(int fd, int type, void *id3); extern void rb_play_codec_run(void); extern void rb_codec_set_halt(int halt); extern void rb_player_sync_set_wait_thread(osThreadId tid); extern void rb_player_sync_wait_close(void); extern bool rb_pcmbuf_suspend_play_loop(void); void app_rbplay_load_playlist(playlist_struct *list); int rb_thread_post_msg(RB_MODULE_EVT evt, uint32_t arg); static rb_ctl_status rb_ctl_get_status(void) { return rb_ctl_context.status; } bool rb_ctl_parse_file(const char *file_path) { /* open file */ _LOG_DBG(1, "open file %s\n", file_path); rb_ctl_context.file_handle = open((const char *)file_path, O_RDWR); if (rb_ctl_context.file_handle <= 0) { _LOG_DBG(1, "open file %s failed\n", file_path); return false; } if (!get_metadata(&rb_ctl_context.song_id3, rb_ctl_context.file_handle, rb_ctl_context.rb_fname)) { _LOG_DBG(0, "get_metadata failed\n"); close(rb_ctl_context.file_handle); return false; } _LOG_DBG(4, "%s bitr:%d,saps %d, freq:%d\n", __func__, rb_ctl_context.song_id3.bitrate, rb_ctl_context.song_id3.samples, rb_ctl_context.song_id3.frequency); rb_thread_set_decode_vars(rb_ctl_context.file_handle, rb_ctl_context.song_id3.codectype, &rb_ctl_context.song_id3); return true; } void rb_ctl_vol_operation(bool inc) { uint32_t ret; struct AF_STREAM_CONFIG_T *stream_cfg = NULL; _LOG_DBG(2, "%s inc:%d , ", __func__, inc); if (inc) { if (rb_ctl_context.rb_player_vol < 16) { rb_ctl_context.rb_player_vol++; } } else { if (rb_ctl_context.rb_player_vol > 1) { rb_ctl_context.rb_player_vol--; } } if (rb_ctl_context.status != RB_CTL_PLAYING) return; ret = af_stream_get_cfg(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, &stream_cfg, true); if (ret == 0) { stream_cfg->vol = rb_ctl_context.rb_player_vol; af_stream_setup(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, stream_cfg); } } bool rb_ctl_is_playing(void) { return (rb_ctl_context.status == RB_CTL_PLAYING); } void rb_ctl_set_vol(uint32_t vol) { uint32_t ret; struct AF_STREAM_CONFIG_T *stream_cfg = NULL; _LOG_DBG(2, "%s set vol as :%d , ", __func__, vol); if (rb_ctl_context.status != RB_CTL_PLAYING) return; vol = (vol > 16) ? 16 : vol; rb_ctl_context.rb_player_vol = vol; ret = af_stream_get_cfg(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, &stream_cfg, true); if (ret == 0) { stream_cfg->vol = rb_ctl_context.rb_player_vol; af_stream_setup(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, stream_cfg); } } void rb_ctl_stop_play(void) { _LOG_DBG(1, "%s \n", __func__); rb_codec_set_halt(1); // close file _LOG_DBG(0, " af stream stop \n"); af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK); _LOG_DBG(0, " af stream close \n"); af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK); _LOG_DBG(0, " close file \n"); if (rb_ctl_context.file_handle != -1) close(rb_ctl_context.file_handle); rb_ctl_context.file_handle = -1; // release frequency _LOG_DBG(0, " release freq \n"); return; } void rb_ctl_sync_stop_play(void) { rb_player_sync_set_wait_thread(osThreadGetId()); rb_ctl_stop_play(); rb_player_sync_wait_close(); // close file _LOG_DBG(0, " close file \n"); if (rb_ctl_context.file_handle != -1) close(rb_ctl_context.file_handle); rb_ctl_context.file_handle = -1; } void rb_ctl_pause_playing(void) { af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK); _LOG_DBG(0, " af stream close \n"); af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK); // wait decoder suspend // osDelay(200); // while(!rb_pcmbuf_suspend_play_loop()) { // osThreadYield(); // osDelay(1); // } _LOG_DBG(1, "%s sucessed ", __func__); } void rb_ctl_resume_playing(void) { #ifndef __TWS__ rb_pcmbuf_init(); #endif } void rb_ctl_set_priority(int priority) { osThreadSetPriority(rb_ctl_tid, (osPriority)priority); } int rb_ctl_get_priority(void) { return (int)osThreadGetPriority(rb_ctl_tid); } int rb_ctl_get_default_priority(void) { return rb_ctl_default_priority; } void rb_ctl_reset_priority(void) { osThreadSetPriority(rb_ctl_tid, (osPriority)rb_ctl_default_priority); } #ifdef __IAG_BLE_INCLUDE__ extern "C" void app_ble_inform_music_switch(uint16_t index); #endif static int rb_ctl_handle_event(RBCTL_MSG_BLOCK *msg_body) { RB_MODULE_EVT evt = (RB_MODULE_EVT)msg_body->evt; uint32_t arg = msg_body->arg; // hal_sysfreq_print(); switch (evt) { case RB_MODULE_EVT_PLAY: if (rb_ctl_context.status != RB_CTL_IDLE) { if (rb_ctl_context.status == RB_CTL_SUSPEND) rb_thread_send_resume(); else _LOG_DBG(2, "%s st %d not in idle \n", __func__, rb_ctl_context.status); break; } _LOG_DBG(3, " %s start %d/%d ", __func__, rb_ctl_context.curr_song_idx, sd_playlist.total_songs); if (sd_playlist.total_songs > 0) { playlist_item *it; it = app_rbplay_get_playitem(rb_ctl_context.curr_song_idx); if (it == NULL) { _LOG_DBG(2, " %s get item fail idx %d", __func__, rb_ctl_context.curr_song_idx); } _LOG_DBG(2, "%s start songidx %d \n", __func__, rb_ctl_context.curr_song_idx); memcpy(rb_ctl_context.rb_audio_file, it->file_path, sizeof(uint16_t) * FILE_PATH_LEN - 4); memcpy(rb_ctl_context.rb_fname, it->file_name, sizeof(rb_ctl_context.rb_fname)); if (rb_ctl_parse_file((const char *)rb_ctl_context.rb_audio_file)) { _LOG_DBG(2, "%s start init, the tid 0x%x\n", __func__, osThreadGetId()); rb_play_codec_init(); _LOG_DBG(1, "%s start run\n", __func__); rb_play_codec_run(); rb_ctl_context.status = RB_CTL_PLAYING; app_rbcodec_ctl_set_play_status(true); break; } else { _LOG_DBG(2, "%s evt %d parse fail,find next\n", __func__, msg_body->evt); rb_thread_post_msg(RB_MODULE_EVT_PLAY_NEXT, true); } } else { _LOG_DBG(2, "%s evt %d no songs\n", __func__, msg_body->evt); } break; case RB_MODULE_EVT_STOP: if (rb_ctl_context.status == RB_CTL_IDLE) { _LOG_DBG(2, "%s st %d in idle \n", __func__, rb_ctl_context.status); break; } if (rb_ctl_context.status == RB_CTL_SUSPEND) { // osDelay(100); rb_ctl_resume_playing(); rb_ctl_context.status = RB_CTL_PLAYING; } rb_ctl_sync_stop_play(); rb_ctl_context.status = RB_CTL_IDLE; app_rbcodec_ctl_set_play_status(false); break; case RB_MODULE_EVT_SUSPEND: if (rb_ctl_context.status != RB_CTL_PLAYING) { _LOG_DBG(2, "%s st %d not running \n", __func__, rb_ctl_context.status); break; } rb_ctl_pause_playing(); rb_ctl_context.status = RB_CTL_SUSPEND; break; case RB_MODULE_EVT_RESUME: if (rb_ctl_context.status != RB_CTL_SUSPEND) { _LOG_DBG(2, "%s st %d not suspend \n", __func__, rb_ctl_context.status); break; } rb_ctl_resume_playing(); rb_ctl_context.status = RB_CTL_PLAYING; break; case RB_MODULE_EVT_PLAY_NEXT: if (rb_ctl_context.status == RB_CTL_PLAYING) { rb_thread_post_msg(RB_MODULE_EVT_STOP, 0); } else if (rb_ctl_context.status == RB_CTL_SUSPEND) { rb_thread_post_msg(RB_MODULE_EVT_STOP, 0); } else { } if (arg == 0) { rb_ctl_context.curr_song_idx--; if (rb_ctl_context.curr_song_idx == 0xffff) rb_ctl_context.curr_song_idx = sd_playlist.total_songs - 1; } else { rb_ctl_context.curr_song_idx++; if (rb_ctl_context.curr_song_idx >= sd_playlist.total_songs) rb_ctl_context.curr_song_idx = 0; } #ifdef __IAG_BLE_INCLUDE__ app_ble_inform_music_switch(rb_ctl_context.curr_song_idx); #endif rb_thread_post_msg(RB_MODULE_EVT_PLAY, 0); break; case RB_MODULE_EVT_CHANGE_VOL: rb_ctl_vol_operation(arg); break; case RB_MODULE_EVT_SET_VOL: rb_ctl_set_vol(arg); break; case RB_MODULE_EVT_CHANGE_IDLE: rb_ctl_stop_play(); rb_ctl_context.status = RB_CTL_IDLE; break; case RB_MODULE_EVT_PLAY_IDX: if (arg > sd_playlist.total_songs - 1) { break; } if (rb_ctl_context.status == RB_CTL_PLAYING) { rb_thread_post_msg(RB_MODULE_EVT_STOP, 0); } else if (rb_ctl_context.status == RB_CTL_SUSPEND) { rb_thread_post_msg(RB_MODULE_EVT_STOP, 0); } else { } rb_ctl_context.curr_song_idx = (uint16_t)arg; rb_thread_post_msg(RB_MODULE_EVT_PLAY, 0); break; // for voice cocah #if 0 case SBCREADER_ACTION_INIT: //prepare fs voiceCocah_prepare_fs(); //init the data buff voiceCocah_read_sbc_data(); break; case SBCREADER_ACTION_RUN: //fill sbc queue voiceCocah_read_sbc_data(); break; case SBCREADER_ACTION_STOP: //release the res voiceCocah_stop_clean(); rb_ctl_reset_priority(); break; #endif #ifdef __TWS__ case RB_MODULE_EVT_RESTORE_DUAL_PLAY: extern void rb_restore_dual_play(uint8_t stream_type); rb_restore_dual_play(arg); break; #endif case RB_MODULE_EVT_DEL_FILE: static rb_ctl_status prev_status = rb_ctl_context.status; if (rb_ctl_context.status == RB_CTL_PLAYING) { rb_thread_post_msg(RB_MODULE_EVT_STOP, 0); rb_thread_post_msg(RB_MODULE_EVT_DEL_FILE, arg); } else if (rb_ctl_context.status == RB_CTL_SUSPEND) { rb_thread_post_msg(RB_MODULE_EVT_STOP, 0); rb_thread_post_msg(RB_MODULE_EVT_DEL_FILE, arg); } else { app_ctl_remove_file(arg); if (prev_status == RB_CTL_PLAYING) { rb_thread_post_msg(RB_MODULE_EVT_PLAY, 0); } } break; #if defined(TWS_LINEIN_PLAYER) case RB_MODULE_EVT_LINEIN_START: extern int app_linein_codec_start(void); app_linein_codec_start(); break; case RB_MODULE_EVT_RECONFIG_STREAM: extern void rb_tws_reconfig_stream(uint32_t arg); rb_tws_reconfig_stream(arg); break; case RB_MODULE_EVT_SET_TWS_MODE: break; #endif #ifdef SBC_RECORD_TEST case SBC_RECORD_ACTION_START: { rb_ctl_context.sbc_record_on = true; app_rbplay_set_store_flag(true); app_rbplay_open_sbc_file(); } break; case SBC_RECORD_ACTION_DATA_IND: { if (rb_ctl_context.sbc_record_on) { app_rbplay_process_sbc_data(); } } break; case SBC_RECORD_ACTION_STOP: { rb_ctl_context.sbc_record_on = false; app_rbplay_set_store_flag(false); app_rbplay_close_sbc_file(); } break; #endif default: break; } if (SBC_RECORD_ACTION_DATA_IND != msg_body->evt) _LOG_DBG(3, "%s rbcodec evt %d ,st %d ended\n", __func__, msg_body->evt, rb_ctl_context.status); return 0; } int rb_thread_post_msg(RB_MODULE_EVT evt, uint32_t arg) { int ret; RBCTL_MSG_BLOCK msg; if (!rb_ctl_tid) return 0; /* APIs */ msg.evt = (uint32_t)evt; msg.arg = (uint32_t)arg; ret = rb_ctl_mailbox_put(&msg); _LOG_DBG(3, "%s ret %d evt:%d\n", __func__, ret, evt); return 0; } void app_wait_player_stoped(void) { while (rb_ctl_context.status != RB_CTL_IDLE) osThreadYield(); } void app_wait_player_suspend(void) { if (rb_ctl_context.status == RB_CTL_IDLE) return; while (rb_ctl_context.status != RB_CTL_SUSPEND) osThreadYield(); } void app_wait_player_resumed(void) { if (rb_ctl_context.status == RB_CTL_IDLE) return; while (rb_ctl_context.status != RB_CTL_PLAYING) osThreadYield(); } void rb_thread_send_play(void) { _LOG_DBG(1, " %s ", __FUNCTION__); rb_thread_post_msg(RB_MODULE_EVT_PLAY, (uint32_t)osThreadGetId()); } void rb_thread_send_stop(void) { _LOG_DBG(1, " %s ", __FUNCTION__); rb_thread_post_msg(RB_MODULE_EVT_STOP, (uint32_t)osThreadGetId()); osDelay(200); app_wait_player_stoped(); } void rb_thread_send_pause(void) { _LOG_DBG(1, " %s ", __FUNCTION__); rb_thread_post_msg(RB_MODULE_EVT_SUSPEND, (uint32_t)osThreadGetId()); app_wait_player_suspend(); _LOG_DBG(1, " %s end", __FUNCTION__); } void rb_thread_send_resume(void) { _LOG_DBG(1, " %s ", __FUNCTION__); rb_thread_post_msg(RB_MODULE_EVT_RESUME, (uint32_t)osThreadGetId()); _LOG_DBG(1, " %s end", __FUNCTION__); } void rb_play_dual_play_restore(uint8_t stream_type) { _LOG_DBG(2, "%s %d \n", __func__, __LINE__); rb_thread_post_msg(RB_MODULE_EVT_RESTORE_DUAL_PLAY, stream_type); _LOG_DBG(2, "%s %d \n", __func__, __LINE__); } void rb_thread_send_switch(bool next) { _LOG_DBG(2, "%s %d \n", __func__, __LINE__); rb_thread_post_msg(RB_MODULE_EVT_PLAY_NEXT, next); _LOG_DBG(2, "%s %d \n", __func__, __LINE__); } void rb_play_linein_start(void) { _LOG_DBG(2, "%s %d \n", __func__, __LINE__); rb_thread_post_msg(RB_MODULE_EVT_LINEIN_START, 0); _LOG_DBG(2, "%s %d \n", __func__, __LINE__); } void rb_play_reconfig_stream(uint32_t arg) { _LOG_DBG(3, "%s %d arg:0x%x\n", __func__, __LINE__, arg); rb_thread_post_msg(RB_MODULE_EVT_RECONFIG_STREAM, arg); _LOG_DBG(2, "%s %d \n", __func__, __LINE__); } void rb_play_set_tws_mode(uint32_t arg) { _LOG_DBG(3, "%s %d arg:0x%x\n", __func__, __LINE__, arg); rb_thread_post_msg(RB_MODULE_EVT_SET_TWS_MODE, arg); _LOG_DBG(2, "%s %d \n", __func__, __LINE__); } void rb_thread_send_play_idx(uint32_t array_subidx) { _LOG_DBG(2, " %s array_subidx %d, ", __FUNCTION__, array_subidx); rb_thread_post_msg(RB_MODULE_EVT_PLAY_IDX, array_subidx); } void rb_thread_send_vol(bool inc) { _LOG_DBG(2, " %s inc %d, ", __FUNCTION__, inc); rb_thread_post_msg(RB_MODULE_EVT_CHANGE_VOL, inc); } void rb_thread_send_status_change(void) { _LOG_DBG(1, " %s , ", __FUNCTION__); rb_thread_post_msg(RB_MODULE_EVT_CHANGE_IDLE, 0); } void rb_thread_set_vol(uint32_t vol) { _LOG_DBG(1, " %s , ", __FUNCTION__); rb_thread_post_msg(RB_MODULE_EVT_SET_VOL, vol); } void rb_thread_send_del_file(uint16_t idx) { _LOG_DBG(1, " %s , ", __FUNCTION__); rb_thread_post_msg(RB_MODULE_EVT_DEL_FILE, idx); } void rb_thread_send_sbc_record_start(void) { _LOG_DBG(1, " %s , ", __FUNCTION__); rb_thread_post_msg(SBC_RECORD_ACTION_START, (uint32_t)osThreadGetId()); } void rb_thread_send_sbc_record_data_ind(void) { if (!rb_ctl_context.sbc_record_on) return; // _LOG_DBG(1," %s , ",__FUNCTION__); rb_thread_post_msg(SBC_RECORD_ACTION_DATA_IND, (uint32_t)osThreadGetId()); } void rb_thread_send_sbc_record_stop(void) { _LOG_DBG(1, " %s , ", __FUNCTION__); rb_thread_post_msg(SBC_RECORD_ACTION_STOP, (uint32_t)osThreadGetId()); } // interface for audio module static bool user_key_pause_stream = false; void app_rbplay_audio_reset_pause_status(void) { user_key_pause_stream = false; } int app_rbplay_audio_onoff(bool onoff, uint16_t aud_id) { _LOG_DBG(3, " %s onoff %d, get status:%d", __FUNCTION__, onoff, rb_ctl_get_status()); if (app_rbcodec_check_hfp_active() && !onoff) { if (RB_CTL_PLAYING == rb_ctl_get_status()) { rb_thread_send_pause(); } } else if (!onoff) { // rb_thread_send_stop(); rb_thread_send_pause(); } else { if (!user_key_pause_stream) { if (RB_CTL_SUSPEND == rb_ctl_get_status()) { rb_thread_send_resume(); app_wait_player_resumed(); } else { rb_thread_send_play(); } } } return 0; } void app_rbplay_pause_resume(void) { _LOG_DBG(2, " %s get status:%d", __func__, rb_ctl_get_status()); user_key_pause_stream = true; if (RB_CTL_SUSPEND == rb_ctl_get_status()) { user_key_pause_stream = false; rb_thread_send_resume(); } if (RB_CTL_PLAYING == rb_ctl_get_status()) { user_key_pause_stream = true; rb_thread_send_pause(); } } // sbc reader run within thread static int rb_ctl_mailbox_init(void) { rb_ctl_mailbox = osMailCreate(osMailQ(rb_ctl_mailbox), NULL); if (rb_ctl_mailbox == NULL) { TRACE(0, "Failed to Create rb_ctl_mailbox\n"); return -1; } return 0; } int rb_ctl_mailbox_put(RBCTL_MSG_BLOCK *msg_src) { osStatus status; RBCTL_MSG_BLOCK *msg_p = NULL; msg_p = (RBCTL_MSG_BLOCK *)osMailAlloc(rb_ctl_mailbox, 0); if (!msg_p) { TRACE(3, "%s fail, evt:%d,arg=%d \n", __func__, msg_src->evt, msg_src->arg); return -1; } ASSERT(msg_p, "osMailAlloc error"); msg_p->evt = msg_src->evt; msg_p->arg = msg_src->arg; status = osMailPut(rb_ctl_mailbox, msg_p); return (int)status; } int rb_ctl_mailbox_free(RBCTL_MSG_BLOCK *msg_p) { osStatus status; status = osMailFree(rb_ctl_mailbox, msg_p); return (int)status; } int rb_ctl_mailbox_get(RBCTL_MSG_BLOCK **msg_p) { osEvent evt; evt = osMailGet(rb_ctl_mailbox, osWaitForever); if (evt.status == osEventMail) { *msg_p = (RBCTL_MSG_BLOCK *)evt.value.p; return 0; } return -1; } #if 0 void rb_ctl_action_init(void) { bool ind = voiceCocah_get_indication(); TRACE(2," %s Cocah %d",__func__,ind); if( !ind) return ; // rb_ctl_set_priority(osPriorityHigh); rb_thread_post_msg(SBCREADER_ACTION_INIT,(uint32_t)osThreadGetId()); } void rb_ctl_action_run(void) { bool ind = voiceCocah_get_indication(); TRACE(2," %s Cocah %d",__func__,ind); if( !ind) return ; rb_thread_post_msg(SBCREADER_ACTION_RUN,(uint32_t)osThreadGetId()); } void rb_ctl_action_stop(void) { bool ind = voiceCocah_get_indication(); TRACE(2," %s Cocah %d",__func__,ind); if( !ind) return ; rb_thread_post_msg(SBCREADER_ACTION_STOP,(uint32_t)osThreadGetId()); } #endif static void rb_ctl_thread(void const *argument) { osEvent evt; #if 1 app_sysfreq_req(APP_SYSFREQ_USER_APP_PLAYER, APP_SYSFREQ_208M); app_rbplay_load_playlist(&sd_playlist); #endif memset(&rb_ctl_context, 0x0, sizeof(rb_ctl_struct)); rb_ctl_context.rb_player_vol = 6; rb_ctl_context.status = RB_CTL_IDLE; rb_ctl_context.curr_song_idx = 0; // load playlist here // voiceCocah_init(); rb_ctl_context.init_done = true; while (1) { RBCTL_MSG_BLOCK *msg_p = NULL; app_sysfreq_req(APP_SYSFREQ_USER_APP_PLAYER, APP_SYSFREQ_32K); if (!rb_ctl_mailbox_get(&msg_p)) { app_sysfreq_req(APP_SYSFREQ_USER_APP_PLAYER, APP_SYSFREQ_104M); rb_ctl_handle_event(msg_p); rb_ctl_mailbox_free(msg_p); } } } int rb_ctl_init(void) { _LOG_DBG(1, "%s \n", __func__); rb_ctl_context.init_done = false; rb_ctl_context.sbc_record_on = false; app_rbplay_open(); rb_ctl_mailbox_init(); rb_ctl_tid = osThreadCreate(osThread(rb_ctl_thread), NULL); rb_ctl_default_priority = osPriorityAboveNormal; if (rb_ctl_tid == NULL) { TRACE(0, "Failed to Create rb_ctl_thread\n"); return 0; } _LOG_DBG(1, "Leave %s \n", __func__); return 0; } uint8_t rb_ctl_get_vol(void) { return rb_ctl_context.rb_player_vol; } bool rb_ctl_is_init_done(void) { return rb_ctl_context.init_done; } bool rb_ctl_is_paused(void) { return (RB_CTL_SUSPEND == rb_ctl_get_status()); } uint16_t rb_ctl_songs_count(void) { return sd_playlist.total_songs; }