forked from len0rd/rockbox
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
This commit is contained in:
parent
2c0a58c238
commit
e5d08722f8
9 changed files with 952 additions and 2 deletions
|
|
@ -51,4 +51,7 @@ recorder/recording.c
|
||||||
#ifdef IRIVER_H100
|
#ifdef IRIVER_H100
|
||||||
playback.c
|
playback.c
|
||||||
metadata.c
|
metadata.c
|
||||||
|
#ifndef SIMULATOR
|
||||||
|
pcm_recording.c
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,9 @@
|
||||||
#include "ata_mmc.h"
|
#include "ata_mmc.h"
|
||||||
#endif
|
#endif
|
||||||
#include "logfdisp.h"
|
#include "logfdisp.h"
|
||||||
|
#if defined(IRIVER_H100) && !defined(SIMULATOR)
|
||||||
|
extern bool pcm_rec_screen(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*---------------------------------------------------*/
|
/*---------------------------------------------------*/
|
||||||
/* SPECIAL DEBUG STUFF */
|
/* SPECIAL DEBUG STUFF */
|
||||||
|
|
@ -1800,6 +1803,9 @@ bool debug_menu(void)
|
||||||
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||||
{ "CPU frequency", dbg_cpufreq },
|
{ "CPU frequency", dbg_cpufreq },
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(IRIVER_H100) && !defined(SIMULATOR)
|
||||||
|
{ "PCM recording", pcm_rec_screen },
|
||||||
|
#endif
|
||||||
#if CONFIG_CPU == SH7034
|
#if CONFIG_CPU == SH7034
|
||||||
#ifdef HAVE_LCD_BITMAP
|
#ifdef HAVE_LCD_BITMAP
|
||||||
#ifdef HAVE_RTC
|
#ifdef HAVE_RTC
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,9 @@
|
||||||
#if (CONFIG_HWCODEC == MASNONE)
|
#if (CONFIG_HWCODEC == MASNONE)
|
||||||
#include "pcm_playback.h"
|
#include "pcm_playback.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(IRIVER_H100) && !defined(SIMULATOR)
|
||||||
|
#include "pcm_record.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_TUNER
|
#ifdef CONFIG_TUNER
|
||||||
#include "radio.h"
|
#include "radio.h"
|
||||||
|
|
@ -293,6 +296,9 @@ void init(void)
|
||||||
#if (CONFIG_HWCODEC == MASNONE)
|
#if (CONFIG_HWCODEC == MASNONE)
|
||||||
pcm_init();
|
pcm_init();
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(IRIVER_H100) && !defined(SIMULATOR)
|
||||||
|
pcm_init_recording();
|
||||||
|
#endif
|
||||||
#ifdef HAVE_CHARGING
|
#ifdef HAVE_CHARGING
|
||||||
car_adapter_mode_init();
|
car_adapter_mode_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
226
apps/pcm_recording.c
Normal file
226
apps/pcm_recording.c
Normal file
|
|
@ -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 <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -126,4 +126,7 @@ drivers/uda1380.c
|
||||||
#if (CONFIG_HWCODEC == MASNONE) && !defined(SIMULATOR)
|
#if (CONFIG_HWCODEC == MASNONE) && !defined(SIMULATOR)
|
||||||
pcm_playback.c
|
pcm_playback.c
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(IRIVER_H100) && !defined(SIMULATOR)
|
||||||
|
pcm_record.c
|
||||||
|
#endif
|
||||||
sound.c
|
sound.c
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
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_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.. */
|
uda1380_write_reg(REG_MIX_CTL, MIX_MODE(3)); /* Not sure which mode is the best one.. */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@
|
||||||
#define PDOR1_R (*(volatile unsigned long *)(MBAR2 + 0x044))
|
#define PDOR1_R (*(volatile unsigned long *)(MBAR2 + 0x044))
|
||||||
#define PDOR2_L (*(volatile unsigned long *)(MBAR2 + 0x054))
|
#define PDOR2_L (*(volatile unsigned long *)(MBAR2 + 0x054))
|
||||||
#define PDOR2_R (*(volatile unsigned long *)(MBAR2 + 0x064))
|
#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 PDOR3 (*(volatile unsigned long *)(MBAR2 + 0x074))
|
||||||
#define UCHANNELTRANSMIT (*(volatile unsigned long *)(MBAR2 + 0x084))
|
#define UCHANNELTRANSMIT (*(volatile unsigned long *)(MBAR2 + 0x084))
|
||||||
#define U1CHANNELRECEIVE (*(volatile unsigned long *)(MBAR2 + 0x088))
|
#define U1CHANNELRECEIVE (*(volatile unsigned long *)(MBAR2 + 0x088))
|
||||||
|
|
@ -262,5 +262,8 @@
|
||||||
/* DMAROUTE config */
|
/* DMAROUTE config */
|
||||||
#define DMA0_REQ_AUDIO_1 0x80
|
#define DMA0_REQ_AUDIO_1 0x80
|
||||||
#define DMA0_REQ_AUDIO_2 0x81
|
#define DMA0_REQ_AUDIO_2 0x81
|
||||||
|
#define DMA1_REQ_AUDIO_1 0x8000
|
||||||
|
#define DMA1_REQ_AUDIO_2 0x8100
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
51
firmware/export/pcm_record.h
Normal file
51
firmware/export/pcm_record.h
Normal file
|
|
@ -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
|
||||||
650
firmware/pcm_record.c
Normal file
650
firmware/pcm_record.c
Normal file
|
|
@ -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 <kernel.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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<LCD_REMOTE_WIDTH-1; x++)
|
||||||
|
{
|
||||||
|
y = buf[offset] * (LCD_REMOTE_HEIGHT / 2) *5; /* The 5 is just 'zooming' */
|
||||||
|
y = y >> 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<EACH_BUFFER_SIZE * num_ready / 4; i++)
|
||||||
|
{
|
||||||
|
*ptr = SWAB32(*ptr);
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(wav_file, rec_buffers[read_index], EACH_BUFFER_SIZE * num_ready);
|
||||||
|
|
||||||
|
read_index+=num_ready;
|
||||||
|
if (read_index >= 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<NUM_BUFFERS; i++)
|
||||||
|
{
|
||||||
|
rec_buffers[i] = (unsigned char*)(buffer_start + EACH_BUFFER_SIZE * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
IIS1CONFIG = 0x800; /* Stop any playback */
|
||||||
|
AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */
|
||||||
|
DATAINCONTROL = 0xc000; /* Generate Interrupt when 6 samples in fifo */
|
||||||
|
DATAINCONTROL |= 0x20; /* PDIR2 source = IIS1recv */
|
||||||
|
|
||||||
|
DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
|
||||||
|
DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
|
||||||
|
DMAROUTE = (DMAROUTE & 0xffff00ff) | DMA1_REQ_AUDIO_2;
|
||||||
|
ICR4 = (ICR4 & 0xffffff00) | 0x0000001c; /* Enable interrupt at level 7, priority 0 */
|
||||||
|
IMR &= ~(1<<15); /* bit 15 is DMA1 */
|
||||||
|
|
||||||
|
init_done = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcmrec_close(void)
|
||||||
|
{
|
||||||
|
uda1380_disable_recording();
|
||||||
|
|
||||||
|
DMAROUTE = (DMAROUTE & 0xffff00ff);
|
||||||
|
ICR4 = (ICR4 & 0xffffff00); /* Disable interrupt */
|
||||||
|
IMR |= (1<<15); /* bit 15 is DMA1 */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcmrec_thread(void)
|
||||||
|
{
|
||||||
|
struct event ev;
|
||||||
|
|
||||||
|
logf("thread pcmrec start");
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
queue_wait(&pcmrec_queue, &ev);
|
||||||
|
|
||||||
|
switch (ev.id)
|
||||||
|
{
|
||||||
|
case PCMREC_OPEN:
|
||||||
|
pcmrec_open();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCMREC_CLOSE:
|
||||||
|
pcmrec_close();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCMREC_START:
|
||||||
|
pcmrec_start();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCMREC_STOP:
|
||||||
|
pcmrec_stop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCMREC_PAUSE:
|
||||||
|
/* todo */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCMREC_RESUME:
|
||||||
|
/* todo */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCMREC_NEW_FILE:
|
||||||
|
/* todo */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCMREC_SET_GAIN:
|
||||||
|
uda1380_set_recvol(rec_gain, rec_gain, rec_volume);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCMREC_GOT_DATA:
|
||||||
|
pcmrec_callback(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYS_USB_CONNECTED:
|
||||||
|
if (!is_recording && !is_stopping)
|
||||||
|
{
|
||||||
|
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
||||||
|
usb_wait_for_disconnect(&pcmrec_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logf("thread pcmrec done");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue