482a242792
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8287 a1c6a512-1295-4272-9138-f99709370657
961 lines
23 KiB
C
961 lines
23 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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 bool is_error; /* An error has occured */
|
|
|
|
static volatile unsigned long num_rec_bytes; /* Num bytes recorded */
|
|
static volatile unsigned long num_file_bytes; /* Num bytes written to current file */
|
|
static volatile int error_count; /* Number of DMA errors */
|
|
|
|
static long record_start_time; /* Value of current_tick when recording was started */
|
|
static long pause_start_time; /* Value of current_tick when pause was started */
|
|
static volatile int buffered_chunks; /* number of valid chunks in buffer */
|
|
|
|
static int wav_file;
|
|
static char recording_filename[MAX_PATH];
|
|
|
|
static volatile bool init_done, close_done, record_done, stop_done, pause_done, resume_done, new_file_done;
|
|
|
|
static int peak_left, peak_right;
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
Some estimates:
|
|
Normal recording rate: 44100 HZ * 4 = 176 KB/s
|
|
Total buffer size: 32 MB / 176 KB/s = 181s before writing to disk
|
|
*/
|
|
|
|
#define CHUNK_SIZE 8192 /* Multiple of 4 */
|
|
#define WRITE_THRESHOLD 250 /* (2 MB) Write when this many chunks (or less) until buffer full */
|
|
|
|
#define GET_CHUNK(x) (short*)(&rec_buffer[CHUNK_SIZE*(x)])
|
|
|
|
static unsigned char *rec_buffer; /* Circular recording buffer */
|
|
static int num_chunks; /* Number of chunks available in rec_buffer */
|
|
|
|
|
|
/*
|
|
Overrun occures when DMA needs to write a new chunk and write_index == read_index
|
|
Solution to this is to optimize pcmrec_callback, use cpu_boost or save to disk
|
|
more often.
|
|
*/
|
|
|
|
static volatile int write_index; /* Current chunk the DMA is writing to */
|
|
static volatile int read_index; /* Oldest chunk that is not written to disk */
|
|
static volatile int read2_index; /* Latest chunk that has not been converted to little endian */
|
|
static long pre_record_ticks; /* pre-record time expressed in ticks */
|
|
static int pre_record_chunks; /* pre-record time expressed in chunks */
|
|
|
|
/***************************************************************************/
|
|
|
|
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);
|
|
static void pcmrec_dma_start(void);
|
|
static void pcmrec_dma_stop(void);
|
|
|
|
/* Event IDs */
|
|
#define PCMREC_INIT 1 /* Enable recording */
|
|
#define PCMREC_CLOSE 2
|
|
|
|
#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
|
|
|
|
/*******************************************************************/
|
|
/* Functions that are not executing in the pcmrec_thread first */
|
|
/*******************************************************************/
|
|
|
|
/* Creates pcmrec_thread */
|
|
void pcm_rec_init(void)
|
|
{
|
|
queue_init(&pcmrec_queue);
|
|
create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name);
|
|
}
|
|
|
|
|
|
/* Initializes recording:
|
|
* - Set up the UDA1380 for recording
|
|
* - Prepare for DMA transfers
|
|
*/
|
|
|
|
void audio_init_recording(void)
|
|
{
|
|
init_done = false;
|
|
queue_post(&pcmrec_queue, PCMREC_INIT, 0);
|
|
|
|
while(!init_done)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
}
|
|
|
|
void audio_close_recording(void)
|
|
{
|
|
close_done = false;
|
|
queue_post(&pcmrec_queue, PCMREC_CLOSE, 0);
|
|
|
|
while(!close_done)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
}
|
|
|
|
unsigned long pcm_rec_status(void)
|
|
{
|
|
unsigned long ret = 0;
|
|
|
|
if (is_recording)
|
|
ret |= AUDIO_STATUS_RECORD;
|
|
if (is_paused)
|
|
ret |= AUDIO_STATUS_PAUSE;
|
|
if (is_error)
|
|
ret |= AUDIO_STATUS_ERROR;
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned long audio_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 audio_num_recorded_bytes(void)
|
|
{
|
|
if (is_recording)
|
|
return num_rec_bytes;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the audio source
|
|
*
|
|
* This functions starts feeding the CPU with audio data over the I2S bus
|
|
*
|
|
* @param source 0=mic, 1=line-in, (todo: 2=spdif)
|
|
*/
|
|
void audio_set_recording_options(int frequency, int quality,
|
|
int source, int channel_mode,
|
|
bool editable, int prerecord_time)
|
|
{
|
|
/* TODO: */
|
|
(void)frequency;
|
|
(void)quality;
|
|
(void)channel_mode;
|
|
(void)editable;
|
|
|
|
/* WARNING: calculation below uses fixed frequency! */
|
|
pre_record_ticks = prerecord_time * HZ;
|
|
pre_record_chunks = ((44100 * prerecord_time * 4)/CHUNK_SIZE)+1;
|
|
if(pre_record_chunks >= (num_chunks-250))
|
|
{
|
|
/* we can't prerecord more than our buffersize minus treshold to write to disk! */
|
|
pre_record_chunks = num_chunks-250;
|
|
/* don't forget to recalculate that time! */
|
|
pre_record_ticks = ((pre_record_chunks * CHUNK_SIZE)/(4*44100)) * HZ;
|
|
}
|
|
|
|
//logf("pcmrec: src=%d", source);
|
|
|
|
switch (source)
|
|
{
|
|
/* mic */
|
|
case 0:
|
|
uda1380_enable_recording(true);
|
|
break;
|
|
|
|
/* line-in */
|
|
case 1:
|
|
uda1380_enable_recording(false);
|
|
break;
|
|
}
|
|
|
|
uda1380_set_monitor(true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Note that microphone is mono, only left value is used
|
|
* See uda1380_set_recvol() for exact ranges.
|
|
*
|
|
* @param type 0=line-in (radio), 1=mic, 2=ADC
|
|
*
|
|
*/
|
|
void audio_set_recording_gain(int left, int right, int type)
|
|
{
|
|
//logf("rcmrec: t=%d l=%d r=%d", type, left, right);
|
|
uda1380_set_recvol(left, right, type);
|
|
}
|
|
|
|
|
|
/**
|
|
* Start recording
|
|
*
|
|
* Use audio_set_recording_options first to select recording options
|
|
*/
|
|
void audio_record(const char *filename)
|
|
{
|
|
if (is_recording)
|
|
{
|
|
logf("record while recording");
|
|
return;
|
|
}
|
|
|
|
strncpy(recording_filename, filename, MAX_PATH - 1);
|
|
recording_filename[MAX_PATH - 1] = 0;
|
|
|
|
record_done = false;
|
|
queue_post(&pcmrec_queue, PCMREC_START, 0);
|
|
|
|
while(!record_done)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
}
|
|
|
|
|
|
void audio_new_file(const char *filename)
|
|
{
|
|
logf("pcm_new_file");
|
|
|
|
new_file_done = false;
|
|
|
|
strncpy(recording_filename, filename, MAX_PATH - 1);
|
|
recording_filename[MAX_PATH - 1] = 0;
|
|
|
|
queue_post(&pcmrec_queue, PCMREC_NEW_FILE, 0);
|
|
|
|
while(!new_file_done)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
|
|
logf("pcm_new_file done");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void audio_stop_recording(void)
|
|
{
|
|
if (!is_recording)
|
|
return;
|
|
|
|
logf("pcm_stop");
|
|
|
|
stop_done = false;
|
|
queue_post(&pcmrec_queue, PCMREC_STOP, 0);
|
|
|
|
while(!stop_done)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
|
|
logf("pcm_stop done");
|
|
}
|
|
|
|
void audio_pause_recording(void)
|
|
{
|
|
if (!is_recording)
|
|
{
|
|
logf("pause when not recording");
|
|
return;
|
|
}
|
|
if (is_paused)
|
|
{
|
|
logf("pause when paused");
|
|
return;
|
|
}
|
|
|
|
pause_done = false;
|
|
queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
|
|
|
|
while(!pause_done)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
}
|
|
|
|
void audio_resume_recording(void)
|
|
{
|
|
if (!is_paused)
|
|
{
|
|
logf("resume when not paused");
|
|
return;
|
|
}
|
|
|
|
resume_done = false;
|
|
queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
|
|
|
|
while(!resume_done)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
}
|
|
|
|
void pcm_rec_get_peaks(int *left, int *right)
|
|
{
|
|
if (left)
|
|
*left = peak_left;
|
|
if (right)
|
|
*right = peak_right;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/* Functions that executes in the context of pcmrec_thread */
|
|
/***************************************************************************/
|
|
|
|
/* Skip PEAK_STRIDE sample-pairs for each compare */
|
|
#define PEAK_STRIDE 3
|
|
|
|
static void pcmrec_find_peaks(int chunk)
|
|
{
|
|
short *ptr, value;
|
|
int peak_l, peak_r;
|
|
int j;
|
|
|
|
ptr = GET_CHUNK(chunk);
|
|
|
|
peak_l = 0;
|
|
peak_r = 0;
|
|
|
|
for (j=0; j<CHUNK_SIZE/4; j+=PEAK_STRIDE+1)
|
|
{
|
|
if ((value = ptr[0]) > peak_l)
|
|
peak_l = value;
|
|
else if (-value > peak_l)
|
|
peak_l = -value;
|
|
ptr++;
|
|
|
|
if ((value = ptr[0]) > peak_r)
|
|
peak_r = value;
|
|
else if (-value > peak_r)
|
|
peak_r = -value;
|
|
ptr++;
|
|
|
|
ptr += PEAK_STRIDE * 2;
|
|
}
|
|
|
|
peak_left = peak_l;
|
|
peak_right = peak_r;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Process the chunks using read_index and write_index.
|
|
*
|
|
* This function is called when queue_get_w_tmo times out.
|
|
*
|
|
* Other functions can also call this function with flush = true when
|
|
* they want to save everything in the buffers to disk.
|
|
*
|
|
*/
|
|
|
|
static void pcmrec_callback(bool flush) __attribute__ ((section (".icode")));
|
|
static void pcmrec_callback(bool flush)
|
|
{
|
|
int num_ready, num_free, num_new;
|
|
unsigned short *ptr;
|
|
int i, j, w;
|
|
|
|
w = write_index;
|
|
|
|
num_new = w - read2_index;
|
|
if (num_new < 0)
|
|
num_new += num_chunks;
|
|
|
|
if (num_new > 0)
|
|
{
|
|
/* Collect peak values for the last buffer only */
|
|
j = w - 1;
|
|
if (j < 0)
|
|
j += num_chunks;
|
|
pcmrec_find_peaks(j);
|
|
}
|
|
|
|
if ((!is_recording || is_paused) && !flush)
|
|
{
|
|
/* not recording = no saving to disk, fake buffer clearing */
|
|
read_index = write_index;
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<num_new; i++)
|
|
{
|
|
/* Convert the samples to little-endian so we only have to write later
|
|
(Less hd-spinning time)
|
|
*/
|
|
ptr = GET_CHUNK(read2_index);
|
|
for (j=0; j<CHUNK_SIZE/2; j++)
|
|
{
|
|
*ptr = htole16(*ptr);
|
|
ptr++;
|
|
}
|
|
|
|
num_rec_bytes += CHUNK_SIZE;
|
|
|
|
read2_index++;
|
|
if (read2_index >= num_chunks)
|
|
read2_index = 0;
|
|
}
|
|
|
|
num_ready = w - read_index;
|
|
if (num_ready < 0)
|
|
num_ready += num_chunks;
|
|
|
|
num_free = num_chunks - num_ready;
|
|
|
|
if (num_free <= WRITE_THRESHOLD || flush)
|
|
{
|
|
logf("writing: %d (%d)", num_ready, flush);
|
|
|
|
for (i=0; i<num_ready; i++)
|
|
{
|
|
if (write(wav_file, GET_CHUNK(read_index), CHUNK_SIZE) != CHUNK_SIZE)
|
|
{
|
|
logf("pcmrec: write err");
|
|
pcmrec_dma_stop();
|
|
return;
|
|
}
|
|
|
|
num_file_bytes += CHUNK_SIZE;
|
|
|
|
read_index++;
|
|
if (read_index >= num_chunks)
|
|
read_index = 0;
|
|
yield();
|
|
}
|
|
|
|
logf("done");
|
|
}
|
|
}
|
|
|
|
/* Abort dma transfer */
|
|
static void pcmrec_dma_stop(void)
|
|
{
|
|
DCR1 = 0;
|
|
|
|
is_error = true;
|
|
is_recording = false;
|
|
|
|
error_count++;
|
|
|
|
logf("dma1 stopped");
|
|
}
|
|
|
|
static void pcmrec_dma_start(void)
|
|
{
|
|
DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
|
|
SAR1 = (unsigned long)&PDIR2; /* Source address */
|
|
BCR1 = CHUNK_SIZE; /* Bytes to transfer */
|
|
|
|
/* Start the DMA transfer.. */
|
|
DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START;
|
|
|
|
/* pre-recording: buffer count */
|
|
buffered_chunks = 0;
|
|
|
|
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 */
|
|
|
|
if (res & 0x70)
|
|
{
|
|
DCR1 = 0; /* Stop DMA transfer */
|
|
error_count++;
|
|
is_recording = false;
|
|
|
|
logf("dma1 err: 0x%x", res);
|
|
|
|
/* Flush recorded data to disk and stop recording */
|
|
queue_post(&pcmrec_queue, PCMREC_STOP, NULL);
|
|
|
|
} else
|
|
{
|
|
write_index++;
|
|
if (write_index >= num_chunks)
|
|
write_index = 0;
|
|
|
|
/* update number of valid chunks for pre-recording */
|
|
if(buffered_chunks < num_chunks)
|
|
buffered_chunks++;
|
|
|
|
if (is_stopping)
|
|
{
|
|
DCR1 = 0; /* Stop DMA transfer */
|
|
is_stopping = false;
|
|
|
|
logf("dma1 stopping");
|
|
|
|
} else if (write_index == read_index)
|
|
{
|
|
DCR1 = 0; /* Stop DMA transfer */
|
|
is_recording = false;
|
|
|
|
logf("dma1 overrun");
|
|
|
|
} else
|
|
{
|
|
DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
|
|
BCR1 = CHUNK_SIZE;
|
|
}
|
|
}
|
|
|
|
IPR |= (1<<15); /* Clear pending interrupt request */
|
|
}
|
|
|
|
/* Create WAVE file and write header */
|
|
/* Sets returns 0 if success, -1 on failure */
|
|
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("rec: create failed: %d", wav_file);
|
|
is_error = true;
|
|
return -1;
|
|
}
|
|
|
|
if (sizeof(header) != write(wav_file, header, sizeof(header)))
|
|
{
|
|
close(wav_file);
|
|
wav_file = -1;
|
|
logf("rec: write failed");
|
|
is_error = true;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Update header and set correct length values */
|
|
static void close_wave(void)
|
|
{
|
|
long l;
|
|
|
|
if (wav_file != -1)
|
|
{
|
|
l = htole32(num_file_bytes + 36);
|
|
lseek(wav_file, 4, SEEK_SET);
|
|
write(wav_file, &l, 4);
|
|
|
|
l = htole32(num_file_bytes);
|
|
lseek(wav_file, 40, SEEK_SET);
|
|
write(wav_file, &l, 4);
|
|
|
|
close(wav_file);
|
|
wav_file = -1;
|
|
}
|
|
}
|
|
|
|
static void pcmrec_start(void)
|
|
{
|
|
int pre_chunks = pre_record_chunks; /* recalculate every time! */
|
|
long pre_ticks = pre_record_ticks; /* recalculate every time! */
|
|
|
|
logf("pcmrec_start");
|
|
|
|
if (is_recording)
|
|
{
|
|
logf("already recording");
|
|
record_done = true;
|
|
return;
|
|
}
|
|
|
|
if (wav_file != -1)
|
|
close(wav_file);
|
|
|
|
if (start_wave() != 0)
|
|
{
|
|
/* failed to create the file */
|
|
record_done = true;
|
|
return;
|
|
}
|
|
|
|
/* pre-recording calculation */
|
|
if(buffered_chunks < pre_chunks)
|
|
{
|
|
/* not enough good chunks available - limit pre-record time */
|
|
pre_chunks = buffered_chunks;
|
|
pre_ticks = ((buffered_chunks * CHUNK_SIZE)/(4*44100)) * HZ;
|
|
}
|
|
record_start_time = current_tick - pre_ticks;
|
|
|
|
read_index = write_index - pre_chunks;
|
|
if(read_index < 0)
|
|
{
|
|
read_index += num_chunks;
|
|
}
|
|
read2_index = read_index;
|
|
|
|
peak_left = 0;
|
|
peak_right = 0;
|
|
|
|
num_rec_bytes = 0;
|
|
num_file_bytes = 0;
|
|
pause_start_time = 0;
|
|
|
|
is_stopping = false;
|
|
is_paused = false;
|
|
is_recording = true;
|
|
|
|
record_done = true;
|
|
}
|
|
|
|
static void pcmrec_stop(void)
|
|
{
|
|
logf("pcmrec_stop");
|
|
|
|
if (!is_recording)
|
|
{
|
|
stop_done = true;
|
|
return;
|
|
}
|
|
|
|
if (!is_paused)
|
|
{
|
|
/* wait for recording to finish */
|
|
is_stopping = true;
|
|
|
|
while (is_stopping && is_recording)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
|
|
is_stopping = false;
|
|
}
|
|
|
|
is_recording = false;
|
|
|
|
/* Flush buffers to file */
|
|
pcmrec_callback(true);
|
|
|
|
close_wave();
|
|
|
|
stop_done = true;
|
|
|
|
/* Finally start dma again for peakmeters and pre-recoding to work. */
|
|
pcmrec_dma_start();
|
|
|
|
logf("pcmrec_stop done");
|
|
}
|
|
|
|
static void pcmrec_new_file(void)
|
|
{
|
|
logf("pcmrec_new_file");
|
|
|
|
if (!is_recording)
|
|
{
|
|
logf("not recording");
|
|
new_file_done = true;
|
|
return;
|
|
}
|
|
|
|
/* Since pcmrec_callback() blocks until the data has been written,
|
|
here is a good approximation when recording to the new file starts
|
|
*/
|
|
record_start_time = current_tick;
|
|
num_rec_bytes = 0;
|
|
|
|
if (is_paused)
|
|
pause_start_time = record_start_time;
|
|
|
|
/* Flush what we got in buffers to file */
|
|
pcmrec_callback(true);
|
|
|
|
close_wave();
|
|
|
|
num_file_bytes = 0;
|
|
|
|
/* start the new file */
|
|
if (start_wave() != 0)
|
|
{
|
|
logf("new_file failed");
|
|
pcmrec_stop();
|
|
}
|
|
|
|
new_file_done = true;
|
|
logf("pcmrec_new_file done");
|
|
}
|
|
|
|
static void pcmrec_pause(void)
|
|
{
|
|
logf("pcmrec_pause");
|
|
|
|
if (!is_recording)
|
|
{
|
|
logf("pause: not recording");
|
|
pause_done = true;
|
|
return;
|
|
}
|
|
|
|
/* Abort DMA transfer and flush to file? */
|
|
|
|
is_stopping = true;
|
|
|
|
while (is_stopping && is_recording)
|
|
sleep_thread();
|
|
wake_up_thread();
|
|
|
|
pause_start_time = current_tick;
|
|
is_paused = true;
|
|
|
|
/* Flush what we got in buffers to file */
|
|
pcmrec_callback(true);
|
|
|
|
pause_done = true;
|
|
|
|
logf("pcmrec_pause done");
|
|
}
|
|
|
|
|
|
static void pcmrec_resume(void)
|
|
{
|
|
logf("pcmrec_resume");
|
|
|
|
if (!is_paused)
|
|
{
|
|
logf("resume: not paused");
|
|
resume_done = true;
|
|
return;
|
|
}
|
|
|
|
is_paused = false;
|
|
is_recording = true;
|
|
|
|
/* Compensate for the time we have been paused */
|
|
if (pause_start_time)
|
|
{
|
|
record_start_time += current_tick - pause_start_time;
|
|
pause_start_time = 0;
|
|
}
|
|
|
|
pcmrec_dma_start();
|
|
|
|
resume_done = true;
|
|
|
|
logf("pcmrec_resume done");
|
|
}
|
|
|
|
|
|
/**
|
|
* audio_init_recording calls this function using PCMREC_INIT
|
|
*
|
|
*/
|
|
static void pcmrec_init(void)
|
|
{
|
|
unsigned long buffer_size;
|
|
|
|
wav_file = -1;
|
|
read_index = 0;
|
|
read2_index = 0;
|
|
write_index = 0;
|
|
pre_record_chunks = 0;
|
|
pre_record_ticks = 0;
|
|
|
|
peak_left = 0;
|
|
peak_right = 0;
|
|
|
|
num_rec_bytes = 0;
|
|
num_file_bytes = 0;
|
|
record_start_time = 0;
|
|
pause_start_time = 0;
|
|
buffered_chunks = 0;
|
|
|
|
is_recording = false;
|
|
is_stopping = false;
|
|
is_paused = false;
|
|
is_error = false;
|
|
|
|
rec_buffer = (unsigned char*)(((unsigned long)audiobuf) & ~3);
|
|
buffer_size = (long)audiobufend - (long)audiobuf - 16;
|
|
|
|
logf("buf size: %d kb", buffer_size/1024);
|
|
|
|
num_chunks = buffer_size / CHUNK_SIZE;
|
|
|
|
logf("num_chunks: %d", num_chunks);
|
|
|
|
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;
|
|
ICR7 = 0x1c; /* Enable interrupt at level 7, priority 0 */
|
|
IMR &= ~(1<<15); /* bit 15 is DMA1 */
|
|
|
|
pcmrec_dma_start();
|
|
|
|
init_done = 1;
|
|
}
|
|
|
|
static void pcmrec_close(void)
|
|
{
|
|
uda1380_disable_recording();
|
|
|
|
DMAROUTE = (DMAROUTE & 0xffff00ff);
|
|
ICR7 = 0x00; /* Disable interrupt */
|
|
IMR |= (1<<15); /* bit 15 is DMA1 */
|
|
|
|
close_done = true;
|
|
}
|
|
|
|
static void pcmrec_thread(void)
|
|
{
|
|
struct event ev;
|
|
|
|
logf("thread pcmrec start");
|
|
|
|
error_count = 0;
|
|
|
|
while (1)
|
|
{
|
|
queue_wait_w_tmo(&pcmrec_queue, &ev, HZ / 40);
|
|
|
|
switch (ev.id)
|
|
{
|
|
case PCMREC_INIT:
|
|
pcmrec_init();
|
|
break;
|
|
|
|
case PCMREC_CLOSE:
|
|
pcmrec_close();
|
|
break;
|
|
|
|
case PCMREC_START:
|
|
pcmrec_start();
|
|
break;
|
|
|
|
case PCMREC_STOP:
|
|
pcmrec_stop();
|
|
break;
|
|
|
|
case PCMREC_PAUSE:
|
|
pcmrec_pause();
|
|
break;
|
|
|
|
case PCMREC_RESUME:
|
|
pcmrec_resume();
|
|
break;
|
|
|
|
case PCMREC_NEW_FILE:
|
|
pcmrec_new_file();
|
|
break;
|
|
|
|
case SYS_TIMEOUT:
|
|
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");
|
|
}
|
|
|
|
/* Select VINL & VINR source: 0=Line-in, 1=FM Radio */
|
|
void pcm_rec_mux(int source)
|
|
{
|
|
#ifdef IRIVER_H300_SERIES
|
|
if(source == 0)
|
|
and_l(~0x40000000, &GPIO_OUT); /* Line In */
|
|
else
|
|
or_l(0x40000000, &GPIO_OUT); /* FM radio */
|
|
|
|
or_l(0x40000000, &GPIO_ENABLE);
|
|
or_l(0x40000000, &GPIO_FUNCTION);
|
|
#else
|
|
if(source == 0)
|
|
and_l(~0x00800000, &GPIO_OUT); /* Line In */
|
|
else
|
|
or_l(0x00800000, &GPIO_OUT); /* FM radio */
|
|
|
|
or_l(0x00800000, &GPIO_ENABLE);
|
|
or_l(0x00800000, &GPIO_FUNCTION);
|
|
#endif
|
|
}
|