At Thu, 19 Apr 2007 14:30:29 -0700 (PDT), Tim Harvey wrote:
--- Takashi Iwai tiwai@suse.de wrote:
The "period" defines the frequency to update the status, usually via the invokation of interrupts. The "period_size" defines the frame sizes corresponding to the "period time". This term corresponds to the "fragment size" on OSS. On major sound hardwares, a ring-buffer is divided to several parts and an irq is issued on each boundary. The period_size defines the size of this chunk.
On some hardwares, the irq is controlled on the basis of a timer. In this case, the period is defined as the timer frequency to invoke an irq.
My system has a hardware buffer of 4096 bytes (so I will use an intermediate buffer ) and is fixed at 8KHz 16bit stereo. The 4K hw buffer would accommodate 128ms of data which is pretty small. Therefore I'll choose to use a 32K intermediate buffer which will accommodate about 1sec of audio data. My IRQ is timer based so I will need to decide upon a reasonable period. As the period seems to dictate the ability for the middle layer to see the status of the buffer I'll use 4 periods allowing a 250ms granularity in my intermediate buffer.
I'm assuming that the snd_pcm_hardware struct has a min AND max period_bytes and periods because the framesize can vary if your hardware supports multiple configurations. As mine only supports 8Khz 16bit stereo, my min/max values should all be the same correct?
So using the above info my struct should look like:
static snd_pcm_hardware_t snd_my_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U16_BE, .rates = SNDRV_PCM_RATE_8000, .rate_min = 8000, .rate_max = 8000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 8192, .period_bytes_max = 8192, .periods_min = 4, .periods_max = 4, };
Does this seem reasonable?
Should be OK, but maybe more finer periods would be better, I guess. But this depends on the irq source you can use.
Because I'm using a 32K intermediate buffer I'll preallocate it with snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 0, 32768);
I want to make sure I'm implementing things properly for full duplex capability. I'm creating the pcm device via:
snd_pcm_new(card, "my dev", 0, 1 /* play streams */, 1 /* capt streams */, &pcm)
If I understand this preallocation a 32K DMAable buffer will be allocated by ALSA for each stream my device can play/capture (= 2 streams, 1 playback and 1 capture). I can obtain the buffer pre-allocated from the runtime data in the prepare callback and use that buffer as my intermediate buffer correct?
If you call snd_pcm_lib_malloc_pages() in hw_params callback (and snd_pcm_lib_free_pages() in hw_free callback). Otherwise, the pre-allocated buffer won't be assigned to the stream at all.
I don't have a great grasp of the 'prepare' callback yet, but because thats the first time you've got access to valid format/buffer info I'll assume that each time 'prepare' is called your PCM buffer/hardware/stream should be initialized.
If I understand correctly the pointer callback should return the num of frames processed in the 'intermediate buffer'. Because this is a virtual ring buffer, I guess I should simply return the offset in the buffer (divided by my bytes_per_frame which is 4) that I last pulled data from?
Exactly. It's an offset between 0 and (buffer_size-1).
I've looked at the example dealing with the hw interrupt and period_elapsed in http://www.alsa-project.org/~iwai/writing-an-alsa-driver/x773.htm. The example doesn't specify where it was getting runtime pointer from so I am assuming that I'll need to keep a pointer to each (playback and capture) substream so that I can call period_elapsed appropriately for each if they are concurrent?
Yes.
Takashi