PCM DUMP

【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");
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章