mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-14 02:27:39 -04:00
Refactor audio thread to run both recording and playback.
Eliminates the pcmrec thread and keeps playback and recording engine
operation mutually-exclusive.
audio_thread.c contains the audio thread which branches to the
correct engine depending upon the request. It also handles the main
audio initialization.
Moves pcm_init into main.c just before dsp_init because I don't want
that one in audio_init in the new file.
(Also makes revision df6e1bc
pointless ;)
Change-Id: Ifc1db24404e6d8dd9ac42d9f4dfbc207aa9a26e1
This commit is contained in:
parent
df6e1bcce5
commit
5857c44017
11 changed files with 553 additions and 564 deletions
|
@ -163,6 +163,7 @@ radio/radioart.c
|
||||||
#if INPUT_SRC_CAPS != 0
|
#if INPUT_SRC_CAPS != 0
|
||||||
audio_path.c
|
audio_path.c
|
||||||
#endif /* INPUT_SRC_CAPS != 0 */
|
#endif /* INPUT_SRC_CAPS != 0 */
|
||||||
|
audio_thread.c
|
||||||
pcmbuf.c
|
pcmbuf.c
|
||||||
codec_thread.c
|
codec_thread.c
|
||||||
playback.c
|
playback.c
|
||||||
|
|
162
apps/audio_thread.c
Normal file
162
apps/audio_thread.c
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2007 Miika Pekkarinen
|
||||||
|
* Copyright (C) 2007-2008 Nicolas Pennequin
|
||||||
|
* Copyright (C) 2011-2013 Michael Sevakis
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
#include "config.h"
|
||||||
|
#include "system.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "logf.h"
|
||||||
|
#include "usb.h"
|
||||||
|
#include "pcm.h"
|
||||||
|
#include "sound.h"
|
||||||
|
#include "audio_thread.h"
|
||||||
|
#ifdef AUDIO_HAVE_RECORDING
|
||||||
|
#include "pcm_record.h"
|
||||||
|
#endif
|
||||||
|
#include "codec_thread.h"
|
||||||
|
#include "voice_thread.h"
|
||||||
|
#include "talk.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
/* Macros to enable logf for queues
|
||||||
|
logging on SYS_TIMEOUT can be disabled */
|
||||||
|
#ifdef SIMULATOR
|
||||||
|
/* Define this for logf output of all queuing except SYS_TIMEOUT */
|
||||||
|
#define AUDIO_LOGQUEUES
|
||||||
|
/* Define this to logf SYS_TIMEOUT messages */
|
||||||
|
/*#define AUDIO_LOGQUEUES_SYS_TIMEOUT*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef AUDIO_LOGQUEUES
|
||||||
|
#define LOGFQUEUE logf
|
||||||
|
#else
|
||||||
|
#define LOGFQUEUE(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool audio_is_initialized = false;
|
||||||
|
|
||||||
|
/* Event queues */
|
||||||
|
struct event_queue audio_queue SHAREDBSS_ATTR;
|
||||||
|
static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR;
|
||||||
|
|
||||||
|
/* Audio thread */
|
||||||
|
static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
|
||||||
|
static const char audio_thread_name[] = "audio";
|
||||||
|
unsigned int audio_thread_id = 0;
|
||||||
|
|
||||||
|
static void NORETURN_ATTR audio_thread(void)
|
||||||
|
{
|
||||||
|
struct queue_event ev;
|
||||||
|
ev.id = SYS_TIMEOUT; /* something not in switch below */
|
||||||
|
|
||||||
|
pcm_postinit();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
switch (ev.id)
|
||||||
|
{
|
||||||
|
/* Starts the playback engine branch */
|
||||||
|
case Q_AUDIO_PLAY:
|
||||||
|
LOGFQUEUE("audio < Q_AUDIO_PLAY");
|
||||||
|
audio_playback_handler(&ev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#ifdef AUDIO_HAVE_RECORDING
|
||||||
|
/* Starts the recording engine branch */
|
||||||
|
case Q_AUDIO_INIT_RECORDING:
|
||||||
|
LOGFQUEUE("audio < Q_AUDIO_INIT_RECORDING");
|
||||||
|
audio_recording_handler(&ev);
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* All return upon USB */
|
||||||
|
case SYS_USB_CONNECTED:
|
||||||
|
LOGFQUEUE("audio < SYS_USB_CONNECTED");
|
||||||
|
voice_stop();
|
||||||
|
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
||||||
|
usb_wait_for_disconnect(&audio_queue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_wait(&audio_queue, &ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the playback and recording status */
|
||||||
|
int audio_status(void)
|
||||||
|
{
|
||||||
|
return playback_status()
|
||||||
|
#ifdef AUDIO_HAVE_RECORDING
|
||||||
|
| pcm_rec_status()
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear all accumulated audio errors for playback and recording */
|
||||||
|
void audio_error_clear(void)
|
||||||
|
{
|
||||||
|
#ifdef AUDIO_HAVE_RECORDING
|
||||||
|
pcm_rec_error_clear();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -- Startup -- **/
|
||||||
|
|
||||||
|
/* Initialize the audio system - called from init() in main.c */
|
||||||
|
void audio_init(void)
|
||||||
|
{
|
||||||
|
/* Can never do this twice */
|
||||||
|
if (audio_is_initialized)
|
||||||
|
{
|
||||||
|
logf("audio: already initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logf("audio: initializing");
|
||||||
|
|
||||||
|
playback_init();
|
||||||
|
|
||||||
|
/* Recording doesn't need init call */
|
||||||
|
|
||||||
|
/* Initialize queues before giving control elsewhere in case it likes
|
||||||
|
to send messages. Thread creation will be delayed however so nothing
|
||||||
|
starts running until ready if something yields such as talk_init. */
|
||||||
|
queue_init(&audio_queue, true);
|
||||||
|
codec_thread_init();
|
||||||
|
|
||||||
|
/* This thread does buffer, so match its priority */
|
||||||
|
audio_thread_id = create_thread(audio_thread, audio_stack,
|
||||||
|
sizeof(audio_stack), 0, audio_thread_name
|
||||||
|
IF_PRIO(, MIN(PRIORITY_BUFFERING, PRIORITY_USER_INTERFACE))
|
||||||
|
IF_COP(, CPU));
|
||||||
|
|
||||||
|
queue_enable_queue_send(&audio_queue, &audio_queue_sender_list,
|
||||||
|
audio_thread_id);
|
||||||
|
|
||||||
|
/* ...now...audio_reset_buffer must know the size of voicefile buffer so
|
||||||
|
init talk first which will init the buffers */
|
||||||
|
talk_init();
|
||||||
|
|
||||||
|
/* Probably safe to say */
|
||||||
|
audio_is_initialized = true;
|
||||||
|
|
||||||
|
sound_settings_apply();
|
||||||
|
}
|
102
apps/audio_thread.h
Normal file
102
apps/audio_thread.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-2007 Miika Pekkarinen
|
||||||
|
* Copyright (C) 2007-2008 Nicolas Pennequin
|
||||||
|
* Copyright (C) 2011-2013 Michael Sevakis
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
#ifndef AUDIO_THREAD_H
|
||||||
|
#define AUDIO_THREAD_H
|
||||||
|
|
||||||
|
/* Define one constant that includes recording related functionality */
|
||||||
|
#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
|
||||||
|
#define AUDIO_HAVE_RECORDING
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
Q_NULL = 0, /* reserved */
|
||||||
|
|
||||||
|
/* -> audio */
|
||||||
|
Q_AUDIO_PLAY,
|
||||||
|
Q_AUDIO_STOP,
|
||||||
|
Q_AUDIO_PAUSE,
|
||||||
|
Q_AUDIO_SKIP,
|
||||||
|
Q_AUDIO_PRE_FF_REWIND,
|
||||||
|
Q_AUDIO_FF_REWIND,
|
||||||
|
Q_AUDIO_FLUSH,
|
||||||
|
Q_AUDIO_DIR_SKIP,
|
||||||
|
|
||||||
|
/* pcmbuf -> audio */
|
||||||
|
Q_AUDIO_TRACK_CHANGED,
|
||||||
|
|
||||||
|
/* audio -> audio */
|
||||||
|
Q_AUDIO_FILL_BUFFER, /* continue buffering next track */
|
||||||
|
|
||||||
|
/* buffering -> audio */
|
||||||
|
Q_AUDIO_BUFFERING, /* some buffer event */
|
||||||
|
Q_AUDIO_FINISH_LOAD_TRACK, /* metadata is buffered */
|
||||||
|
Q_AUDIO_HANDLE_FINISHED, /* some other type is buffered */
|
||||||
|
|
||||||
|
/* codec -> audio (*) */
|
||||||
|
Q_AUDIO_CODEC_SEEK_COMPLETE,
|
||||||
|
Q_AUDIO_CODEC_COMPLETE,
|
||||||
|
|
||||||
|
/* audio -> codec */
|
||||||
|
Q_CODEC_LOAD,
|
||||||
|
Q_CODEC_RUN,
|
||||||
|
Q_CODEC_PAUSE,
|
||||||
|
Q_CODEC_SEEK,
|
||||||
|
Q_CODEC_STOP,
|
||||||
|
Q_CODEC_UNLOAD,
|
||||||
|
|
||||||
|
/* -> codec */
|
||||||
|
Q_CODEC_DO_CALLBACK,
|
||||||
|
|
||||||
|
/* -> recording */
|
||||||
|
#ifdef HAVE_RECORDING
|
||||||
|
Q_AUDIO_INIT_RECORDING,
|
||||||
|
Q_AUDIO_CLOSE_RECORDING,
|
||||||
|
Q_AUDIO_RECORDING_OPTIONS,
|
||||||
|
Q_AUDIO_RECORD,
|
||||||
|
Q_AUDIO_RESUME,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*- settings -*/
|
||||||
|
|
||||||
|
#ifdef HAVE_DISK_STORAGE
|
||||||
|
/* -> audio */
|
||||||
|
Q_AUDIO_UPDATE_WATERMARK, /* buffering watermark needs updating */
|
||||||
|
#endif
|
||||||
|
/* -> audio */
|
||||||
|
Q_AUDIO_REMAKE_AUDIO_BUFFER, /* buffer needs to be reinitialized */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* (*) If you change these, you must check audio_clear_track_notifications
|
||||||
|
in playback.c for correctness */
|
||||||
|
|
||||||
|
void audio_init(void);
|
||||||
|
void playback_init(void);
|
||||||
|
unsigned int playback_status(void);
|
||||||
|
|
||||||
|
void audio_playback_handler(struct queue_event *ev);
|
||||||
|
#ifdef AUDIO_HAVE_RECORDING
|
||||||
|
void audio_recording_handler(struct queue_event *ev);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* AUDIO_THREAD_H */
|
|
@ -26,6 +26,7 @@
|
||||||
#include "codecs.h"
|
#include "codecs.h"
|
||||||
#include "codec_thread.h"
|
#include "codec_thread.h"
|
||||||
#include "pcmbuf.h"
|
#include "pcmbuf.h"
|
||||||
|
#include "audio_thread.h"
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "buffering.h"
|
#include "buffering.h"
|
||||||
#include "dsp_core.h"
|
#include "dsp_core.h"
|
||||||
|
|
11
apps/main.c
11
apps/main.c
|
@ -87,6 +87,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (CONFIG_CODEC == SWCODEC)
|
#if (CONFIG_CODEC == SWCODEC)
|
||||||
|
#include "audio_thread.h"
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "tdspeed.h"
|
#include "tdspeed.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -386,6 +387,7 @@ static void init(void)
|
||||||
|
|
||||||
storage_init();
|
storage_init();
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
pcm_init();
|
||||||
dsp_init();
|
dsp_init();
|
||||||
#endif
|
#endif
|
||||||
settings_reset();
|
settings_reset();
|
||||||
|
@ -422,10 +424,6 @@ static void init(void)
|
||||||
|
|
||||||
audio_init();
|
audio_init();
|
||||||
|
|
||||||
#if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING)
|
|
||||||
pcm_rec_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
settings_apply_skins();
|
settings_apply_skins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,6 +639,7 @@ static void init(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
pcm_init();
|
||||||
dsp_init();
|
dsp_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -727,10 +726,6 @@ static void init(void)
|
||||||
audio_init();
|
audio_init();
|
||||||
CHART("<audio_init");
|
CHART("<audio_init");
|
||||||
|
|
||||||
#if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING)
|
|
||||||
pcm_rec_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* runtime database has to be initialized after audio_init() */
|
/* runtime database has to be initialized after audio_init() */
|
||||||
cpu_boost(false);
|
cpu_boost(false);
|
||||||
|
|
||||||
|
|
438
apps/playback.c
438
apps/playback.c
|
@ -27,7 +27,6 @@
|
||||||
#include "core_alloc.h"
|
#include "core_alloc.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "ata.h"
|
#include "ata.h"
|
||||||
#include "usb.h"
|
|
||||||
#include "codecs.h"
|
#include "codecs.h"
|
||||||
#include "codec_thread.h"
|
#include "codec_thread.h"
|
||||||
#include "voice_thread.h"
|
#include "voice_thread.h"
|
||||||
|
@ -38,6 +37,7 @@
|
||||||
#include "playlist.h"
|
#include "playlist.h"
|
||||||
#include "abrepeat.h"
|
#include "abrepeat.h"
|
||||||
#include "pcmbuf.h"
|
#include "pcmbuf.h"
|
||||||
|
#include "audio_thread.h"
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
@ -46,10 +46,6 @@
|
||||||
#include "tagcache.h"
|
#include "tagcache.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef AUDIO_HAVE_RECORDING
|
|
||||||
#include "pcm_record.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_LCD_BITMAP
|
#ifdef HAVE_LCD_BITMAP
|
||||||
#ifdef HAVE_ALBUMART
|
#ifdef HAVE_ALBUMART
|
||||||
#include "albumart.h"
|
#include "albumart.h"
|
||||||
|
@ -104,8 +100,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Miscellaneous **/
|
/** Miscellaneous **/
|
||||||
bool audio_is_initialized = false; /* (A,O-) */
|
extern unsigned int audio_thread_id; /* from audio_thread.c */
|
||||||
extern struct codec_api ci; /* (A,C) */
|
extern struct event_queue audio_queue; /* from audio_thread.c */
|
||||||
|
extern bool audio_is_initialized; /* from audio_thread.c */
|
||||||
|
extern struct codec_api ci; /* from codecs.c */
|
||||||
|
|
||||||
/** Possible arrangements of the main buffer **/
|
/** Possible arrangements of the main buffer **/
|
||||||
static enum audio_buffer_state
|
static enum audio_buffer_state
|
||||||
|
@ -190,7 +188,6 @@ static enum filling_state
|
||||||
STATE_FINISHED, /* all remaining tracks are fully buffered */
|
STATE_FINISHED, /* all remaining tracks are fully buffered */
|
||||||
STATE_ENDING, /* audio playback is ending */
|
STATE_ENDING, /* audio playback is ending */
|
||||||
STATE_ENDED, /* audio playback is done */
|
STATE_ENDED, /* audio playback is done */
|
||||||
STATE_USB, /* USB mode, ignore most messages */
|
|
||||||
} filling = STATE_IDLE;
|
} filling = STATE_IDLE;
|
||||||
|
|
||||||
/* Track info - holds information about each track in the buffer */
|
/* Track info - holds information about each track in the buffer */
|
||||||
|
@ -330,15 +327,6 @@ static int codec_skip_status;
|
||||||
static bool codec_seeking = false; /* Codec seeking ack expected? */
|
static bool codec_seeking = false; /* Codec seeking ack expected? */
|
||||||
static unsigned int position_key = 0;
|
static unsigned int position_key = 0;
|
||||||
|
|
||||||
/* Event queues */
|
|
||||||
static struct event_queue audio_queue SHAREDBSS_ATTR;
|
|
||||||
|
|
||||||
/* Audio thread */
|
|
||||||
static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR;
|
|
||||||
static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
|
|
||||||
static const char audio_thread_name[] = "audio";
|
|
||||||
static unsigned int audio_thread_id = 0;
|
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
enum audio_start_playback_flags
|
enum audio_start_playback_flags
|
||||||
{
|
{
|
||||||
|
@ -2985,37 +2973,131 @@ static void audio_on_audio_flush(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AUDIO_HAVE_RECORDING
|
/* Called by audio thread when playback is started */
|
||||||
/* Load the requested encoder type
|
void audio_playback_handler(struct queue_event *ev)
|
||||||
(Q_AUDIO_LOAD_ENCODER) */
|
|
||||||
static void audio_on_load_encoder(int afmt)
|
|
||||||
{
|
{
|
||||||
bool res = true;
|
|
||||||
|
|
||||||
if (play_status != PLAY_STOPPED)
|
|
||||||
audio_stop_playback(); /* Can't load both types at once */
|
|
||||||
else
|
|
||||||
codec_unload(); /* Encoder still loaded, stop and unload it */
|
|
||||||
|
|
||||||
if (afmt != AFMT_UNKNOWN)
|
|
||||||
{
|
|
||||||
res = codec_load(-1, afmt | CODEC_TYPE_ENCODER);
|
|
||||||
if (res)
|
|
||||||
codec_go(); /* These are run immediately */
|
|
||||||
}
|
|
||||||
|
|
||||||
queue_reply(&audio_queue, res);
|
|
||||||
}
|
|
||||||
#endif /* AUDIO_HAVE_RECORDING */
|
|
||||||
|
|
||||||
static void audio_thread(void)
|
|
||||||
{
|
|
||||||
struct queue_event ev;
|
|
||||||
|
|
||||||
pcm_postinit();
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
switch (ev->id)
|
||||||
|
{
|
||||||
|
/** Codec and track change messages **/
|
||||||
|
case Q_AUDIO_CODEC_COMPLETE:
|
||||||
|
/* Codec is done processing track and has gone idle */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_CODEC_COMPLETE: %ld",
|
||||||
|
(long)ev->data);
|
||||||
|
audio_on_codec_complete(ev->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_CODEC_SEEK_COMPLETE:
|
||||||
|
/* Codec is done seeking */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_SEEK_COMPLETE");
|
||||||
|
audio_on_codec_seek_complete();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_TRACK_CHANGED:
|
||||||
|
/* PCM track change done */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_TRACK_CHANGED");
|
||||||
|
audio_on_track_changed();
|
||||||
|
break;
|
||||||
|
|
||||||
|
/** Control messages **/
|
||||||
|
case Q_AUDIO_PLAY:
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_PLAY");
|
||||||
|
audio_start_playback(ev->data, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef AUDIO_HAVE_RECORDING
|
||||||
|
/* So we can go straight from playback to recording */
|
||||||
|
case Q_AUDIO_INIT_RECORDING:
|
||||||
|
#endif
|
||||||
|
case SYS_USB_CONNECTED:
|
||||||
|
case Q_AUDIO_STOP:
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_STOP");
|
||||||
|
audio_stop_playback();
|
||||||
|
if (ev->data != 0)
|
||||||
|
queue_clear(&audio_queue);
|
||||||
|
return; /* no more playback */
|
||||||
|
|
||||||
|
case Q_AUDIO_PAUSE:
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_PAUSE");
|
||||||
|
audio_on_pause(ev->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_SKIP:
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_SKIP");
|
||||||
|
audio_on_skip();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_DIR_SKIP:
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_DIR_SKIP");
|
||||||
|
audio_on_dir_skip(ev->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_PRE_FF_REWIND:
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_PRE_FF_REWIND");
|
||||||
|
audio_on_pre_ff_rewind();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_FF_REWIND:
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_FF_REWIND");
|
||||||
|
audio_on_ff_rewind(ev->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_FLUSH:
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_FLUSH: %d", (int)ev->data);
|
||||||
|
audio_on_audio_flush();
|
||||||
|
break;
|
||||||
|
|
||||||
|
/** Buffering messages **/
|
||||||
|
case Q_AUDIO_BUFFERING:
|
||||||
|
/* some buffering event */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_BUFFERING: %d", (int)ev->data);
|
||||||
|
audio_on_buffering(ev->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_FILL_BUFFER:
|
||||||
|
/* continue buffering next track */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_FILL_BUFFER");
|
||||||
|
audio_on_fill_buffer();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_FINISH_LOAD_TRACK:
|
||||||
|
/* metadata is buffered */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_FINISH_LOAD_TRACK");
|
||||||
|
audio_on_finish_load_track(ev->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Q_AUDIO_HANDLE_FINISHED:
|
||||||
|
/* some other type is buffered */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_HANDLE_FINISHED");
|
||||||
|
audio_on_handle_finished(ev->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/** Miscellaneous messages **/
|
||||||
|
case Q_AUDIO_REMAKE_AUDIO_BUFFER:
|
||||||
|
/* buffer needs to be reinitialized */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_REMAKE_AUDIO_BUFFER");
|
||||||
|
audio_start_playback(0, AUDIO_START_RESTART | AUDIO_START_NEWBUF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef HAVE_DISK_STORAGE
|
||||||
|
case Q_AUDIO_UPDATE_WATERMARK:
|
||||||
|
/* buffering watermark needs updating */
|
||||||
|
LOGFQUEUE("playback < Q_AUDIO_UPDATE_WATERMARK: %d",
|
||||||
|
(int)ev->data);
|
||||||
|
audio_update_filebuf_watermark(ev->data);
|
||||||
|
break;
|
||||||
|
#endif /* HAVE_DISK_STORAGE */
|
||||||
|
|
||||||
|
case SYS_TIMEOUT:
|
||||||
|
LOGFQUEUE_SYS_TIMEOUT("playback < SYS_TIMEOUT");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* LOGFQUEUE("audio < default : %08lX", ev->id); */
|
||||||
|
break;
|
||||||
|
} /* end switch */
|
||||||
|
|
||||||
switch (filling)
|
switch (filling)
|
||||||
{
|
{
|
||||||
/* Active states */
|
/* Active states */
|
||||||
|
@ -3039,174 +3121,22 @@ static void audio_thread(void)
|
||||||
if (audio_pcmbuf_track_change_scan())
|
if (audio_pcmbuf_track_change_scan())
|
||||||
{
|
{
|
||||||
/* Transfer notification to audio queue event */
|
/* Transfer notification to audio queue event */
|
||||||
ev.id = Q_AUDIO_TRACK_CHANGED;
|
ev->id = Q_AUDIO_TRACK_CHANGED;
|
||||||
ev.data = 1;
|
ev->data = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* If doing auto skip, poll pcmbuf track notifications a bit
|
/* If doing auto skip, poll pcmbuf track notifications a bit
|
||||||
faster to promply detect the transition */
|
faster to promply detect the transition */
|
||||||
queue_wait_w_tmo(&audio_queue, &ev,
|
queue_wait_w_tmo(&audio_queue, ev,
|
||||||
skip_pending == TRACK_SKIP_NONE ?
|
skip_pending == TRACK_SKIP_NONE ? HZ/2 : HZ/10);
|
||||||
HZ/2 : HZ/10);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Idle states */
|
/* Idle states */
|
||||||
default:
|
default:
|
||||||
queue_wait(&audio_queue, &ev);
|
queue_wait(&audio_queue, ev);
|
||||||
|
|
||||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
|
||||||
switch (ev.id)
|
|
||||||
{
|
|
||||||
#ifdef AUDIO_HAVE_RECORDING
|
|
||||||
/* Must monitor the encoder message for recording so it can remove
|
|
||||||
it if we process the insertion before it does. It cannot simply
|
|
||||||
be removed from under recording however. */
|
|
||||||
case Q_AUDIO_LOAD_ENCODER:
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case SYS_USB_DISCONNECTED:
|
|
||||||
filling = STATE_IDLE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (filling == STATE_USB)
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PLATFORM */
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ev.id)
|
|
||||||
{
|
|
||||||
/** Codec and track change messages **/
|
|
||||||
case Q_AUDIO_CODEC_COMPLETE:
|
|
||||||
/* Codec is done processing track and has gone idle */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_CODEC_COMPLETE: %ld", (long)ev.data);
|
|
||||||
audio_on_codec_complete(ev.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_CODEC_SEEK_COMPLETE:
|
|
||||||
/* Codec is done seeking */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_SEEK_COMPLETE");
|
|
||||||
audio_on_codec_seek_complete();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_TRACK_CHANGED:
|
|
||||||
/* PCM track change done */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED");
|
|
||||||
audio_on_track_changed();
|
|
||||||
break;
|
|
||||||
|
|
||||||
/** Control messages **/
|
|
||||||
case Q_AUDIO_PLAY:
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_PLAY");
|
|
||||||
audio_start_playback(ev.data, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_STOP:
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_STOP");
|
|
||||||
audio_stop_playback();
|
|
||||||
if (ev.data != 0)
|
|
||||||
queue_clear(&audio_queue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_PAUSE:
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_PAUSE");
|
|
||||||
audio_on_pause(ev.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_SKIP:
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_SKIP");
|
|
||||||
audio_on_skip();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_DIR_SKIP:
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_DIR_SKIP");
|
|
||||||
audio_on_dir_skip(ev.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_PRE_FF_REWIND:
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_PRE_FF_REWIND");
|
|
||||||
audio_on_pre_ff_rewind();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_FF_REWIND:
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_FF_REWIND");
|
|
||||||
audio_on_ff_rewind(ev.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_FLUSH:
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_FLUSH: %d", (int)ev.data);
|
|
||||||
audio_on_audio_flush();
|
|
||||||
break;
|
|
||||||
|
|
||||||
/** Buffering messages **/
|
|
||||||
case Q_AUDIO_BUFFERING:
|
|
||||||
/* some buffering event */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_BUFFERING: %d", (int)ev.data);
|
|
||||||
audio_on_buffering(ev.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_FILL_BUFFER:
|
|
||||||
/* continue buffering next track */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER");
|
|
||||||
audio_on_fill_buffer();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_FINISH_LOAD_TRACK:
|
|
||||||
/* metadata is buffered */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_FINISH_LOAD_TRACK");
|
|
||||||
audio_on_finish_load_track(ev.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Q_AUDIO_HANDLE_FINISHED:
|
|
||||||
/* some other type is buffered */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_HANDLE_FINISHED");
|
|
||||||
audio_on_handle_finished(ev.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/** Miscellaneous messages **/
|
|
||||||
case Q_AUDIO_REMAKE_AUDIO_BUFFER:
|
|
||||||
/* buffer needs to be reinitialized */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_REMAKE_AUDIO_BUFFER");
|
|
||||||
audio_start_playback(0, AUDIO_START_RESTART | AUDIO_START_NEWBUF);
|
|
||||||
break;
|
|
||||||
|
|
||||||
#ifdef HAVE_DISK_STORAGE
|
|
||||||
case Q_AUDIO_UPDATE_WATERMARK:
|
|
||||||
/* buffering watermark needs updating */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_UPDATE_WATERMARK: %d", (int)ev.data);
|
|
||||||
audio_update_filebuf_watermark(ev.data);
|
|
||||||
break;
|
|
||||||
#endif /* HAVE_DISK_STORAGE */
|
|
||||||
|
|
||||||
#ifdef AUDIO_HAVE_RECORDING
|
|
||||||
case Q_AUDIO_LOAD_ENCODER:
|
|
||||||
/* load an encoder for recording */
|
|
||||||
LOGFQUEUE("audio < Q_AUDIO_LOAD_ENCODER: %d", (int)ev.data);
|
|
||||||
audio_on_load_encoder(ev.data);
|
|
||||||
break;
|
|
||||||
#endif /* AUDIO_HAVE_RECORDING */
|
|
||||||
|
|
||||||
case SYS_USB_CONNECTED:
|
|
||||||
LOGFQUEUE("audio < SYS_USB_CONNECTED");
|
|
||||||
audio_stop_playback();
|
|
||||||
#ifdef PLAYBACK_VOICE
|
|
||||||
voice_stop();
|
|
||||||
#endif
|
|
||||||
filling = STATE_USB;
|
|
||||||
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SYS_TIMEOUT:
|
|
||||||
LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* LOGFQUEUE("audio < default : %08lX", ev.id); */
|
|
||||||
break;
|
|
||||||
} /* end switch */
|
|
||||||
} /* end while */
|
} /* end while */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3356,27 +3286,6 @@ bool audio_pcmbuf_may_play(void)
|
||||||
|
|
||||||
/** -- External interfaces -- **/
|
/** -- External interfaces -- **/
|
||||||
|
|
||||||
/* Return the playback and recording status */
|
|
||||||
int audio_status(void)
|
|
||||||
{
|
|
||||||
unsigned int ret = play_status;
|
|
||||||
|
|
||||||
#ifdef AUDIO_HAVE_RECORDING
|
|
||||||
/* Do this here for constitency with mpeg.c version */
|
|
||||||
ret |= pcm_rec_status();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return (int)ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear all accumulated audio errors for playback and recording */
|
|
||||||
void audio_error_clear(void)
|
|
||||||
{
|
|
||||||
#ifdef AUDIO_HAVE_RECORDING
|
|
||||||
pcm_rec_error_clear();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get a copy of the id3 data for the for current track + offset + skip delta */
|
/* Get a copy of the id3 data for the for current track + offset + skip delta */
|
||||||
bool audio_peek_track(struct mp3entry *id3, int offset)
|
bool audio_peek_track(struct mp3entry *id3, int offset)
|
||||||
{
|
{
|
||||||
|
@ -3599,7 +3508,7 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
|
||||||
{
|
{
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
|
|
||||||
if (audio_is_initialized)
|
if (audio_is_initialized && thread_self() != audio_thread_id)
|
||||||
{
|
{
|
||||||
audio_hard_stop();
|
audio_hard_stop();
|
||||||
}
|
}
|
||||||
|
@ -3656,15 +3565,6 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_RECORDING
|
|
||||||
/* Stop audio, voice and obtain all available buffer space */
|
|
||||||
unsigned char * audio_get_recording_buffer(size_t *buffer_size)
|
|
||||||
{
|
|
||||||
audio_hard_stop();
|
|
||||||
return audio_get_buffer(true, buffer_size);
|
|
||||||
}
|
|
||||||
#endif /* HAVE_RECORDING */
|
|
||||||
|
|
||||||
/* Restore audio buffer to a particular state (promoting status) */
|
/* Restore audio buffer to a particular state (promoting status) */
|
||||||
bool audio_restore_playback(int type)
|
bool audio_restore_playback(int type)
|
||||||
{
|
{
|
||||||
|
@ -3755,30 +3655,6 @@ void playback_release_aa_slot(int slot)
|
||||||
}
|
}
|
||||||
#endif /* HAVE_ALBUMART */
|
#endif /* HAVE_ALBUMART */
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_RECORDING
|
|
||||||
/* Load an encoder and run it */
|
|
||||||
bool audio_load_encoder(int afmt)
|
|
||||||
{
|
|
||||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
|
||||||
LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: %d", afmt);
|
|
||||||
return audio_queue_send(Q_AUDIO_LOAD_ENCODER, afmt) != 0;
|
|
||||||
#else
|
|
||||||
(void)afmt;
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop an encoder and unload it */
|
|
||||||
void audio_remove_encoder(void)
|
|
||||||
{
|
|
||||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
|
||||||
LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: NULL");
|
|
||||||
audio_queue_send(Q_AUDIO_LOAD_ENCODER, AFMT_UNKNOWN);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif /* HAVE_RECORDING */
|
|
||||||
|
|
||||||
/* Is an automatic skip in progress? If called outside transition callbacks,
|
/* Is an automatic skip in progress? If called outside transition callbacks,
|
||||||
indicates the last skip type at the time it was processed and isn't very
|
indicates the last skip type at the time it was processed and isn't very
|
||||||
meaningful. */
|
meaningful. */
|
||||||
|
@ -3866,58 +3742,24 @@ void audio_set_crossfade(int enable)
|
||||||
}
|
}
|
||||||
#endif /* HAVE_CROSSFADE */
|
#endif /* HAVE_CROSSFADE */
|
||||||
|
|
||||||
|
unsigned int playback_status(void)
|
||||||
/** -- Startup -- **/
|
|
||||||
|
|
||||||
/* Initialize the audio system - called from init() in main.c */
|
|
||||||
void audio_init(void)
|
|
||||||
{
|
{
|
||||||
/* Can never do this twice */
|
return play_status;
|
||||||
if (audio_is_initialized)
|
|
||||||
{
|
|
||||||
logf("audio: already initialized");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logf("audio: initializing");
|
/** -- Startup -- **/
|
||||||
|
void playback_init(void)
|
||||||
/* Initialize queues before giving control elsewhere in case it likes
|
{
|
||||||
to send messages. Thread creation will be delayed however so nothing
|
logf("playback: initializing");
|
||||||
starts running until ready if something yields such as talk_init. */
|
|
||||||
queue_init(&audio_queue, true);
|
|
||||||
|
|
||||||
mutex_init(&id3_mutex);
|
|
||||||
|
|
||||||
pcm_init();
|
|
||||||
|
|
||||||
codec_thread_init();
|
|
||||||
|
|
||||||
/* This thread does buffer, so match its priority */
|
|
||||||
audio_thread_id = create_thread(audio_thread, audio_stack,
|
|
||||||
sizeof(audio_stack), 0, audio_thread_name
|
|
||||||
IF_PRIO(, MIN(PRIORITY_BUFFERING, PRIORITY_USER_INTERFACE))
|
|
||||||
IF_COP(, CPU));
|
|
||||||
|
|
||||||
queue_enable_queue_send(&audio_queue, &audio_queue_sender_list,
|
|
||||||
audio_thread_id);
|
|
||||||
|
|
||||||
/* Initialize the track buffering system */
|
/* Initialize the track buffering system */
|
||||||
|
mutex_init(&id3_mutex);
|
||||||
track_list_init();
|
track_list_init();
|
||||||
buffering_init();
|
buffering_init();
|
||||||
|
|
||||||
#ifdef HAVE_CROSSFADE
|
#ifdef HAVE_CROSSFADE
|
||||||
/* Set crossfade setting for next buffer init which should be about... */
|
/* Set crossfade setting for next buffer init which should be about... */
|
||||||
pcmbuf_request_crossfade_enable(global_settings.crossfade);
|
pcmbuf_request_crossfade_enable(global_settings.crossfade);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ...now...audio_reset_buffer must know the size of voicefile buffer so
|
|
||||||
init talk first which will init the buffers */
|
|
||||||
talk_init();
|
|
||||||
|
|
||||||
/* Probably safe to say */
|
|
||||||
audio_is_initialized = true;
|
|
||||||
|
|
||||||
sound_settings_apply();
|
|
||||||
#ifdef HAVE_DISK_STORAGE
|
#ifdef HAVE_DISK_STORAGE
|
||||||
audio_set_buffer_margin(global_settings.buffer_margin);
|
audio_set_buffer_margin(global_settings.buffer_margin);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -92,68 +92,6 @@ size_t audio_get_filebuflen(void);
|
||||||
otherwise the result is undefined. */
|
otherwise the result is undefined. */
|
||||||
bool audio_automatic_skip(void);
|
bool audio_automatic_skip(void);
|
||||||
|
|
||||||
/* Define one constant that includes recording related functionality */
|
unsigned int playback_status(void);
|
||||||
#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
|
|
||||||
#define AUDIO_HAVE_RECORDING
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum {
|
|
||||||
Q_NULL = 0, /* reserved */
|
|
||||||
|
|
||||||
/* -> audio */
|
|
||||||
Q_AUDIO_PLAY = 1,
|
|
||||||
Q_AUDIO_STOP,
|
|
||||||
Q_AUDIO_PAUSE,
|
|
||||||
Q_AUDIO_SKIP,
|
|
||||||
Q_AUDIO_PRE_FF_REWIND,
|
|
||||||
Q_AUDIO_FF_REWIND,
|
|
||||||
Q_AUDIO_FLUSH,
|
|
||||||
Q_AUDIO_DIR_SKIP,
|
|
||||||
|
|
||||||
/* pcmbuf -> audio */
|
|
||||||
Q_AUDIO_TRACK_CHANGED,
|
|
||||||
|
|
||||||
/* audio -> audio */
|
|
||||||
Q_AUDIO_FILL_BUFFER, /* continue buffering next track */
|
|
||||||
|
|
||||||
/* buffering -> audio */
|
|
||||||
Q_AUDIO_BUFFERING, /* some buffer event */
|
|
||||||
Q_AUDIO_FINISH_LOAD_TRACK, /* metadata is buffered */
|
|
||||||
Q_AUDIO_HANDLE_FINISHED, /* some other type is buffered */
|
|
||||||
|
|
||||||
/* codec -> audio (*) */
|
|
||||||
Q_AUDIO_CODEC_SEEK_COMPLETE,
|
|
||||||
Q_AUDIO_CODEC_COMPLETE,
|
|
||||||
|
|
||||||
/* audio -> codec */
|
|
||||||
Q_CODEC_LOAD,
|
|
||||||
Q_CODEC_RUN,
|
|
||||||
Q_CODEC_PAUSE,
|
|
||||||
Q_CODEC_SEEK,
|
|
||||||
Q_CODEC_STOP,
|
|
||||||
Q_CODEC_UNLOAD,
|
|
||||||
|
|
||||||
|
|
||||||
/*- miscellanous -*/
|
|
||||||
#ifdef AUDIO_HAVE_RECORDING
|
|
||||||
/* -> codec */
|
|
||||||
Q_AUDIO_LOAD_ENCODER, /* load an encoder for recording */
|
|
||||||
#endif
|
|
||||||
/* -> codec */
|
|
||||||
Q_CODEC_DO_CALLBACK,
|
|
||||||
|
|
||||||
|
|
||||||
/*- settings -*/
|
|
||||||
|
|
||||||
#ifdef HAVE_DISK_STORAGE
|
|
||||||
/* -> audio */
|
|
||||||
Q_AUDIO_UPDATE_WATERMARK, /* buffering watermark needs updating */
|
|
||||||
#endif
|
|
||||||
/* -> audio */
|
|
||||||
Q_AUDIO_REMAKE_AUDIO_BUFFER, /* buffer needs to be reinitialized */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* (*) If you change these, you must check audio_clear_track_notifications
|
|
||||||
in playback.c for correctness */
|
|
||||||
|
|
||||||
#endif /* _PLAYBACK_H */
|
#endif /* _PLAYBACK_H */
|
||||||
|
|
|
@ -38,11 +38,13 @@
|
||||||
#ifdef HAVE_SPDIF_IN
|
#ifdef HAVE_SPDIF_IN
|
||||||
#include "spdif.h"
|
#include "spdif.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "audio_thread.h"
|
||||||
|
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
|
|
||||||
|
extern struct event_queue audio_queue;
|
||||||
|
|
||||||
/** General recording state **/
|
/** General recording state **/
|
||||||
static bool is_initialized = false; /* Subsystem ready? */
|
|
||||||
static bool is_recording; /* We are recording */
|
static bool is_recording; /* We are recording */
|
||||||
static bool is_paused; /* We have paused */
|
static bool is_paused; /* We have paused */
|
||||||
static unsigned long errors; /* An error has occured */
|
static unsigned long errors; /* An error has occured */
|
||||||
|
@ -230,14 +232,6 @@ enum
|
||||||
|
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
|
|
||||||
static struct event_queue pcmrec_queue SHAREDBSS_ATTR;
|
|
||||||
static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR;
|
|
||||||
static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
|
|
||||||
static const char pcmrec_thread_name[] = "pcmrec";
|
|
||||||
static unsigned int pcmrec_thread_id = 0;
|
|
||||||
|
|
||||||
static void pcmrec_thread(void);
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PCMREC_NULL = 0,
|
PCMREC_NULL = 0,
|
||||||
|
@ -248,15 +242,24 @@ enum
|
||||||
PCMREC_STOP, /* stop the current recording */
|
PCMREC_STOP, /* stop the current recording */
|
||||||
PCMREC_PAUSE, /* pause the current recording */
|
PCMREC_PAUSE, /* pause the current recording */
|
||||||
PCMREC_RESUME, /* resume the current recording */
|
PCMREC_RESUME, /* resume the current recording */
|
||||||
#if 0
|
|
||||||
PCMREC_FLUSH_NUM, /* flush a number of files out */
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*******************************************************************/
|
/*******************************************************************/
|
||||||
/* Functions that are not executing in the pcmrec_thread first */
|
/* Functions that are not executing in the audio thread first */
|
||||||
/*******************************************************************/
|
/*******************************************************************/
|
||||||
|
|
||||||
|
static void pcmrec_raise_error_status(unsigned long e)
|
||||||
|
{
|
||||||
|
pcm_rec_lock(); /* DMA sets this too */
|
||||||
|
errors |= e;
|
||||||
|
pcm_rec_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcmrec_raise_warning_status(unsigned long w)
|
||||||
|
{
|
||||||
|
warnings |= w;
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback for when more data is ready - called in interrupt context */
|
/* Callback for when more data is ready - called in interrupt context */
|
||||||
static void pcm_rec_have_more(void **start, size_t *size)
|
static void pcm_rec_have_more(void **start, size_t *size)
|
||||||
{
|
{
|
||||||
|
@ -268,7 +271,7 @@ static void pcm_rec_have_more(void **start, size_t *size)
|
||||||
/* set pcm ovf if processing start position is inside current
|
/* set pcm ovf if processing start position is inside current
|
||||||
write chunk */
|
write chunk */
|
||||||
if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
|
if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
|
||||||
warnings |= PCMREC_W_PCM_BUFFER_OVF;
|
pcmrec_raise_warning_status(PCMREC_W_PCM_BUFFER_OVF);
|
||||||
|
|
||||||
dma_wr_pos = next_pos;
|
dma_wr_pos = next_pos;
|
||||||
}
|
}
|
||||||
|
@ -285,7 +288,7 @@ static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status)
|
||||||
if (status == PCM_DMAST_ERR_DMA)
|
if (status == PCM_DMAST_ERR_DMA)
|
||||||
{
|
{
|
||||||
/* Flush recorded data to disk and stop recording */
|
/* Flush recorded data to disk and stop recording */
|
||||||
queue_post(&pcmrec_queue, PCMREC_STOP, 0);
|
errors |= PCMREC_E_DMA;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
/* else try again next transmission - frame is invalid */
|
/* else try again next transmission - frame is invalid */
|
||||||
|
@ -315,9 +318,9 @@ void pcm_rec_error_clear(void)
|
||||||
/**
|
/**
|
||||||
* Check mode, errors and warnings
|
* Check mode, errors and warnings
|
||||||
*/
|
*/
|
||||||
unsigned long pcm_rec_status(void)
|
unsigned int pcm_rec_status(void)
|
||||||
{
|
{
|
||||||
unsigned long ret = 0;
|
unsigned int ret = 0;
|
||||||
|
|
||||||
if (is_recording)
|
if (is_recording)
|
||||||
ret |= AUDIO_STATUS_RECORD;
|
ret |= AUDIO_STATUS_RECORD;
|
||||||
|
@ -379,20 +382,6 @@ unsigned long pcm_rec_sample_rate(void)
|
||||||
} /* audio_get_sample_rate */
|
} /* audio_get_sample_rate */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates pcmrec_thread
|
|
||||||
*/
|
|
||||||
void pcm_rec_init(void)
|
|
||||||
{
|
|
||||||
queue_init(&pcmrec_queue, true);
|
|
||||||
pcmrec_thread_id =
|
|
||||||
create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
|
|
||||||
0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
|
|
||||||
IF_COP(, CPU));
|
|
||||||
queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send,
|
|
||||||
pcmrec_thread_id);
|
|
||||||
} /* pcm_rec_init */
|
|
||||||
|
|
||||||
/** audio_* group **/
|
/** audio_* group **/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -401,7 +390,7 @@ void pcm_rec_init(void)
|
||||||
void audio_init_recording(void)
|
void audio_init_recording(void)
|
||||||
{
|
{
|
||||||
logf("audio_init_recording");
|
logf("audio_init_recording");
|
||||||
queue_send(&pcmrec_queue, PCMREC_INIT, 0);
|
queue_send(&audio_queue, Q_AUDIO_INIT_RECORDING, 1);
|
||||||
logf("audio_init_recording done");
|
logf("audio_init_recording done");
|
||||||
} /* audio_init_recording */
|
} /* audio_init_recording */
|
||||||
|
|
||||||
|
@ -411,7 +400,7 @@ void audio_init_recording(void)
|
||||||
void audio_close_recording(void)
|
void audio_close_recording(void)
|
||||||
{
|
{
|
||||||
logf("audio_close_recording");
|
logf("audio_close_recording");
|
||||||
queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
|
queue_send(&audio_queue, Q_AUDIO_CLOSE_RECORDING, 0);
|
||||||
logf("audio_close_recording done");
|
logf("audio_close_recording done");
|
||||||
} /* audio_close_recording */
|
} /* audio_close_recording */
|
||||||
|
|
||||||
|
@ -421,7 +410,7 @@ void audio_close_recording(void)
|
||||||
void audio_set_recording_options(struct audio_recording_options *options)
|
void audio_set_recording_options(struct audio_recording_options *options)
|
||||||
{
|
{
|
||||||
logf("audio_set_recording_options");
|
logf("audio_set_recording_options");
|
||||||
queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
|
queue_send(&audio_queue, Q_AUDIO_RECORDING_OPTIONS, (intptr_t)options);
|
||||||
logf("audio_set_recording_options done");
|
logf("audio_set_recording_options done");
|
||||||
} /* audio_set_recording_options */
|
} /* audio_set_recording_options */
|
||||||
|
|
||||||
|
@ -432,7 +421,7 @@ void audio_record(const char *filename)
|
||||||
{
|
{
|
||||||
logf("audio_record: %s", filename);
|
logf("audio_record: %s", filename);
|
||||||
flush_interrupt();
|
flush_interrupt();
|
||||||
queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
|
queue_send(&audio_queue, Q_AUDIO_RECORD, (intptr_t)filename);
|
||||||
logf("audio_record_done");
|
logf("audio_record_done");
|
||||||
} /* audio_record */
|
} /* audio_record */
|
||||||
|
|
||||||
|
@ -451,7 +440,7 @@ void audio_stop_recording(void)
|
||||||
{
|
{
|
||||||
logf("audio_stop_recording");
|
logf("audio_stop_recording");
|
||||||
flush_interrupt();
|
flush_interrupt();
|
||||||
queue_post(&pcmrec_queue, PCMREC_STOP, 0);
|
queue_post(&audio_queue, Q_AUDIO_STOP, 0);
|
||||||
logf("audio_stop_recording done");
|
logf("audio_stop_recording done");
|
||||||
} /* audio_stop_recording */
|
} /* audio_stop_recording */
|
||||||
|
|
||||||
|
@ -462,7 +451,7 @@ void audio_pause_recording(void)
|
||||||
{
|
{
|
||||||
logf("audio_pause_recording");
|
logf("audio_pause_recording");
|
||||||
flush_interrupt();
|
flush_interrupt();
|
||||||
queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
|
queue_post(&audio_queue, Q_AUDIO_PAUSE, 0);
|
||||||
logf("audio_pause_recording done");
|
logf("audio_pause_recording done");
|
||||||
} /* audio_pause_recording */
|
} /* audio_pause_recording */
|
||||||
|
|
||||||
|
@ -472,7 +461,7 @@ void audio_pause_recording(void)
|
||||||
void audio_resume_recording(void)
|
void audio_resume_recording(void)
|
||||||
{
|
{
|
||||||
logf("audio_resume_recording");
|
logf("audio_resume_recording");
|
||||||
queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
|
queue_post(&audio_queue, Q_AUDIO_RESUME, 0);
|
||||||
logf("audio_resume_recording done");
|
logf("audio_resume_recording done");
|
||||||
} /* audio_resume_recording */
|
} /* audio_resume_recording */
|
||||||
|
|
||||||
|
@ -517,10 +506,46 @@ unsigned long audio_num_recorded_bytes(void)
|
||||||
|
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
/* */
|
/* */
|
||||||
/* Functions that execute in the context of pcmrec_thread */
|
/* Functions that execute in the context of audio thread */
|
||||||
/* */
|
/* */
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
|
|
||||||
|
static void pcmrec_init_state(void)
|
||||||
|
{
|
||||||
|
flush_interrupts = 0;
|
||||||
|
|
||||||
|
/* warings and errors */
|
||||||
|
warnings =
|
||||||
|
errors = 0;
|
||||||
|
|
||||||
|
/* pcm FIFO */
|
||||||
|
dma_lock = true;
|
||||||
|
pcm_rd_pos = 0;
|
||||||
|
dma_wr_pos = 0;
|
||||||
|
pcm_enc_pos = 0;
|
||||||
|
|
||||||
|
/* encoder FIFO */
|
||||||
|
enc_wr_index = 0;
|
||||||
|
enc_rd_index = 0;
|
||||||
|
|
||||||
|
/* filename queue */
|
||||||
|
fnq_rd_pos = 0;
|
||||||
|
fnq_wr_pos = 0;
|
||||||
|
|
||||||
|
/* stats */
|
||||||
|
num_rec_bytes = 0;
|
||||||
|
num_rec_samples = 0;
|
||||||
|
#if 0
|
||||||
|
accum_rec_bytes = 0;
|
||||||
|
accum_pcm_samples = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pre_record_ticks = 0;
|
||||||
|
|
||||||
|
is_recording = false;
|
||||||
|
is_paused = false;
|
||||||
|
} /* pcmrec_init_state */
|
||||||
|
|
||||||
/** Filename Queue **/
|
/** Filename Queue **/
|
||||||
|
|
||||||
/* returns true if the queue is empty */
|
/* returns true if the queue is empty */
|
||||||
|
@ -594,7 +619,7 @@ static void pcmrec_close_file(int *fd_p)
|
||||||
return; /* preserve error */
|
return; /* preserve error */
|
||||||
|
|
||||||
if (close(*fd_p) != 0)
|
if (close(*fd_p) != 0)
|
||||||
errors |= PCMREC_E_IO;
|
pcmrec_raise_error_status(PCMREC_E_IO);
|
||||||
|
|
||||||
*fd_p = -1;
|
*fd_p = -1;
|
||||||
} /* pcmrec_close_file */
|
} /* pcmrec_close_file */
|
||||||
|
@ -646,7 +671,7 @@ static void pcmrec_start_file(void)
|
||||||
{
|
{
|
||||||
logf("start file: fnq empty");
|
logf("start file: fnq empty");
|
||||||
*filename = '\0';
|
*filename = '\0';
|
||||||
errors |= PCMREC_E_FNQ_DESYNC;
|
pcmrec_raise_error_status(PCMREC_E_FNQ_DESYNC);
|
||||||
}
|
}
|
||||||
else if (errors != 0)
|
else if (errors != 0)
|
||||||
{
|
{
|
||||||
|
@ -656,7 +681,7 @@ static void pcmrec_start_file(void)
|
||||||
{
|
{
|
||||||
/* Any previous file should have been closed */
|
/* Any previous file should have been closed */
|
||||||
logf("start file: file already open");
|
logf("start file: file already open");
|
||||||
errors |= PCMREC_E_FNQ_DESYNC;
|
pcmrec_raise_error_status(PCMREC_E_FNQ_DESYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors != 0)
|
if (errors != 0)
|
||||||
|
@ -671,7 +696,7 @@ static void pcmrec_start_file(void)
|
||||||
if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
|
if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
|
||||||
{
|
{
|
||||||
logf("start file: enc error");
|
logf("start file: enc error");
|
||||||
errors |= PCMREC_E_ENCODER;
|
pcmrec_raise_error_status(PCMREC_E_ENCODER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors != 0)
|
if (errors != 0)
|
||||||
|
@ -706,7 +731,7 @@ static inline void pcmrec_write_chunk(void)
|
||||||
{
|
{
|
||||||
logf("wr chk enc error %lu %lu",
|
logf("wr chk enc error %lu %lu",
|
||||||
rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
|
rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
|
||||||
errors |= PCMREC_E_ENCODER;
|
pcmrec_raise_error_status(PCMREC_E_ENCODER);
|
||||||
}
|
}
|
||||||
} /* pcmrec_write_chunk */
|
} /* pcmrec_write_chunk */
|
||||||
|
|
||||||
|
@ -725,7 +750,7 @@ static void pcmrec_end_file(void)
|
||||||
if (rec_fdata.chunk->flags & CHUNKF_ERROR)
|
if (rec_fdata.chunk->flags & CHUNKF_ERROR)
|
||||||
{
|
{
|
||||||
logf("end file: enc error");
|
logf("end file: enc error");
|
||||||
errors |= PCMREC_E_ENCODER;
|
pcmrec_raise_error_status(PCMREC_E_ENCODER);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -946,7 +971,7 @@ static void pcmrec_flush(unsigned flush_num)
|
||||||
|
|
||||||
/* sync file */
|
/* sync file */
|
||||||
if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
|
if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
|
||||||
errors |= PCMREC_E_IO;
|
pcmrec_raise_error_status(PCMREC_E_IO);
|
||||||
|
|
||||||
cpu_boost(false);
|
cpu_boost(false);
|
||||||
|
|
||||||
|
@ -1001,7 +1026,7 @@ static void pcmrec_new_stream(const char *filename, /* next file name */
|
||||||
|
|
||||||
if (filename)
|
if (filename)
|
||||||
strlcpy(path, filename, MAX_PATH);
|
strlcpy(path, filename, MAX_PATH);
|
||||||
queue_reply(&pcmrec_queue, 0); /* We have all we need */
|
queue_reply(&audio_queue, 0); /* We have all we need */
|
||||||
|
|
||||||
data.pre_chunk = NULL;
|
data.pre_chunk = NULL;
|
||||||
data.chunk = GET_ENC_CHUNK(enc_wr_index);
|
data.chunk = GET_ENC_CHUNK(enc_wr_index);
|
||||||
|
@ -1129,51 +1154,18 @@ static void pcmrec_new_stream(const char *filename, /* next file name */
|
||||||
pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
|
pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
|
||||||
} /* pcmrec_new_stream */
|
} /* pcmrec_new_stream */
|
||||||
|
|
||||||
|
|
||||||
/** event handlers for pcmrec thread */
|
/** event handlers for pcmrec thread */
|
||||||
|
|
||||||
/* PCMREC_INIT */
|
/* PCMREC_INIT */
|
||||||
static void pcmrec_init(void)
|
static void pcmrec_init(void)
|
||||||
{
|
{
|
||||||
is_initialized = true;
|
|
||||||
|
|
||||||
unsigned char *buffer;
|
|
||||||
send_event(RECORDING_EVENT_START, NULL);
|
send_event(RECORDING_EVENT_START, NULL);
|
||||||
|
|
||||||
/* warings and errors */
|
|
||||||
warnings =
|
|
||||||
errors = 0;
|
|
||||||
|
|
||||||
pcmrec_close_file(&rec_fdata.rec_file);
|
pcmrec_close_file(&rec_fdata.rec_file);
|
||||||
rec_fdata.rec_file = -1;
|
|
||||||
|
|
||||||
/* pcm FIFO */
|
pcmrec_init_state();
|
||||||
dma_lock = true;
|
|
||||||
pcm_rd_pos = 0;
|
|
||||||
dma_wr_pos = 0;
|
|
||||||
pcm_enc_pos = 0;
|
|
||||||
|
|
||||||
/* encoder FIFO */
|
unsigned char *buffer = audio_get_buffer(true, &rec_buffer_size);
|
||||||
enc_wr_index = 0;
|
|
||||||
enc_rd_index = 0;
|
|
||||||
|
|
||||||
/* filename queue */
|
|
||||||
fnq_rd_pos = 0;
|
|
||||||
fnq_wr_pos = 0;
|
|
||||||
|
|
||||||
/* stats */
|
|
||||||
num_rec_bytes = 0;
|
|
||||||
num_rec_samples = 0;
|
|
||||||
#if 0
|
|
||||||
accum_rec_bytes = 0;
|
|
||||||
accum_pcm_samples = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pre_record_ticks = 0;
|
|
||||||
|
|
||||||
is_recording = false;
|
|
||||||
is_paused = false;
|
|
||||||
|
|
||||||
buffer = audio_get_recording_buffer(&rec_buffer_size);
|
|
||||||
|
|
||||||
/* Line align pcm_buffer 2^5=32 bytes */
|
/* Line align pcm_buffer 2^5=32 bytes */
|
||||||
pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
|
pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
|
||||||
|
@ -1188,23 +1180,25 @@ static void pcmrec_init(void)
|
||||||
/* PCMREC_CLOSE */
|
/* PCMREC_CLOSE */
|
||||||
static void pcmrec_close(void)
|
static void pcmrec_close(void)
|
||||||
{
|
{
|
||||||
is_initialized = false;
|
|
||||||
dma_lock = true;
|
dma_lock = true;
|
||||||
pre_record_ticks = 0; /* Can't be prerecording any more */
|
pre_record_ticks = 0; /* Can't be prerecording any more */
|
||||||
warnings = 0;
|
warnings = 0;
|
||||||
|
codec_unload();
|
||||||
pcm_close_recording();
|
pcm_close_recording();
|
||||||
reset_hardware();
|
reset_hardware();
|
||||||
audio_remove_encoder();
|
|
||||||
send_event(RECORDING_EVENT_STOP, NULL);
|
send_event(RECORDING_EVENT_STOP, NULL);
|
||||||
} /* pcmrec_close */
|
} /* pcmrec_close */
|
||||||
|
|
||||||
/* PCMREC_OPTIONS */
|
/* PCMREC_OPTIONS */
|
||||||
static void pcmrec_set_recording_options(
|
static void pcmrec_set_recording_options(
|
||||||
|
struct event_queue *q,
|
||||||
struct audio_recording_options *options)
|
struct audio_recording_options *options)
|
||||||
{
|
{
|
||||||
/* stop DMA transfer */
|
/* stop everything */
|
||||||
dma_lock = true;
|
dma_lock = true;
|
||||||
|
codec_unload();
|
||||||
pcm_stop_recording();
|
pcm_stop_recording();
|
||||||
|
pcmrec_init_state();
|
||||||
|
|
||||||
rec_frequency = options->rec_frequency;
|
rec_frequency = options->rec_frequency;
|
||||||
rec_source = options->rec_source;
|
rec_source = options->rec_source;
|
||||||
|
@ -1243,10 +1237,13 @@ static void pcmrec_set_recording_options(
|
||||||
/* apply hardware setting to start monitoring now */
|
/* apply hardware setting to start monitoring now */
|
||||||
pcm_apply_settings();
|
pcm_apply_settings();
|
||||||
|
|
||||||
queue_reply(&pcmrec_queue, 0); /* Release sender */
|
if (codec_load(-1, enc_config.afmt | CODEC_TYPE_ENCODER))
|
||||||
|
|
||||||
if (audio_load_encoder(enc_config.afmt))
|
|
||||||
{
|
{
|
||||||
|
queue_reply(q, true);
|
||||||
|
|
||||||
|
/* run immediately */
|
||||||
|
codec_go();
|
||||||
|
|
||||||
/* start DMA transfer */
|
/* start DMA transfer */
|
||||||
dma_lock = pre_record_ticks == 0;
|
dma_lock = pre_record_ticks == 0;
|
||||||
pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback,
|
pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback,
|
||||||
|
@ -1255,7 +1252,7 @@ static void pcmrec_set_recording_options(
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logf("set rec opt: enc load failed");
|
logf("set rec opt: enc load failed");
|
||||||
errors |= PCMREC_E_LOAD_ENCODER;
|
pcmrec_raise_error_status(PCMREC_E_LOAD_ENCODER);
|
||||||
}
|
}
|
||||||
} /* pcmrec_set_recording_options */
|
} /* pcmrec_set_recording_options */
|
||||||
|
|
||||||
|
@ -1468,97 +1465,65 @@ static void pcmrec_resume(void)
|
||||||
logf("pcmrec_resume done");
|
logf("pcmrec_resume done");
|
||||||
} /* pcmrec_resume */
|
} /* pcmrec_resume */
|
||||||
|
|
||||||
static void pcmrec_thread(void) NORETURN_ATTR;
|
/* Called by audio thread when recording is initialized */
|
||||||
static void pcmrec_thread(void)
|
void audio_recording_handler(struct queue_event *ev)
|
||||||
{
|
{
|
||||||
struct queue_event ev;
|
logf("audio recording start");
|
||||||
|
|
||||||
logf("thread pcmrec start");
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (is_recording)
|
switch (ev->id)
|
||||||
{
|
{
|
||||||
/* Poll periodically to flush data */
|
case Q_AUDIO_INIT_RECORDING:
|
||||||
queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
|
|
||||||
|
|
||||||
if (ev.id == SYS_TIMEOUT)
|
|
||||||
{
|
|
||||||
/* Messages that interrupt this will complete it */
|
|
||||||
pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
|
|
||||||
PCMREC_FLUSH_INTERRUPTABLE);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Not doing anything - sit and wait for commands */
|
|
||||||
queue_wait(&pcmrec_queue, &ev);
|
|
||||||
|
|
||||||
/* Some messages must be handled even if not initialized */
|
|
||||||
switch (ev.id)
|
|
||||||
{
|
|
||||||
case PCMREC_INIT:
|
|
||||||
case SYS_USB_CONNECTED:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!is_initialized)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ev.id)
|
|
||||||
{
|
|
||||||
case PCMREC_INIT:
|
|
||||||
pcmrec_init();
|
pcmrec_init();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCMREC_CLOSE:
|
case SYS_USB_CONNECTED:
|
||||||
|
if (is_recording)
|
||||||
|
break;
|
||||||
|
/* Fall-through */
|
||||||
|
case Q_AUDIO_CLOSE_RECORDING:
|
||||||
pcmrec_close();
|
pcmrec_close();
|
||||||
|
return; /* no more recording */
|
||||||
|
|
||||||
|
case Q_AUDIO_RECORDING_OPTIONS:
|
||||||
|
pcmrec_set_recording_options(&audio_queue,
|
||||||
|
(struct audio_recording_options *)ev->data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCMREC_OPTIONS:
|
case Q_AUDIO_RECORD:
|
||||||
pcmrec_set_recording_options(
|
|
||||||
(struct audio_recording_options *)ev.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PCMREC_RECORD:
|
|
||||||
clear_flush_interrupt();
|
clear_flush_interrupt();
|
||||||
pcmrec_record((const char *)ev.data);
|
pcmrec_record((const char *)ev->data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCMREC_STOP:
|
case Q_AUDIO_STOP:
|
||||||
clear_flush_interrupt();
|
clear_flush_interrupt();
|
||||||
pcmrec_stop();
|
pcmrec_stop();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCMREC_PAUSE:
|
case Q_AUDIO_PAUSE:
|
||||||
clear_flush_interrupt();
|
clear_flush_interrupt();
|
||||||
pcmrec_pause();
|
pcmrec_pause();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCMREC_RESUME:
|
case Q_AUDIO_RESUME:
|
||||||
pcmrec_resume();
|
pcmrec_resume();
|
||||||
break;
|
break;
|
||||||
#if 0
|
|
||||||
case PCMREC_FLUSH_NUM:
|
|
||||||
pcmrec_flush((unsigned)ev.data);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case SYS_USB_CONNECTED:
|
|
||||||
if (is_recording)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (is_initialized)
|
case SYS_TIMEOUT:
|
||||||
pcmrec_close();
|
/* Messages that interrupt this will complete it */
|
||||||
|
pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
|
||||||
|
PCMREC_FLUSH_INTERRUPTABLE);
|
||||||
|
|
||||||
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
if (errors & PCMREC_E_DMA)
|
||||||
usb_wait_for_disconnect(&pcmrec_queue);
|
queue_post(&audio_queue, Q_AUDIO_STOP, 0);
|
||||||
flush_interrupts = 0;
|
|
||||||
break;
|
break;
|
||||||
} /* end switch */
|
} /* end switch */
|
||||||
|
|
||||||
|
queue_wait_w_tmo(&audio_queue, ev,
|
||||||
|
is_recording ? HZ/5 : TIMEOUT_BLOCK);
|
||||||
} /* end while */
|
} /* end while */
|
||||||
} /* pcmrec_thread */
|
} /* audio_recording_handler */
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* */
|
/* */
|
||||||
|
@ -1696,7 +1661,7 @@ struct enc_chunk_hdr * enc_get_chunk(void)
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
|
if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
|
||||||
{
|
{
|
||||||
errors |= PCMREC_E_CHUNK_OVF;
|
pcmrec_raise_error_status(PCMREC_E_CHUNK_OVF);
|
||||||
logf("finish chk ovf: %d", enc_wr_index);
|
logf("finish chk ovf: %d", enc_wr_index);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1718,7 +1683,7 @@ void enc_finish_chunk(void)
|
||||||
if ((long)chunk->flags < 0)
|
if ((long)chunk->flags < 0)
|
||||||
{
|
{
|
||||||
/* encoder set error flag */
|
/* encoder set error flag */
|
||||||
errors |= PCMREC_E_ENCODER;
|
pcmrec_raise_error_status(PCMREC_E_ENCODER);
|
||||||
logf("finish chk enc error");
|
logf("finish chk enc error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1737,7 +1702,7 @@ void enc_finish_chunk(void)
|
||||||
else if (is_recording) /* buffer full */
|
else if (is_recording) /* buffer full */
|
||||||
{
|
{
|
||||||
/* keep current position and put up warning flag */
|
/* keep current position and put up warning flag */
|
||||||
warnings |= PCMREC_W_ENC_BUFFER_OVF;
|
pcmrec_raise_warning_status(PCMREC_W_ENC_BUFFER_OVF);
|
||||||
logf("enc_buffer ovf");
|
logf("enc_buffer ovf");
|
||||||
DEC_ENC_INDEX(enc_wr_index);
|
DEC_ENC_INDEX(enc_wr_index);
|
||||||
if (pcmrec_context)
|
if (pcmrec_context)
|
||||||
|
|
|
@ -42,6 +42,8 @@
|
||||||
/* encoder has written past end of allocated space */
|
/* encoder has written past end of allocated space */
|
||||||
#define PCMREC_E_CHUNK_OVF 0x80010000
|
#define PCMREC_E_CHUNK_OVF 0x80010000
|
||||||
#endif /* DEBUG */
|
#endif /* DEBUG */
|
||||||
|
/* DMA callback has reported an error */
|
||||||
|
#define PCMREC_E_DMA 0x80020000
|
||||||
|
|
||||||
/** General functions for high level codec recording **/
|
/** General functions for high level codec recording **/
|
||||||
/* pcm_rec_error_clear is deprecated for general use. audio_error_clear
|
/* pcm_rec_error_clear is deprecated for general use. audio_error_clear
|
||||||
|
@ -49,7 +51,7 @@
|
||||||
void pcm_rec_error_clear(void);
|
void pcm_rec_error_clear(void);
|
||||||
/* pcm_rec_status is deprecated for general use. audio_status merges the
|
/* pcm_rec_status is deprecated for general use. audio_status merges the
|
||||||
results for consistency with the hardware codec version */
|
results for consistency with the hardware codec version */
|
||||||
unsigned long pcm_rec_status(void);
|
unsigned int pcm_rec_status(void);
|
||||||
unsigned long pcm_rec_get_warnings(void);
|
unsigned long pcm_rec_get_warnings(void);
|
||||||
void pcm_rec_init(void) INIT_ATTR;
|
void pcm_rec_init(void) INIT_ATTR;
|
||||||
int pcm_rec_current_bitrate(void);
|
int pcm_rec_current_bitrate(void);
|
||||||
|
|
|
@ -1074,10 +1074,6 @@ bool recording_screen(bool no_source)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
/* This should be done before touching audio settings */
|
|
||||||
while (!pcm_is_initialized())
|
|
||||||
sleep(0);
|
|
||||||
|
|
||||||
/* recording_menu gets messed up: so prevent manus talking */
|
/* recording_menu gets messed up: so prevent manus talking */
|
||||||
talk_disable(true);
|
talk_disable(true);
|
||||||
/* audio_init_recording stops anything playing when it takes the audio
|
/* audio_init_recording stops anything playing when it takes the audio
|
||||||
|
@ -1209,11 +1205,6 @@ bool recording_screen(bool no_source)
|
||||||
trig_width[i] = vp_top[i].width - pm_x[i];
|
trig_width[i] = vp_top[i].width - pm_x[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
|
||||||
audio_close_recording();
|
|
||||||
audio_init_recording();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
rec_init_recording_options(&rec_options);
|
rec_init_recording_options(&rec_options);
|
||||||
rec_set_recording_options(&rec_options);
|
rec_set_recording_options(&rec_options);
|
||||||
|
|
||||||
|
|
|
@ -64,21 +64,11 @@
|
||||||
#define IO_PRIORITY_BACKGROUND 32
|
#define IO_PRIORITY_BACKGROUND 32
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
|
||||||
#ifdef HAVE_RECORDING
|
|
||||||
#ifdef HAVE_HARDWARE_CLICK
|
|
||||||
#define BASETHREADS 18
|
|
||||||
#else
|
|
||||||
#define BASETHREADS 17
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
# ifdef HAVE_HARDWARE_CLICK
|
# ifdef HAVE_HARDWARE_CLICK
|
||||||
# define BASETHREADS 17
|
# define BASETHREADS 17
|
||||||
# else
|
# else
|
||||||
# define BASETHREADS 16
|
# define BASETHREADS 16
|
||||||
# endif
|
# endif
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
# define BASETHREADS 11
|
# define BASETHREADS 11
|
||||||
#endif /* CONFIG_CODE == * */
|
#endif /* CONFIG_CODE == * */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue