Re: Driver design question

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2006-10-20 at 14:55 +0200, Takashi Iwai wrote:
> > Anyway, I still can't make it work with the PCM indirect API.  The best
> > I've managed to produce is choppy sound that seems to underrun every
> > period.  Do you see what could be wrong with my code?
> 
> You should give a fixed period size (0x2000), i.e. set both
> period_bytes_min and period_bytes_max = 0x4000 (and periods_min = 2) in
> snd_pcm_hardware.  In this way, the ack is called at least at each
> time 0x2000 words are processed.
> 

I am doing this already.

> BTW, what does dream_get_mpu_data_poll_status_byte() do?
> Does it check whether you can another push 0x2000 words?
> 

Yes, exactly.

> If so, you should check this before claling
> snd_pcm_indirect_playback_transfer() in dream_work_transfer().
> 

OK, I've made this change.

> 
> Possibly, the block transfer patch might be not needed if you add this
> check.
> 

OK, I feel I am getting closer.  But it seems that only the first period
is played.  Upon further inspection it looks like
snd_pcm_indirect_playback_transfer only causes dream_playback_copy to be
invoked the first time:

~ # aplay /usr/share/sounds/test/phone.wav 
Playing WAVE '/usr/share/sounds/test/phone.wav': Signed 16 bit Little
Endian, Rate 44100 Hz, Stereo
hw_params: stereo yes, format 16bit, rate 44100
dream_pcm_open: channel 0x0 eight_bit 0x0 stereo 0x1 rate 0xac44
dream_playback_ack
dream_playback_copy: not running!
dream_pcm_start: channel is 0
dream_work_transfer: polling status byte returned 0
calling snd_pcm_indirect_playback_transfer
in dream_playback_copy: 0x4000 bytes DMA area 0xcdfb0000 sw_data 0x0
dream_playback_ack
dream_playback_ack
dream_work_transfer: polling status byte returned 0
calling snd_pcm_indirect_playback_transfer
dream_playback_ack
playback size is 0x1005
dream_playback_ack
playback size is 0x100a
dream_playback_ack
playback size is 0x100f
dream_playback_ack
playback size is 0x1015
dream_playback_ack
dream_playback_ack

(eventually underruns and errors are reported)

Here is the code:

/* transfer the given size of data to the hardware */
static void dream_playback_copy(snd_pcm_substream_t *substream,
                                snd_pcm_indirect_t *rec, size_t bytes)
{
        struct snd_card_dream *dream = snd_pcm_substream_chip(substream);
        snd_pcm_runtime_t *runtime = substream->runtime;
        struct snd_card_dream_pcm *dpcm = runtime->private_data;
        unsigned char *src = runtime->dma_area + rec->sw_data;

        unsigned short * pcm_word = (unsigned short *) src;
        int words = bytes / 2;

        printk("in dream_playback_copy: 0x%x bytes DMA area 0x%p sw_data 0x%i\n", (int) bytes, runtime->dma_area, rec->sw_data);

        if (dpcm->stopping)
                words--;

        while (words--) {
                mpu_write16(dream, &dream->midi2, cpu_to_le16(*pcm_word++), 0); /* TBD: don't swap for 8 bits WAV */
                dpcm->playback_chunk_left--;
                if (! dpcm->playback_chunk_left) {
                        dpcm->playback_chunk_left = 0x2000;
                        dream_pcm_end_xfer(dpcm->dream, dpcm->channel);
                }
        }

        if (dpcm->stopping) {
                mdelay(100);
                dream_pcm_close(dream, dpcm->channel);
                dpcm->running = 0;
        }
}

static void dream_work_transfer(void *data)
{
        struct snd_card_dream *dream = data;
        int res;
        snd_pcm_substream_t *substream = dream->playback_substream;
        snd_pcm_runtime_t *runtime = substream->runtime;
        struct snd_card_dream_pcm *dpcm = runtime->private_data;

        if (!substream || !runtime)
                return;

        if (! dpcm->running) {
                printk("dream_playback_copy: not running!\n");
                /* Send PCM_START */
                dream_pcm_start(dream, dpcm->channel);
                dpcm->running = 1;
        }

        res = dream_get_mpu_data_poll_status_byte(dream,
                                                  &dream->midi2,
                                                  MPU401_IT_MASK,
                                                  MPU401_IT_VALUE, 100);

        printk("dream_work_transfer: polling status byte returned %i\n", res);

        if (res < 0)
                return;

        printk("calling snd_pcm_indirect_playback_transfer\n", res);
        snd_pcm_indirect_playback_transfer(substream, &dream->playback_rec, dream_playback_copy);
}

/* ack callback - just schedule work */
static int dream_playback_ack(snd_pcm_substream_t *substream)
{
        struct snd_card_dream *dream = snd_pcm_substream_chip(substream);

        printk("dream_playback_ack\n");
        schedule_work(&dream->playback_work);

        return 0;
}

/* cancel work */
static void dream_playback_cancel(snd_pcm_substream_t *substream)
{
        struct snd_card_dream *dream = snd_pcm_substream_chip(substream);

        cancel_delayed_work(&dream->playback_work);
}

/* trigger callback */
static int dream_playback_trigger(snd_pcm_substream_t *substream, int cmd)
{
        snd_pcm_runtime_t *runtime = substream->runtime;
        struct snd_card_dream_pcm *dpcm = runtime->private_data;

        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
                dream_playback_ack(substream); /* fill the data */
                snd_card_dream_pcm_timer_start(substream);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                dpcm->stopping = 1;
                dream_playback_ack(substream); /* send final incomplete 1/2 bufffer and close */
                snd_card_dream_pcm_timer_stop(substream);
                //dream_playback_cancel(substream);
                break;
        default:
                return -EINVAL;
        }
        return 0;
}

static snd_pcm_uframes_t dream_playback_pointer(snd_pcm_substream_t *substream)
{
        struct snd_card_dream *dream = snd_pcm_substream_chip(substream);
        snd_pcm_uframes_t pointer;

        pointer = snd_pcm_indirect_playback_pointer(substream, &dream->playback_rec, dream->playback_hw_ptr);
        //printk("dream_playback_pointer: 0x%x\n", (unsigned int) pointer);
        return pointer;
}

#endif
static int snd_card_dream_pcm_prepare(snd_pcm_substream_t * substream)
{
        snd_pcm_runtime_t *runtime = substream->runtime;
        struct snd_card_dream_pcm *dpcm = runtime->private_data;
        struct snd_card_dream *dream = snd_pcm_substream_chip(substream);

        dpcm->frames_per_period = runtime->rate / HZ;
        dpcm->frac_per_period = runtime->rate % HZ;
        dpcm->playback_chunk_left = 0x2000;

        memset(&dream->playback_rec, 0, sizeof(snd_pcm_indirect_t));
        dream->playback_rec.hw_buffer_size = 0x4000; /* byte size */
        dream->playback_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
        dream->playback_hw_ptr = 0;
        dream->playback_hw_ptr_frac = 0;
        return 0;
}


static void snd_card_dream_pcm_timer_function(unsigned long data)
{
        struct snd_card_dream_pcm *dpcm = (struct snd_card_dream_pcm *)data;

        dpcm->timer.expires = 1 + jiffies;
        add_timer(&dpcm->timer);

#ifdef RLR_PCM_REPLAY
        if (!dpcm->substream) {
                printk("substream null in timer func\n");
                return;
        }
        struct snd_card_dream *dream = snd_pcm_substream_chip(dpcm->substream);
        snd_pcm_runtime_t *runtime = dpcm->substream->runtime;
        if (!runtime) {
                printk("runtime null in timer func\n");
                return;
        }
        unsigned int size;
        dream->playback_hw_ptr += dpcm->frames_per_period;
        dream->playback_hw_ptr_frac += dpcm->frac_per_period;
        if (dream->playback_hw_ptr_frac >= HZ) {
                dream->playback_hw_ptr++;
                dream->playback_hw_ptr_frac -= HZ;
        }
        dream->playback_hw_ptr %= runtime->buffer_size;

        if (dream->playback_hw_ptr < dream->playback_last_hw_ptr)
                size = runtime->buffer_size + dream->playback_hw_ptr - dream->playback_last_hw_ptr;
        else
                size = dream->playback_hw_ptr - dream->playback_last_hw_ptr;
        dream->playback_last_hw_ptr = dream->playback_hw_ptr;
        dream->playback_size += size;
        if (dream->playback_size >= runtime->period_size) {
                printk("playback size is 0x%x\n", dream->playback_size);
                dream->playback_size %= runtime->period_size;
                snd_pcm_period_elapsed(dpcm->substream);
	}
}

static snd_pcm_ops_t dream_playback_ops = {
        .open = snd_card_dream_playback_open,
        .close = snd_card_dream_playback_close,
        .ioctl = snd_pcm_lib_ioctl,
        .hw_params = snd_card_dream_hw_params,
        .hw_free = snd_card_dream_hw_free,
        .prepare = snd_card_dream_playback_prepare,
        .pointer = dream_playback_pointer,
        .trigger = dream_playback_trigger,
        .ack = dream_playback_ack,
};



-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux