576 lines
14 KiB
C++
576 lines
14 KiB
C++
/***************************************************************************
|
|
*
|
|
* 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 */
|
|
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef MBED
|
|
#include "mbed.h"
|
|
#include "rtos.h"
|
|
#endif
|
|
#include "app_overlay.h"
|
|
#include "apps.h"
|
|
#include "audioflinger.h"
|
|
#include "codecs.h"
|
|
#include "eq_export.h"
|
|
#include "hal_overlay.h"
|
|
#include "hal_trace.h"
|
|
#include "metadata.h"
|
|
|
|
#include "app_key.h"
|
|
#include "app_thread.h"
|
|
#include "app_utils.h"
|
|
#include "rbpcmbuf.h"
|
|
#include "rbplay.h"
|
|
#include "rbplaysd.h"
|
|
#include "utils.h"
|
|
|
|
#ifdef __TWS__
|
|
#include "app_tws.h"
|
|
#endif
|
|
|
|
// TODO: remove
|
|
#define BT_STREAM_RBCODEC 0x10 // from rockbox decoder
|
|
|
|
extern "C" {
|
|
void flac_codec_main(int r);
|
|
void flac_codec_run(void);
|
|
void wav_codec_main(int r);
|
|
void wav_codec_run(void);
|
|
void mpa_codec_main(int r);
|
|
void mpa_codec_run(void);
|
|
void ape_codec_main(int r);
|
|
void ape_codec_run(void);
|
|
void sbc_codec_main(int r);
|
|
void sbc_codec_run(void);
|
|
}
|
|
|
|
extern void rb_pcm_player_open(enum AUD_BITS_T bits,
|
|
enum AUD_SAMPRATE_T sample_rate,
|
|
enum AUD_CHANNEL_NUM_T channel_num, uint8_t vol);
|
|
|
|
#if defined(__TWS__)
|
|
typedef struct _rb_tws_codec_info {
|
|
uint8_t update_codec_info;
|
|
int32_t sample_freq;
|
|
uint8_t channel_num;
|
|
} rb_tws_codec_info;
|
|
|
|
rb_tws_codec_info codec_info = {1, 44100, 2};
|
|
#endif
|
|
|
|
static osThreadId rb_decode_tid = NULL;
|
|
static osThreadId rb_caller_tid = NULL;
|
|
|
|
typedef struct {
|
|
uint32_t evt;
|
|
uint32_t arg;
|
|
} RBTHREAD_MSG_BLOCK;
|
|
|
|
#define RBTHREAD_MAILBOX_MAX (10)
|
|
osMailQDef(rb_decode_mailbox, RBTHREAD_MAILBOX_MAX, RBTHREAD_MSG_BLOCK);
|
|
int rb_decode_mailbox_put(RBTHREAD_MSG_BLOCK *msg_src);
|
|
static osMailQId rb_decode_mailbox = NULL;
|
|
|
|
static void rb_decode_thread(void const *argument);
|
|
osThreadDef(rb_decode_thread, osPriorityAboveNormal, 1, 1024 * 2,
|
|
"rb_decorder");
|
|
|
|
// rbcodec info
|
|
static int song_fd;
|
|
static int song_format;
|
|
static struct mp3entry *current_id3;
|
|
static uint8_t rbplay_loop_on;
|
|
|
|
static volatile int rb_decode_halt_flag = 1;
|
|
|
|
struct codec_api ci_api;
|
|
struct codec_api *ci = &ci_api;
|
|
|
|
// TODO
|
|
volatile osThreadId thread_tid_waiter = NULL;
|
|
|
|
void init_dsp(void);
|
|
void codec_configure(int setting, intptr_t value);
|
|
|
|
uint16_t g_rbplayer_curr_song_idx = 0;
|
|
|
|
extern void app_rbplay_exit(void);
|
|
|
|
extern void bt_change_to_iic(APP_KEY_STATUS *status, void *param);
|
|
|
|
extern void rb_thread_send_switch(bool next);
|
|
extern void rb_thread_send_status_change(void);
|
|
|
|
enum APP_SYSFREQ_FREQ_T rb_player_get_work_freq(void);
|
|
|
|
static void rb_player_sync_close_done(void) { thread_tid_waiter = NULL; }
|
|
|
|
extern void rb_check_stream_reconfig(int32_t freq, uint8_t ch);
|
|
static void f_codec_pcmbuf_insert_callback(const void *ch1, const void *ch2,
|
|
int count) {
|
|
struct dsp_buffer src;
|
|
struct dsp_buffer dst;
|
|
|
|
src.remcount = count;
|
|
src.pin[0] = (const unsigned char *)ch1;
|
|
src.pin[1] = (const unsigned char *)ch2;
|
|
src.proc_mask = 0;
|
|
|
|
if (rb_codec_running() == 0)
|
|
return;
|
|
|
|
#ifndef __TWS__
|
|
while (src.remcount > 0) {
|
|
dst.remcount = 0;
|
|
dst.p16out = (short *)rb_pcmbuf_request_buffer(&dst.bufcount);
|
|
|
|
if (dst.p16out == NULL) {
|
|
warn("No pcm buffer");
|
|
osThreadYield();
|
|
} else {
|
|
dsp_process(ci->dsp, &src, &dst);
|
|
|
|
if (dst.remcount > 0) {
|
|
rb_pcmbuf_write(dst.remcount);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
|
|
if (codec_info.update_codec_info) {
|
|
rb_set_sbc_encoder_freq_ch(
|
|
codec_info.sample_freq,
|
|
codec_info.channel_num); // should call this to set trigger timer
|
|
rb_check_stream_reconfig(codec_info.sample_freq, codec_info.channel_num);
|
|
codec_info.update_codec_info = 0;
|
|
}
|
|
|
|
if (tws_local_player_need_tran_2_slave()) {
|
|
rb_tws_start_master_player(BT_STREAM_RBCODEC);
|
|
}
|
|
while (1) {
|
|
uint8_t *pcm_buff = NULL;
|
|
dst.remcount = 0;
|
|
dst.bufcount = MIN(src.remcount, 128); /* Arbitrary min request */
|
|
dst.p16out = (short *)rb_pcmbuf_request_buffer(&dst.bufcount);
|
|
pcm_buff = (uint8_t *)dst.p16out;
|
|
ASSERT(pcm_buff, "Should request buffer");
|
|
|
|
dsp_process(ci->dsp, &src, &dst);
|
|
|
|
if (dst.remcount > 0) {
|
|
while (rb_push_pcm_in_tws_buffer(pcm_buff, dst.remcount * 2 * 2) == 0) {
|
|
osDelay(2);
|
|
}
|
|
}
|
|
|
|
if (src.remcount <= 0) {
|
|
return; /* No input remains and DSP purged */
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void f_audio_codec_update_elapsed(unsigned long elapsed) {
|
|
// info("Update elapsed: %d", elapsed);
|
|
return;
|
|
}
|
|
|
|
static size_t f_codec_filebuf_callback(void *ptr, size_t size) {
|
|
ssize_t ret;
|
|
ret = read(song_fd, ptr, size);
|
|
if (ret < 0) {
|
|
error("File read error: %d", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void *f_codec_request_buffer_callback(size_t *realsize, size_t reqsize) {
|
|
return NULL;
|
|
}
|
|
|
|
static void *f_codec_advance_buffer_callback(size_t amount) {
|
|
off_t ret = lseek(song_fd, (off_t)(ci->curpos + amount), SEEK_SET);
|
|
if (ret < 0) {
|
|
error("File seek fail");
|
|
return NULL;
|
|
}
|
|
|
|
ci->curpos += amount;
|
|
return (void *)ci;
|
|
}
|
|
|
|
static bool f_codec_seek_buffer_callback(size_t newpos) {
|
|
off_t ret = lseek(song_fd, (off_t)newpos, SEEK_SET);
|
|
if (ret < 0) {
|
|
error("File seek fail");
|
|
return false;
|
|
}
|
|
|
|
ci->curpos = newpos;
|
|
return true;
|
|
}
|
|
|
|
static void f_codec_seek_complete_callback(void) {
|
|
info("Seek complete");
|
|
dsp_configure(ci->dsp, DSP_FLUSH, 0);
|
|
}
|
|
|
|
static void f_audio_codec_update_offset(size_t offset) {}
|
|
|
|
static void f_codec_configure_callback(int setting, intptr_t value) {
|
|
dsp_configure(ci->dsp, setting, value);
|
|
#ifdef __TWS__
|
|
if (setting == DSP_SET_FREQUENCY) {
|
|
if (codec_info.sample_freq != value)
|
|
codec_info.update_codec_info = 1;
|
|
codec_info.sample_freq = value;
|
|
} else if (setting == DSP_SET_STEREO_MODE) {
|
|
if (codec_info.channel_num != (value == STEREO_MONO ? 1 : 2))
|
|
codec_info.update_codec_info = 1;
|
|
codec_info.channel_num = value == STEREO_MONO ? 1 : 2;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static enum codec_command_action f_codec_get_command_callback(intptr_t *param) {
|
|
if (rb_decode_halt_flag == 1)
|
|
return CODEC_ACTION_HALT;
|
|
|
|
return CODEC_ACTION_NULL;
|
|
}
|
|
|
|
static bool f_codec_loop_track_callback(void) { return false; }
|
|
|
|
static void init_ci_file(void) {
|
|
ci->codec_get_buffer = 0;
|
|
ci->pcmbuf_insert = f_codec_pcmbuf_insert_callback;
|
|
ci->set_elapsed = f_audio_codec_update_elapsed;
|
|
ci->read_filebuf = f_codec_filebuf_callback;
|
|
ci->request_buffer = f_codec_request_buffer_callback;
|
|
ci->advance_buffer = f_codec_advance_buffer_callback;
|
|
ci->seek_buffer = f_codec_seek_buffer_callback;
|
|
ci->seek_complete = f_codec_seek_complete_callback;
|
|
ci->set_offset = f_audio_codec_update_offset;
|
|
ci->configure = f_codec_configure_callback;
|
|
ci->get_command = f_codec_get_command_callback;
|
|
ci->loop_track = f_codec_loop_track_callback;
|
|
}
|
|
|
|
static void rb_play_init(void) {
|
|
init_dsp();
|
|
|
|
init_ci_file();
|
|
|
|
#ifndef __TWS__
|
|
rb_pcmbuf_init();
|
|
#endif
|
|
}
|
|
|
|
void rb_play_codec_init(void) {
|
|
RBTHREAD_MSG_BLOCK msg;
|
|
msg.evt = (uint32_t)RB_CTRL_CMD_CODEC_INIT;
|
|
msg.arg = (uint32_t)0;
|
|
rb_decode_mailbox_put(&msg);
|
|
}
|
|
|
|
void rb_play_codec_run(void) {
|
|
RBTHREAD_MSG_BLOCK msg;
|
|
msg.evt = (uint32_t)RB_CTRL_CMD_CODEC_RUN;
|
|
msg.arg = (uint32_t)0;
|
|
rb_decode_mailbox_put(&msg);
|
|
}
|
|
|
|
static int rb_codec_init_desc(void) {
|
|
info("Init decode format: %d", song_format);
|
|
|
|
switch (song_format) {
|
|
case AFMT_MPA_L1:
|
|
case AFMT_MPA_L2:
|
|
case AFMT_MPA_L3:
|
|
app_overlay_select(APP_OVERLAY_MPA);
|
|
mpa_codec_main(CODEC_LOAD);
|
|
break;
|
|
// TODO: add APP_OVERLAY_APE
|
|
#if 0
|
|
case AFMT_APE:
|
|
app_overlay_select(APP_OVERLAY_APE);
|
|
ape_codec_main(CODEC_LOAD);
|
|
break;
|
|
case AFMT_SBC:
|
|
app_overlay_select(APP_OVERLAY_A2DP);
|
|
sbc_codec_main(CODEC_LOAD);
|
|
break;
|
|
case AFMT_FLAC:
|
|
app_overlay_select(APP_OVERLAY_FLAC);
|
|
flac_codec_main(CODEC_LOAD);
|
|
break;
|
|
case AFMT_PCM_WAV:
|
|
app_overlay_select(APP_OVERLAY_WAV);
|
|
wav_codec_main(CODEC_LOAD);
|
|
break;
|
|
#endif
|
|
default:
|
|
error("unkown codec type init\n");
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rb_codec_loop_on(void) {
|
|
#ifdef __TWS__
|
|
// set start transfer to slave
|
|
tws_local_player_set_tran_2_slave_flag(1);
|
|
#endif
|
|
switch (song_format) {
|
|
case AFMT_MPA_L1:
|
|
case AFMT_MPA_L2:
|
|
case AFMT_MPA_L3:
|
|
mpa_codec_run();
|
|
break;
|
|
#if 0
|
|
case AFMT_SBC:
|
|
sbc_codec_run();
|
|
break;
|
|
case AFMT_FLAC:
|
|
flac_codec_run();
|
|
break;
|
|
case AFMT_PCM_WAV:
|
|
wav_codec_run();
|
|
break;
|
|
case AFMT_APE:
|
|
ape_codec_run();
|
|
break;
|
|
#endif
|
|
default:
|
|
error("unkown codec type run\n");
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rb_thread_process_evt(RB_CTRL_CMD_T evt) {
|
|
info("Decode event:%d", evt);
|
|
|
|
switch (evt) {
|
|
case RB_CTRL_CMD_CODEC_INIT:
|
|
rb_decode_halt_flag = 0;
|
|
|
|
rb_play_init();
|
|
|
|
/* get id3 */
|
|
/* init ci info */
|
|
ci->filesize = filesize(song_fd);
|
|
ci->id3 = current_id3;
|
|
ci->curpos = 0;
|
|
|
|
dsp_configure(ci->dsp, DSP_RESET, 0);
|
|
dsp_configure(ci->dsp, DSP_FLUSH, 0);
|
|
|
|
rb_codec_init_desc();
|
|
break;
|
|
case RB_CTRL_CMD_CODEC_RUN:
|
|
rbplay_loop_on = 1;
|
|
|
|
info("Play start");
|
|
app_sysfreq_req(APP_SYSFREQ_USER_APP_PLAYER, rb_player_get_work_freq());
|
|
app_stop_10_second_timer(APP_POWEROFF_TIMER_ID);
|
|
rb_codec_loop_on();
|
|
#if defined(__BTIF_AUTOPOWEROFF__)
|
|
app_start_10_second_timer(APP_POWEROFF_TIMER_ID);
|
|
#endif
|
|
song_fd = 0;
|
|
rb_decode_halt_flag = 1;
|
|
if (thread_tid_waiter) {
|
|
rb_player_sync_close_done();
|
|
} else {
|
|
rb_thread_send_status_change();
|
|
rb_thread_send_switch(true);
|
|
}
|
|
#ifdef __TWS__
|
|
// should update codec info after play one music
|
|
codec_info.update_codec_info = 1;
|
|
#endif
|
|
rbplay_loop_on = 0;
|
|
info("Play end");
|
|
break;
|
|
default:
|
|
error("Unkown rb cmd %d\n", evt);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rb_decode_mailbox_put(RBTHREAD_MSG_BLOCK *msg_src) {
|
|
osStatus status;
|
|
|
|
RBTHREAD_MSG_BLOCK *msg_p = NULL;
|
|
|
|
msg_p = (RBTHREAD_MSG_BLOCK *)osMailAlloc(rb_decode_mailbox, 0);
|
|
if (!msg_p) {
|
|
TRACE(3, "%s fail, evt:%d,arg=%d \n", __func__, msg_src->evt, msg_src->arg);
|
|
return -1;
|
|
}
|
|
|
|
msg_p->evt = msg_src->evt;
|
|
msg_p->arg = msg_src->arg;
|
|
|
|
status = osMailPut(rb_decode_mailbox, msg_p);
|
|
|
|
return (int)status;
|
|
}
|
|
|
|
int rb_decode_mailbox_free(RBTHREAD_MSG_BLOCK *msg_p) {
|
|
osStatus status;
|
|
|
|
status = osMailFree(rb_decode_mailbox, msg_p);
|
|
|
|
return (int)status;
|
|
}
|
|
|
|
int rb_decode_mailbox_get(RBTHREAD_MSG_BLOCK **msg_p) {
|
|
osEvent evt;
|
|
evt = osMailGet(rb_decode_mailbox, osWaitForever);
|
|
if (evt.status == osEventMail) {
|
|
*msg_p = (RBTHREAD_MSG_BLOCK *)evt.value.p;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void rb_decode_thread(void const *argument) {
|
|
RB_CTRL_CMD_T action;
|
|
RBTHREAD_MSG_BLOCK *msg_p;
|
|
|
|
while (1) {
|
|
app_sysfreq_req(APP_SYSFREQ_USER_APP_PLAYER, APP_SYSFREQ_32K);
|
|
// evt = osSignalWait(0, osWaitForever);
|
|
if (0 == rb_decode_mailbox_get(&msg_p)) {
|
|
app_sysfreq_req(APP_SYSFREQ_USER_APP_PLAYER, APP_SYSFREQ_104M);
|
|
|
|
action = (RB_CTRL_CMD_T)msg_p->evt;
|
|
rb_caller_tid = (osThreadId)msg_p->arg;
|
|
|
|
TRACE(3, "[%s] action:%d ,tid,0x%x", __func__, action, rb_caller_tid);
|
|
rb_thread_process_evt(action);
|
|
|
|
rb_decode_mailbox_free(msg_p);
|
|
if (rb_caller_tid)
|
|
osSignalSet(rb_decode_tid, 0x1203);
|
|
rb_caller_tid = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int app_rbplay_open(void) {
|
|
if (rb_decode_tid != NULL) {
|
|
warn("Decode thread reopen");
|
|
return -1;
|
|
}
|
|
|
|
rb_decode_mailbox = osMailCreate(osMailQ(rb_decode_mailbox), NULL);
|
|
if (rb_decode_mailbox == NULL) {
|
|
error("Failed to Create rb_decode_mailbox");
|
|
return -1;
|
|
}
|
|
|
|
rb_decode_tid = osThreadCreate(osThread(rb_decode_thread), NULL);
|
|
if (rb_decode_tid == NULL) {
|
|
error("Failed to Create rb_thread \n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rb_codec_running(void) { return ((rb_decode_halt_flag == 0) ? 1 : 0); }
|
|
|
|
void rb_codec_set_halt(int halt) { rb_decode_halt_flag = halt; }
|
|
|
|
void rb_thread_set_decode_vars(int fd, int type, void *id3) {
|
|
song_fd = fd;
|
|
song_format = type;
|
|
current_id3 = (struct mp3entry *)id3;
|
|
}
|
|
|
|
void rb_player_sync_set_wait_thread(osThreadId tid) {
|
|
if (rbplay_loop_on)
|
|
thread_tid_waiter = tid;
|
|
else
|
|
thread_tid_waiter = NULL;
|
|
}
|
|
|
|
void rb_player_sync_wait_close(void) {
|
|
while (NULL != thread_tid_waiter) {
|
|
osThreadYield();
|
|
}
|
|
}
|
|
|
|
enum APP_SYSFREQ_FREQ_T rb_player_get_work_freq(void) {
|
|
enum APP_SYSFREQ_FREQ_T freq;
|
|
|
|
hal_sysfreq_print();
|
|
|
|
info("bitrate:%d freq:%d\n", ci->id3->bitrate, ci->id3->frequency);
|
|
#ifndef __TWS__
|
|
enum AUD_SAMPRATE_T sample_rate = AUD_SAMPRATE_44100;
|
|
sample_rate = (enum AUD_SAMPRATE_T)ci->id3->frequency;
|
|
if (sample_rate > AUD_SAMPRATE_48000)
|
|
freq = APP_SYSFREQ_208M;
|
|
else if (sample_rate > AUD_SAMPRATE_44100)
|
|
freq = APP_SYSFREQ_104M;
|
|
else
|
|
freq = APP_SYSFREQ_52M;
|
|
|
|
if (ci->id3->bitrate > 192)
|
|
freq = APP_SYSFREQ_208M;
|
|
else if (ci->id3->bitrate > 128)
|
|
freq = APP_SYSFREQ_104M;
|
|
else
|
|
freq = APP_SYSFREQ_52M;
|
|
|
|
switch (song_format) {
|
|
case AFMT_APE:
|
|
freq = APP_SYSFREQ_208M;
|
|
break;
|
|
case AFMT_FLAC:
|
|
freq = APP_SYSFREQ_208M;
|
|
break;
|
|
case AFMT_PCM_WAV:
|
|
freq = APP_SYSFREQ_208M;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#else
|
|
freq = APP_SYSFREQ_208M;
|
|
#endif
|
|
info("Decode thread run at: %d", freq);
|
|
return freq;
|
|
}
|