2007-10-06 22:27:27 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 by Michael Sevakis
|
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2007-10-06 22:27:27 +00:00
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "system.h"
|
|
|
|
#include "kernel.h"
|
2009-10-22 00:59:42 +00:00
|
|
|
|
|
|
|
/* Define LOGF_ENABLE to enable logf output in this file */
|
|
|
|
/*#define LOGF_ENABLE*/
|
2007-10-06 22:27:27 +00:00
|
|
|
#include "logf.h"
|
|
|
|
#include "audio.h"
|
|
|
|
#include "sound.h"
|
2008-12-12 11:01:07 +00:00
|
|
|
#include "general.h"
|
2011-06-29 06:37:04 +00:00
|
|
|
#include "pcm-internal.h"
|
|
|
|
#include "pcm_mixer.h"
|
2007-10-06 22:27:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Aspects implemented in the target-specific portion:
|
|
|
|
*
|
|
|
|
* ==Playback==
|
|
|
|
* Public -
|
|
|
|
* pcm_postinit
|
|
|
|
* pcm_get_bytes_waiting
|
|
|
|
* pcm_play_lock
|
|
|
|
* pcm_play_unlock
|
|
|
|
* Semi-private -
|
2012-02-23 13:14:46 +00:00
|
|
|
* pcm_play_dma_complete_callback
|
|
|
|
* pcm_play_dma_status_callback
|
2007-10-06 22:27:27 +00:00
|
|
|
* pcm_play_dma_init
|
2011-09-01 12:15:43 +00:00
|
|
|
* pcm_play_dma_postinit
|
2007-10-06 22:27:27 +00:00
|
|
|
* pcm_play_dma_start
|
|
|
|
* pcm_play_dma_stop
|
|
|
|
* pcm_play_dma_pause
|
|
|
|
* pcm_play_dma_get_peak_buffer
|
|
|
|
* Data Read/Written within TSP -
|
2008-12-12 11:01:07 +00:00
|
|
|
* pcm_sampr (R)
|
|
|
|
* pcm_fsel (R)
|
|
|
|
* pcm_curr_sampr (R)
|
2007-10-06 22:27:27 +00:00
|
|
|
* pcm_playing (R)
|
|
|
|
* pcm_paused (R)
|
|
|
|
*
|
2008-12-12 11:01:07 +00:00
|
|
|
* ==Playback/Recording==
|
2010-05-24 16:42:32 +00:00
|
|
|
* Public -
|
|
|
|
* pcm_dma_addr
|
2008-12-12 11:01:07 +00:00
|
|
|
* Semi-private -
|
|
|
|
* pcm_dma_apply_settings
|
|
|
|
*
|
2007-10-06 22:27:27 +00:00
|
|
|
* ==Recording==
|
|
|
|
* Public -
|
|
|
|
* pcm_rec_lock
|
|
|
|
* pcm_rec_unlock
|
|
|
|
* Semi-private -
|
2012-02-23 13:14:46 +00:00
|
|
|
* pcm_rec_dma_complete_callback
|
|
|
|
* pcm_rec_dma_status_callback
|
2007-10-06 22:27:27 +00:00
|
|
|
* pcm_rec_dma_init
|
|
|
|
* pcm_rec_dma_close
|
|
|
|
* pcm_rec_dma_start
|
|
|
|
* pcm_rec_dma_stop
|
|
|
|
* pcm_rec_dma_get_peak_buffer
|
|
|
|
* Data Read/Written within TSP -
|
|
|
|
* pcm_recording (R)
|
|
|
|
*
|
|
|
|
* States are set _after_ the target's pcm driver is called so that it may
|
2007-10-07 23:19:09 +00:00
|
|
|
* know from whence the state is changed. One exception is init.
|
2007-10-06 22:27:27 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-09-01 12:15:43 +00:00
|
|
|
/* 'true' when all stages of pcm initialization have completed */
|
|
|
|
static bool pcm_is_ready = false;
|
|
|
|
|
2012-02-23 13:14:46 +00:00
|
|
|
/* The registered callback function to ask for more mp3 data */
|
2013-04-05 08:36:05 +00:00
|
|
|
volatile pcm_play_callback_type
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_callback_for_more SHAREDBSS_ATTR = NULL;
|
|
|
|
/* The registered callback function to inform of DMA status */
|
|
|
|
volatile pcm_status_callback_type
|
|
|
|
pcm_play_status_callback SHAREDBSS_ATTR = NULL;
|
2007-10-06 22:27:27 +00:00
|
|
|
/* PCM playback state */
|
2008-04-06 04:34:57 +00:00
|
|
|
volatile bool pcm_playing SHAREDBSS_ATTR = false;
|
2007-10-06 22:27:27 +00:00
|
|
|
/* PCM paused state. paused implies playing */
|
2008-04-06 04:34:57 +00:00
|
|
|
volatile bool pcm_paused SHAREDBSS_ATTR = false;
|
2007-10-06 22:27:27 +00:00
|
|
|
/* samplerate of currently playing audio - undefined if stopped */
|
2008-04-06 04:34:57 +00:00
|
|
|
unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
|
2008-12-12 11:01:07 +00:00
|
|
|
/* samplerate waiting to be set */
|
|
|
|
unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT;
|
|
|
|
/* samplerate frequency selection index */
|
|
|
|
int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
2013-04-05 08:36:05 +00:00
|
|
|
static void pcm_play_data_start_int(const void *addr, size_t size);
|
|
|
|
static void pcm_play_pause_int(bool play);
|
|
|
|
void pcm_play_stop_int(void);
|
|
|
|
|
2013-04-23 07:20:49 +00:00
|
|
|
#if !defined(HAVE_SW_VOLUME_CONTROL) || defined(PCM_SW_VOLUME_UNBUFFERED)
|
|
|
|
/** Standard hw volume/unbuffered control functions - otherwise, see
|
|
|
|
** pcm_sw_volume.c **/
|
2013-04-05 08:36:05 +00:00
|
|
|
static inline void pcm_play_dma_start_int(const void *addr, size_t size)
|
|
|
|
{
|
2013-08-23 18:18:08 +00:00
|
|
|
#ifdef HAVE_SW_VOLUME_CONTROL
|
|
|
|
/* Smoothed transition might not have happened so sync now */
|
|
|
|
pcm_sync_pcm_factors();
|
|
|
|
#endif
|
2013-04-05 08:36:05 +00:00
|
|
|
pcm_play_dma_start(addr, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void pcm_play_dma_pause_int(bool pause)
|
|
|
|
{
|
|
|
|
if (pause || pcm_get_bytes_waiting() > 0)
|
|
|
|
{
|
|
|
|
pcm_play_dma_pause(pause);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
logf(" no data");
|
|
|
|
pcm_play_data_start_int(NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void pcm_play_dma_stop_int(void)
|
|
|
|
{
|
|
|
|
pcm_play_dma_stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const void * pcm_play_dma_get_peak_buffer_int(int *count)
|
|
|
|
{
|
|
|
|
return pcm_play_dma_get_peak_buffer(count);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
|
|
|
|
const void **addr, size_t *size)
|
|
|
|
{
|
|
|
|
/* Check status callback first if error */
|
|
|
|
if (status < PCM_DMAST_OK)
|
|
|
|
status = pcm_play_dma_status_callback(status);
|
|
|
|
|
|
|
|
if (status >= PCM_DMAST_OK && pcm_get_more_int(addr, size))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Error, callback missing or no more DMA to do */
|
|
|
|
pcm_play_stop_int();
|
|
|
|
return false;
|
|
|
|
}
|
2013-04-23 07:20:49 +00:00
|
|
|
#endif /* !HAVE_SW_VOLUME_CONTROL || PCM_SW_VOLUME_UNBUFFERED */
|
2013-04-05 08:36:05 +00:00
|
|
|
|
|
|
|
static void pcm_play_data_start_int(const void *addr, size_t size)
|
2010-05-24 16:42:32 +00:00
|
|
|
{
|
2013-04-05 08:36:05 +00:00
|
|
|
ALIGN_AUDIOBUF(addr, size);
|
|
|
|
|
|
|
|
if ((addr && size) || pcm_get_more_int(&addr, &size))
|
|
|
|
{
|
|
|
|
pcm_apply_settings();
|
|
|
|
logf(" pcm_play_dma_start_int");
|
|
|
|
pcm_play_dma_start_int(addr, size);
|
|
|
|
pcm_playing = true;
|
|
|
|
pcm_paused = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Force a stop */
|
|
|
|
logf(" pcm_play_stop_int");
|
|
|
|
pcm_play_stop_int();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcm_play_pause_int(bool play)
|
|
|
|
{
|
|
|
|
if (play)
|
|
|
|
pcm_apply_settings();
|
|
|
|
|
|
|
|
logf(" pcm_play_dma_pause_int");
|
|
|
|
pcm_play_dma_pause_int(!play);
|
|
|
|
pcm_paused = !play && pcm_playing;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_stop_int(void)
|
|
|
|
{
|
|
|
|
pcm_play_dma_stop_int();
|
2010-05-24 16:42:32 +00:00
|
|
|
pcm_callback_for_more = NULL;
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_play_status_callback = NULL;
|
2010-05-24 16:42:32 +00:00
|
|
|
pcm_paused = false;
|
|
|
|
pcm_playing = false;
|
|
|
|
}
|
|
|
|
|
2011-09-01 12:15:43 +00:00
|
|
|
static void pcm_wait_for_init(void)
|
|
|
|
{
|
|
|
|
while (!pcm_is_ready)
|
|
|
|
sleep(0);
|
|
|
|
}
|
|
|
|
|
2007-10-06 22:27:27 +00:00
|
|
|
/**
|
2010-05-13 04:12:23 +00:00
|
|
|
* Perform peak calculation on a buffer of packed 16-bit samples.
|
2007-10-06 22:27:27 +00:00
|
|
|
*
|
|
|
|
* Used for recording and playback.
|
|
|
|
*/
|
2012-05-03 00:53:07 +00:00
|
|
|
static void pcm_peak_peeker(const int16_t *p, int count,
|
|
|
|
struct pcm_peaks *peaks)
|
2007-10-06 22:27:27 +00:00
|
|
|
{
|
2012-05-03 00:53:07 +00:00
|
|
|
uint32_t peak_l = 0, peak_r = 0;
|
|
|
|
const int16_t *pend = p + 2 * count;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2012-05-03 00:53:07 +00:00
|
|
|
int32_t s;
|
|
|
|
|
|
|
|
s = p[0];
|
|
|
|
|
|
|
|
if (s < 0)
|
|
|
|
s = -s;
|
|
|
|
|
|
|
|
if ((uint32_t)s > peak_l)
|
|
|
|
peak_l = s;
|
|
|
|
|
|
|
|
s = p[1];
|
|
|
|
|
|
|
|
if (s < 0)
|
|
|
|
s = -s;
|
|
|
|
|
|
|
|
if ((uint32_t)s > peak_r)
|
|
|
|
peak_r = s;
|
|
|
|
|
|
|
|
p += 4 * 2; /* Every 4th sample, interleaved */
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
2012-05-03 00:53:07 +00:00
|
|
|
while (p < pend);
|
2007-10-06 22:27:27 +00:00
|
|
|
|
2012-05-03 00:53:07 +00:00
|
|
|
peaks->left = peak_l;
|
|
|
|
peaks->right = peak_r;
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
2011-07-02 11:55:38 +00:00
|
|
|
void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active,
|
|
|
|
const void *addr, int count)
|
2007-10-06 22:27:27 +00:00
|
|
|
{
|
|
|
|
long tick = current_tick;
|
|
|
|
|
2011-07-02 11:55:38 +00:00
|
|
|
/* Peak no farther ahead than expected period to avoid overcalculation */
|
|
|
|
long period = tick - peaks->tick;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
|
|
|
/* Keep reasonable limits on period */
|
|
|
|
if (period < 1)
|
|
|
|
period = 1;
|
|
|
|
else if (period > HZ/5)
|
|
|
|
period = HZ/5;
|
|
|
|
|
2012-05-03 00:53:07 +00:00
|
|
|
peaks->period = (3*peaks->period + period) / 4;
|
2011-07-02 11:55:38 +00:00
|
|
|
peaks->tick = tick;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
2011-07-02 11:55:38 +00:00
|
|
|
if (active)
|
2007-10-06 22:27:27 +00:00
|
|
|
{
|
2011-07-02 11:55:38 +00:00
|
|
|
int framecount = peaks->period*pcm_curr_sampr / HZ;
|
2007-10-06 22:27:27 +00:00
|
|
|
count = MIN(framecount, count);
|
|
|
|
|
|
|
|
if (count > 0)
|
2012-05-03 00:53:07 +00:00
|
|
|
pcm_peak_peeker(addr, count, peaks);
|
2008-12-14 11:42:47 +00:00
|
|
|
/* else keep previous peak values */
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-07-02 11:55:38 +00:00
|
|
|
/* peaks are zero */
|
2012-05-03 00:53:07 +00:00
|
|
|
peaks->left = peaks->right = 0;
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
2011-07-02 11:55:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_calculate_peaks(int *left, int *right)
|
|
|
|
{
|
2012-05-03 00:53:07 +00:00
|
|
|
/* peak data for the global peak values - i.e. what the final output is */
|
|
|
|
static struct pcm_peaks peaks;
|
|
|
|
|
2011-07-02 11:55:38 +00:00
|
|
|
int count;
|
2013-04-05 08:36:05 +00:00
|
|
|
const void *addr = pcm_play_dma_get_peak_buffer_int(&count);
|
2011-07-02 11:55:38 +00:00
|
|
|
|
2012-05-03 00:53:07 +00:00
|
|
|
pcm_do_peak_calculation(&peaks, pcm_playing && !pcm_paused,
|
2011-07-02 11:55:38 +00:00
|
|
|
addr, count);
|
2007-10-06 22:27:27 +00:00
|
|
|
|
|
|
|
if (left)
|
2012-05-03 00:53:07 +00:00
|
|
|
*left = peaks.left;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
|
|
|
if (right)
|
2012-05-03 00:53:07 +00:00
|
|
|
*right = peaks.right;
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
2013-04-05 08:36:05 +00:00
|
|
|
const void * pcm_get_peak_buffer(int *count)
|
2010-02-10 19:44:11 +00:00
|
|
|
{
|
2013-04-05 08:36:05 +00:00
|
|
|
return pcm_play_dma_get_peak_buffer_int(count);
|
2010-02-10 19:44:11 +00:00
|
|
|
}
|
|
|
|
|
2010-05-24 16:42:32 +00:00
|
|
|
bool pcm_is_playing(void)
|
|
|
|
{
|
|
|
|
return pcm_playing;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool pcm_is_paused(void)
|
|
|
|
{
|
|
|
|
return pcm_paused;
|
|
|
|
}
|
|
|
|
|
2007-10-06 22:27:27 +00:00
|
|
|
/****************************************************************************
|
|
|
|
* Functions that do not require targeted implementation but only a targeted
|
|
|
|
* interface
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* This should only be called at startup before any audio playback or
|
|
|
|
recording is attempted */
|
|
|
|
void pcm_init(void)
|
|
|
|
{
|
|
|
|
logf("pcm_init");
|
|
|
|
|
2008-12-12 11:01:07 +00:00
|
|
|
pcm_set_frequency(HW_SAMPR_DEFAULT);
|
|
|
|
|
2007-10-06 22:27:27 +00:00
|
|
|
logf(" pcm_play_dma_init");
|
|
|
|
pcm_play_dma_init();
|
|
|
|
}
|
|
|
|
|
2011-09-01 12:15:43 +00:00
|
|
|
/* Finish delayed init */
|
|
|
|
void pcm_postinit(void)
|
|
|
|
{
|
|
|
|
logf("pcm_postinit");
|
|
|
|
|
|
|
|
logf(" pcm_play_dma_postinit");
|
|
|
|
|
|
|
|
pcm_play_dma_postinit();
|
|
|
|
|
|
|
|
pcm_is_ready = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool pcm_is_initialized(void)
|
|
|
|
{
|
|
|
|
return pcm_is_ready;
|
|
|
|
}
|
|
|
|
|
2010-05-24 16:42:32 +00:00
|
|
|
void pcm_play_data(pcm_play_callback_type get_more,
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_status_callback_type status_cb,
|
|
|
|
const void *start, size_t size)
|
2007-10-06 22:27:27 +00:00
|
|
|
{
|
|
|
|
logf("pcm_play_data");
|
|
|
|
|
|
|
|
pcm_play_lock();
|
|
|
|
|
|
|
|
pcm_callback_for_more = get_more;
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_play_status_callback = status_cb;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
2013-04-05 08:36:05 +00:00
|
|
|
logf(" pcm_play_data_start_int");
|
|
|
|
pcm_play_data_start_int(start, size);
|
2007-10-06 22:27:27 +00:00
|
|
|
|
|
|
|
pcm_play_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_pause(bool play)
|
|
|
|
{
|
|
|
|
logf("pcm_play_pause: %s", play ? "play" : "pause");
|
|
|
|
|
|
|
|
pcm_play_lock();
|
|
|
|
|
|
|
|
if (play == pcm_paused && pcm_playing)
|
|
|
|
{
|
2013-04-05 08:36:05 +00:00
|
|
|
logf(" pcm_play_pause_int");
|
|
|
|
pcm_play_pause_int(play);
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pcm_play_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_stop(void)
|
|
|
|
{
|
|
|
|
logf("pcm_play_stop");
|
|
|
|
|
|
|
|
pcm_play_lock();
|
|
|
|
|
|
|
|
if (pcm_playing)
|
|
|
|
{
|
2013-04-05 08:36:05 +00:00
|
|
|
logf(" pcm_play_stop_int");
|
|
|
|
pcm_play_stop_int();
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pcm_play_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**/
|
|
|
|
|
2008-12-12 11:01:07 +00:00
|
|
|
/* set frequency next frequency used by the audio hardware -
|
|
|
|
* what pcm_apply_settings will set */
|
|
|
|
void pcm_set_frequency(unsigned int samplerate)
|
|
|
|
{
|
|
|
|
logf("pcm_set_frequency");
|
|
|
|
|
2010-06-26 10:07:17 +00:00
|
|
|
int index;
|
|
|
|
|
|
|
|
#ifdef CONFIG_SAMPR_TYPES
|
|
|
|
unsigned int type = samplerate & SAMPR_TYPE_MASK;
|
|
|
|
samplerate &= ~SAMPR_TYPE_MASK;
|
|
|
|
|
|
|
|
/* For now, supported targets have direct conversion when configured with
|
|
|
|
* CONFIG_SAMPR_TYPES.
|
|
|
|
* Some hypothetical target with independent rates would need slightly
|
|
|
|
* different handling throughout this source. */
|
2011-12-08 19:20:00 +00:00
|
|
|
samplerate = pcm_sampr_to_hw_sampr(samplerate, type);
|
2010-06-26 10:07:17 +00:00
|
|
|
#endif /* CONFIG_SAMPR_TYPES */
|
|
|
|
|
|
|
|
index = round_value_to_list32(samplerate, hw_freq_sampr,
|
|
|
|
HW_NUM_FREQ, false);
|
2008-12-12 11:01:07 +00:00
|
|
|
|
|
|
|
if (samplerate != hw_freq_sampr[index])
|
|
|
|
index = HW_FREQ_DEFAULT; /* Invalid = default */
|
|
|
|
|
|
|
|
pcm_sampr = hw_freq_sampr[index];
|
|
|
|
pcm_fsel = index;
|
|
|
|
}
|
|
|
|
|
2013-05-23 17:58:51 +00:00
|
|
|
/* return last-set frequency */
|
|
|
|
unsigned int pcm_get_frequency(void)
|
|
|
|
{
|
|
|
|
return pcm_sampr;
|
|
|
|
}
|
|
|
|
|
2008-12-12 11:01:07 +00:00
|
|
|
/* apply pcm settings to the hardware */
|
|
|
|
void pcm_apply_settings(void)
|
|
|
|
{
|
|
|
|
logf("pcm_apply_settings");
|
|
|
|
|
2011-09-01 12:15:43 +00:00
|
|
|
pcm_wait_for_init();
|
|
|
|
|
2008-12-12 11:01:07 +00:00
|
|
|
if (pcm_sampr != pcm_curr_sampr)
|
|
|
|
{
|
|
|
|
logf(" pcm_dma_apply_settings");
|
|
|
|
pcm_dma_apply_settings();
|
|
|
|
pcm_curr_sampr = pcm_sampr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-06 22:27:27 +00:00
|
|
|
#ifdef HAVE_RECORDING
|
|
|
|
/** Low level pcm recording apis **/
|
|
|
|
|
|
|
|
/* Next start for recording peaks */
|
2010-05-12 14:05:36 +00:00
|
|
|
static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
|
2007-10-06 22:27:27 +00:00
|
|
|
/* the registered callback function for when more data is available */
|
2010-05-24 16:42:32 +00:00
|
|
|
static volatile pcm_rec_callback_type
|
2008-04-06 04:34:57 +00:00
|
|
|
pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
|
2012-02-23 13:14:46 +00:00
|
|
|
volatile pcm_status_callback_type
|
|
|
|
pcm_rec_status_callback SHAREDBSS_ATTR = NULL;
|
2007-10-06 22:27:27 +00:00
|
|
|
/* DMA transfer in is currently active */
|
2008-04-06 04:34:57 +00:00
|
|
|
volatile bool pcm_recording SHAREDBSS_ATTR = false;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
2010-05-24 16:42:32 +00:00
|
|
|
/* Called internally by functions to reset the state */
|
|
|
|
static void pcm_recording_stopped(void)
|
|
|
|
{
|
|
|
|
pcm_recording = false;
|
|
|
|
pcm_callback_more_ready = NULL;
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_rec_status_callback = NULL;
|
2010-05-24 16:42:32 +00:00
|
|
|
}
|
|
|
|
|
2007-10-06 22:27:27 +00:00
|
|
|
/**
|
|
|
|
* Return recording peaks - From the end of the last peak up to
|
|
|
|
* current write position.
|
|
|
|
*/
|
|
|
|
void pcm_calculate_rec_peaks(int *left, int *right)
|
|
|
|
{
|
2012-05-03 00:53:07 +00:00
|
|
|
static struct pcm_peaks peaks;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
|
|
|
if (pcm_recording)
|
|
|
|
{
|
2012-05-03 00:53:07 +00:00
|
|
|
const int16_t *peak_addr = pcm_rec_peak_addr;
|
|
|
|
const int16_t *addr = pcm_rec_dma_get_peak_buffer();
|
2010-05-12 14:05:36 +00:00
|
|
|
|
|
|
|
if (addr != NULL)
|
2007-10-06 22:27:27 +00:00
|
|
|
{
|
2012-05-03 00:53:07 +00:00
|
|
|
int count = (addr - peak_addr) / 2; /* Interleaved L+R */
|
2007-10-06 22:27:27 +00:00
|
|
|
|
2010-05-12 14:05:36 +00:00
|
|
|
if (count > 0)
|
|
|
|
{
|
2012-05-03 00:53:07 +00:00
|
|
|
pcm_peak_peeker(peak_addr, count, &peaks);
|
2010-05-12 14:05:36 +00:00
|
|
|
|
|
|
|
if (peak_addr == pcm_rec_peak_addr)
|
|
|
|
pcm_rec_peak_addr = addr;
|
|
|
|
}
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
2008-12-14 11:42:47 +00:00
|
|
|
/* else keep previous peak values */
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-05-03 00:53:07 +00:00
|
|
|
peaks.left = peaks.right = 0;
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (left)
|
2012-05-03 00:53:07 +00:00
|
|
|
*left = peaks.left;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
|
|
|
if (right)
|
2012-05-03 00:53:07 +00:00
|
|
|
*right = peaks.right;
|
|
|
|
}
|
2007-10-06 22:27:27 +00:00
|
|
|
|
2010-05-24 16:42:32 +00:00
|
|
|
bool pcm_is_recording(void)
|
|
|
|
{
|
|
|
|
return pcm_recording;
|
|
|
|
}
|
|
|
|
|
2007-10-06 22:27:27 +00:00
|
|
|
/****************************************************************************
|
|
|
|
* Functions that do not require targeted implementation but only a targeted
|
|
|
|
* interface
|
|
|
|
*/
|
2010-05-24 16:42:32 +00:00
|
|
|
|
2007-10-06 22:27:27 +00:00
|
|
|
void pcm_init_recording(void)
|
|
|
|
{
|
|
|
|
logf("pcm_init_recording");
|
|
|
|
|
2011-09-01 12:15:43 +00:00
|
|
|
pcm_wait_for_init();
|
|
|
|
|
2011-06-29 06:37:04 +00:00
|
|
|
/* Stop the beasty before attempting recording */
|
|
|
|
mixer_reset();
|
|
|
|
|
2007-10-07 23:19:09 +00:00
|
|
|
/* Recording init is locked unlike general pcm init since this is not
|
|
|
|
* just a one-time event at startup and it should and must be safe by
|
|
|
|
* now. */
|
2007-10-06 22:27:27 +00:00
|
|
|
pcm_rec_lock();
|
|
|
|
|
|
|
|
logf(" pcm_rec_dma_init");
|
2010-05-24 16:42:32 +00:00
|
|
|
pcm_recording_stopped();
|
2007-10-06 22:27:27 +00:00
|
|
|
pcm_rec_dma_init();
|
|
|
|
|
|
|
|
pcm_rec_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_close_recording(void)
|
|
|
|
{
|
|
|
|
logf("pcm_close_recording");
|
|
|
|
|
|
|
|
pcm_rec_lock();
|
|
|
|
|
|
|
|
if (pcm_recording)
|
|
|
|
{
|
|
|
|
logf(" pcm_rec_dma_stop");
|
|
|
|
pcm_rec_dma_stop();
|
2010-05-24 16:42:32 +00:00
|
|
|
pcm_recording_stopped();
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
logf(" pcm_rec_dma_close");
|
|
|
|
pcm_rec_dma_close();
|
|
|
|
|
|
|
|
pcm_rec_unlock();
|
|
|
|
}
|
|
|
|
|
2010-05-24 16:42:32 +00:00
|
|
|
void pcm_record_data(pcm_rec_callback_type more_ready,
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_status_callback_type status_cb,
|
|
|
|
void *addr, size_t size)
|
2007-10-06 22:27:27 +00:00
|
|
|
{
|
|
|
|
logf("pcm_record_data");
|
|
|
|
|
2012-02-23 13:14:46 +00:00
|
|
|
ALIGN_AUDIOBUF(addr, size);
|
2010-05-12 14:05:36 +00:00
|
|
|
|
2012-02-23 13:14:46 +00:00
|
|
|
if (!(addr && size))
|
2007-10-06 22:27:27 +00:00
|
|
|
{
|
|
|
|
logf(" no buffer");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcm_rec_lock();
|
|
|
|
|
|
|
|
pcm_callback_more_ready = more_ready;
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_rec_status_callback = status_cb;
|
2007-10-06 22:27:27 +00:00
|
|
|
|
2010-05-12 14:05:36 +00:00
|
|
|
/* Need a physical DMA address translation, if not already physical. */
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_rec_peak_addr = pcm_rec_dma_addr(addr);
|
2010-05-12 14:05:36 +00:00
|
|
|
|
2007-10-06 22:27:27 +00:00
|
|
|
logf(" pcm_rec_dma_start");
|
2008-12-12 11:01:07 +00:00
|
|
|
pcm_apply_settings();
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_rec_dma_start(addr, size);
|
2007-10-06 22:27:27 +00:00
|
|
|
pcm_recording = true;
|
|
|
|
|
|
|
|
pcm_rec_unlock();
|
|
|
|
} /* pcm_record_data */
|
|
|
|
|
|
|
|
void pcm_stop_recording(void)
|
|
|
|
{
|
|
|
|
logf("pcm_stop_recording");
|
|
|
|
|
|
|
|
pcm_rec_lock();
|
|
|
|
|
|
|
|
if (pcm_recording)
|
|
|
|
{
|
|
|
|
logf(" pcm_rec_dma_stop");
|
|
|
|
pcm_rec_dma_stop();
|
2010-05-24 16:42:32 +00:00
|
|
|
pcm_recording_stopped();
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pcm_rec_unlock();
|
|
|
|
} /* pcm_stop_recording */
|
|
|
|
|
2012-02-23 13:14:46 +00:00
|
|
|
bool pcm_rec_dma_complete_callback(enum pcm_dma_status status,
|
|
|
|
void **addr, size_t *size)
|
2010-05-12 14:05:36 +00:00
|
|
|
{
|
2012-02-23 13:14:46 +00:00
|
|
|
/* Check status callback first if error */
|
|
|
|
if (status < PCM_DMAST_OK)
|
|
|
|
status = pcm_rec_dma_status_callback(status);
|
2010-05-12 14:05:36 +00:00
|
|
|
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_rec_callback_type have_more = pcm_callback_more_ready;
|
2010-05-12 14:05:36 +00:00
|
|
|
|
2012-02-23 13:14:46 +00:00
|
|
|
if (have_more && status >= PCM_DMAST_OK)
|
2010-05-24 16:42:32 +00:00
|
|
|
{
|
2012-02-23 13:14:46 +00:00
|
|
|
/* Call registered callback to obtain next buffer */
|
|
|
|
have_more(addr, size);
|
|
|
|
ALIGN_AUDIOBUF(*addr, *size);
|
2010-05-12 14:05:36 +00:00
|
|
|
|
2012-02-23 13:14:46 +00:00
|
|
|
if (*addr && *size)
|
2010-05-24 16:42:32 +00:00
|
|
|
{
|
|
|
|
/* Need a physical DMA address translation, if not already
|
|
|
|
* physical. */
|
2012-02-23 13:14:46 +00:00
|
|
|
pcm_rec_peak_addr = pcm_rec_dma_addr(*addr);
|
|
|
|
return true;
|
2010-05-24 16:42:32 +00:00
|
|
|
}
|
|
|
|
}
|
2008-12-13 06:01:08 +00:00
|
|
|
|
2010-05-24 16:42:32 +00:00
|
|
|
/* Error, callback missing or no more DMA to do */
|
|
|
|
pcm_rec_dma_stop();
|
|
|
|
pcm_recording_stopped();
|
2012-02-23 13:14:46 +00:00
|
|
|
|
|
|
|
return false;
|
2007-10-06 22:27:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_RECORDING */
|