diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/SOURCES | 3 | ||||
-rw-r--r-- | firmware/drivers/mas.c | 17 | ||||
-rw-r--r-- | firmware/drivers/tuner/s1a0903x01.c | 3 | ||||
-rw-r--r-- | firmware/export/audio.h | 2 | ||||
-rw-r--r-- | firmware/export/id3.h | 246 | ||||
-rw-r--r-- | firmware/export/mas.h | 6 | ||||
-rw-r--r-- | firmware/export/mpeg.h | 66 | ||||
-rw-r--r-- | firmware/export/pcm_record.h | 66 | ||||
-rw-r--r-- | firmware/export/replaygain.h | 33 | ||||
-rw-r--r-- | firmware/mp3_playback.c | 5 | ||||
-rw-r--r-- | firmware/pcm_record.c | 1787 | ||||
-rw-r--r-- | firmware/powermgmt.c | 3 |
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 | |||
191 | pcm.c | 191 | pcm.c |
192 | #ifdef HAVE_RECORDING | 192 | #ifdef HAVE_RECORDING |
193 | enc_base.c | 193 | enc_base.c |
194 | #ifndef SIMULATOR | ||
195 | pcm_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 | ||
471 | static int pllfreq; | ||
472 | |||
473 | void mas_store_pllfreq(int freq) | ||
474 | { | ||
475 | pllfreq = freq; | ||
476 | } | ||
477 | |||
478 | int 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 | */ | ||
34 | enum | ||
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" | ||
88 | enum 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_* */ | ||
114 | extern const int rec_format_afmt[REC_NUM_FORMATS]; | ||
115 | /* get AFMT_* corresponding REC_FORMAT_* */ | ||
116 | extern 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 */ | ||
131 | struct 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 */ | ||
146 | extern const struct afmt_entry audio_formats[AFMT_NUM_CODECS]; | ||
147 | |||
148 | struct 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 | |||
231 | enum { | ||
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 | |||
239 | bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename); | ||
240 | bool mp3info(struct mp3entry *entry, const char *filename); | ||
241 | char* id3_get_num_genre(unsigned int genre_num); | ||
242 | int getid3v2len(int fd); | ||
243 | void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig); | ||
244 | void 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); | |||
163 | unsigned long mas_readver(void); | 163 | unsigned long mas_readver(void); |
164 | 164 | ||
165 | #endif | 165 | #endif |
166 | |||
167 | #if CONFIG_TUNER & S1A0903X01 | ||
168 | void mas_store_pllfreq(int freq); | ||
169 | int 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 | ||
51 | int mpeg_get_mas_pllfreq(void); | ||
52 | #endif | ||
53 | |||
54 | #endif | ||
55 | unsigned long mpeg_get_last_header(void); | ||
56 | |||
57 | /* in order to keep the recording here, I have to expose this */ | ||
58 | void rec_tick(void); | ||
59 | void playback_tick(void); /* FixMe: get rid of this, use mp3_get_playtime() */ | ||
60 | |||
61 | void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3)); | ||
62 | void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3)); | ||
63 | void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3)); | ||
64 | void 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 */ | ||
52 | void 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 */ | ||
55 | unsigned long pcm_rec_status(void); | ||
56 | unsigned long pcm_rec_get_warnings(void); | ||
57 | void pcm_rec_init(void); | ||
58 | int pcm_rec_current_bitrate(void); | ||
59 | int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */ | ||
60 | int pcm_rec_rec_format(void); /* Format index or -1 otherwise */ | ||
61 | unsigned long pcm_rec_sample_rate(void); | ||
62 | int 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 | |||
27 | long get_replaygain_int(long int_gain); | ||
28 | long parse_replaygain(const char* key, const char* value, | ||
29 | struct mp3entry* entry, char* buffer, int length); | ||
30 | long 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 */ | ||
79 | extern void playback_tick(void); | ||
80 | extern void rec_tick(void); | ||
81 | |||
79 | #ifndef SIMULATOR | 82 | #ifndef SIMULATOR |
80 | 83 | ||
81 | unsigned long mas_version_code; | 84 | unsigned 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 | |||
39 | extern struct thread_entry *codec_thread_p; | ||
40 | |||
41 | /** General recording state **/ | ||
42 | static bool is_recording; /* We are recording */ | ||
43 | static bool is_paused; /* We have paused */ | ||
44 | static unsigned long errors; /* An error has occured */ | ||
45 | static unsigned long warnings; /* Warning */ | ||
46 | static 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 */ | ||
56 | static inline void flush_interrupt(void) | ||
57 | { | ||
58 | flush_interrupts++; | ||
59 | logf("flush int: %d", flush_interrupts); | ||
60 | } | ||
61 | |||
62 | static 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 **/ | ||
69 | static size_t num_rec_bytes; /* Num bytes recorded */ | ||
70 | static 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 | ||
74 | static unsigned long long accum_rec_bytes; /* total size written to chunks */ | ||
75 | static 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 */ | ||
79 | static 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 **/ | ||
89 | static int rec_source; /* current rec_source setting */ | ||
90 | static int rec_frequency; /* current frequency setting */ | ||
91 | static unsigned long sample_rate; /* Sample rate in HZ */ | ||
92 | static int num_channels; /* Current number of channels */ | ||
93 | static int rec_mono_mode; /* how mono is created */ | ||
94 | static struct encoder_config enc_config; /* Current encoder configuration */ | ||
95 | static 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 | |||
140 | static size_t rec_buffer_size; /* size of available buffer */ | ||
141 | static unsigned char *pcm_buffer; /* circular recording buffer */ | ||
142 | static unsigned char *enc_buffer; /* circular encoding buffer */ | ||
143 | #ifdef DEBUG | ||
144 | static 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 */ | ||
148 | static volatile int dma_wr_pos; /* current DMA write pos */ | ||
149 | static int pcm_rd_pos; /* current PCM read pos */ | ||
150 | static int pcm_enc_pos; /* position encoder is processing */ | ||
151 | static volatile bool dma_lock; /* lock DMA write position */ | ||
152 | static int enc_wr_index; /* encoder chunk write index */ | ||
153 | static int enc_rd_index; /* encoder chunk read index */ | ||
154 | static int enc_num_chunks; /* number of chunks in ringbuffer */ | ||
155 | static size_t enc_chunk_size; /* maximum encoder chunk size */ | ||
156 | static unsigned long enc_sample_rate; /* sample rate used by encoder */ | ||
157 | static bool pcmrec_context = false; /* called by pcmrec thread? */ | ||
158 | static bool pcm_buffer_empty; /* all pcm chunks processed? */ | ||
159 | |||
160 | /** file flushing **/ | ||
161 | static int low_watermark; /* Low watermark to stop flush */ | ||
162 | static int high_watermark; /* max chunk limit for data flush */ | ||
163 | static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */ | ||
164 | static int last_ata_spinup_time = -1;/* previous spin time used */ | ||
165 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
166 | static 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 **/ | ||
184 | static 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 */ | ||
189 | static unsigned char *fn_queue; /* pointer to first filename */ | ||
190 | static ssize_t fnq_size; /* capacity of queue in bytes */ | ||
191 | static int fnq_rd_pos; /* current read position */ | ||
192 | static 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 | |||
204 | enum | ||
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 | |||
218 | static struct event_queue pcmrec_queue SHAREDBSS_ATTR; | ||
219 | static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR; | ||
220 | static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)]; | ||
221 | static const char pcmrec_thread_name[] = "pcmrec"; | ||
222 | static struct thread_entry *pcmrec_thread_p; | ||
223 | |||
224 | static void pcmrec_thread(void); | ||
225 | |||
226 | enum | ||
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 */ | ||
246 | static 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 | |||
276 | static 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 | */ | ||
289 | void pcm_rec_error_clear(void) | ||
290 | { | ||
291 | errors = warnings = 0; | ||
292 | } /* pcm_rec_error_clear */ | ||
293 | |||
294 | /** | ||
295 | * Check mode, errors and warnings | ||
296 | */ | ||
297 | unsigned 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 | */ | ||
321 | unsigned long pcm_rec_get_warnings(void) | ||
322 | { | ||
323 | return warnings; | ||
324 | } | ||
325 | |||
326 | #if 0 | ||
327 | int 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 | ||
337 | int pcm_rec_encoder_afmt(void) | ||
338 | { | ||
339 | return enc_config.afmt; | ||
340 | } /* pcm_rec_encoder_afmt */ | ||
341 | #endif | ||
342 | |||
343 | #if 0 | ||
344 | int 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 | ||
351 | unsigned 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 | */ | ||
364 | void 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 | */ | ||
380 | void 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 | */ | ||
391 | void 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 | */ | ||
401 | void 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 | */ | ||
411 | void 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 | */ | ||
422 | void audio_new_file(const char *filename) | ||
423 | { | ||
424 | audio_record(filename); | ||
425 | } /* audio_new_file */ | ||
426 | |||
427 | /** | ||
428 | * Stop current recording if recording | ||
429 | */ | ||
430 | void 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 | */ | ||
441 | void 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 | */ | ||
452 | void 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 | */ | ||
466 | void 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 | */ | ||
477 | unsigned 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 | */ | ||
490 | unsigned 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 */ | ||
507 | static 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 */ | ||
513 | static 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 */ | ||
519 | static 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 */ | ||
529 | static 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 */ | ||
543 | static 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 */ | ||
558 | static 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 */ | ||
571 | static 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 | */ | ||
588 | static 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 */ | ||
611 | static 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 | |||
617 | static 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 | |||
671 | static 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 | |||
693 | static 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 | */ | ||
731 | static 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 | */ | ||
791 | static 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 | */ | ||
952 | static 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 | |||
957 | static 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 | |||
963 | static 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 */ | ||
1110 | static 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 */ | ||
1161 | static 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 */ | ||
1172 | static 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) */ | ||
1234 | static 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 */ | ||
1344 | static 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 */ | ||
1397 | static 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 */ | ||
1419 | static 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 | |||
1441 | static void pcmrec_thread(void) __attribute__((noreturn)); | ||
1442 | static 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 */ | ||
1527 | void 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) */ | ||
1537 | void 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 */ | ||
1650 | struct 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 */ | ||
1672 | void 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 */ | ||
1720 | unsigned 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 */ | ||
1766 | size_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 |