summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/SOURCES3
-rw-r--r--apps/debug_menu.c6
-rw-r--r--apps/main.c6
-rw-r--r--apps/pcm_recording.c226
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/drivers/uda1380.c4
-rw-r--r--firmware/export/mcf5249.h5
-rw-r--r--firmware/export/pcm_record.h51
-rw-r--r--firmware/pcm_record.c650
9 files changed, 952 insertions, 2 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index d6ea2c9491..fb8acd75c6 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -51,4 +51,7 @@ recorder/recording.c
51#ifdef IRIVER_H100 51#ifdef IRIVER_H100
52playback.c 52playback.c
53metadata.c 53metadata.c
54#ifndef SIMULATOR
55pcm_recording.c
56#endif
54#endif 57#endif
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 71f730d2c5..7203eb820a 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -58,6 +58,9 @@
58#include "ata_mmc.h" 58#include "ata_mmc.h"
59#endif 59#endif
60#include "logfdisp.h" 60#include "logfdisp.h"
61#if defined(IRIVER_H100) && !defined(SIMULATOR)
62extern bool pcm_rec_screen(void);
63#endif
61 64
62/*---------------------------------------------------*/ 65/*---------------------------------------------------*/
63/* SPECIAL DEBUG STUFF */ 66/* SPECIAL DEBUG STUFF */
@@ -1800,6 +1803,9 @@ bool debug_menu(void)
1800#ifdef HAVE_ADJUSTABLE_CPU_FREQ 1803#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1801 { "CPU frequency", dbg_cpufreq }, 1804 { "CPU frequency", dbg_cpufreq },
1802#endif 1805#endif
1806#if defined(IRIVER_H100) && !defined(SIMULATOR)
1807 { "PCM recording", pcm_rec_screen },
1808#endif
1803#if CONFIG_CPU == SH7034 1809#if CONFIG_CPU == SH7034
1804#ifdef HAVE_LCD_BITMAP 1810#ifdef HAVE_LCD_BITMAP
1805#ifdef HAVE_RTC 1811#ifdef HAVE_RTC
diff --git a/apps/main.c b/apps/main.c
index 5a47e720e0..38851a5c7d 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -63,6 +63,9 @@
63#if (CONFIG_HWCODEC == MASNONE) 63#if (CONFIG_HWCODEC == MASNONE)
64#include "pcm_playback.h" 64#include "pcm_playback.h"
65#endif 65#endif
66#if defined(IRIVER_H100) && !defined(SIMULATOR)
67#include "pcm_record.h"
68#endif
66 69
67#ifdef CONFIG_TUNER 70#ifdef CONFIG_TUNER
68#include "radio.h" 71#include "radio.h"
@@ -293,6 +296,9 @@ void init(void)
293#if (CONFIG_HWCODEC == MASNONE) 296#if (CONFIG_HWCODEC == MASNONE)
294 pcm_init(); 297 pcm_init();
295#endif 298#endif
299#if defined(IRIVER_H100) && !defined(SIMULATOR)
300 pcm_init_recording();
301#endif
296#ifdef HAVE_CHARGING 302#ifdef HAVE_CHARGING
297 car_adapter_mode_init(); 303 car_adapter_mode_init();
298#endif 304#endif
diff --git a/apps/pcm_recording.c b/apps/pcm_recording.c
new file mode 100644
index 0000000000..dbaf4e0c2a
--- /dev/null
+++ b/apps/pcm_recording.c
@@ -0,0 +1,226 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include "config.h"
21
22#include <stdio.h>
23#include <stdbool.h>
24#include <stdlib.h>
25
26#include "system.h"
27#include "lcd.h"
28#include "led.h"
29#include "audio.h"
30#include "button.h"
31#include "kernel.h"
32#include "settings.h"
33#include "lang.h"
34#include "font.h"
35#include "icons.h"
36#include "screens.h"
37#include "status.h"
38#include "menu.h"
39#include "sound_menu.h"
40#include "timefuncs.h"
41#include "debug.h"
42#include "misc.h"
43#include "tree.h"
44#include "string.h"
45#include "dir.h"
46#include "errno.h"
47#include "atoi.h"
48#include "sound.h"
49#include "ata.h"
50#include "logf.h"
51#include "uda1380.h"
52#include "pcm_record.h"
53
54
55bool pcm_rec_screen(void)
56{
57 char buf[80];
58 char filename[MAX_PATH];
59 char *rec_sources[2] = {"Line-in","Mic"};
60 int line=0;
61 int play_vol;
62 int rec_monitor, rec_gain, rec_vol, rec_source, rec_count, rec_waveform;
63 int rec_time;
64 int done, button;
65 int w, h;
66
67 lcd_setfont(FONT_SYSFIXED);
68 lcd_getstringsize("M", &w, &h);
69 lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8);
70
71 play_vol = 120;
72
73 //cpu_boost(true);
74
75 uda1380_enable_output(true);
76 uda1380_setvol(play_vol, play_vol);
77
78 rec_monitor = 0; // No record feedback
79 rec_source = 1; // Mic
80 rec_gain = 0; // 0-15
81 rec_vol = 0; // 0-255
82 rec_count = 0;
83 rec_waveform = 0;
84
85 pcm_open_recording();
86 pcm_set_recording_options(rec_source, rec_waveform);
87 pcm_set_recording_gain(rec_gain, rec_vol);
88
89 //rec_create_directory();
90
91 done = 0;
92 while(!done)
93 {
94 line = 0;
95
96 snprintf(buf, sizeof(buf), "PlayVolume: %3d", play_vol);
97 lcd_puts(0,line++, buf);
98 snprintf(buf, sizeof(buf), "Gain : %2d Volume : %2d", rec_gain, rec_vol);
99 lcd_puts(0,line++, buf);
100 snprintf(buf, sizeof(buf), "Monitor: %2d Waveform: %2d", rec_monitor, rec_waveform);
101 lcd_puts(0,line++, buf);
102
103 line++;
104
105 rec_time = pcm_recorded_time();
106
107 snprintf(buf, sizeof(buf), "Status: %s", pcm_status() & AUDIO_STATUS_RECORD ? "RUNNING" : "STOPPED");
108 lcd_puts(0,line++, buf);
109 snprintf(buf, sizeof(buf), "Source: %s", rec_sources[rec_source]);
110 lcd_puts(0,line++, buf);
111 snprintf(buf, sizeof(buf), "File : %s", filename);
112 lcd_puts(0,line++, buf);
113 snprintf(buf, sizeof(buf), "Time : %02d:%02d.%02d", rec_time/HZ/60, (rec_time/HZ)%60, rec_time%HZ);
114 lcd_puts(0,line++, buf);
115
116 line++;
117
118 snprintf(buf, sizeof(buf), "MODE : Select source");
119 lcd_puts(0,line++, buf);
120 snprintf(buf, sizeof(buf), "UP/DOWN : Record volume");
121 lcd_puts(0,line++, buf);
122 snprintf(buf, sizeof(buf), "LFT/RGHT: Record gain");
123 lcd_puts(0,line++, buf);
124 snprintf(buf, sizeof(buf), "RMT MENU: Toggle monitor");
125 lcd_puts(0,line++, buf);
126 snprintf(buf, sizeof(buf), "RMT PLAY: Toggle waveform");
127 lcd_puts(0,line++, buf);
128
129
130 lcd_update();
131
132
133 button = button_get_w_tmo(HZ/8);
134 switch (button)
135 {
136 case BUTTON_OFF:
137 done = true;
138 break;
139
140 case BUTTON_REC:
141 if (pcm_status() & AUDIO_STATUS_RECORD)
142 {
143 pcm_stop_recording();
144
145 } else
146 {
147 snprintf(filename, MAX_PATH, "/record-%0d.wav", rec_count++);
148 pcm_record(filename);
149 }
150 break;
151
152 case BUTTON_ON:
153 break;
154
155 case BUTTON_MODE:
156 rec_source = 1 - rec_source;
157 pcm_set_recording_options(rec_source, rec_waveform);
158 break;
159
160 case BUTTON_RIGHT:
161 case BUTTON_RIGHT | BUTTON_REPEAT:
162 if (rec_gain < 15)
163 rec_gain++;
164
165 pcm_set_recording_gain(rec_gain, rec_vol);
166 break;
167
168 case BUTTON_LEFT:
169 case BUTTON_LEFT | BUTTON_REPEAT:
170 if (rec_gain > 0)
171 rec_gain--;
172
173 pcm_set_recording_gain(rec_gain, rec_vol);
174 break;
175
176 case BUTTON_RC_MENU:
177 rec_monitor = 1 - rec_monitor;
178 uda1380_set_monitor(rec_monitor);
179 break;
180
181 case BUTTON_RC_ON:
182 rec_waveform = 1 - rec_waveform;
183 pcm_set_recording_options(rec_source, rec_waveform);
184 break;
185
186 case BUTTON_UP:
187 case BUTTON_UP | BUTTON_REPEAT:
188 if (rec_vol<255)
189 rec_vol++;
190 pcm_set_recording_gain(rec_gain, rec_vol);
191 break;
192
193 case BUTTON_DOWN:
194 case BUTTON_DOWN | BUTTON_REPEAT:
195 if (rec_vol)
196 rec_vol--;
197 pcm_set_recording_gain(rec_gain, rec_vol);
198 break;
199
200 case SYS_USB_CONNECTED:
201 if (pcm_status() & AUDIO_STATUS_RECORD)
202 {
203 // ignore usb while recording
204 } else
205 {
206 pcm_stop_recording();
207
208 uda1380_enable_output(false);
209
210 default_event_handler(SYS_USB_CONNECTED);
211 return false;
212 }
213 break;
214
215 }
216
217 }
218
219 pcm_stop_recording();
220 pcm_close_recording();
221
222 uda1380_enable_output(false);
223
224 return true;
225}
226
diff --git a/firmware/SOURCES b/firmware/SOURCES
index dbe84267d2..f0a3501903 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -126,4 +126,7 @@ drivers/uda1380.c
126#if (CONFIG_HWCODEC == MASNONE) && !defined(SIMULATOR) 126#if (CONFIG_HWCODEC == MASNONE) && !defined(SIMULATOR)
127pcm_playback.c 127pcm_playback.c
128#endif 128#endif
129#if defined(IRIVER_H100) && !defined(SIMULATOR)
130pcm_record.c
131#endif
129sound.c 132sound.c
diff --git a/firmware/drivers/uda1380.c b/firmware/drivers/uda1380.c
index 23d917f8ae..bba7d19d04 100644
--- a/firmware/drivers/uda1380.c
+++ b/firmware/drivers/uda1380.c
@@ -200,7 +200,7 @@ void uda1380_close(void)
200 */ 200 */
201void uda1380_enable_recording(bool source_mic) 201void uda1380_enable_recording(bool source_mic)
202{ 202{
203 uda1380_write_reg(REG_0, uda1380_regs[REG_0] | EN_ADC); 203 uda1380_write_reg(REG_0, uda1380_regs[REG_0] | EN_ADC);
204 204
205 if (source_mic) 205 if (source_mic)
206 { 206 {
@@ -214,6 +214,8 @@ void uda1380_enable_recording(bool source_mic)
214 uda1380_write_reg(REG_PGA, (uda1380_regs[REG_PGA] & PGA_GAIN_MASK) | PGA_GAINL(0) | PGA_GAINR(0)); /* PGA_GAIN: 0=0 dB, F=24dB */ 214 uda1380_write_reg(REG_PGA, (uda1380_regs[REG_PGA] & PGA_GAIN_MASK) | PGA_GAINL(0) | PGA_GAINR(0)); /* PGA_GAIN: 0=0 dB, F=24dB */
215 } 215 }
216 216
217 sleep(HZ/8);
218
217 uda1380_write_reg(REG_I2S, uda1380_regs[REG_I2S] | I2S_MODE_MASTER); 219 uda1380_write_reg(REG_I2S, uda1380_regs[REG_I2S] | I2S_MODE_MASTER);
218 uda1380_write_reg(REG_MIX_CTL, MIX_MODE(3)); /* Not sure which mode is the best one.. */ 220 uda1380_write_reg(REG_MIX_CTL, MIX_MODE(3)); /* Not sure which mode is the best one.. */
219 221
diff --git a/firmware/export/mcf5249.h b/firmware/export/mcf5249.h
index 398b31156d..66e92eeb50 100644
--- a/firmware/export/mcf5249.h
+++ b/firmware/export/mcf5249.h
@@ -159,7 +159,7 @@
159#define PDOR1_R (*(volatile unsigned long *)(MBAR2 + 0x044)) 159#define PDOR1_R (*(volatile unsigned long *)(MBAR2 + 0x044))
160#define PDOR2_L (*(volatile unsigned long *)(MBAR2 + 0x054)) 160#define PDOR2_L (*(volatile unsigned long *)(MBAR2 + 0x054))
161#define PDOR2_R (*(volatile unsigned long *)(MBAR2 + 0x064)) 161#define PDOR2_R (*(volatile unsigned long *)(MBAR2 + 0x064))
162#define PDIR3 (*(volatile unsigned long *)(MBAR2 + 0x074)) 162#define PDIR2 (*(volatile unsigned long *)(MBAR2 + 0x074))
163#define PDOR3 (*(volatile unsigned long *)(MBAR2 + 0x074)) 163#define PDOR3 (*(volatile unsigned long *)(MBAR2 + 0x074))
164#define UCHANNELTRANSMIT (*(volatile unsigned long *)(MBAR2 + 0x084)) 164#define UCHANNELTRANSMIT (*(volatile unsigned long *)(MBAR2 + 0x084))
165#define U1CHANNELRECEIVE (*(volatile unsigned long *)(MBAR2 + 0x088)) 165#define U1CHANNELRECEIVE (*(volatile unsigned long *)(MBAR2 + 0x088))
@@ -262,5 +262,8 @@
262/* DMAROUTE config */ 262/* DMAROUTE config */
263#define DMA0_REQ_AUDIO_1 0x80 263#define DMA0_REQ_AUDIO_1 0x80
264#define DMA0_REQ_AUDIO_2 0x81 264#define DMA0_REQ_AUDIO_2 0x81
265#define DMA1_REQ_AUDIO_1 0x8000
266#define DMA1_REQ_AUDIO_2 0x8100
267
265 268
266#endif 269#endif
diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h
new file mode 100644
index 0000000000..964d3a003e
--- /dev/null
+++ b/firmware/export/pcm_record.h
@@ -0,0 +1,51 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/*
21 * Function names are taken from apps/recorder/recording.c to
22 * make the integration later easier..
23 *
24 */
25
26#ifndef PCM_RECORD_H
27#define PCM_RECORD_H
28
29unsigned long pcm_status(void);
30
31void pcm_init_recording(void);
32
33void pcm_open_recording(void);
34void pcm_close_recording(void);
35
36
37void pcm_set_recording_options(int source, bool enable_waveform);
38void pcm_set_recording_gain(int gain, int volume);
39
40void pcm_record(const char *filename);
41void pcm_stop_recording(void);
42
43//void pcm_new_file(const char *filename);
44
45
46unsigned long pcm_recorded_time(void);
47unsigned long pcm_num_recorded_bytes(void);
48void pcm_pause_recording(void);
49void pcm_resume_recording(void);
50
51#endif
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c
new file mode 100644
index 0000000000..d11458427d
--- /dev/null
+++ b/firmware/pcm_record.c
@@ -0,0 +1,650 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include "config.h"
21#include "debug.h"
22#include "panic.h"
23#include "thread.h"
24
25#include <kernel.h>
26#include <stdio.h>
27#include <string.h>
28#include <stdarg.h>
29#include <string.h>
30
31#include "cpu.h"
32#include "i2c.h"
33#include "uda1380.h"
34#include "system.h"
35#include "usb.h"
36
37#include "buffer.h"
38#include "audio.h"
39#include "button.h"
40#include "file.h"
41#include "sprintf.h"
42#include "logf.h"
43#include "button.h"
44#include "lcd.h"
45#include "lcd-remote.h"
46#include "pcm_playback.h"
47#include "pcm_record.h"
48
49
50/***************************************************************************/
51
52static volatile bool is_recording; /* We are recording */
53static volatile bool is_stopping; /* Are we going to stop */
54static volatile bool is_paused; /* We have paused */
55
56static volatile int num_rec_bytes;
57static volatile int int_count; /* Number of DMA completed interrupts */
58static volatile int error_count; /* Number of DMA errors */
59
60static unsigned long record_start_time; /* Value of current_tick when recording was started */
61static unsigned long pause_start_time; /* Value of current_tick when pause was started */
62
63static int rec_gain, rec_volume;
64static bool show_waveform;
65static int init_done = 0;
66static int wav_file;
67static char recording_filename[MAX_PATH];
68
69/***************************************************************************/
70
71/*
72 Some estimates:
73 44100 HZ * 4 = 176400 bytes/s
74 Refresh LCD 10 HZ = 176400 / 10 = 17640 bytes ~=~ 1024*16 bytes
75
76 If NUM_BUFFERS is 80 we can hold ~8 sec of data in memory
77 ALL_BUFFER_SIZE will be 1024*16 * 80 = 1310720 bytes
78*/
79
80#define NUM_BUFFERS 80
81#define EACH_BUFFER_SIZE (1024*16) /* Multiple of 4. Use small value to get responsive waveform */
82#define ALL_BUFFERS_SIZE (NUM_BUFFERS * EACH_BUFFER_SIZE)
83
84#define WRITE_THRESHOLD 40 /* Minimum number of buffers before write to file */
85
86static unsigned char *rec_buffers[NUM_BUFFERS];
87
88/*
89 Overrun occures when DMA needs to write a new buffer and write_index == read_index
90 Solution to this is to optimize pcmrec_callback, use cpu_boost somewhere or increase
91 the total buffer size (or WRITE_THRESHOLD)
92*/
93
94static int write_index; /* Which buffer the DMA is currently recording */
95static int read_index; /* The oldest buffer that the pcmrec_callback has not read */
96
97/***************************************************************************/
98
99static struct event_queue pcmrec_queue;
100static long pcmrec_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
101static const char pcmrec_thread_name[] = "pcmrec";
102
103static void pcmrec_thread(void);
104
105/* Event IDs */
106#define PCMREC_OPEN 1 /* Enable recording */
107#define PCMREC_CLOSE 2 /* Disable recording */
108#define PCMREC_START 3 /* Start a new recording */
109#define PCMREC_STOP 4 /* Stop the current recording */
110#define PCMREC_PAUSE 10
111#define PCMREC_RESUME 11
112#define PCMREC_NEW_FILE 12
113#define PCMREC_SET_GAIN 13
114#define PCMREC_GOT_DATA 20 /* DMA1 notifies when data has arrived */
115
116
117/*******************************************************************/
118/* Functions that are not executing in the pcmrec_thread first */
119/*******************************************************************/
120
121void pcm_init_recording(void)
122{
123 int_count = 0;
124 error_count = 0;
125
126 show_waveform = 0;
127 is_recording = 0;
128 is_stopping = 0;
129 num_rec_bytes = 0;
130 wav_file = -1;
131 read_index = 0;
132 write_index = 0;
133
134 queue_init(&pcmrec_queue);
135 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name);
136}
137
138void pcm_open_recording(void)
139{
140 init_done = 0;
141
142 logf("pcm_open_rec");
143
144 queue_post(&pcmrec_queue, PCMREC_OPEN, 0);
145
146 while (init_done)
147 {
148 sleep(HZ >> 8);
149 }
150
151 logf("pcm_open_rec done");
152}
153
154void pcm_close_recording(void)
155{
156 /* todo: synchronize completion with pcmrec thread */
157 queue_post(&pcmrec_queue, PCMREC_CLOSE, 0);
158}
159
160
161
162unsigned long pcm_status(void)
163{
164 unsigned long ret = 0;
165
166 if (is_recording)
167 ret |= AUDIO_STATUS_RECORD;
168
169 return ret;
170}
171
172
173
174void pcm_new_file(const char *filename)
175{
176 /* todo */
177 filename = filename;
178
179}
180
181unsigned long pcm_recorded_time(void)
182{
183 if (is_recording)
184 {
185 if(is_paused)
186 return pause_start_time - record_start_time;
187 else
188 return current_tick - record_start_time;
189 }
190
191 return 0;
192}
193
194unsigned long pcm_num_recorded_bytes(void)
195{
196
197 if (is_recording)
198 {
199 return num_rec_bytes;
200 }
201 else
202 return 0;
203}
204
205void pcm_pause_recording(void)
206{
207 /* todo */
208}
209
210void pcm_resume_recording(void)
211{
212 /* todo */
213}
214
215
216/**
217 * Sets the audio source
218 *
219 * Side effect: This functions starts feeding the CPU with audio data over the I2S bus
220 *
221 * @param source 0=line-in, 1=mic
222 */
223void pcm_set_recording_options(int source, bool enable_waveform)
224{
225 uda1380_enable_recording(source);
226
227 show_waveform = enable_waveform;
228
229}
230
231
232/**
233 *
234 * @param gain line-in and microphone gain (0-15)
235 * @param volume ADC volume (0-255)
236 */
237void pcm_set_recording_gain(int gain, int volume)
238{
239 rec_gain = gain;
240 rec_volume = volume;
241
242 queue_post(&pcmrec_queue, PCMREC_SET_GAIN, 0);
243
244}
245
246/**
247 * Start recording
248 *
249 * Use pcm_set_recording_options before calling record
250 */
251void pcm_record(const char *filename)
252{
253 strncpy(recording_filename, filename, MAX_PATH - 1);
254 recording_filename[MAX_PATH - 1] = 0;
255
256 queue_post(&pcmrec_queue, PCMREC_START, 0);
257}
258
259/**
260 *
261 */
262void pcm_stop_recording(void)
263{
264 if (is_recording)
265 is_stopping = 1;
266
267 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
268
269 logf("pcm_stop_recording");
270
271 while (is_stopping)
272 {
273 sleep(HZ >> 4);
274 }
275
276 logf("pcm_stop_recording done");
277
278
279}
280
281
282/***************************************************************************/
283/* Functions that executes in the context of pcmrec_thread */
284/***************************************************************************/
285
286
287/**
288 * Process the buffers using read_index and write_index.
289 *
290 * DMA1 handler posts to pcmrec_queue so that pcmrec_thread calls this
291 * function. Also pcmrec_stop will call this function when the recording
292 * is stopping, and that call will have flush = true.
293 *
294 */
295
296void pcmrec_callback(bool flush) __attribute__ ((section (".icode")));
297void pcmrec_callback(bool flush)
298{
299 int num_ready;
300
301 num_ready = write_index - read_index;
302 if (num_ready < 0)
303 num_ready += NUM_BUFFERS;
304
305 /* we can consume up to num_ready buffers */
306
307#ifdef HAVE_REMOTE_LCD
308 /* Draw waveform on remote LCD */
309 if (show_waveform && num_ready>0)
310 {
311 short *buf;
312 long x,y,offset;
313 int show_index;
314
315 /* Just display the last buffer (most recent one) */
316 show_index = read_index + num_ready - 1;
317 buf = (short*)rec_buffers[show_index];
318
319 lcd_remote_clear_display();
320
321 offset = 0;
322 for (x=0; x<LCD_REMOTE_WIDTH-1; x++)
323 {
324 y = buf[offset] * (LCD_REMOTE_HEIGHT / 2) *5; /* The 5 is just 'zooming' */
325 y = y >> 15; /* Divide with SHRT_MAX */
326 y += LCD_REMOTE_HEIGHT/2;
327
328 if (y < 2) y=2;
329 if (y >= LCD_REMOTE_HEIGHT-2) y = LCD_REMOTE_HEIGHT-2;
330
331 REMOTE_DRAW_PIXEL(x,y);
332
333 offset += (EACH_BUFFER_SIZE/2) / LCD_REMOTE_WIDTH;
334 }
335
336 lcd_remote_update();
337 }
338
339#endif
340
341 /* Note: This might be a good place to call the 'codec' later */
342
343 /* Check that we have the minimum amount of data to save or */
344 /* that if it's closing time which mean we have to save.. */
345 if (wav_file != -1)
346 {
347 if (num_ready >= WRITE_THRESHOLD || flush)
348 {
349 unsigned long *ptr = (unsigned long*)rec_buffers[read_index];
350 int i;
351
352 for (i=0; i<EACH_BUFFER_SIZE * num_ready / 4; i++)
353 {
354 *ptr = SWAB32(*ptr);
355 ptr++;
356 }
357
358 write(wav_file, rec_buffers[read_index], EACH_BUFFER_SIZE * num_ready);
359
360 read_index+=num_ready;
361 if (read_index >= NUM_BUFFERS)
362 read_index -= NUM_BUFFERS;
363 }
364
365 } else
366 {
367 /* In this case we must consume the buffers otherwise we will */
368 /* get 'dma1 overrun' pretty fast */
369
370 read_index+=num_ready;
371 if (read_index >= NUM_BUFFERS)
372 read_index -= NUM_BUFFERS;
373 }
374}
375
376
377
378
379void pcmrec_dma_start(void)
380{
381 DAR1 = (unsigned long)rec_buffers[write_index++]; /* Destination address */
382 SAR1 = (unsigned long)&PDIR2; /* Source address */
383 BCR1 = EACH_BUFFER_SIZE; /* Bytes to transfer */
384
385 /* Start the DMA transfer.. */
386 DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START;
387
388 logf("dma1 started");
389}
390
391
392/* DMA1 Interrupt is called when the DMA has finished transfering a chunk */
393void DMA1(void) __attribute__ ((interrupt_handler, section(".icode")));
394void DMA1(void)
395{
396 int res = DSR1;
397
398 DSR1 = 1; /* Clear interrupt */
399
400 int_count++;
401
402 if (res & 0x70)
403 {
404 DCR1 = 0; /* Stop DMA transfer */
405 error_count++;
406 is_recording = 0;
407
408 logf("dma1 err 0x%x", res);
409
410 } else
411 {
412 num_rec_bytes += EACH_BUFFER_SIZE;
413
414 write_index++;
415 if (write_index >= NUM_BUFFERS)
416 write_index = 0;
417
418 if (is_stopping || !is_recording)
419 {
420 DCR1 = 0; /* Stop DMA transfer */
421 is_recording = 0;
422
423 logf("dma1 stopping");
424
425 } else if (write_index == read_index)
426 {
427 DCR1 = 0; /* Stop DMA transfer */
428 is_recording = 0;
429
430 logf("dma1 overrun");
431
432 } else
433 {
434 DAR1 = (unsigned long)rec_buffers[write_index]; /* Destination address */
435 BCR1 = EACH_BUFFER_SIZE;
436
437 queue_post(&pcmrec_queue, PCMREC_GOT_DATA, NULL);
438
439 }
440 }
441
442 IPR |= (1<<15); /* Clear pending interrupt request */
443}
444
445static int start_wave(void)
446{
447 unsigned char header[44] =
448 {
449 'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',
450 0x10,0,0,0,1,0,2,0,0x44,0xac,0,0,0x10,0xb1,2,0,
451 4,0,0x10,0,'d','a','t','a',0,0,0,0
452 };
453
454 wav_file = open(recording_filename, O_RDWR|O_CREAT|O_TRUNC);
455 if (wav_file < 0)
456 {
457 wav_file = -1;
458 logf("create failed: %d", wav_file);
459 return -1;
460 }
461
462 if (sizeof(header) != write(wav_file, header, sizeof(header)))
463 {
464 close(wav_file);
465 wav_file = -1;
466 logf("write failed");
467 return -2;
468 }
469
470 return 0;
471}
472
473/* Update header and set correct length values */
474static void close_wave(void)
475{
476 long l;
477
478 l = SWAB32(num_rec_bytes + 36);
479 lseek(wav_file, 4, SEEK_SET);
480 write(wav_file, &l, 4);
481
482 l = SWAB32(num_rec_bytes);
483 lseek(wav_file, 40, SEEK_SET);
484 write(wav_file, &l, 4);
485
486 close(wav_file);
487 wav_file = -1;
488}
489
490static void pcmrec_start(void)
491{
492 logf("pcmrec_start");
493
494 if (is_recording)
495 return;
496
497 if (wav_file != -1)
498 close(wav_file);
499
500 logf("rec: %s", recording_filename);
501
502 start_wave(); /* todo: send signal to pcm_record if we have failed */
503
504 num_rec_bytes = 0;
505
506 /* Store the current time */
507 record_start_time = current_tick;
508
509 write_index = 0;
510 read_index = 0;
511
512 is_stopping = 0;
513 is_paused = 0;
514 is_recording = 1;
515
516 pcmrec_dma_start();
517
518}
519
520static void pcmrec_stop(void)
521{
522 /* wait for recording to finish */
523
524 /* todo: Abort current DMA transfer using DCR1.. */
525
526 logf("pcmrec_stop");
527
528 while (is_recording)
529 {
530 sleep(HZ >> 4);
531 }
532
533 logf("pcmrec_stop done");
534
535 /* Write unfinished buffers to file */
536 pcmrec_callback(true);
537
538 close_wave();
539
540 is_stopping = 0;
541}
542
543static void pcmrec_open(void)
544{
545 unsigned long buffer_start;
546 int i;
547
548 show_waveform = 0;
549 is_recording = 0;
550 is_stopping = 0;
551 num_rec_bytes = 0;
552 wav_file = -1;
553 read_index = 0;
554 write_index = 0;
555
556 buffer_start = (unsigned long)(&audiobuf[(audiobufend - audiobuf) - (ALL_BUFFERS_SIZE + 16)]);
557 buffer_start &= ~3;
558
559 for (i=0; i<NUM_BUFFERS; i++)
560 {
561 rec_buffers[i] = (unsigned char*)(buffer_start + EACH_BUFFER_SIZE * i);
562 }
563
564 IIS1CONFIG = 0x800; /* Stop any playback */
565 AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */
566 DATAINCONTROL = 0xc000; /* Generate Interrupt when 6 samples in fifo */
567 DATAINCONTROL |= 0x20; /* PDIR2 source = IIS1recv */
568
569 DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
570 DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
571 DMAROUTE = (DMAROUTE & 0xffff00ff) | DMA1_REQ_AUDIO_2;
572 ICR4 = (ICR4 & 0xffffff00) | 0x0000001c; /* Enable interrupt at level 7, priority 0 */
573 IMR &= ~(1<<15); /* bit 15 is DMA1 */
574
575 init_done = 1;
576}
577
578static void pcmrec_close(void)
579{
580 uda1380_disable_recording();
581
582 DMAROUTE = (DMAROUTE & 0xffff00ff);
583 ICR4 = (ICR4 & 0xffffff00); /* Disable interrupt */
584 IMR |= (1<<15); /* bit 15 is DMA1 */
585
586}
587
588static void pcmrec_thread(void)
589{
590 struct event ev;
591
592 logf("thread pcmrec start");
593
594 while (1)
595 {
596 queue_wait(&pcmrec_queue, &ev);
597
598 switch (ev.id)
599 {
600 case PCMREC_OPEN:
601 pcmrec_open();
602 break;
603
604 case PCMREC_CLOSE:
605 pcmrec_close();
606 break;
607
608 case PCMREC_START:
609 pcmrec_start();
610 break;
611
612 case PCMREC_STOP:
613 pcmrec_stop();
614 break;
615
616 case PCMREC_PAUSE:
617 /* todo */
618 break;
619
620 case PCMREC_RESUME:
621 /* todo */
622 break;
623
624 case PCMREC_NEW_FILE:
625 /* todo */
626 break;
627
628 case PCMREC_SET_GAIN:
629 uda1380_set_recvol(rec_gain, rec_gain, rec_volume);
630 break;
631
632 case PCMREC_GOT_DATA:
633 pcmrec_callback(false);
634 break;
635
636 case SYS_USB_CONNECTED:
637 if (!is_recording && !is_stopping)
638 {
639 usb_acknowledge(SYS_USB_CONNECTED_ACK);
640 usb_wait_for_disconnect(&pcmrec_queue);
641 }
642
643 break;
644 }
645 }
646
647 logf("thread pcmrec done");
648}
649
650