【ALSA】【USBAudio】【PCMDUMP】
基本原理
通過ALSA Capture 提供的接口,在kernel實現Audioloop.
Kernel實現
1. 實現AudioCapture節點操作集
在sound/usb/pcm.c加入如下代碼:
struct usb_dummy_capture{
struct snd_pcm_substream *pcm_substream;
snd_pcm_format_t pcm_format; /* current audio format (for hw_params callback) */
unsigned int channels; /* current number of channels (for hw_params callback) */
unsigned int cur_rate; /* current rate (for hw_params callback) */
unsigned int period_bytes; /* current period bytes (for hw_params callback) */
unsigned int running: 1; /* running status */
unsigned int hwptr_done; /* processed byte position in the buffer */
unsigned int transfer_done; /* processed frames since last period update */
};
static struct usb_dummy_capture g_dummy_capture;
static struct snd_pcm_hardware snd_usb_dummy_hardware =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 1024 * 1024,
.period_bytes_min = 64,
.period_bytes_max = 512 * 1024,
.periods_min = 2,
.periods_max = 1024,
};
static int snd_usb_dummy_open(struct snd_pcm_substream *substream)
{
g_dummy_capture.pcm_substream = substream;
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw = snd_usb_dummy_hardware;
return 0;
}
static int snd_usb_dummy_close(struct snd_pcm_substream *substream)
{
g_dummy_capture.running = 0;
return 0;
}
static int snd_usb_dummy_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
int ret;
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
g_dummy_capture.pcm_format = params_format(hw_params);
g_dummy_capture.period_bytes = params_period_bytes(hw_params);
g_dummy_capture.channels = params_channels(hw_params);
g_dummy_capture.cur_rate = params_rate(hw_params);
g_dummy_capture.hwptr_done = 0;
g_dummy_capture.transfer_done = 0;
return 0;
}
static int snd_usb_dummy_hw_free(struct snd_pcm_substream *substream)
{
g_dummy_capture.running = 0;
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int snd_usb_dummy_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
g_dummy_capture.hwptr_done = 0;
g_dummy_capture.transfer_done = 0;
runtime->delay = 0;
return ret;
}
static int snd_usb_substream_dummy_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
g_dummy_capture.hwptr_done = 0;
g_dummy_capture.transfer_done = 0;
g_dummy_capture.running = 1;
return 0;
case SNDRV_PCM_TRIGGER_STOP:
g_dummy_capture.running = 0;
return 0;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
g_dummy_capture.running = 0;
return 0;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
g_dummy_capture.hwptr_done = 0;
g_dummy_capture.transfer_done = 0;
g_dummy_capture.running = 1;
return 0;
}
return -EINVAL;
}
static snd_pcm_uframes_t snd_usb_dummy_pcm_pointer(struct snd_pcm_substream *substream)
{
unsigned int hwptr_done;
hwptr_done = g_dummy_capture.hwptr_done;
return hwptr_done / (substream->runtime->frame_bits >> 3);
}
static struct snd_pcm_ops snd_usb_dummy_capture_ops = {
.open = snd_usb_dummy_open,
.close = snd_usb_dummy_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_usb_dummy_hw_params,
.hw_free = snd_usb_dummy_hw_free,
.prepare = snd_usb_dummy_pcm_prepare,
.trigger = snd_usb_substream_dummy_capture_trigger,
.pointer = snd_usb_dummy_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
2.替換原始Audiocapture操作集
void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream)
{
snd_pcm_set_ops(pcm, stream,
stream == SNDRV_PCM_STREAM_PLAYBACK ?
- &snd_usb_playback_ops : &snd_usb_capture_ops);
+ &snd_usb_playback_ops : &snd_usb_dummy_capture_ops);
}
3.實現AudioLoop操作
static void retire_usb_dummy(unsigned char * pcmData,
unsigned int size)
{
if(0 == g_dummy_capture.running || NULL == g_dummy_capture.pcm_substream)
return;
struct snd_pcm_runtime *runtime = g_dummy_capture.pcm_substream->runtime;
unsigned int stride, frames, bytes, oldptr;
int period_elapsed = 0;
stride = runtime->frame_bits >> 3;
frames = size / stride;
bytes = frames * stride;
oldptr = g_dummy_capture.hwptr_done;
g_dummy_capture.hwptr_done += bytes;
if (g_dummy_capture.hwptr_done >= runtime->buffer_size * stride)
g_dummy_capture.hwptr_done -= runtime->buffer_size * stride;
frames = (bytes + (oldptr % stride)) / stride;
g_dummy_capture.transfer_done += frames;
if (g_dummy_capture.transfer_done >= runtime->period_size) {
g_dummy_capture.transfer_done -= runtime->period_size;
period_elapsed = 1;
}
/* copy a data chunk */
if (oldptr + bytes > runtime->buffer_size * stride) {
unsigned int bytes1 =
runtime->buffer_size * stride - oldptr;
memcpy(runtime->dma_area + oldptr, pcmData, bytes1);
memcpy(runtime->dma_area, pcmData + bytes1, bytes - bytes1);
} else {
memcpy(runtime->dma_area + oldptr, pcmData, bytes);
}
if (period_elapsed && g_dummy_capture.running)
snd_pcm_period_elapsed(g_dummy_capture.pcm_substream);
}
@@ -1355,11 +1415,19 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
runtime->buffer_size * stride - subs->hwptr_done;
memcpy(urb->transfer_buffer,
runtime->dma_area + subs->hwptr_done, bytes1);
+
+ retire_usb_dummy(runtime->dma_area + subs->hwptr_done,bytes1);
+
memcpy(urb->transfer_buffer + bytes1,
runtime->dma_area, bytes - bytes1);
+
+ retire_usb_dummy(runtime->dma_area,bytes - bytes1);
+
} else {
memcpy(urb->transfer_buffer,
runtime->dma_area + subs->hwptr_done, bytes);
+
+ retire_usb_dummy(runtime->dma_area+ subs->hwptr_done,bytes);
}
subs->hwptr_done += bytes;
4.添加AudioCapture節點
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 7db2f89..a763553 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -352,7 +352,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
as->fmt_type = fp->fmt_type;
err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
- stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
+ 1,//stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
&pcm);
if (err < 0) {
kfree(as);
@@ -368,6 +368,8 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
strcpy(pcm->name, "USB Audio");
snd_usb_init_substream(as, stream, fp);
+ snd_usb_set_pcm_ops(pcm, SNDRV_PCM_STREAM_CAPTURE);
+ snd_printdd(KERN_INFO "usb Audio create a stream for cpature\n");