summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2013-05-31 02:41:02 -0400
committerMichael Sevakis <jethead71@rockbox.org>2013-05-31 03:20:35 -0400
commit5857c44017a1641fce7f00da7f16c143daacbaf6 (patch)
treeb8a7ff134977ee8dd6b25b5591f4eb81172f74ab
parentdf6e1bcce5071e02b5cd46736bff87ca0dcceffe (diff)
downloadrockbox-5857c44.tar.gz
rockbox-5857c44.tar.bz2
rockbox-5857c44.zip
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
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/audio_thread.c162
-rw-r--r--apps/audio_thread.h102
-rw-r--r--apps/codec_thread.c1
-rw-r--r--apps/main.c11
-rw-r--r--apps/playback.c342
-rw-r--r--apps/playback.h64
-rw-r--r--apps/recorder/pcm_record.c305
-rw-r--r--apps/recorder/pcm_record.h4
-rw-r--r--apps/recorder/recording.c9
-rw-r--r--firmware/export/thread.h22
11 files changed, 506 insertions, 517 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 4b28f8888b..b22f597b4a 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -163,6 +163,7 @@ radio/radioart.c
#if INPUT_SRC_CAPS != 0
audio_path.c
#endif /* INPUT_SRC_CAPS != 0 */
+audio_thread.c
pcmbuf.c
codec_thread.c
playback.c
diff --git a/apps/audio_thread.c b/apps/audio_thread.c
new file mode 100644
index 0000000000..2f01f7a8c2
--- /dev/null
+++ b/apps/audio_thread.c
@@ -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();
+}
diff --git a/apps/audio_thread.h b/apps/audio_thread.h
new file mode 100644
index 0000000000..7e0ad28849
--- /dev/null
+++ b/apps/audio_thread.h
@@ -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 */
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 308b2ff982..54bc28e19a 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -26,6 +26,7 @@
#include "codecs.h"
#include "codec_thread.h"
#include "pcmbuf.h"
+#include "audio_thread.h"
#include "playback.h"
#include "buffering.h"
#include "dsp_core.h"
diff --git a/apps/main.c b/apps/main.c
index 6b6566c7ca..57257aff66 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -87,6 +87,7 @@
#endif
#if (CONFIG_CODEC == SWCODEC)
+#include "audio_thread.h"
#include "playback.h"
#include "tdspeed.h"
#endif
@@ -386,6 +387,7 @@ static void init(void)
storage_init();
#if CONFIG_CODEC == SWCODEC
+ pcm_init();
dsp_init();
#endif
settings_reset();
@@ -422,10 +424,6 @@ static void init(void)
audio_init();
-#if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING)
- pcm_rec_init();
-#endif
-
settings_apply_skins();
}
@@ -641,6 +639,7 @@ static void init(void)
}
#if CONFIG_CODEC == SWCODEC
+ pcm_init();
dsp_init();
#endif
@@ -727,10 +726,6 @@ static void init(void)
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() */
cpu_boost(false);
diff --git a/apps/playback.c b/apps/playback.c
index 15a3fe9a61..01fa7a9dda 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -27,7 +27,6 @@
#include "core_alloc.h"
#include "sound.h"
#include "ata.h"
-#include "usb.h"
#include "codecs.h"
#include "codec_thread.h"
#include "voice_thread.h"
@@ -38,6 +37,7 @@
#include "playlist.h"
#include "abrepeat.h"
#include "pcmbuf.h"
+#include "audio_thread.h"
#include "playback.h"
#include "misc.h"
#include "settings.h"
@@ -46,10 +46,6 @@
#include "tagcache.h"
#endif
-#ifdef AUDIO_HAVE_RECORDING
-#include "pcm_record.h"
-#endif
-
#ifdef HAVE_LCD_BITMAP
#ifdef HAVE_ALBUMART
#include "albumart.h"
@@ -104,8 +100,10 @@
*/
/** Miscellaneous **/
-bool audio_is_initialized = false; /* (A,O-) */
-extern struct codec_api ci; /* (A,C) */
+extern unsigned int audio_thread_id; /* from audio_thread.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 **/
static enum audio_buffer_state
@@ -190,7 +188,6 @@ static enum filling_state
STATE_FINISHED, /* all remaining tracks are fully buffered */
STATE_ENDING, /* audio playback is ending */
STATE_ENDED, /* audio playback is done */
- STATE_USB, /* USB mode, ignore most messages */
} filling = STATE_IDLE;
/* 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 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 */
enum audio_start_playback_flags
{
@@ -2985,228 +2973,170 @@ static void audio_on_audio_flush(void)
}
}
-#ifdef AUDIO_HAVE_RECORDING
-/* Load the requested encoder type
- (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)
+/* Called by audio thread when playback is started */
+void audio_playback_handler(struct queue_event *ev)
{
- struct queue_event ev;
-
- pcm_postinit();
-
while (1)
{
- switch (filling)
- {
- /* Active states */
- case STATE_FULL:
- case STATE_END_OF_PLAYLIST:
- if (buf_get_watermark() == 0)
- {
- /* End of buffering for now, let's calculate the watermark,
- register for a low buffer event and unboost */
- audio_update_filebuf_watermark(0);
- add_event(BUFFER_EVENT_BUFFER_LOW, true,
- buffer_event_buffer_low_callback);
- }
- /* Fall-through */
- case STATE_FINISHED:
- /* All data was buffered */
- cancel_cpu_boost();
- /* Fall-through */
- case STATE_FILLING:
- case STATE_ENDING:
- if (audio_pcmbuf_track_change_scan())
- {
- /* Transfer notification to audio queue event */
- ev.id = Q_AUDIO_TRACK_CHANGED;
- ev.data = 1;
- }
- else
- {
- /* If doing auto skip, poll pcmbuf track notifications a bit
- faster to promply detect the transition */
- queue_wait_w_tmo(&audio_queue, &ev,
- skip_pending == TRACK_SKIP_NONE ?
- HZ/2 : HZ/10);
- }
- break;
-
- /* Idle states */
- default:
- 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)
+ 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);
+ 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("audio < Q_AUDIO_SEEK_COMPLETE");
+ LOGFQUEUE("playback < 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");
+ LOGFQUEUE("playback < 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);
+ 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("audio < Q_AUDIO_STOP");
+ LOGFQUEUE("playback < Q_AUDIO_STOP");
audio_stop_playback();
- if (ev.data != 0)
+ if (ev->data != 0)
queue_clear(&audio_queue);
- break;
+ return; /* no more playback */
case Q_AUDIO_PAUSE:
- LOGFQUEUE("audio < Q_AUDIO_PAUSE");
- audio_on_pause(ev.data);
+ LOGFQUEUE("playback < Q_AUDIO_PAUSE");
+ audio_on_pause(ev->data);
break;
case Q_AUDIO_SKIP:
- LOGFQUEUE("audio < Q_AUDIO_SKIP");
+ LOGFQUEUE("playback < Q_AUDIO_SKIP");
audio_on_skip();
break;
case Q_AUDIO_DIR_SKIP:
- LOGFQUEUE("audio < Q_AUDIO_DIR_SKIP");
- audio_on_dir_skip(ev.data);
+ LOGFQUEUE("playback < Q_AUDIO_DIR_SKIP");
+ audio_on_dir_skip(ev->data);
break;
case Q_AUDIO_PRE_FF_REWIND:
- LOGFQUEUE("audio < Q_AUDIO_PRE_FF_REWIND");
+ LOGFQUEUE("playback < 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);
+ LOGFQUEUE("playback < Q_AUDIO_FF_REWIND");
+ audio_on_ff_rewind(ev->data);
break;
case Q_AUDIO_FLUSH:
- LOGFQUEUE("audio < Q_AUDIO_FLUSH: %d", (int)ev.data);
+ LOGFQUEUE("playback < 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);
+ 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("audio < Q_AUDIO_FILL_BUFFER");
+ LOGFQUEUE("playback < 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);
+ 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("audio < Q_AUDIO_HANDLE_FINISHED");
- audio_on_handle_finished(ev.data);
+ 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("audio < Q_AUDIO_REMAKE_AUDIO_BUFFER");
+ 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("audio < Q_AUDIO_UPDATE_WATERMARK: %d", (int)ev.data);
- audio_update_filebuf_watermark(ev.data);
+ LOGFQUEUE("playback < 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);
+ case SYS_TIMEOUT:
+ LOGFQUEUE_SYS_TIMEOUT("playback < SYS_TIMEOUT");
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);
+ default:
+ /* LOGFQUEUE("audio < default : %08lX", ev->id); */
break;
+ } /* end switch */
- case SYS_TIMEOUT:
- LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT");
+ switch (filling)
+ {
+ /* Active states */
+ case STATE_FULL:
+ case STATE_END_OF_PLAYLIST:
+ if (buf_get_watermark() == 0)
+ {
+ /* End of buffering for now, let's calculate the watermark,
+ register for a low buffer event and unboost */
+ audio_update_filebuf_watermark(0);
+ add_event(BUFFER_EVENT_BUFFER_LOW, true,
+ buffer_event_buffer_low_callback);
+ }
+ /* Fall-through */
+ case STATE_FINISHED:
+ /* All data was buffered */
+ cancel_cpu_boost();
+ /* Fall-through */
+ case STATE_FILLING:
+ case STATE_ENDING:
+ if (audio_pcmbuf_track_change_scan())
+ {
+ /* Transfer notification to audio queue event */
+ ev->id = Q_AUDIO_TRACK_CHANGED;
+ ev->data = 1;
+ }
+ else
+ {
+ /* If doing auto skip, poll pcmbuf track notifications a bit
+ faster to promply detect the transition */
+ queue_wait_w_tmo(&audio_queue, ev,
+ skip_pending == TRACK_SKIP_NONE ? HZ/2 : HZ/10);
+ }
break;
+ /* Idle states */
default:
- /* LOGFQUEUE("audio < default : %08lX", ev.id); */
- break;
- } /* end switch */
+ queue_wait(&audio_queue, ev);
+ }
} /* end while */
}
@@ -3356,27 +3286,6 @@ bool audio_pcmbuf_may_play(void)
/** -- 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 */
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;
- if (audio_is_initialized)
+ if (audio_is_initialized && thread_self() != audio_thread_id)
{
audio_hard_stop();
}
@@ -3656,15 +3565,6 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
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) */
bool audio_restore_playback(int type)
{
@@ -3755,30 +3655,6 @@ void playback_release_aa_slot(int slot)
}
#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,
indicates the last skip type at the time it was processed and isn't very
meaningful. */
@@ -3866,58 +3742,24 @@ void audio_set_crossfade(int enable)
}
#endif /* HAVE_CROSSFADE */
+unsigned int playback_status(void)
+{
+ return play_status;
+}
/** -- Startup -- **/
-
-/* Initialize the audio system - called from init() in main.c */
-void audio_init(void)
+void playback_init(void)
{
- /* Can never do this twice */
- if (audio_is_initialized)
- {
- logf("audio: already initialized");
- return;
- }
-
- logf("audio: initializing");
-
- /* 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);
-
- 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);
+ logf("playback: initializing");
/* Initialize the track buffering system */
+ mutex_init(&id3_mutex);
track_list_init();
buffering_init();
-
#ifdef HAVE_CROSSFADE
/* Set crossfade setting for next buffer init which should be about... */
pcmbuf_request_crossfade_enable(global_settings.crossfade);
#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
audio_set_buffer_margin(global_settings.buffer_margin);
#endif
diff --git a/apps/playback.h b/apps/playback.h
index 5135c988cb..865e9a313b 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -92,68 +92,6 @@ size_t audio_get_filebuflen(void);
otherwise the result is undefined. */
bool audio_automatic_skip(void);
-/* 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 = 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 */
+unsigned int playback_status(void);
#endif /* _PLAYBACK_H */
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
index fe7a54a565..a45dcc2d11 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -38,11 +38,13 @@
#ifdef HAVE_SPDIF_IN
#include "spdif.h"
#endif
+#include "audio_thread.h"
/***************************************************************************/
+extern struct event_queue audio_queue;
+
/** General recording state **/
-static bool is_initialized = false; /* Subsystem ready? */
static bool is_recording; /* We are recording */
static bool is_paused; /* We have paused */
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
{
PCMREC_NULL = 0,
@@ -248,14 +242,23 @@ enum
PCMREC_STOP, /* stop the current recording */
PCMREC_PAUSE, /* pause 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 */
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
write chunk */
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;
}
@@ -285,7 +288,7 @@ static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status)
if (status == PCM_DMAST_ERR_DMA)
{
/* Flush recorded data to disk and stop recording */
- queue_post(&pcmrec_queue, PCMREC_STOP, 0);
+ errors |= PCMREC_E_DMA;
return status;
}
/* else try again next transmission - frame is invalid */
@@ -315,9 +318,9 @@ void pcm_rec_error_clear(void)
/**
* 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)
ret |= AUDIO_STATUS_RECORD;
@@ -379,20 +382,6 @@ unsigned long pcm_rec_sample_rate(void)
} /* audio_get_sample_rate */
#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 **/
/**
@@ -401,7 +390,7 @@ void pcm_rec_init(void)
void audio_init_recording(void)
{
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");
} /* audio_init_recording */
@@ -411,7 +400,7 @@ void audio_init_recording(void)
void audio_close_recording(void)
{
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");
} /* audio_close_recording */
@@ -421,7 +410,7 @@ void audio_close_recording(void)
void audio_set_recording_options(struct audio_recording_options *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");
} /* audio_set_recording_options */
@@ -432,7 +421,7 @@ void audio_record(const char *filename)
{
logf("audio_record: %s", filename);
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");
} /* audio_record */
@@ -451,7 +440,7 @@ void audio_stop_recording(void)
{
logf("audio_stop_recording");
flush_interrupt();
- queue_post(&pcmrec_queue, PCMREC_STOP, 0);
+ queue_post(&audio_queue, Q_AUDIO_STOP, 0);
logf("audio_stop_recording done");
} /* audio_stop_recording */
@@ -462,7 +451,7 @@ void audio_pause_recording(void)
{
logf("audio_pause_recording");
flush_interrupt();
- queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
+ queue_post(&audio_queue, Q_AUDIO_PAUSE, 0);
logf("audio_pause_recording done");
} /* audio_pause_recording */
@@ -472,7 +461,7 @@ void audio_pause_recording(void)
void audio_resume_recording(void)
{
logf("audio_resume_recording");
- queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
+ queue_post(&audio_queue, Q_AUDIO_RESUME, 0);
logf("audio_resume_recording done");
} /* 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 **/
/* returns true if the queue is empty */
@@ -594,7 +619,7 @@ static void pcmrec_close_file(int *fd_p)
return; /* preserve error */
if (close(*fd_p) != 0)
- errors |= PCMREC_E_IO;
+ pcmrec_raise_error_status(PCMREC_E_IO);
*fd_p = -1;
} /* pcmrec_close_file */
@@ -646,7 +671,7 @@ static void pcmrec_start_file(void)
{
logf("start file: fnq empty");
*filename = '\0';
- errors |= PCMREC_E_FNQ_DESYNC;
+ pcmrec_raise_error_status(PCMREC_E_FNQ_DESYNC);
}
else if (errors != 0)
{
@@ -656,7 +681,7 @@ static void pcmrec_start_file(void)
{
/* Any previous file should have been closed */
logf("start file: file already open");
- errors |= PCMREC_E_FNQ_DESYNC;
+ pcmrec_raise_error_status(PCMREC_E_FNQ_DESYNC);
}
if (errors != 0)
@@ -671,7 +696,7 @@ static void pcmrec_start_file(void)
if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
{
logf("start file: enc error");
- errors |= PCMREC_E_ENCODER;
+ pcmrec_raise_error_status(PCMREC_E_ENCODER);
}
if (errors != 0)
@@ -706,7 +731,7 @@ static inline void pcmrec_write_chunk(void)
{
logf("wr chk enc error %lu %lu",
rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
- errors |= PCMREC_E_ENCODER;
+ pcmrec_raise_error_status(PCMREC_E_ENCODER);
}
} /* pcmrec_write_chunk */
@@ -725,7 +750,7 @@ static void pcmrec_end_file(void)
if (rec_fdata.chunk->flags & CHUNKF_ERROR)
{
logf("end file: enc error");
- errors |= PCMREC_E_ENCODER;
+ pcmrec_raise_error_status(PCMREC_E_ENCODER);
}
else
{
@@ -946,7 +971,7 @@ static void pcmrec_flush(unsigned flush_num)
/* sync file */
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);
@@ -1001,7 +1026,7 @@ static void pcmrec_new_stream(const char *filename, /* next file name */
if (filename)
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.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_new_stream */
+
/** event handlers for pcmrec thread */
/* PCMREC_INIT */
static void pcmrec_init(void)
{
- is_initialized = true;
-
- unsigned char *buffer;
send_event(RECORDING_EVENT_START, NULL);
-
- /* warings and errors */
- warnings =
- errors = 0;
-
pcmrec_close_file(&rec_fdata.rec_file);
- rec_fdata.rec_file = -1;
- /* pcm FIFO */
- dma_lock = true;
- pcm_rd_pos = 0;
- dma_wr_pos = 0;
- pcm_enc_pos = 0;
+ pcmrec_init_state();
- /* 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;
-
- buffer = audio_get_recording_buffer(&rec_buffer_size);
+ unsigned char *buffer = audio_get_buffer(true, &rec_buffer_size);
/* Line align pcm_buffer 2^5=32 bytes */
pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
@@ -1188,23 +1180,25 @@ static void pcmrec_init(void)
/* PCMREC_CLOSE */
static void pcmrec_close(void)
{
- is_initialized = false;
dma_lock = true;
pre_record_ticks = 0; /* Can't be prerecording any more */
warnings = 0;
+ codec_unload();
pcm_close_recording();
reset_hardware();
- audio_remove_encoder();
send_event(RECORDING_EVENT_STOP, NULL);
} /* pcmrec_close */
/* PCMREC_OPTIONS */
static void pcmrec_set_recording_options(
+ struct event_queue *q,
struct audio_recording_options *options)
{
- /* stop DMA transfer */
+ /* stop everything */
dma_lock = true;
+ codec_unload();
pcm_stop_recording();
+ pcmrec_init_state();
rec_frequency = options->rec_frequency;
rec_source = options->rec_source;
@@ -1243,10 +1237,13 @@ static void pcmrec_set_recording_options(
/* apply hardware setting to start monitoring now */
pcm_apply_settings();
- queue_reply(&pcmrec_queue, 0); /* Release sender */
-
- if (audio_load_encoder(enc_config.afmt))
+ if (codec_load(-1, enc_config.afmt | CODEC_TYPE_ENCODER))
{
+ queue_reply(q, true);
+
+ /* run immediately */
+ codec_go();
+
/* start DMA transfer */
dma_lock = pre_record_ticks == 0;
pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback,
@@ -1255,7 +1252,7 @@ static void pcmrec_set_recording_options(
else
{
logf("set rec opt: enc load failed");
- errors |= PCMREC_E_LOAD_ENCODER;
+ pcmrec_raise_error_status(PCMREC_E_LOAD_ENCODER);
}
} /* pcmrec_set_recording_options */
@@ -1468,97 +1465,65 @@ static void pcmrec_resume(void)
logf("pcmrec_resume done");
} /* pcmrec_resume */
-static void pcmrec_thread(void) NORETURN_ATTR;
-static void pcmrec_thread(void)
+/* Called by audio thread when recording is initialized */
+void audio_recording_handler(struct queue_event *ev)
{
- struct queue_event ev;
-
- logf("thread pcmrec start");
+ logf("audio recording start");
- while(1)
+ while (1)
{
- if (is_recording)
+ switch (ev->id)
{
- /* Poll periodically to flush data */
- 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();
- break;
-
- case PCMREC_CLOSE:
- pcmrec_close();
- break;
+ case Q_AUDIO_INIT_RECORDING:
+ pcmrec_init();
+ break;
- case PCMREC_OPTIONS:
- pcmrec_set_recording_options(
- (struct audio_recording_options *)ev.data);
+ case SYS_USB_CONNECTED:
+ if (is_recording)
break;
+ /* Fall-through */
+ case Q_AUDIO_CLOSE_RECORDING:
+ pcmrec_close();
+ return; /* no more recording */
+
+ case Q_AUDIO_RECORDING_OPTIONS:
+ pcmrec_set_recording_options(&audio_queue,
+ (struct audio_recording_options *)ev->data);
+ break;
- case PCMREC_RECORD:
- clear_flush_interrupt();
- pcmrec_record((const char *)ev.data);
- break;
+ case Q_AUDIO_RECORD:
+ clear_flush_interrupt();
+ pcmrec_record((const char *)ev->data);
+ break;
- case PCMREC_STOP:
- clear_flush_interrupt();
- pcmrec_stop();
- break;
+ case Q_AUDIO_STOP:
+ clear_flush_interrupt();
+ pcmrec_stop();
+ break;
- case PCMREC_PAUSE:
- clear_flush_interrupt();
- pcmrec_pause();
- break;
+ case Q_AUDIO_PAUSE:
+ clear_flush_interrupt();
+ pcmrec_pause();
+ break;
- case PCMREC_RESUME:
- pcmrec_resume();
- break;
-#if 0
- case PCMREC_FLUSH_NUM:
- pcmrec_flush((unsigned)ev.data);
- break;
-#endif
- case SYS_USB_CONNECTED:
- if (is_recording)
- break;
+ case Q_AUDIO_RESUME:
+ pcmrec_resume();
+ break;
- if (is_initialized)
- pcmrec_close();
+ case SYS_TIMEOUT:
+ /* Messages that interrupt this will complete it */
+ pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
+ PCMREC_FLUSH_INTERRUPTABLE);
- usb_acknowledge(SYS_USB_CONNECTED_ACK);
- usb_wait_for_disconnect(&pcmrec_queue);
- flush_interrupts = 0;
- break;
+ if (errors & PCMREC_E_DMA)
+ queue_post(&audio_queue, Q_AUDIO_STOP, 0);
+ break;
} /* end switch */
+
+ queue_wait_w_tmo(&audio_queue, ev,
+ is_recording ? HZ/5 : TIMEOUT_BLOCK);
} /* end while */
-} /* pcmrec_thread */
+} /* audio_recording_handler */
/****************************************************************************/
/* */
@@ -1696,7 +1661,7 @@ struct enc_chunk_hdr * enc_get_chunk(void)
#ifdef DEBUG
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);
}
#endif
@@ -1718,7 +1683,7 @@ void enc_finish_chunk(void)
if ((long)chunk->flags < 0)
{
/* encoder set error flag */
- errors |= PCMREC_E_ENCODER;
+ pcmrec_raise_error_status(PCMREC_E_ENCODER);
logf("finish chk enc error");
}
@@ -1737,7 +1702,7 @@ void enc_finish_chunk(void)
else if (is_recording) /* buffer full */
{
/* 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");
DEC_ENC_INDEX(enc_wr_index);
if (pcmrec_context)
diff --git a/apps/recorder/pcm_record.h b/apps/recorder/pcm_record.h
index 1ac6faefb2..bff7881605 100644
--- a/apps/recorder/pcm_record.h
+++ b/apps/recorder/pcm_record.h
@@ -42,6 +42,8 @@
/* encoder has written past end of allocated space */
#define PCMREC_E_CHUNK_OVF 0x80010000
#endif /* DEBUG */
+/* DMA callback has reported an error */
+#define PCMREC_E_DMA 0x80020000
/** General functions for high level codec recording **/
/* pcm_rec_error_clear is deprecated for general use. audio_error_clear
@@ -49,7 +51,7 @@
void pcm_rec_error_clear(void);
/* pcm_rec_status is deprecated for general use. audio_status merges the
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);
void pcm_rec_init(void) INIT_ATTR;
int pcm_rec_current_bitrate(void);
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 203c7101b7..53dc5cbf95 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -1074,10 +1074,6 @@ bool recording_screen(bool no_source)
#endif
#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 */
talk_disable(true);
/* 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];
}
-#if CONFIG_CODEC == SWCODEC
- audio_close_recording();
- audio_init_recording();
-#endif
-
rec_init_recording_options(&rec_options);
rec_set_recording_options(&rec_options);
diff --git a/firmware/export/thread.h b/firmware/export/thread.h
index 577cdc1e61..da395b8ffa 100644
--- a/firmware/export/thread.h
+++ b/firmware/export/thread.h
@@ -64,23 +64,13 @@
#define IO_PRIORITY_BACKGROUND 32
#if CONFIG_CODEC == SWCODEC
-
-#ifdef HAVE_RECORDING
-#ifdef HAVE_HARDWARE_CLICK
-#define BASETHREADS 18
-#else
-#define BASETHREADS 17
-#endif
-#else
-#ifdef HAVE_HARDWARE_CLICK
-#define BASETHREADS 17
-#else
-#define BASETHREADS 16
-#endif
-#endif
-
+# ifdef HAVE_HARDWARE_CLICK
+# define BASETHREADS 17
+# else
+# define BASETHREADS 16
+# endif
#else
-#define BASETHREADS 11
+# define BASETHREADS 11
#endif /* CONFIG_CODE == * */
#ifndef TARGET_EXTRA_THREADS