[alsa-devel] playback delay and problems with simultaneous playback/capture

mein_name at ist-einmalig.de mein_name at ist-einmalig.de
Wed Jan 6 15:51:31 CET 2010


I am currently testing my driver but I've got some problems with the "pcm machine"...as it is my first ALSA driver I am open for every hint. The code currently is not conform to linux kernel coding style, I will do all such cosmetically stuff after functionality is given.

1.
Playing a file works fine, but when the ALSA middle layer triggers stop command, the DSP still has data to play, so sending a stop signal to the DSP at this moment would result in breaking the playback. The strange thing here is, that it seems there are 6 frames missing, but the buffer of the DSP is only about 3 frames long. I am not sure if the problem here is, that I am not correctly "calculating" the pcm_pointer:

static snd_pcm_uframes_t
  snd_card_mychip_pcm_pointer(struct snd_pcm_substream *substream)
{
  struct snd_pcm_runtime *runtime = substream->runtime;
  struct snd_mychip_pcm *dpcm = runtime->private_data;


  /* get the current hardware pointer */
  // TODO: calc correct return value

  print_log(DEBUGC "pcm pointer: ret b=%lu\n",
     bytes_to_frames(runtime, dpcm->pcm_buf_pos));

  return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
}


2.
when playing and capturing data simultaneously, I get troubles in both. Playback or Capture alone is working, but if both come togheter, troubles occur. The data-transfer from ALSA to DSP and DSP to ALSA I am doing in the timer function, I am not sure if this could be the problem...but to send data to the DSP I have to check a flag if the DSP is able to receive data, if so, I can send data, otherwise I have to wait until DSP is ready to receive data. That is done with dsp_sendstream. When capturing, the DSP is sending an interrupt every captured period that is available, so I store that in a buffer from where it got read frequently via the timer function (done by readbuf)...everytime when going through this function I think that this way may work, as it does normally, but I am absolutely not sure if this is a "right" way. I know that there are so called "copy" and "silence" callbacks, but using the copy callback for playback I lost frames, so I did it like it is now...

static void snd_card_mychip_pcm_timer_function(unsigned long data)
{
    struct snd_mychip_pcm *dpcm = (struct snd_mychip_pcm *)data;
    unsigned long flags;
    int count, pos, status;

    // disable interrupts
    spin_lock_irqsave(&dpcm->lock, flags);

    // restart the timer to be called again after one jiffie = 1 ms
    dpcm->timer.expires = 1 + jiffies;
    add_timer(&dpcm->timer);

    if (dpcm->substream->stream == 0) { // playback stream
        // increase counters by bytes per jiffie
        if (dpcm->sendingnotpossible == 0) {     
          dpcm->pcm_irq_pos += dpcm->pcm_jiffie;  //0..MAX_PERIOD_SIZE step=jiffie
          dpcm->pcm_buf_pos += dpcm->pcm_jiffie;  //0..MAX_BUFFER_SIZE step=jiffie
        }
        // if pcm_count has been reached, call snd_pcm_period_elapsed
        // to signal the end of one period (e.g. 288 for mpg or 768 for wav)
        if (dpcm->pcm_irq_pos >= dpcm->pcm_count) { // >= MAX_PERIOD_SIZE? 
            count=dpcm->pcm_count;        // number of bytes to be sent
            pos=dpcm->pcm_buf_pos-count;  // start offset in DMA buffer
            // try to send data to dsp
            status = dsp_sendstream(dpcm->substream->pcm->device, dpcm->substream->runtime->dma_area+pos, count);
            if (status != 0) {  // sending not possible
                dpcm->sendingnotpossible=1;
            } else {            // sending successful
                dpcm->sendingnotpossible=0;
                dpcm->sentframes++;
                if (dpcm->sentframes == 3) // 3 frames in buffer so we can start playback
                {
                  // start decoder playback
                  status=dsp_decod_action(dpcm->substream->pcm->device, DECOD_START);
                  print_log(DEBUGA "reply of dsp_decod_action = %i\n",status);
                }
                // wrap counter if end of buffer is reached
                dpcm->pcm_buf_pos %= dpcm->pcm_size;
                // wrap this counter too
                dpcm->pcm_irq_pos %= dpcm->pcm_count;    
                // tell the PCM middle layer when the buffer position goes across the prescribed period size
                snd_pcm_period_elapsed(dpcm->substream);
                print_log(DEBUGC "timer_function: Period elapsed (p) - pcm_count=%i - pcm_size=%i - src=%p - jiffie=%i\n",dpcm->pcm_count,dpcm->pcm_size,dpcm->substream->runtime->dma_area,dpcm->pcm_jiffie);
            }
            // enable interrupts
            spin_unlock_irqrestore(&dpcm->lock, flags);
        } else {  
            // wrap counter if end of buffer is reached
            dpcm->pcm_buf_pos %= dpcm->pcm_size;
            // enable interrupts
            spin_unlock_irqrestore(&dpcm->lock, flags);
        }
    } else if (dpcm->substream->stream == 1) { // capture stream
        count = dpcm->pcm_count;  // number of bytes to be captured
        pos = dpcm->pcm_buf_pos;  // start offset in DMA buffer
        // read from HIF buffer
        status = readbuf(dpcm->substream->pcm->device, dpcm->substream->runtime->dma_area+pos, count);
        if (status == 0) {  // we got new data from HIF buffer => increase pointers..
            dpcm->pcm_buf_pos += count;
            // wrap counter if end of buffer is reached
            dpcm->pcm_buf_pos %= dpcm->pcm_size;
            // wrap this counter too
            //dpcm->pcm_irq_pos %= dpcm->pcm_count;    
            // tell the PCM middle layer when the buffer position goes across the prescribed period size
            snd_pcm_period_elapsed(dpcm->substream);
            print_log(DEBUG "timer_function: Period elapsed (c) - pcm_count=%i - pcm_size=%i - src=%p - jiffie=%i\n",dpcm->pcm_count,dpcm->pcm_size,dpcm->substream->runtime->dma_area,dpcm->pcm_jiffie);
        }
        // enable interrupts
        spin_unlock_irqrestore(&dpcm->lock, flags);
    }
}

3.
Sometimes I am getting underruns with aplay or xruns with arecord. Currently I am not quite sure why they occur, but I guess this has to do with some of above problems?

-bash-3.2# aplay 20575_24k_100.wav
Playing WAVE '20575_24k_100.wav ' : Signed 16 bit Little Endian, Rate 24000 Hz, Mono
underrun!!! (at least 8.019 ms long)


As a reference I used the dummy driver and the tutorial Writing an ALSA Driver. If there are some other drivers that may be similar and I can look at, I am thankful for every hint! So far thanks for helping me!

Greetings,
Max

-- 
Jetzt kostenlos herunterladen: Internet Explorer 8 und Mozilla Firefox 3.5 -
sicherer, schneller und einfacher! http://portal.gmx.net/de/go/chbrowser


More information about the Alsa-devel mailing list