summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2008-10-14 11:12:20 +0000
committerBjörn Stenberg <bjorn@haxx.se>2008-10-14 11:12:20 +0000
commit9558c4956d3d603c4d132af88633767810f3ba62 (patch)
treeb0d739f6246a7175de106562a2c755724f977cbe /firmware
parent91b9c6139b3447d7c0f4f2924ef73a9dc323703b (diff)
downloadrockbox-9558c4956d3d603c4d132af88633767810f3ba62.tar.gz
rockbox-9558c4956d3d603c4d132af88633767810f3ba62.zip
Moved pcm_record from firmware to apps. Cleaned up some. Now all code using struct mp3entry is in apps.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18807 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/drivers/mas.c17
-rw-r--r--firmware/drivers/tuner/s1a0903x01.c3
-rw-r--r--firmware/export/audio.h2
-rw-r--r--firmware/export/id3.h246
-rw-r--r--firmware/export/mas.h6
-rw-r--r--firmware/export/mpeg.h66
-rw-r--r--firmware/export/pcm_record.h66
-rw-r--r--firmware/export/replaygain.h33
-rw-r--r--firmware/mp3_playback.c5
-rw-r--r--firmware/pcm_record.c1787
-rw-r--r--firmware/powermgmt.c3
12 files changed, 28 insertions, 2209 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 9f7007245d..2cd1ba131c 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -191,9 +191,6 @@ pcm_sampr.c
191pcm.c 191pcm.c
192#ifdef HAVE_RECORDING 192#ifdef HAVE_RECORDING
193enc_base.c 193enc_base.c
194#ifndef SIMULATOR
195pcm_record.c
196#endif /* SIMULATOR */
197#endif /* HAVE_RECORDING */ 194#endif /* HAVE_RECORDING */
198#endif /* BOOTLOADER */ 195#endif /* BOOTLOADER */
199 196
diff --git a/firmware/drivers/mas.c b/firmware/drivers/mas.c
index 9d1761b419..4f384d3b98 100644
--- a/firmware/drivers/mas.c
+++ b/firmware/drivers/mas.c
@@ -466,3 +466,20 @@ unsigned long mas_readver(void)
466} 466}
467 467
468#endif 468#endif
469
470#if CONFIG_TUNER & S1A0903X01
471static int pllfreq;
472
473void mas_store_pllfreq(int freq)
474{
475 pllfreq = freq;
476}
477
478int mas_get_pllfreq(void)
479{
480 return pllfreq;
481}
482#endif
483
484
485
diff --git a/firmware/drivers/tuner/s1a0903x01.c b/firmware/drivers/tuner/s1a0903x01.c
index 517d41984d..f6442b269f 100644
--- a/firmware/drivers/tuner/s1a0903x01.c
+++ b/firmware/drivers/tuner/s1a0903x01.c
@@ -26,7 +26,6 @@
26#include "kernel.h" 26#include "kernel.h"
27#include "tuner.h" /* tuner abstraction interface */ 27#include "tuner.h" /* tuner abstraction interface */
28#include "fmradio.h" /* physical interface driver */ 28#include "fmradio.h" /* physical interface driver */
29#include "mpeg.h"
30#include "sound.h" 29#include "sound.h"
31 30
32#define DEFAULT_IN1 0x100003 /* Mute */ 31#define DEFAULT_IN1 0x100003 /* Mute */
@@ -64,7 +63,7 @@ int s1a0903x01_set(int setting, int value)
64 int pitch = 1000; 63 int pitch = 1000;
65 64
66 /* 4th harmonic falls in the FM frequency range */ 65 /* 4th harmonic falls in the FM frequency range */
67 int if_freq = 4 * mpeg_get_mas_pllfreq(); 66 int if_freq = 4 * mas_get_pllfreq();
68 67
69 /* shift the mas harmonic >= 300 kHz away using the direction 68 /* shift the mas harmonic >= 300 kHz away using the direction
70 * which needs less shifting. */ 69 * which needs less shifting. */
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 9530082050..661247df2f 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -30,8 +30,6 @@
30#include "pcm_sampr.h" 30#include "pcm_sampr.h"
31#include "pcm.h" 31#include "pcm.h"
32#ifdef HAVE_RECORDING 32#ifdef HAVE_RECORDING
33#include "pcm_record.h"
34#include "id3.h"
35#include "enc_base.h" 33#include "enc_base.h"
36#endif /* HAVE_RECORDING */ 34#endif /* HAVE_RECORDING */
37#endif /* CONFIG_CODEC == SWCODEC */ 35#endif /* CONFIG_CODEC == SWCODEC */
diff --git a/firmware/export/id3.h b/firmware/export/id3.h
deleted file mode 100644
index da2faf1b12..0000000000
--- a/firmware/export/id3.h
+++ /dev/null
@@ -1,246 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Daniel Stenberg
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef ID3_H
22#define ID3_H
23
24#include <stdbool.h>
25#include "config.h"
26#include "file.h"
27
28#define ID3V2_BUF_SIZE 300
29
30/* Audio file types. */
31/* NOTE: The values of the AFMT_* items are used for the %fc tag in the WPS
32 - so new entries MUST be added to the end to maintain compatibility.
33 */
34enum
35{
36 AFMT_UNKNOWN = 0, /* Unknown file format */
37
38 /* start formats */
39
40 AFMT_MPA_L1, /* MPEG Audio layer 1 */
41 AFMT_MPA_L2, /* MPEG Audio layer 2 */
42 AFMT_MPA_L3, /* MPEG Audio layer 3 */
43
44#if CONFIG_CODEC == SWCODEC
45 AFMT_AIFF, /* Audio Interchange File Format */
46 AFMT_PCM_WAV, /* Uncompressed PCM in a WAV file */
47 AFMT_OGG_VORBIS, /* Ogg Vorbis */
48 AFMT_FLAC, /* FLAC */
49 AFMT_MPC, /* Musepack */
50 AFMT_A52, /* A/52 (aka AC3) audio */
51 AFMT_WAVPACK, /* WavPack */
52 AFMT_ALAC, /* Apple Lossless Audio Codec */
53 AFMT_AAC, /* Advanced Audio Coding (AAC) in M4A container */
54 AFMT_SHN, /* Shorten */
55 AFMT_SID, /* SID File Format */
56 AFMT_ADX, /* ADX File Format */
57 AFMT_NSF, /* NESM (NES Sound Format) */
58 AFMT_SPEEX, /* Ogg Speex speech */
59 AFMT_SPC, /* SPC700 save state */
60 AFMT_APE, /* Monkey's Audio (APE) */
61 AFMT_WMA, /* WMAV1/V2 in ASF */
62 AFMT_MOD, /* Amiga MOD File Format */
63 AFMT_SAP, /* Amiga 8Bit SAP Format */
64#endif
65
66 /* add new formats at any index above this line to have a sensible order -
67 specified array index inits are used */
68 /* format arrays defined in id3.c */
69
70 AFMT_NUM_CODECS,
71
72#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING)
73 /* masks to decompose parts */
74 CODEC_AFMT_MASK = 0x0fff,
75 CODEC_TYPE_MASK = 0x7000,
76
77 /* switch for specifying codec type when requesting a filename */
78 CODEC_TYPE_DECODER = (0 << 12), /* default */
79 CODEC_TYPE_ENCODER = (1 << 12),
80#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */
81};
82
83#if CONFIG_CODEC == SWCODEC
84#define CODEC_EXTENSION "codec"
85
86#ifdef HAVE_RECORDING
87#define ENCODER_SUFFIX "_enc"
88enum rec_format_indexes
89{
90 __REC_FORMAT_START_INDEX = -1,
91
92 /* start formats */
93
94 REC_FORMAT_PCM_WAV,
95 REC_FORMAT_AIFF,
96 REC_FORMAT_WAVPACK,
97 REC_FORMAT_MPA_L3,
98
99 /* add new formats at any index above this line to have a sensible order -
100 specified array index inits are used
101 REC_FORMAT_CFG_NUM_BITS should allocate enough bits to hold the range
102 REC_FORMAT_CFG_VALUE_LIST should be in same order as indexes
103 */
104
105 REC_NUM_FORMATS,
106
107 REC_FORMAT_DEFAULT = REC_FORMAT_PCM_WAV,
108 REC_FORMAT_CFG_NUM_BITS = 2
109};
110
111#define REC_FORMAT_CFG_VAL_LIST "wave,aiff,wvpk,mpa3"
112
113/* get REC_FORMAT_* corresponding AFMT_* */
114extern const int rec_format_afmt[REC_NUM_FORMATS];
115/* get AFMT_* corresponding REC_FORMAT_* */
116extern const int afmt_rec_format[AFMT_NUM_CODECS];
117
118#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
119 { label, root_fname, enc_root_fname, ext_list }
120#else /* !HAVE_RECORDING */
121#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
122 { label, root_fname, ext_list }
123#endif /* HAVE_RECORDING */
124#else /* !SWCODEC */
125
126#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \
127 { label, ext_list }
128#endif /* CONFIG_CODEC == SWCODEC */
129
130/* record describing the audio format */
131struct afmt_entry
132{
133 char label[8]; /* format label */
134#if CONFIG_CODEC == SWCODEC
135 char *codec_root_fn; /* root codec filename (sans _enc and .codec) */
136#ifdef HAVE_RECORDING
137 char *codec_enc_root_fn; /* filename of encoder codec */
138#endif
139#endif
140 char *ext_list; /* double NULL terminated extension
141 list for type with the first as
142 the default for recording */
143};
144
145/* database of labels and codecs. add formats per above enum */
146extern const struct afmt_entry audio_formats[AFMT_NUM_CODECS];
147
148struct mp3entry {
149 char path[MAX_PATH];
150 char* title;
151 char* artist;
152 char* album;
153 char* genre_string;
154 char* disc_string;
155 char* track_string;
156 char* year_string;
157 char* composer;
158 char* comment;
159 char* albumartist;
160 char* grouping;
161 int discnum;
162 int tracknum;
163 int version;
164 int layer;
165 int year;
166 unsigned char id3version;
167 unsigned int codectype;
168 unsigned int bitrate;
169 unsigned long frequency;
170 unsigned long id3v2len;
171 unsigned long id3v1len;
172 unsigned long first_frame_offset; /* Byte offset to first real MP3 frame.
173 Used for skipping leading garbage to
174 avoid gaps between tracks. */
175 unsigned long vbr_header_pos;
176 unsigned long filesize; /* without headers; in bytes */
177 unsigned long length; /* song length in ms */
178 unsigned long elapsed; /* ms played */
179
180 int lead_trim; /* Number of samples to skip at the beginning */
181 int tail_trim; /* Number of samples to remove from the end */
182
183 /* Added for Vorbis */
184 unsigned long samples; /* number of samples in track */
185
186 /* MP3 stream specific info */
187 unsigned long frame_count; /* number of frames in the file (if VBR) */
188
189 /* Used for A52/AC3 */
190 unsigned long bytesperframe; /* number of bytes per frame (if CBR) */
191
192 /* Xing VBR fields */
193 bool vbr;
194 bool has_toc; /* True if there is a VBR header in the file */
195 unsigned char toc[100]; /* table of contents */
196
197 /* these following two fields are used for local buffering */
198 char id3v2buf[ID3V2_BUF_SIZE];
199 char id3v1buf[4][92];
200
201 /* resume related */
202 unsigned long offset; /* bytes played */
203 int index; /* playlist index */
204
205 /* runtime database fields */
206 long tagcache_idx; /* 0=invalid, otherwise idx+1 */
207 int rating;
208 int score;
209 long playcount;
210 long lastplayed;
211 long playtime;
212
213 /* replaygain support */
214
215#if CONFIG_CODEC == SWCODEC
216 char* track_gain_string;
217 char* album_gain_string;
218 long track_gain; /* 7.24 signed fixed point. 0 for no gain. */
219 long album_gain;
220 long track_peak; /* 7.24 signed fixed point. 0 for no peak. */
221 long album_peak;
222#endif
223
224 /* Cuesheet support */
225 int cuesheet_type; /* 0: none, 1: external, 2: embedded */
226
227 /* Musicbrainz Track ID */
228 char* mb_track_id;
229};
230
231enum {
232 ID3_VER_1_0 = 1,
233 ID3_VER_1_1,
234 ID3_VER_2_2,
235 ID3_VER_2_3,
236 ID3_VER_2_4
237};
238
239bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename);
240bool mp3info(struct mp3entry *entry, const char *filename);
241char* id3_get_num_genre(unsigned int genre_num);
242int getid3v2len(int fd);
243void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig);
244void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig);
245
246#endif
diff --git a/firmware/export/mas.h b/firmware/export/mas.h
index ab6135cbfa..493ed6a63c 100644
--- a/firmware/export/mas.h
+++ b/firmware/export/mas.h
@@ -163,3 +163,9 @@ int mas_codec_readreg(int reg);
163unsigned long mas_readver(void); 163unsigned long mas_readver(void);
164 164
165#endif 165#endif
166
167#if CONFIG_TUNER & S1A0903X01
168void mas_store_pllfreq(int freq);
169int mas_get_pllfreq(void);
170#endif
171
diff --git a/firmware/export/mpeg.h b/firmware/export/mpeg.h
deleted file mode 100644
index ce2cff0069..0000000000
--- a/firmware/export/mpeg.h
+++ /dev/null
@@ -1,66 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef _MPEG_H_
22#define _MPEG_H_
23
24#include <stdbool.h>
25#include "id3.h"
26
27#define MPEG_SWAP_CHUNKSIZE 0x2000
28#define MPEG_HIGH_WATER 2 /* We leave 2 bytes empty because otherwise we
29 wouldn't be able to see the difference between
30 an empty buffer and a full one. */
31#define MPEG_LOW_WATER 0x60000
32#define MPEG_RECORDING_LOW_WATER 0x80000
33#define MPEG_LOW_WATER_CHUNKSIZE 0x40000
34#define MPEG_LOW_WATER_SWAP_CHUNKSIZE 0x10000
35#ifdef HAVE_MMC
36#define MPEG_PLAY_PENDING_THRESHOLD 0x20000
37#define MPEG_PLAY_PENDING_SWAPSIZE 0x20000
38#else
39#define MPEG_PLAY_PENDING_THRESHOLD 0x10000
40#define MPEG_PLAY_PENDING_SWAPSIZE 0x10000
41#endif
42
43#define MPEG_MAX_PRERECORD_SECONDS 30
44
45/* For ID3 info and VBR header */
46#define MPEG_RESERVED_HEADER_SPACE (4096 + 576)
47
48#if (CONFIG_CODEC == MAS3587F) || defined(SIMULATOR)
49
50#if CONFIG_TUNER & S1A0903X01
51int mpeg_get_mas_pllfreq(void);
52#endif
53
54#endif
55unsigned long mpeg_get_last_header(void);
56
57/* in order to keep the recording here, I have to expose this */
58void rec_tick(void);
59void playback_tick(void); /* FixMe: get rid of this, use mp3_get_playtime() */
60
61void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3));
62void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3));
63void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3));
64void audio_set_cuesheet_callback(bool (*handler)(const char *filename));
65
66#endif
diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h
deleted file mode 100644
index f805313fe5..0000000000
--- a/firmware/export/pcm_record.h
+++ /dev/null
@@ -1,66 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef PCM_RECORD_H
23#define PCM_RECORD_H
24
25#define DMA_REC_ERROR_DMA (-1)
26#ifdef HAVE_SPDIF_REC
27#define DMA_REC_ERROR_SPDIF (-2)
28#endif
29
30/** Warnings **/
31/* pcm (dma) buffer has overflowed */
32#define PCMREC_W_PCM_BUFFER_OVF 0x00000001
33/* encoder output buffer has overflowed */
34#define PCMREC_W_ENC_BUFFER_OVF 0x00000002
35/** Errors **/
36/* failed to load encoder */
37#define PCMREC_E_LOAD_ENCODER 0x80001000
38/* error originating in encoder */
39#define PCMREC_E_ENCODER 0x80002000
40/* filename queue has desynced from stream markers */
41#define PCMREC_E_FNQ_DESYNC 0x80004000
42/* I/O error has occurred */
43#define PCMREC_E_IO 0x80008000
44#ifdef DEBUG
45/* encoder has written past end of allotted space */
46#define PCMREC_E_CHUNK_OVF 0x80010000
47#endif /* DEBUG */
48
49/** General functions for high level codec recording **/
50/* pcm_rec_error_clear is deprecated for general use. audio_error_clear
51 should be used */
52void pcm_rec_error_clear(void);
53/* pcm_rec_status is deprecated for general use. audio_status merges the
54 results for consistency with the hardware codec version */
55unsigned long pcm_rec_status(void);
56unsigned long pcm_rec_get_warnings(void);
57void pcm_rec_init(void);
58int pcm_rec_current_bitrate(void);
59int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */
60int pcm_rec_rec_format(void); /* Format index or -1 otherwise */
61unsigned long pcm_rec_sample_rate(void);
62int pcm_get_num_unprocessed(void);
63
64/* audio.h contains audio_* recording functions */
65
66#endif /* PCM_RECORD_H */
diff --git a/firmware/export/replaygain.h b/firmware/export/replaygain.h
deleted file mode 100644
index dbc079b1d3..0000000000
--- a/firmware/export/replaygain.h
+++ /dev/null
@@ -1,33 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Magnus Holmgren
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _REPLAYGAIN_H
23#define _REPLAYGAIN_H
24
25#include "id3.h"
26
27long get_replaygain_int(long int_gain);
28long parse_replaygain(const char* key, const char* value,
29 struct mp3entry* entry, char* buffer, int length);
30long parse_replaygain_int(bool album, long gain, long peak,
31 struct mp3entry* entry, char* buffer, int length);
32
33#endif
diff --git a/firmware/mp3_playback.c b/firmware/mp3_playback.c
index 66ea3159e3..2bbd08d789 100644
--- a/firmware/mp3_playback.c
+++ b/firmware/mp3_playback.c
@@ -26,7 +26,6 @@
26#include "debug.h" 26#include "debug.h"
27#include "panic.h" 27#include "panic.h"
28#include <kernel.h> 28#include <kernel.h>
29#include "mpeg.h" /* ToDo: remove crosslinks */
30#include "mp3_playback.h" 29#include "mp3_playback.h"
31#include "sound.h" 30#include "sound.h"
32#ifndef SIMULATOR 31#ifndef SIMULATOR
@@ -76,6 +75,10 @@ bool audio_is_initialized = false;
76 75
77/* FIX: this code pretty much assumes a MAS */ 76/* FIX: this code pretty much assumes a MAS */
78 77
78/* dirty calls to mpeg.c */
79extern void playback_tick(void);
80extern void rec_tick(void);
81
79#ifndef SIMULATOR 82#ifndef SIMULATOR
80 83
81unsigned long mas_version_code; 84unsigned long mas_version_code;
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c
deleted file mode 100644
index 0e0102af97..0000000000
--- a/firmware/pcm_record.c
+++ /dev/null
@@ -1,1787 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "system.h"
22#include "kernel.h"
23#include "logf.h"
24#include "thread.h"
25#include <string.h>
26#include "ata.h"
27#include "usb.h"
28#include "buffer.h"
29#include "general.h"
30#include "audio.h"
31#include "sound.h"
32#include "id3.h"
33#ifdef HAVE_SPDIF_IN
34#include "spdif.h"
35#endif
36
37/***************************************************************************/
38
39extern struct thread_entry *codec_thread_p;
40
41/** General recording state **/
42static bool is_recording; /* We are recording */
43static bool is_paused; /* We have paused */
44static unsigned long errors; /* An error has occured */
45static unsigned long warnings; /* Warning */
46static int flush_interrupts = 0; /* Number of messages queued that
47 should interrupt a flush in
48 progress -
49 for a safety net and a prompt
50 response to stop, split and pause
51 requests -
52 only interrupts a flush initiated
53 by pcmrec_flush(0) */
54
55/* Utility functions for setting/clearing flushing interrupt flag */
56static inline void flush_interrupt(void)
57{
58 flush_interrupts++;
59 logf("flush int: %d", flush_interrupts);
60}
61
62static inline void clear_flush_interrupt(void)
63{
64 if (--flush_interrupts < 0)
65 flush_interrupts = 0;
66}
67
68/** Stats on encoded data for current file **/
69static size_t num_rec_bytes; /* Num bytes recorded */
70static unsigned long num_rec_samples; /* Number of PCM samples recorded */
71
72/** Stats on encoded data for all files from start to stop **/
73#if 0
74static unsigned long long accum_rec_bytes; /* total size written to chunks */
75static unsigned long long accum_pcm_samples; /* total pcm count processed */
76#endif
77
78/* Keeps data about current file and is sent as event data for codec */
79static struct enc_file_event_data rec_fdata IDATA_ATTR =
80{
81 .chunk = NULL,
82 .new_enc_size = 0,
83 .new_num_pcm = 0,
84 .rec_file = -1,
85 .num_pcm_samples = 0
86};
87
88/** These apply to current settings **/
89static int rec_source; /* current rec_source setting */
90static int rec_frequency; /* current frequency setting */
91static unsigned long sample_rate; /* Sample rate in HZ */
92static int num_channels; /* Current number of channels */
93static int rec_mono_mode; /* how mono is created */
94static struct encoder_config enc_config; /* Current encoder configuration */
95static unsigned long pre_record_ticks; /* pre-record time in ticks */
96
97/****************************************************************************
98 use 2 circular buffers:
99 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
100 enc_buffer=encoded audio buffer: storage for encoder output data
101
102 Flow:
103 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
104 2. if enough pcm data are available the encoder codec does encoding of pcm
105 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
106 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
107
108 Functions calls (basic encoder steps):
109 1.main: audio_load_encoder(); start the encoder
110 2.encoder: enc_get_inputs(); get encoder recording settings
111 3.encoder: enc_set_parameters(); set the encoder parameters
112 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
113 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
114 6.encoder: enc_get_chunk(); get a ptr to next enc chunk
115 7.encoder: <process enc chunk> compress and store data to enc chunk
116 8.encoder: enc_finish_chunk(); inform main about chunk processed and
117 is available to be written to a file.
118 Encoder can place any number of chunks
119 of PCM data in a single output chunk
120 but must stay within its output chunk
121 size
122 9.encoder: repeat 4. to 8.
123 A.pcmrec: enc_events_callback(); called for certain events
124
125 (*) Optional step
126****************************************************************************/
127
128/** buffer parameters where incoming PCM data is placed **/
129#define PCM_NUM_CHUNKS 256 /* Power of 2 */
130#define PCM_CHUNK_SIZE 8192 /* Power of 2 */
131#define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
132
133#define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
134#define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
135#define INC_ENC_INDEX(index) \
136 { if (++index >= enc_num_chunks) index = 0; }
137#define DEC_ENC_INDEX(index) \
138 { if (--index < 0) index = enc_num_chunks - 1; }
139
140static size_t rec_buffer_size; /* size of available buffer */
141static unsigned char *pcm_buffer; /* circular recording buffer */
142static unsigned char *enc_buffer; /* circular encoding buffer */
143#ifdef DEBUG
144static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
145 aid to check if the encoder data
146 spilled out of its chunk */
147#endif /* DEBUG */
148static volatile int dma_wr_pos; /* current DMA write pos */
149static int pcm_rd_pos; /* current PCM read pos */
150static int pcm_enc_pos; /* position encoder is processing */
151static volatile bool dma_lock; /* lock DMA write position */
152static int enc_wr_index; /* encoder chunk write index */
153static int enc_rd_index; /* encoder chunk read index */
154static int enc_num_chunks; /* number of chunks in ringbuffer */
155static size_t enc_chunk_size; /* maximum encoder chunk size */
156static unsigned long enc_sample_rate; /* sample rate used by encoder */
157static bool pcmrec_context = false; /* called by pcmrec thread? */
158static bool pcm_buffer_empty; /* all pcm chunks processed? */
159
160/** file flushing **/
161static int low_watermark; /* Low watermark to stop flush */
162static int high_watermark; /* max chunk limit for data flush */
163static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
164static int last_ata_spinup_time = -1;/* previous spin time used */
165#ifdef HAVE_PRIORITY_SCHEDULING
166static int flood_watermark; /* boost thread priority when here */
167#endif
168
169/* Constants that control watermarks */
170#define LOW_SECONDS 1 /* low watermark time till empty */
171#define MINI_CHUNKS 10 /* chunk count for mini flush */
172#ifdef HAVE_PRIORITY_SCHEDULING
173#define PRIO_SECONDS 10 /* max flush time before priority boost */
174#endif
175#if MEM <= 16
176#define PANIC_SECONDS 5 /* flood watermark time until full */
177#define FLUSH_SECONDS 7 /* flush watermark time until full */
178#else
179#define PANIC_SECONDS 8
180#define FLUSH_SECONDS 10
181#endif /* MEM */
182
183/** encoder events **/
184static void (*enc_events_callback)(enum enc_events event, void *data);
185
186/** Path queue for files to write **/
187#define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
188#define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
189static unsigned char *fn_queue; /* pointer to first filename */
190static ssize_t fnq_size; /* capacity of queue in bytes */
191static int fnq_rd_pos; /* current read position */
192static int fnq_wr_pos; /* current write position */
193#define FNQ_NEXT(pos) \
194 ({ int p = (pos) + MAX_PATH; \
195 if (p >= fnq_size) \
196 p = 0; \
197 p; })
198#define FNQ_PREV(pos) \
199 ({ int p = (pos) - MAX_PATH; \
200 if (p < 0) \
201 p = fnq_size - MAX_PATH; \
202 p; })
203
204enum
205{
206 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
207 incoming messages - combine
208 with other constants */
209 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
210 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
211 chunks */
212 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
213 reached */
214};
215
216/***************************************************************************/
217
218static struct event_queue pcmrec_queue SHAREDBSS_ATTR;
219static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR;
220static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
221static const char pcmrec_thread_name[] = "pcmrec";
222static struct thread_entry *pcmrec_thread_p;
223
224static void pcmrec_thread(void);
225
226enum
227{
228 PCMREC_NULL = 0,
229 PCMREC_INIT, /* enable recording */
230 PCMREC_CLOSE, /* close recording */
231 PCMREC_OPTIONS, /* set recording options */
232 PCMREC_RECORD, /* record a new file */
233 PCMREC_STOP, /* stop the current recording */
234 PCMREC_PAUSE, /* pause the current recording */
235 PCMREC_RESUME, /* resume the current recording */
236#if 0
237 PCMREC_FLUSH_NUM, /* flush a number of files out */
238#endif
239};
240
241/*******************************************************************/
242/* Functions that are not executing in the pcmrec_thread first */
243/*******************************************************************/
244
245/* Callback for when more data is ready - called in interrupt context */
246static int pcm_rec_have_more(int status)
247{
248 if (status < 0)
249 {
250 /* some error condition */
251 if (status == DMA_REC_ERROR_DMA)
252 {
253 /* Flush recorded data to disk and stop recording */
254 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
255 return -1;
256 }
257 /* else try again next transmission */
258 }
259 else if (!dma_lock)
260 {
261 /* advance write position */
262 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
263
264 /* set pcm ovf if processing start position is inside current
265 write chunk */
266 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
267 warnings |= PCMREC_W_PCM_BUFFER_OVF;
268
269 dma_wr_pos = next_pos;
270 }
271
272 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
273 return 0;
274} /* pcm_rec_have_more */
275
276static void reset_hardware(void)
277{
278 /* reset pcm to defaults (playback only) */
279 pcm_set_frequency(HW_SAMPR_DEFAULT);
280 audio_set_output_source(AUDIO_SRC_PLAYBACK);
281 pcm_apply_settings();
282}
283
284/** pcm_rec_* group **/
285
286/**
287 * Clear all errors and warnings
288 */
289void pcm_rec_error_clear(void)
290{
291 errors = warnings = 0;
292} /* pcm_rec_error_clear */
293
294/**
295 * Check mode, errors and warnings
296 */
297unsigned long pcm_rec_status(void)
298{
299 unsigned long ret = 0;
300
301 if (is_recording)
302 ret |= AUDIO_STATUS_RECORD;
303 else if (pre_record_ticks)
304 ret |= AUDIO_STATUS_PRERECORD;
305
306 if (is_paused)
307 ret |= AUDIO_STATUS_PAUSE;
308
309 if (errors)
310 ret |= AUDIO_STATUS_ERROR;
311
312 if (warnings)
313 ret |= AUDIO_STATUS_WARNING;
314
315 return ret;
316} /* pcm_rec_status */
317
318/**
319 * Return warnings that have occured since recording started
320 */
321unsigned long pcm_rec_get_warnings(void)
322{
323 return warnings;
324}
325
326#if 0
327int pcm_rec_current_bitrate(void)
328{
329 if (accum_pcm_samples == 0)
330 return 0;
331
332 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
333} /* pcm_rec_current_bitrate */
334#endif
335
336#if 0
337int pcm_rec_encoder_afmt(void)
338{
339 return enc_config.afmt;
340} /* pcm_rec_encoder_afmt */
341#endif
342
343#if 0
344int pcm_rec_rec_format(void)
345{
346 return afmt_rec_format[enc_config.afmt];
347} /* pcm_rec_rec_format */
348#endif
349
350#ifdef HAVE_SPDIF_IN
351unsigned long pcm_rec_sample_rate(void)
352{
353 /* Which is better ?? */
354#if 0
355 return enc_sample_rate;
356#endif
357 return sample_rate;
358} /* audio_get_sample_rate */
359#endif
360
361/**
362 * Creates pcmrec_thread
363 */
364void pcm_rec_init(void)
365{
366 queue_init(&pcmrec_queue, true);
367 pcmrec_thread_p =
368 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
369 0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
370 IF_COP(, CPU));
371 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send,
372 pcmrec_thread_p);
373} /* pcm_rec_init */
374
375/** audio_* group **/
376
377/**
378 * Initializes recording - call before calling any other recording function
379 */
380void audio_init_recording(unsigned int buffer_offset)
381{
382 logf("audio_init_recording");
383 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
384 logf("audio_init_recording done");
385 (void)buffer_offset;
386} /* audio_init_recording */
387
388/**
389 * Closes recording - call audio_stop_recording first
390 */
391void audio_close_recording(void)
392{
393 logf("audio_close_recording");
394 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
395 logf("audio_close_recording done");
396} /* audio_close_recording */
397
398/**
399 * Sets recording parameters
400 */
401void audio_set_recording_options(struct audio_recording_options *options)
402{
403 logf("audio_set_recording_options");
404 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
405 logf("audio_set_recording_options done");
406} /* audio_set_recording_options */
407
408/**
409 * Start recording if not recording or else split
410 */
411void audio_record(const char *filename)
412{
413 logf("audio_record: %s", filename);
414 flush_interrupt();
415 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
416 logf("audio_record_done");
417} /* audio_record */
418
419/**
420 * audio_record wrapper for API compatibility with HW codec
421 */
422void audio_new_file(const char *filename)
423{
424 audio_record(filename);
425} /* audio_new_file */
426
427/**
428 * Stop current recording if recording
429 */
430void audio_stop_recording(void)
431{
432 logf("audio_stop_recording");
433 flush_interrupt();
434 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
435 logf("audio_stop_recording done");
436} /* audio_stop_recording */
437
438/**
439 * Pause current recording
440 */
441void audio_pause_recording(void)
442{
443 logf("audio_pause_recording");
444 flush_interrupt();
445 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
446 logf("audio_pause_recording done");
447} /* audio_pause_recording */
448
449/**
450 * Resume current recording if paused
451 */
452void audio_resume_recording(void)
453{
454 logf("audio_resume_recording");
455 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
456 logf("audio_resume_recording done");
457} /* audio_resume_recording */
458
459/**
460 * Note that microphone is mono, only left value is used
461 * See audiohw_set_recvol() for exact ranges.
462 *
463 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
464 *
465 */
466void audio_set_recording_gain(int left, int right, int type)
467{
468 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
469 audiohw_set_recvol(left, right, type);
470} /* audio_set_recording_gain */
471
472/** Information about current state **/
473
474/**
475 * Return current recorded time in ticks (playback eqivalent time)
476 */
477unsigned long audio_recorded_time(void)
478{
479 if (!is_recording || enc_sample_rate == 0)
480 return 0;
481
482 /* return actual recorded time a la encoded data even if encoder rate
483 doesn't match the pcm rate */
484 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
485} /* audio_recorded_time */
486
487/**
488 * Return number of bytes encoded to output
489 */
490unsigned long audio_num_recorded_bytes(void)
491{
492 if (!is_recording)
493 return 0;
494
495 return num_rec_bytes;
496} /* audio_num_recorded_bytes */
497
498/***************************************************************************/
499/* */
500/* Functions that execute in the context of pcmrec_thread */
501/* */
502/***************************************************************************/
503
504/** Filename Queue **/
505
506/* returns true if the queue is empty */
507static inline bool pcmrec_fnq_is_empty(void)
508{
509 return fnq_rd_pos == fnq_wr_pos;
510} /* pcmrec_fnq_is_empty */
511
512/* empties the filename queue */
513static inline void pcmrec_fnq_set_empty(void)
514{
515 fnq_rd_pos = fnq_wr_pos;
516} /* pcmrec_fnq_set_empty */
517
518/* returns true if the queue is full */
519static bool pcmrec_fnq_is_full(void)
520{
521 ssize_t size = fnq_wr_pos - fnq_rd_pos;
522 if (size < 0)
523 size += fnq_size;
524
525 return size >= fnq_size - MAX_PATH;
526} /* pcmrec_fnq_is_full */
527
528/* queue another filename - will overwrite oldest one if full */
529static bool pcmrec_fnq_add_filename(const char *filename)
530{
531 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
532 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
533
534 if (fnq_rd_pos != fnq_wr_pos)
535 return true;
536
537 /* queue full */
538 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
539 return true;
540} /* pcmrec_fnq_add_filename */
541
542/* replace the last filename added */
543static bool pcmrec_fnq_replace_tail(const char *filename)
544{
545 int pos;
546
547 if (pcmrec_fnq_is_empty())
548 return false;
549
550 pos = FNQ_PREV(fnq_wr_pos);
551
552 strncpy(fn_queue + pos, filename, MAX_PATH);
553
554 return true;
555} /* pcmrec_fnq_replace_tail */
556
557/* pulls the next filename from the queue */
558static bool pcmrec_fnq_get_filename(char *filename)
559{
560 if (pcmrec_fnq_is_empty())
561 return false;
562
563 if (filename)
564 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
565
566 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
567 return true;
568} /* pcmrec_fnq_get_filename */
569
570/* close the file number pointed to by fd_p */
571static void pcmrec_close_file(int *fd_p)
572{
573 if (*fd_p < 0)
574 return; /* preserve error */
575
576 if (close(*fd_p) != 0)
577 errors |= PCMREC_E_IO;
578
579 *fd_p = -1;
580} /* pcmrec_close_file */
581
582/** Data Flushing **/
583
584/**
585 * called after callback to update sizes if codec changed the amount of data
586 * a chunk represents
587 */
588static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
589 unsigned long prev_num_pcm)
590{
591 if (rec_fdata.new_enc_size != prev_enc_size)
592 {
593 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
594 num_rec_bytes += size_diff;
595#if 0
596 accum_rec_bytes += size_diff;
597#endif
598 }
599
600 if (rec_fdata.new_num_pcm != prev_num_pcm)
601 {
602 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
603 num_rec_samples += pcm_diff;
604#if 0
605 accum_pcm_samples += pcm_diff;
606#endif
607 }
608} /* pcmrec_update_sizes_inl */
609
610/* don't need to inline every instance */
611static void pcmrec_update_sizes(size_t prev_enc_size,
612 unsigned long prev_num_pcm)
613{
614 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
615} /* pcmrec_update_sizes */
616
617static void pcmrec_start_file(void)
618{
619 size_t enc_size = rec_fdata.new_enc_size;
620 unsigned long num_pcm = rec_fdata.new_num_pcm;
621 int curr_rec_file = rec_fdata.rec_file;
622 char filename[MAX_PATH];
623
624 /* must always pull the filename that matches with this queue */
625 if (!pcmrec_fnq_get_filename(filename))
626 {
627 logf("start file: fnq empty");
628 *filename = '\0';
629 errors |= PCMREC_E_FNQ_DESYNC;
630 }
631 else if (errors != 0)
632 {
633 logf("start file: error already");
634 }
635 else if (curr_rec_file >= 0)
636 {
637 /* Any previous file should have been closed */
638 logf("start file: file already open");
639 errors |= PCMREC_E_FNQ_DESYNC;
640 }
641
642 if (errors != 0)
643 rec_fdata.chunk->flags |= CHUNKF_ERROR;
644
645 /* encoder can set error flag here and should increase
646 enc_new_size and pcm_new_size to reflect additional
647 data written if any */
648 rec_fdata.filename = filename;
649 enc_events_callback(ENC_START_FILE, &rec_fdata);
650
651 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
652 {
653 logf("start file: enc error");
654 errors |= PCMREC_E_ENCODER;
655 }
656
657 if (errors != 0)
658 {
659 pcmrec_close_file(&curr_rec_file);
660 /* Write no more to this file */
661 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
662 }
663 else
664 {
665 pcmrec_update_sizes(enc_size, num_pcm);
666 }
667
668 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
669} /* pcmrec_start_file */
670
671static inline void pcmrec_write_chunk(void)
672{
673 size_t enc_size = rec_fdata.new_enc_size;
674 unsigned long num_pcm = rec_fdata.new_num_pcm;
675
676 if (errors != 0)
677 rec_fdata.chunk->flags |= CHUNKF_ERROR;
678
679 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
680
681 if ((long)rec_fdata.chunk->flags >= 0)
682 {
683 pcmrec_update_sizes_inl(enc_size, num_pcm);
684 }
685 else if (errors == 0)
686 {
687 logf("wr chk enc error %lu %lu",
688 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
689 errors |= PCMREC_E_ENCODER;
690 }
691} /* pcmrec_write_chunk */
692
693static void pcmrec_end_file(void)
694{
695 /* all data in output buffer for current file will have been
696 written and encoder can now do any nescessary steps to
697 finalize the written file */
698 size_t enc_size = rec_fdata.new_enc_size;
699 unsigned long num_pcm = rec_fdata.new_num_pcm;
700
701 enc_events_callback(ENC_END_FILE, &rec_fdata);
702
703 if (errors == 0)
704 {
705 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
706 {
707 logf("end file: enc error");
708 errors |= PCMREC_E_ENCODER;
709 }
710 else
711 {
712 pcmrec_update_sizes(enc_size, num_pcm);
713 }
714 }
715
716 /* Force file close if error */
717 if (errors != 0)
718 pcmrec_close_file(&rec_fdata.rec_file);
719
720 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
721} /* pcmrec_end_file */
722
723/**
724 * Update buffer watermarks with spinup time compensation
725 *
726 * All this assumes reasonable data rates, chunk sizes and sufficient
727 * memory for the most part. Some dumb checks are included but perhaps
728 * are pointless since this all will break down at extreme limits that
729 * are currently not applicable to any supported device.
730 */
731static void pcmrec_refresh_watermarks(void)
732{
733 logf("ata spinup: %d", ata_spinup_time);
734
735 /* set the low mark for when flushing stops if automatic */
736 low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1))
737 / enc_chunk_size;
738 logf("low wmk: %d", low_watermark);
739
740#ifdef HAVE_PRIORITY_SCHEDULING
741 /* panic boost thread priority if 2 seconds of ground is lost -
742 this allows encoder to boost with just under a second of
743 pcm data (if not yet full enough to boost itself)
744 and not falsely trip the alarm. */
745 flood_watermark = enc_num_chunks -
746 (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1))
747 / enc_chunk_size;
748
749 if (flood_watermark < low_watermark)
750 {
751 logf("warning: panic < low");
752 flood_watermark = low_watermark;
753 }
754
755 logf("flood at: %d", flood_watermark);
756#endif
757 spinup_time = last_ata_spinup_time = ata_spinup_time;
758
759 /* write at 8s + st remaining in enc_buffer - range 12s to
760 20s total - default to 3.5s spinup. */
761 if (spinup_time == 0)
762 spinup_time = 35*HZ/10; /* default - cozy */
763 else if (spinup_time < 2*HZ)
764 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
765 else if (spinup_time > 10*HZ)
766 spinup_time = 10*HZ; /* do you have a functioning HD? */
767
768 /* try to start writing with 10s remaining after disk spinup */
769 high_watermark = enc_num_chunks -
770 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
771 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
772
773 if (high_watermark < low_watermark)
774 {
775 high_watermark = low_watermark;
776 low_watermark /= 2;
777 logf("warning: low 'write at'");
778 }
779
780 logf("write at: %d", high_watermark);
781} /* pcmrec_refresh_watermarks */
782
783/**
784 * Process the chunks
785 *
786 * This function is called when queue_get_w_tmo times out.
787 *
788 * Set flush_num to the number of files to flush to disk or to
789 * a PCMREC_FLUSH_* constant.
790 */
791static void pcmrec_flush(unsigned flush_num)
792{
793#ifdef HAVE_PRIORITY_SCHEDULING
794 static unsigned long last_flush_tick; /* tick when function returned */
795 unsigned long start_tick; /* When flush started */
796 unsigned long prio_tick; /* Timeout for auto boost */
797 int prio_pcmrec; /* Current thread priority for pcmrec */
798 int prio_codec; /* Current thread priority for codec */
799#endif
800 int num_ready; /* Number of chunks ready at start */
801 unsigned remaining; /* Number of file starts remaining */
802 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
803 bool interruptable; /* Flush can be interupted */
804
805 num_ready = enc_wr_index - enc_rd_index;
806 if (num_ready < 0)
807 num_ready += enc_num_chunks;
808
809 /* save interruptable flag and remove it to get the actual count */
810 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
811 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
812
813 if (flush_num == 0)
814 {
815 if (!is_recording)
816 return;
817
818 if (ata_spinup_time != last_ata_spinup_time)
819 pcmrec_refresh_watermarks();
820
821 /* enough available? no? then leave */
822 if (num_ready < high_watermark)
823 return;
824 } /* endif (flush_num == 0) */
825
826#ifdef HAVE_PRIORITY_SCHEDULING
827 start_tick = current_tick;
828 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
829
830 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
831 {
832 /* if we're getting called too much and this isn't forced,
833 boost stat by expiring timeout in advance */
834 logf("too frequent flush");
835 prio_tick = current_tick - 1;
836 }
837
838 prio_pcmrec = -1;
839 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
840 need init */
841#endif
842
843 logf("writing:%d(%d):%s%s", num_ready, flush_num,
844 interruptable ? "i" : "",
845 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
846
847 cpu_boost(true);
848
849 remaining = flush_num;
850 chunks_flushed = 0;
851
852 while (num_ready > 0)
853 {
854 /* check current number of encoder chunks */
855 int num = enc_wr_index - enc_rd_index;
856 if (num < 0)
857 num += enc_num_chunks;
858
859 if (num <= low_watermark &&
860 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
861 {
862 logf("low data: %d", num);
863 break; /* data remaining is below threshold */
864 }
865
866 if (interruptable && flush_interrupts > 0)
867 {
868 logf("int at: %d", num);
869 break; /* interrupted */
870 }
871
872#ifdef HAVE_PRIORITY_SCHEDULING
873 if (prio_pcmrec == -1 && (num >= flood_watermark ||
874 TIME_AFTER(current_tick, prio_tick)))
875 {
876 /* losing ground or holding without progress - boost
877 priority until finished */
878 logf("pcmrec: boost (%s)",
879 num >= flood_watermark ? "num" : "time");
880 prio_pcmrec = thread_set_priority(NULL,
881 thread_get_priority(NULL) - 4);
882 prio_codec = thread_set_priority(codec_thread_p,
883 thread_get_priority(codec_thread_p) - 4);
884 }
885#endif
886
887 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
888 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
889 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
890
891 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
892 {
893 pcmrec_start_file();
894 if (--remaining == 0)
895 num_ready = 0; /* stop on next loop - must write this
896 chunk if it has data */
897 }
898
899 pcmrec_write_chunk();
900
901 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
902 pcmrec_end_file();
903
904 INC_ENC_INDEX(enc_rd_index);
905
906 if (errors != 0)
907 {
908 pcmrec_end_file();
909 break;
910 }
911
912 if (flush_num == PCMREC_FLUSH_MINI &&
913 ++chunks_flushed >= MINI_CHUNKS)
914 {
915 logf("mini flush break");
916 break;
917 }
918 /* no yielding; the file apis called in the codecs do that
919 sufficiently */
920 } /* end while */
921
922 /* sync file */
923 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
924 errors |= PCMREC_E_IO;
925
926 cpu_boost(false);
927
928#ifdef HAVE_PRIORITY_SCHEDULING
929 if (prio_pcmrec != -1)
930 {
931 /* return to original priorities */
932 logf("pcmrec: unboost priority");
933 thread_set_priority(NULL, prio_pcmrec);
934 thread_set_priority(codec_thread_p, prio_codec);
935 }
936
937 last_flush_tick = current_tick; /* save tick when we left */
938#endif
939
940 logf("done");
941} /* pcmrec_flush */
942
943/**
944 * Marks a new stream in the buffer and gives the encoder a chance for special
945 * handling of transition from one to the next. The encoder may change the
946 * chunk that ends the old stream by requesting more chunks and similiarly for
947 * the new but must always advance the position though the interface. It can
948 * later reject any data it cares to when writing the file but should mark the
949 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
950 * a NULL data pointer without error as well.
951 */
952static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
953{
954 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
955} /* pcmrec_get_chunk_index */
956
957static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
958{
959 DEC_ENC_INDEX(index);
960 return GET_ENC_CHUNK(index);
961} /* pcmrec_get_prev_chunk */
962
963static void pcmrec_new_stream(const char *filename, /* next file name */
964 unsigned long flags, /* CHUNKF_* flags */
965 int pre_index) /* index for prerecorded data */
966{
967 logf("pcmrec_new_stream");
968 char path[MAX_PATH]; /* place to copy filename so sender can be released */
969
970 struct enc_buffer_event_data data;
971 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
972 new filename */
973 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
974 stream */
975 bool did_flush = false; /* did a flush occurr? */
976
977 if (filename)
978 strncpy(path, filename, MAX_PATH);
979 queue_reply(&pcmrec_queue, 0); /* We have all we need */
980
981 data.pre_chunk = NULL;
982 data.chunk = GET_ENC_CHUNK(enc_wr_index);
983
984 /* end chunk */
985 if (flags & CHUNKF_END_FILE)
986 {
987 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
988
989 if (data.chunk->flags & CHUNKF_START_FILE)
990 {
991 /* cannot start and end on same unprocessed chunk */
992 logf("file end on start");
993 flags &= ~CHUNKF_END_FILE;
994 }
995 else if (enc_rd_index == enc_wr_index)
996 {
997 /* all data flushed but file not ended - chunk will be left
998 empty */
999 logf("end on dead end");
1000 data.chunk->flags = 0;
1001 data.chunk->enc_size = 0;
1002 data.chunk->num_pcm = 0;
1003 data.chunk->enc_data = NULL;
1004 INC_ENC_INDEX(enc_wr_index);
1005 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1006 }
1007 else
1008 {
1009 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1010
1011 if (last->flags & CHUNKF_END_FILE)
1012 {
1013 /* end already processed and marked - can't end twice */
1014 logf("file end again");
1015 flags &= ~CHUNKF_END_FILE;
1016 }
1017 }
1018 }
1019
1020 /* start chunk */
1021 if (flags & CHUNKF_START_FILE)
1022 {
1023 bool pre = flags & CHUNKF_PRERECORD;
1024
1025 if (pre)
1026 {
1027 logf("stream prerecord start");
1028 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1029 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1030 }
1031 else
1032 {
1033 logf("stream normal start");
1034 start = data.chunk;
1035 start->flags &= CHUNKF_START_FILE;
1036 }
1037
1038 /* if encoder hasn't yet processed the last start - abort the start
1039 of the previous file queued or else it will be empty and invalid */
1040 if (start->flags & CHUNKF_START_FILE)
1041 {
1042 logf("replacing fnq tail: %s", filename);
1043 fnq_add_fn = pcmrec_fnq_replace_tail;
1044 }
1045 else
1046 {
1047 logf("adding filename: %s", filename);
1048 fnq_add_fn = pcmrec_fnq_add_filename;
1049 }
1050 }
1051
1052 data.flags = flags;
1053 pcmrec_context = true; /* switch encoder context */
1054 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1055 pcmrec_context = false; /* switch back */
1056
1057 if (flags & CHUNKF_END_FILE)
1058 {
1059 int i = pcmrec_get_chunk_index(data.chunk);
1060 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1061 }
1062
1063 if (start)
1064 {
1065 if (!(flags & CHUNKF_PRERECORD))
1066 {
1067 /* get stats on data added to start - sort of a prerecord
1068 operation */
1069 int i = pcmrec_get_chunk_index(data.chunk);
1070 struct enc_chunk_hdr *chunk = data.chunk;
1071
1072 logf("start data: %d %d", i, enc_wr_index);
1073
1074 num_rec_bytes = 0;
1075 num_rec_samples = 0;
1076
1077 while (i != enc_wr_index)
1078 {
1079 num_rec_bytes += chunk->enc_size;
1080 num_rec_samples += chunk->num_pcm;
1081 INC_ENC_INDEX(i);
1082 chunk = GET_ENC_CHUNK(i);
1083 }
1084
1085 start->flags &= ~CHUNKF_START_FILE;
1086 start = data.chunk;
1087 }
1088
1089 start->flags |= CHUNKF_START_FILE;
1090
1091 /* flush all pending files out if full and adding */
1092 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1093 {
1094 logf("fnq full");
1095 pcmrec_flush(PCMREC_FLUSH_ALL);
1096 did_flush = true;
1097 }
1098
1099 fnq_add_fn(path);
1100 }
1101
1102 /* Make sure to complete any interrupted high watermark */
1103 if (!did_flush)
1104 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1105} /* pcmrec_new_stream */
1106
1107/** event handlers for pcmrec thread */
1108
1109/* PCMREC_INIT */
1110static void pcmrec_init(void)
1111{
1112 unsigned char *buffer;
1113
1114 /* warings and errors */
1115 warnings =
1116 errors = 0;
1117
1118 pcmrec_close_file(&rec_fdata.rec_file);
1119 rec_fdata.rec_file = -1;
1120
1121 /* pcm FIFO */
1122 dma_lock = true;
1123 pcm_rd_pos = 0;
1124 dma_wr_pos = 0;
1125 pcm_enc_pos = 0;
1126
1127 /* encoder FIFO */
1128 enc_wr_index = 0;
1129 enc_rd_index = 0;
1130
1131 /* filename queue */
1132 fnq_rd_pos = 0;
1133 fnq_wr_pos = 0;
1134
1135 /* stats */
1136 num_rec_bytes = 0;
1137 num_rec_samples = 0;
1138#if 0
1139 accum_rec_bytes = 0;
1140 accum_pcm_samples = 0;
1141#endif
1142
1143 pre_record_ticks = 0;
1144
1145 is_recording = false;
1146 is_paused = false;
1147
1148 buffer = audio_get_recording_buffer(&rec_buffer_size);
1149
1150 /* Line align pcm_buffer 2^4=16 bytes */
1151 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4);
1152 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1153 PCM_MAX_FEED_SIZE, 2);
1154 /* Adjust available buffer for possible align advancement */
1155 rec_buffer_size -= pcm_buffer - buffer;
1156
1157 pcm_init_recording();
1158} /* pcmrec_init */
1159
1160/* PCMREC_CLOSE */
1161static void pcmrec_close(void)
1162{
1163 dma_lock = true;
1164 pre_record_ticks = 0; /* Can't be prerecording any more */
1165 warnings = 0;
1166 pcm_close_recording();
1167 reset_hardware();
1168 audio_remove_encoder();
1169} /* pcmrec_close */
1170
1171/* PCMREC_OPTIONS */
1172static void pcmrec_set_recording_options(
1173 struct audio_recording_options *options)
1174{
1175 /* stop DMA transfer */
1176 dma_lock = true;
1177 pcm_stop_recording();
1178
1179 rec_frequency = options->rec_frequency;
1180 rec_source = options->rec_source;
1181 num_channels = options->rec_channels == 1 ? 1 : 2;
1182 rec_mono_mode = options->rec_mono_mode;
1183 pre_record_ticks = options->rec_prerecord_time * HZ;
1184 enc_config = options->enc_config;
1185 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1186
1187#ifdef HAVE_SPDIF_IN
1188 if (rec_source == AUDIO_SRC_SPDIF)
1189 {
1190 /* must measure SPDIF sample rate before configuring codecs */
1191 unsigned long sr = spdif_measure_frequency();
1192 /* round to master list for SPDIF rate */
1193 int index = round_value_to_list32(sr, audio_master_sampr_list,
1194 SAMPR_NUM_FREQ, false);
1195 sample_rate = audio_master_sampr_list[index];
1196 /* round to HW playback rates for monitoring */
1197 index = round_value_to_list32(sr, hw_freq_sampr,
1198 HW_NUM_FREQ, false);
1199 pcm_set_frequency(hw_freq_sampr[index]);
1200 /* encoders with a limited number of rates do their own rounding */
1201 }
1202 else
1203#endif
1204 {
1205 /* set sample rate from frequency selection */
1206 sample_rate = rec_freq_sampr[rec_frequency];
1207 pcm_set_frequency(sample_rate);
1208 }
1209
1210 /* set monitoring */
1211 audio_set_output_source(rec_source);
1212
1213 /* apply hardware setting to start monitoring now */
1214 pcm_apply_settings();
1215
1216 queue_reply(&pcmrec_queue, 0); /* Release sender */
1217
1218 if (audio_load_encoder(enc_config.afmt))
1219 {
1220 /* start DMA transfer */
1221 dma_lock = pre_record_ticks == 0;
1222 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1223 PCM_CHUNK_SIZE);
1224 }
1225 else
1226 {
1227 logf("set rec opt: enc load failed");
1228 errors |= PCMREC_E_LOAD_ENCODER;
1229 }
1230} /* pcmrec_set_recording_options */
1231
1232/* PCMREC_RECORD - start recording (not gapless)
1233 or split stream (gapless) */
1234static void pcmrec_record(const char *filename)
1235{
1236 unsigned long pre_sample_ticks;
1237 int rd_start;
1238 unsigned long flags;
1239 int pre_index;
1240
1241 logf("pcmrec_record: %s", filename);
1242
1243 /* reset stats */
1244 num_rec_bytes = 0;
1245 num_rec_samples = 0;
1246
1247 if (!is_recording)
1248 {
1249#if 0
1250 accum_rec_bytes = 0;
1251 accum_pcm_samples = 0;
1252#endif
1253 warnings = 0; /* reset warnings */
1254
1255 rd_start = enc_wr_index;
1256 pre_sample_ticks = 0;
1257
1258 pcmrec_refresh_watermarks();
1259
1260 if (pre_record_ticks)
1261 {
1262 int i = rd_start;
1263 /* calculate number of available chunks */
1264 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1265 enc_num_chunks) % enc_num_chunks;
1266 /* overflow at 974 seconds of prerecording at 44.1kHz */
1267 unsigned long pre_record_sample_ticks =
1268 enc_sample_rate*pre_record_ticks;
1269 int pre_chunks = 0; /* Counter to limit prerecorded time to
1270 prevent flood state at outset */
1271
1272 logf("pre-st: %ld", pre_record_sample_ticks);
1273
1274 /* Get exact measure of recorded data as number of samples aren't
1275 nescessarily going to be the max for each chunk */
1276 for (; avail_pre_chunks-- > 0;)
1277 {
1278 struct enc_chunk_hdr *chunk;
1279 unsigned long chunk_sample_ticks;
1280
1281 DEC_ENC_INDEX(i);
1282
1283 chunk = GET_ENC_CHUNK(i);
1284
1285 /* must have data to be counted */
1286 if (chunk->enc_data == NULL)
1287 continue;
1288
1289 chunk_sample_ticks = chunk->num_pcm*HZ;
1290
1291 rd_start = i;
1292 pre_sample_ticks += chunk_sample_ticks;
1293 num_rec_bytes += chunk->enc_size;
1294 num_rec_samples += chunk->num_pcm;
1295 pre_chunks++;
1296
1297 /* stop here if enough already */
1298 if (pre_chunks >= high_watermark ||
1299 pre_sample_ticks >= pre_record_sample_ticks)
1300 {
1301 logf("pre-chks: %d", pre_chunks);
1302 break;
1303 }
1304 }
1305
1306#if 0
1307 accum_rec_bytes = num_rec_bytes;
1308 accum_pcm_samples = num_rec_samples;
1309#endif
1310 }
1311
1312 enc_rd_index = rd_start;
1313
1314 /* filename queue should be empty */
1315 if (!pcmrec_fnq_is_empty())
1316 {
1317 logf("fnq: not empty!");
1318 pcmrec_fnq_set_empty();
1319 }
1320
1321 flags = CHUNKF_START_FILE;
1322 if (pre_sample_ticks > 0)
1323 flags |= CHUNKF_PRERECORD;
1324
1325 pre_index = enc_rd_index;
1326
1327 dma_lock = false;
1328 is_paused = false;
1329 is_recording = true;
1330 }
1331 else
1332 {
1333 /* already recording, just split the stream */
1334 logf("inserting split");
1335 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1336 pre_index = 0;
1337 }
1338
1339 pcmrec_new_stream(filename, flags, pre_index);
1340 logf("pcmrec_record done");
1341} /* pcmrec_record */
1342
1343/* PCMREC_STOP */
1344static void pcmrec_stop(void)
1345{
1346 logf("pcmrec_stop");
1347
1348 if (is_recording)
1349 {
1350 dma_lock = true; /* lock dma write position */
1351
1352 /* flush all available data first to avoid overflow while waiting
1353 for encoding to finish */
1354 pcmrec_flush(PCMREC_FLUSH_ALL);
1355
1356 /* wait for encoder to finish remaining data */
1357 while (errors == 0 && !pcm_buffer_empty)
1358 yield();
1359
1360 /* end stream at last data */
1361 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1362
1363 /* flush anything else encoder added */
1364 pcmrec_flush(PCMREC_FLUSH_ALL);
1365
1366 /* remove any pending file start not yet processed - should be at
1367 most one at enc_wr_index */
1368 pcmrec_fnq_get_filename(NULL);
1369 /* encoder should abort any chunk it was in midst of processing */
1370 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1371
1372 /* filename queue should be empty */
1373 if (!pcmrec_fnq_is_empty())
1374 {
1375 logf("fnq: not empty!");
1376 pcmrec_fnq_set_empty();
1377 }
1378
1379 /* be absolutely sure the file is closed */
1380 if (errors != 0)
1381 pcmrec_close_file(&rec_fdata.rec_file);
1382 rec_fdata.rec_file = -1;
1383
1384 is_recording = false;
1385 is_paused = false;
1386 dma_lock = pre_record_ticks == 0;
1387 }
1388 else
1389 {
1390 logf("not recording");
1391 }
1392
1393 logf("pcmrec_stop done");
1394} /* pcmrec_stop */
1395
1396/* PCMREC_PAUSE */
1397static void pcmrec_pause(void)
1398{
1399 logf("pcmrec_pause");
1400
1401 if (!is_recording)
1402 {
1403 logf("not recording");
1404 }
1405 else if (is_paused)
1406 {
1407 logf("already paused");
1408 }
1409 else
1410 {
1411 dma_lock = true;
1412 is_paused = true;
1413 }
1414
1415 logf("pcmrec_pause done");
1416} /* pcmrec_pause */
1417
1418/* PCMREC_RESUME */
1419static void pcmrec_resume(void)
1420{
1421 logf("pcmrec_resume");
1422
1423 if (!is_recording)
1424 {
1425 logf("not recording");
1426 }
1427 else if (!is_paused)
1428 {
1429 logf("not paused");
1430 }
1431 else
1432 {
1433 is_paused = false;
1434 is_recording = true;
1435 dma_lock = false;
1436 }
1437
1438 logf("pcmrec_resume done");
1439} /* pcmrec_resume */
1440
1441static void pcmrec_thread(void) __attribute__((noreturn));
1442static void pcmrec_thread(void)
1443{
1444 struct queue_event ev;
1445
1446 logf("thread pcmrec start");
1447
1448 while(1)
1449 {
1450 if (is_recording)
1451 {
1452 /* Poll periodically to flush data */
1453 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1454
1455 if (ev.id == SYS_TIMEOUT)
1456 {
1457 /* Messages that interrupt this will complete it */
1458 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1459 PCMREC_FLUSH_INTERRUPTABLE);
1460 continue;
1461 }
1462 }
1463 else
1464 {
1465 /* Not doing anything - sit and wait for commands */
1466 queue_wait(&pcmrec_queue, &ev);
1467 }
1468
1469 switch (ev.id)
1470 {
1471 case PCMREC_INIT:
1472 pcmrec_init();
1473 break;
1474
1475 case PCMREC_CLOSE:
1476 pcmrec_close();
1477 break;
1478
1479 case PCMREC_OPTIONS:
1480 pcmrec_set_recording_options(
1481 (struct audio_recording_options *)ev.data);
1482 break;
1483
1484 case PCMREC_RECORD:
1485 clear_flush_interrupt();
1486 pcmrec_record((const char *)ev.data);
1487 break;
1488
1489 case PCMREC_STOP:
1490 clear_flush_interrupt();
1491 pcmrec_stop();
1492 break;
1493
1494 case PCMREC_PAUSE:
1495 clear_flush_interrupt();
1496 pcmrec_pause();
1497 break;
1498
1499 case PCMREC_RESUME:
1500 pcmrec_resume();
1501 break;
1502#if 0
1503 case PCMREC_FLUSH_NUM:
1504 pcmrec_flush((unsigned)ev.data);
1505 break;
1506#endif
1507 case SYS_USB_CONNECTED:
1508 if (is_recording)
1509 break;
1510 pcmrec_close();
1511 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1512 usb_wait_for_disconnect(&pcmrec_queue);
1513 flush_interrupts = 0;
1514 break;
1515 } /* end switch */
1516 } /* end while */
1517} /* pcmrec_thread */
1518
1519/****************************************************************************/
1520/* */
1521/* following functions will be called by the encoder codec */
1522/* in a free-threaded manner */
1523/* */
1524/****************************************************************************/
1525
1526/* pass the encoder settings to the encoder */
1527void enc_get_inputs(struct enc_inputs *inputs)
1528{
1529 inputs->sample_rate = sample_rate;
1530 inputs->num_channels = num_channels;
1531 inputs->rec_mono_mode = rec_mono_mode;
1532 inputs->config = &enc_config;
1533} /* enc_get_inputs */
1534
1535/* set the encoder dimensions (called by encoder codec at initialization and
1536 termination) */
1537void enc_set_parameters(struct enc_parameters *params)
1538{
1539 size_t bufsize, resbytes;
1540
1541 logf("enc_set_parameters");
1542
1543 if (!params)
1544 {
1545 logf("reset");
1546 /* Encoder is terminating */
1547 memset(&enc_config, 0, sizeof (enc_config));
1548 enc_sample_rate = 0;
1549 cancel_cpu_boost(); /* Make sure no boost remains */
1550 return;
1551 }
1552
1553 enc_sample_rate = params->enc_sample_rate;
1554 logf("enc sampr:%lu", enc_sample_rate);
1555
1556 pcm_rd_pos = dma_wr_pos;
1557 pcm_enc_pos = pcm_rd_pos;
1558
1559 enc_config.afmt = params->afmt;
1560 /* addition of the header is always implied - chunk size 4-byte aligned */
1561 enc_chunk_size =
1562 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1563 enc_events_callback = params->events_callback;
1564
1565 logf("chunk size:%lu", enc_chunk_size);
1566
1567 /*** Configure the buffers ***/
1568
1569 /* Layout of recording buffer:
1570 * [ax] = possible alignment x multiple
1571 * [sx] = possible size alignment of x multiple
1572 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1573 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1574 */
1575 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1576 logf("resbytes:%lu", resbytes);
1577
1578 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1579 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1580#ifdef DEBUG
1581 - sizeof (*wrap_id_p)
1582#endif
1583 ;
1584
1585 enc_num_chunks = bufsize / enc_chunk_size;
1586 logf("num chunks:%d", enc_num_chunks);
1587
1588 /* get real amount used by encoder chunks */
1589 bufsize = enc_num_chunks*enc_chunk_size;
1590 logf("enc size:%lu", bufsize);
1591
1592#ifdef DEBUG
1593 /* add magic at wraparound for spillover checks */
1594 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1595 bufsize += sizeof (*wrap_id_p);
1596 *wrap_id_p = ENC_CHUNK_MAGIC;
1597#endif
1598
1599 /** set OUT parameters **/
1600 params->enc_buffer = enc_buffer;
1601 params->buf_chunk_size = enc_chunk_size;
1602 params->num_chunks = enc_num_chunks;
1603
1604 /* calculate reserve buffer start and return pointer to encoder */
1605 params->reserve_buffer = NULL;
1606 if (resbytes > 0)
1607 {
1608 params->reserve_buffer = enc_buffer + bufsize;
1609 bufsize += resbytes;
1610 }
1611
1612 /* place filename queue at end of buffer using up whatever remains */
1613 fnq_rd_pos = 0; /* reset */
1614 fnq_wr_pos = 0; /* reset */
1615 fn_queue = enc_buffer + bufsize;
1616 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1617 fnq_size /= MAX_PATH;
1618 if (fnq_size > FNQ_MAX_NUM_PATHS)
1619 fnq_size = FNQ_MAX_NUM_PATHS;
1620 fnq_size *= MAX_PATH;
1621 logf("fnq files:%ld", fnq_size / MAX_PATH);
1622
1623#if defined(DEBUG)
1624 logf("ab :%08lX", (uintptr_t)audiobuf);
1625 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1626 logf("enc:%08lX", (uintptr_t)enc_buffer);
1627 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1628 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1629 logf("fnq:%08lX", (uintptr_t)fn_queue);
1630 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1631 logf("abe:%08lX", (uintptr_t)audiobufend);
1632#endif
1633
1634 /* init all chunk headers and reset indexes */
1635 enc_rd_index = 0;
1636 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1637 {
1638 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1639#ifdef DEBUG
1640 chunk->id = ENC_CHUNK_MAGIC;
1641#endif
1642 chunk->flags = 0;
1643 }
1644
1645 logf("enc_set_parameters done");
1646} /* enc_set_parameters */
1647
1648/* return encoder chunk at current write position -
1649 NOTE: can be called by pcmrec thread when splitting streams */
1650struct enc_chunk_hdr * enc_get_chunk(void)
1651{
1652 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1653
1654#ifdef DEBUG
1655 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1656 {
1657 errors |= PCMREC_E_CHUNK_OVF;
1658 logf("finish chk ovf: %d", enc_wr_index);
1659 }
1660#endif
1661
1662 chunk->flags &= CHUNKF_START_FILE;
1663
1664 if (!is_recording)
1665 chunk->flags |= CHUNKF_PRERECORD;
1666
1667 return chunk;
1668} /* enc_get_chunk */
1669
1670/* releases the current chunk into the available chunks -
1671 NOTE: can be called by pcmrec thread when splitting streams */
1672void enc_finish_chunk(void)
1673{
1674 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1675
1676 if ((long)chunk->flags < 0)
1677 {
1678 /* encoder set error flag */
1679 errors |= PCMREC_E_ENCODER;
1680 logf("finish chk enc error");
1681 }
1682
1683 /* advance enc_wr_index to the next encoder chunk */
1684 INC_ENC_INDEX(enc_wr_index);
1685
1686 if (enc_rd_index != enc_wr_index)
1687 {
1688 num_rec_bytes += chunk->enc_size;
1689 num_rec_samples += chunk->num_pcm;
1690#if 0
1691 accum_rec_bytes += chunk->enc_size;
1692 accum_pcm_samples += chunk->num_pcm;
1693#endif
1694 }
1695 else if (is_recording) /* buffer full */
1696 {
1697 /* keep current position and put up warning flag */
1698 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1699 logf("enc_buffer ovf");
1700 DEC_ENC_INDEX(enc_wr_index);
1701 if (pcmrec_context)
1702 {
1703 /* if stream splitting, keep this out of circulation and
1704 flush a small number, then readd - cannot risk losing
1705 stream markers */
1706 logf("mini flush");
1707 pcmrec_flush(PCMREC_FLUSH_MINI);
1708 INC_ENC_INDEX(enc_wr_index);
1709 }
1710 }
1711 else
1712 {
1713 /* advance enc_rd_index for prerecording */
1714 INC_ENC_INDEX(enc_rd_index);
1715 }
1716} /* enc_finish_chunk */
1717
1718/* passes a pointer to next chunk of unprocessed wav data */
1719/* TODO: this really should give the actual size returned */
1720unsigned char * enc_get_pcm_data(size_t size)
1721{
1722 int wp = dma_wr_pos;
1723 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1724
1725 /* limit the requested pcm data size */
1726 if (size > PCM_MAX_FEED_SIZE)
1727 size = PCM_MAX_FEED_SIZE;
1728
1729 if (avail >= size)
1730 {
1731 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1732 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1733
1734 pcm_enc_pos = pcm_rd_pos;
1735 pcm_rd_pos = next_pos;
1736
1737 /* ptr must point to continous data at wraparound position */
1738 if ((size_t)pcm_rd_pos < size)
1739 {
1740 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1741 pcm_buffer, pcm_rd_pos);
1742 }
1743
1744 if (avail >= (sample_rate << 2))
1745 {
1746 /* Filling up - boost codec */
1747 trigger_cpu_boost();
1748 }
1749
1750 pcm_buffer_empty = false;
1751 return ptr;
1752 }
1753
1754 /* not enough data available - encoder should idle */
1755 pcm_buffer_empty = true;
1756
1757 cancel_cpu_boost();
1758
1759 /* Sleep long enough to allow one frame on average */
1760 sleep(0);
1761
1762 return NULL;
1763} /* enc_get_pcm_data */
1764
1765/* puts some pcm data back in the queue */
1766size_t enc_unget_pcm_data(size_t size)
1767{
1768 int wp = dma_wr_pos;
1769 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1770 2*PCM_CHUNK_SIZE;
1771
1772 /* allow one interrupt to occur during this call and not have the
1773 new read position inside the DMA destination chunk */
1774 if ((ssize_t)old_avail > 0)
1775 {
1776 /* limit size to amount of old data remaining */
1777 if (size > old_avail)
1778 size = old_avail;
1779
1780 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1781 pcm_rd_pos = pcm_enc_pos;
1782
1783 return size;
1784 }
1785
1786 return 0;
1787} /* enc_unget_pcm_data */
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index f0fbd7df92..86fadff2a2 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -46,9 +46,6 @@
46#ifdef HAVE_LCD_BITMAP 46#ifdef HAVE_LCD_BITMAP
47#include "font.h" 47#include "font.h"
48#endif 48#endif
49#if defined(HAVE_RECORDING) && (CONFIG_CODEC == SWCODEC)
50#include "pcm_record.h"
51#endif
52#include "logf.h" 49#include "logf.h"
53#include "lcd-remote.h" 50#include "lcd-remote.h"
54#ifdef SIMULATOR 51#ifdef SIMULATOR