From e5d08722f8e1a46ba73af7a6ea7a09833799a25f Mon Sep 17 00:00:00 2001 From: Andy Date: Sun, 19 Jun 2005 03:05:53 +0000 Subject: Iriver: First attempt at recording. Use Info->Debug->PCM recording to test recording of wav-files. Seams to work fine except occasional 100 ms noise at pos 100 ms (not later) so initialization or synch problem.. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6763 a1c6a512-1295-4272-9138-f99709370657 --- apps/SOURCES | 3 + apps/debug_menu.c | 6 + apps/main.c | 6 + apps/pcm_recording.c | 226 +++++++++++++++ firmware/SOURCES | 3 + firmware/drivers/uda1380.c | 4 +- firmware/export/mcf5249.h | 5 +- firmware/export/pcm_record.h | 51 ++++ firmware/pcm_record.c | 650 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 952 insertions(+), 2 deletions(-) create mode 100644 apps/pcm_recording.c create mode 100644 firmware/export/pcm_record.h create mode 100644 firmware/pcm_record.c 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 #ifdef IRIVER_H100 playback.c metadata.c +#ifndef SIMULATOR +pcm_recording.c +#endif #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 @@ #include "ata_mmc.h" #endif #include "logfdisp.h" +#if defined(IRIVER_H100) && !defined(SIMULATOR) +extern bool pcm_rec_screen(void); +#endif /*---------------------------------------------------*/ /* SPECIAL DEBUG STUFF */ @@ -1800,6 +1803,9 @@ bool debug_menu(void) #ifdef HAVE_ADJUSTABLE_CPU_FREQ { "CPU frequency", dbg_cpufreq }, #endif +#if defined(IRIVER_H100) && !defined(SIMULATOR) + { "PCM recording", pcm_rec_screen }, +#endif #if CONFIG_CPU == SH7034 #ifdef HAVE_LCD_BITMAP #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 @@ #if (CONFIG_HWCODEC == MASNONE) #include "pcm_playback.h" #endif +#if defined(IRIVER_H100) && !defined(SIMULATOR) +#include "pcm_record.h" +#endif #ifdef CONFIG_TUNER #include "radio.h" @@ -293,6 +296,9 @@ void init(void) #if (CONFIG_HWCODEC == MASNONE) pcm_init(); #endif +#if defined(IRIVER_H100) && !defined(SIMULATOR) + pcm_init_recording(); +#endif #ifdef HAVE_CHARGING car_adapter_mode_init(); #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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include "system.h" +#include "lcd.h" +#include "led.h" +#include "audio.h" +#include "button.h" +#include "kernel.h" +#include "settings.h" +#include "lang.h" +#include "font.h" +#include "icons.h" +#include "screens.h" +#include "status.h" +#include "menu.h" +#include "sound_menu.h" +#include "timefuncs.h" +#include "debug.h" +#include "misc.h" +#include "tree.h" +#include "string.h" +#include "dir.h" +#include "errno.h" +#include "atoi.h" +#include "sound.h" +#include "ata.h" +#include "logf.h" +#include "uda1380.h" +#include "pcm_record.h" + + +bool pcm_rec_screen(void) +{ + char buf[80]; + char filename[MAX_PATH]; + char *rec_sources[2] = {"Line-in","Mic"}; + int line=0; + int play_vol; + int rec_monitor, rec_gain, rec_vol, rec_source, rec_count, rec_waveform; + int rec_time; + int done, button; + int w, h; + + lcd_setfont(FONT_SYSFIXED); + lcd_getstringsize("M", &w, &h); + lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8); + + play_vol = 120; + + //cpu_boost(true); + + uda1380_enable_output(true); + uda1380_setvol(play_vol, play_vol); + + rec_monitor = 0; // No record feedback + rec_source = 1; // Mic + rec_gain = 0; // 0-15 + rec_vol = 0; // 0-255 + rec_count = 0; + rec_waveform = 0; + + pcm_open_recording(); + pcm_set_recording_options(rec_source, rec_waveform); + pcm_set_recording_gain(rec_gain, rec_vol); + + //rec_create_directory(); + + done = 0; + while(!done) + { + line = 0; + + snprintf(buf, sizeof(buf), "PlayVolume: %3d", play_vol); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "Gain : %2d Volume : %2d", rec_gain, rec_vol); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "Monitor: %2d Waveform: %2d", rec_monitor, rec_waveform); + lcd_puts(0,line++, buf); + + line++; + + rec_time = pcm_recorded_time(); + + snprintf(buf, sizeof(buf), "Status: %s", pcm_status() & AUDIO_STATUS_RECORD ? "RUNNING" : "STOPPED"); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "Source: %s", rec_sources[rec_source]); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "File : %s", filename); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "Time : %02d:%02d.%02d", rec_time/HZ/60, (rec_time/HZ)%60, rec_time%HZ); + lcd_puts(0,line++, buf); + + line++; + + snprintf(buf, sizeof(buf), "MODE : Select source"); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "UP/DOWN : Record volume"); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "LFT/RGHT: Record gain"); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "RMT MENU: Toggle monitor"); + lcd_puts(0,line++, buf); + snprintf(buf, sizeof(buf), "RMT PLAY: Toggle waveform"); + lcd_puts(0,line++, buf); + + + lcd_update(); + + + button = button_get_w_tmo(HZ/8); + switch (button) + { + case BUTTON_OFF: + done = true; + break; + + case BUTTON_REC: + if (pcm_status() & AUDIO_STATUS_RECORD) + { + pcm_stop_recording(); + + } else + { + snprintf(filename, MAX_PATH, "/record-%0d.wav", rec_count++); + pcm_record(filename); + } + break; + + case BUTTON_ON: + break; + + case BUTTON_MODE: + rec_source = 1 - rec_source; + pcm_set_recording_options(rec_source, rec_waveform); + break; + + case BUTTON_RIGHT: + case BUTTON_RIGHT | BUTTON_REPEAT: + if (rec_gain < 15) + rec_gain++; + + pcm_set_recording_gain(rec_gain, rec_vol); + break; + + case BUTTON_LEFT: + case BUTTON_LEFT | BUTTON_REPEAT: + if (rec_gain > 0) + rec_gain--; + + pcm_set_recording_gain(rec_gain, rec_vol); + break; + + case BUTTON_RC_MENU: + rec_monitor = 1 - rec_monitor; + uda1380_set_monitor(rec_monitor); + break; + + case BUTTON_RC_ON: + rec_waveform = 1 - rec_waveform; + pcm_set_recording_options(rec_source, rec_waveform); + break; + + case BUTTON_UP: + case BUTTON_UP | BUTTON_REPEAT: + if (rec_vol<255) + rec_vol++; + pcm_set_recording_gain(rec_gain, rec_vol); + break; + + case BUTTON_DOWN: + case BUTTON_DOWN | BUTTON_REPEAT: + if (rec_vol) + rec_vol--; + pcm_set_recording_gain(rec_gain, rec_vol); + break; + + case SYS_USB_CONNECTED: + if (pcm_status() & AUDIO_STATUS_RECORD) + { + // ignore usb while recording + } else + { + pcm_stop_recording(); + + uda1380_enable_output(false); + + default_event_handler(SYS_USB_CONNECTED); + return false; + } + break; + + } + + } + + pcm_stop_recording(); + pcm_close_recording(); + + uda1380_enable_output(false); + + return true; +} + 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 #if (CONFIG_HWCODEC == MASNONE) && !defined(SIMULATOR) pcm_playback.c #endif +#if defined(IRIVER_H100) && !defined(SIMULATOR) +pcm_record.c +#endif sound.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) */ void uda1380_enable_recording(bool source_mic) { - uda1380_write_reg(REG_0, uda1380_regs[REG_0] | EN_ADC); + uda1380_write_reg(REG_0, uda1380_regs[REG_0] | EN_ADC); if (source_mic) { @@ -214,6 +214,8 @@ void uda1380_enable_recording(bool source_mic) 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 */ } + sleep(HZ/8); + uda1380_write_reg(REG_I2S, uda1380_regs[REG_I2S] | I2S_MODE_MASTER); uda1380_write_reg(REG_MIX_CTL, MIX_MODE(3)); /* Not sure which mode is the best one.. */ 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 @@ #define PDOR1_R (*(volatile unsigned long *)(MBAR2 + 0x044)) #define PDOR2_L (*(volatile unsigned long *)(MBAR2 + 0x054)) #define PDOR2_R (*(volatile unsigned long *)(MBAR2 + 0x064)) -#define PDIR3 (*(volatile unsigned long *)(MBAR2 + 0x074)) +#define PDIR2 (*(volatile unsigned long *)(MBAR2 + 0x074)) #define PDOR3 (*(volatile unsigned long *)(MBAR2 + 0x074)) #define UCHANNELTRANSMIT (*(volatile unsigned long *)(MBAR2 + 0x084)) #define U1CHANNELRECEIVE (*(volatile unsigned long *)(MBAR2 + 0x088)) @@ -262,5 +262,8 @@ /* DMAROUTE config */ #define DMA0_REQ_AUDIO_1 0x80 #define DMA0_REQ_AUDIO_2 0x81 +#define DMA1_REQ_AUDIO_1 0x8000 +#define DMA1_REQ_AUDIO_2 0x8100 + #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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* + * Function names are taken from apps/recorder/recording.c to + * make the integration later easier.. + * + */ + +#ifndef PCM_RECORD_H +#define PCM_RECORD_H + +unsigned long pcm_status(void); + +void pcm_init_recording(void); + +void pcm_open_recording(void); +void pcm_close_recording(void); + + +void pcm_set_recording_options(int source, bool enable_waveform); +void pcm_set_recording_gain(int gain, int volume); + +void pcm_record(const char *filename); +void pcm_stop_recording(void); + +//void pcm_new_file(const char *filename); + + +unsigned long pcm_recorded_time(void); +unsigned long pcm_num_recorded_bytes(void); +void pcm_pause_recording(void); +void pcm_resume_recording(void); + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "debug.h" +#include "panic.h" +#include "thread.h" + +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "i2c.h" +#include "uda1380.h" +#include "system.h" +#include "usb.h" + +#include "buffer.h" +#include "audio.h" +#include "button.h" +#include "file.h" +#include "sprintf.h" +#include "logf.h" +#include "button.h" +#include "lcd.h" +#include "lcd-remote.h" +#include "pcm_playback.h" +#include "pcm_record.h" + + +/***************************************************************************/ + +static volatile bool is_recording; /* We are recording */ +static volatile bool is_stopping; /* Are we going to stop */ +static volatile bool is_paused; /* We have paused */ + +static volatile int num_rec_bytes; +static volatile int int_count; /* Number of DMA completed interrupts */ +static volatile int error_count; /* Number of DMA errors */ + +static unsigned long record_start_time; /* Value of current_tick when recording was started */ +static unsigned long pause_start_time; /* Value of current_tick when pause was started */ + +static int rec_gain, rec_volume; +static bool show_waveform; +static int init_done = 0; +static int wav_file; +static char recording_filename[MAX_PATH]; + +/***************************************************************************/ + +/* + Some estimates: + 44100 HZ * 4 = 176400 bytes/s + Refresh LCD 10 HZ = 176400 / 10 = 17640 bytes ~=~ 1024*16 bytes + + If NUM_BUFFERS is 80 we can hold ~8 sec of data in memory + ALL_BUFFER_SIZE will be 1024*16 * 80 = 1310720 bytes +*/ + +#define NUM_BUFFERS 80 +#define EACH_BUFFER_SIZE (1024*16) /* Multiple of 4. Use small value to get responsive waveform */ +#define ALL_BUFFERS_SIZE (NUM_BUFFERS * EACH_BUFFER_SIZE) + +#define WRITE_THRESHOLD 40 /* Minimum number of buffers before write to file */ + +static unsigned char *rec_buffers[NUM_BUFFERS]; + +/* + Overrun occures when DMA needs to write a new buffer and write_index == read_index + Solution to this is to optimize pcmrec_callback, use cpu_boost somewhere or increase + the total buffer size (or WRITE_THRESHOLD) +*/ + +static int write_index; /* Which buffer the DMA is currently recording */ +static int read_index; /* The oldest buffer that the pcmrec_callback has not read */ + +/***************************************************************************/ + +static struct event_queue pcmrec_queue; +static long pcmrec_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)]; +static const char pcmrec_thread_name[] = "pcmrec"; + +static void pcmrec_thread(void); + +/* Event IDs */ +#define PCMREC_OPEN 1 /* Enable recording */ +#define PCMREC_CLOSE 2 /* Disable recording */ +#define PCMREC_START 3 /* Start a new recording */ +#define PCMREC_STOP 4 /* Stop the current recording */ +#define PCMREC_PAUSE 10 +#define PCMREC_RESUME 11 +#define PCMREC_NEW_FILE 12 +#define PCMREC_SET_GAIN 13 +#define PCMREC_GOT_DATA 20 /* DMA1 notifies when data has arrived */ + + +/*******************************************************************/ +/* Functions that are not executing in the pcmrec_thread first */ +/*******************************************************************/ + +void pcm_init_recording(void) +{ + int_count = 0; + error_count = 0; + + show_waveform = 0; + is_recording = 0; + is_stopping = 0; + num_rec_bytes = 0; + wav_file = -1; + read_index = 0; + write_index = 0; + + queue_init(&pcmrec_queue); + create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name); +} + +void pcm_open_recording(void) +{ + init_done = 0; + + logf("pcm_open_rec"); + + queue_post(&pcmrec_queue, PCMREC_OPEN, 0); + + while (init_done) + { + sleep(HZ >> 8); + } + + logf("pcm_open_rec done"); +} + +void pcm_close_recording(void) +{ + /* todo: synchronize completion with pcmrec thread */ + queue_post(&pcmrec_queue, PCMREC_CLOSE, 0); +} + + + +unsigned long pcm_status(void) +{ + unsigned long ret = 0; + + if (is_recording) + ret |= AUDIO_STATUS_RECORD; + + return ret; +} + + + +void pcm_new_file(const char *filename) +{ + /* todo */ + filename = filename; + +} + +unsigned long pcm_recorded_time(void) +{ + if (is_recording) + { + if(is_paused) + return pause_start_time - record_start_time; + else + return current_tick - record_start_time; + } + + return 0; +} + +unsigned long pcm_num_recorded_bytes(void) +{ + + if (is_recording) + { + return num_rec_bytes; + } + else + return 0; +} + +void pcm_pause_recording(void) +{ + /* todo */ +} + +void pcm_resume_recording(void) +{ + /* todo */ +} + + +/** + * Sets the audio source + * + * Side effect: This functions starts feeding the CPU with audio data over the I2S bus + * + * @param source 0=line-in, 1=mic + */ +void pcm_set_recording_options(int source, bool enable_waveform) +{ + uda1380_enable_recording(source); + + show_waveform = enable_waveform; + +} + + +/** + * + * @param gain line-in and microphone gain (0-15) + * @param volume ADC volume (0-255) + */ +void pcm_set_recording_gain(int gain, int volume) +{ + rec_gain = gain; + rec_volume = volume; + + queue_post(&pcmrec_queue, PCMREC_SET_GAIN, 0); + +} + +/** + * Start recording + * + * Use pcm_set_recording_options before calling record + */ +void pcm_record(const char *filename) +{ + strncpy(recording_filename, filename, MAX_PATH - 1); + recording_filename[MAX_PATH - 1] = 0; + + queue_post(&pcmrec_queue, PCMREC_START, 0); +} + +/** + * + */ +void pcm_stop_recording(void) +{ + if (is_recording) + is_stopping = 1; + + queue_post(&pcmrec_queue, PCMREC_STOP, 0); + + logf("pcm_stop_recording"); + + while (is_stopping) + { + sleep(HZ >> 4); + } + + logf("pcm_stop_recording done"); + + +} + + +/***************************************************************************/ +/* Functions that executes in the context of pcmrec_thread */ +/***************************************************************************/ + + +/** + * Process the buffers using read_index and write_index. + * + * DMA1 handler posts to pcmrec_queue so that pcmrec_thread calls this + * function. Also pcmrec_stop will call this function when the recording + * is stopping, and that call will have flush = true. + * + */ + +void pcmrec_callback(bool flush) __attribute__ ((section (".icode"))); +void pcmrec_callback(bool flush) +{ + int num_ready; + + num_ready = write_index - read_index; + if (num_ready < 0) + num_ready += NUM_BUFFERS; + + /* we can consume up to num_ready buffers */ + +#ifdef HAVE_REMOTE_LCD + /* Draw waveform on remote LCD */ + if (show_waveform && num_ready>0) + { + short *buf; + long x,y,offset; + int show_index; + + /* Just display the last buffer (most recent one) */ + show_index = read_index + num_ready - 1; + buf = (short*)rec_buffers[show_index]; + + lcd_remote_clear_display(); + + offset = 0; + for (x=0; x> 15; /* Divide with SHRT_MAX */ + y += LCD_REMOTE_HEIGHT/2; + + if (y < 2) y=2; + if (y >= LCD_REMOTE_HEIGHT-2) y = LCD_REMOTE_HEIGHT-2; + + REMOTE_DRAW_PIXEL(x,y); + + offset += (EACH_BUFFER_SIZE/2) / LCD_REMOTE_WIDTH; + } + + lcd_remote_update(); + } + +#endif + + /* Note: This might be a good place to call the 'codec' later */ + + /* Check that we have the minimum amount of data to save or */ + /* that if it's closing time which mean we have to save.. */ + if (wav_file != -1) + { + if (num_ready >= WRITE_THRESHOLD || flush) + { + unsigned long *ptr = (unsigned long*)rec_buffers[read_index]; + int i; + + for (i=0; i= NUM_BUFFERS) + read_index -= NUM_BUFFERS; + } + + } else + { + /* In this case we must consume the buffers otherwise we will */ + /* get 'dma1 overrun' pretty fast */ + + read_index+=num_ready; + if (read_index >= NUM_BUFFERS) + read_index -= NUM_BUFFERS; + } +} + + + + +void pcmrec_dma_start(void) +{ + DAR1 = (unsigned long)rec_buffers[write_index++]; /* Destination address */ + SAR1 = (unsigned long)&PDIR2; /* Source address */ + BCR1 = EACH_BUFFER_SIZE; /* Bytes to transfer */ + + /* Start the DMA transfer.. */ + DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; + + logf("dma1 started"); +} + + +/* DMA1 Interrupt is called when the DMA has finished transfering a chunk */ +void DMA1(void) __attribute__ ((interrupt_handler, section(".icode"))); +void DMA1(void) +{ + int res = DSR1; + + DSR1 = 1; /* Clear interrupt */ + + int_count++; + + if (res & 0x70) + { + DCR1 = 0; /* Stop DMA transfer */ + error_count++; + is_recording = 0; + + logf("dma1 err 0x%x", res); + + } else + { + num_rec_bytes += EACH_BUFFER_SIZE; + + write_index++; + if (write_index >= NUM_BUFFERS) + write_index = 0; + + if (is_stopping || !is_recording) + { + DCR1 = 0; /* Stop DMA transfer */ + is_recording = 0; + + logf("dma1 stopping"); + + } else if (write_index == read_index) + { + DCR1 = 0; /* Stop DMA transfer */ + is_recording = 0; + + logf("dma1 overrun"); + + } else + { + DAR1 = (unsigned long)rec_buffers[write_index]; /* Destination address */ + BCR1 = EACH_BUFFER_SIZE; + + queue_post(&pcmrec_queue, PCMREC_GOT_DATA, NULL); + + } + } + + IPR |= (1<<15); /* Clear pending interrupt request */ +} + +static int start_wave(void) +{ + unsigned char header[44] = + { + 'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ', + 0x10,0,0,0,1,0,2,0,0x44,0xac,0,0,0x10,0xb1,2,0, + 4,0,0x10,0,'d','a','t','a',0,0,0,0 + }; + + wav_file = open(recording_filename, O_RDWR|O_CREAT|O_TRUNC); + if (wav_file < 0) + { + wav_file = -1; + logf("create failed: %d", wav_file); + return -1; + } + + if (sizeof(header) != write(wav_file, header, sizeof(header))) + { + close(wav_file); + wav_file = -1; + logf("write failed"); + return -2; + } + + return 0; +} + +/* Update header and set correct length values */ +static void close_wave(void) +{ + long l; + + l = SWAB32(num_rec_bytes + 36); + lseek(wav_file, 4, SEEK_SET); + write(wav_file, &l, 4); + + l = SWAB32(num_rec_bytes); + lseek(wav_file, 40, SEEK_SET); + write(wav_file, &l, 4); + + close(wav_file); + wav_file = -1; +} + +static void pcmrec_start(void) +{ + logf("pcmrec_start"); + + if (is_recording) + return; + + if (wav_file != -1) + close(wav_file); + + logf("rec: %s", recording_filename); + + start_wave(); /* todo: send signal to pcm_record if we have failed */ + + num_rec_bytes = 0; + + /* Store the current time */ + record_start_time = current_tick; + + write_index = 0; + read_index = 0; + + is_stopping = 0; + is_paused = 0; + is_recording = 1; + + pcmrec_dma_start(); + +} + +static void pcmrec_stop(void) +{ + /* wait for recording to finish */ + + /* todo: Abort current DMA transfer using DCR1.. */ + + logf("pcmrec_stop"); + + while (is_recording) + { + sleep(HZ >> 4); + } + + logf("pcmrec_stop done"); + + /* Write unfinished buffers to file */ + pcmrec_callback(true); + + close_wave(); + + is_stopping = 0; +} + +static void pcmrec_open(void) +{ + unsigned long buffer_start; + int i; + + show_waveform = 0; + is_recording = 0; + is_stopping = 0; + num_rec_bytes = 0; + wav_file = -1; + read_index = 0; + write_index = 0; + + buffer_start = (unsigned long)(&audiobuf[(audiobufend - audiobuf) - (ALL_BUFFERS_SIZE + 16)]); + buffer_start &= ~3; + + for (i=0; i