/* * synchronize shm ring buffer with hardware */ static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_uframes_t appl_ptr, slave_appl_ptr, slave_bsize; snd_pcm_uframes_t size, slave_hw_ptr; const snd_pcm_channel_area_t *src_areas, *dst_areas; /* calculate the size to transfer */ size = dmix->appl_ptr - dmix->last_appl_ptr; if (! size) return; slave_bsize = dmix->shmptr->s.buffer_size; slave_hw_ptr = dmix->slave_hw_ptr; /* don't write on the last active period - this area may be cleared * by the driver during mix operation... */ slave_hw_ptr -= slave_hw_ptr % dmix->shmptr->s.period_size; slave_hw_ptr += slave_bsize; if (dmix->slave_hw_ptr > dmix->slave_appl_ptr) slave_hw_ptr -= dmix->shmptr->s.boundary; if (dmix->slave_appl_ptr + size >= slave_hw_ptr) size = slave_hw_ptr - dmix->slave_appl_ptr; if (! size) return; /* add sample areas here */ src_areas = snd_pcm_mmap_areas(pcm); dst_areas = snd_pcm_mmap_areas(dmix->spcm); appl_ptr = dmix->last_appl_ptr % pcm->buffer_size; dmix->last_appl_ptr += size; dmix->last_appl_ptr %= pcm->boundary; slave_appl_ptr = dmix->slave_appl_ptr % slave_bsize; dmix->slave_appl_ptr += size; dmix->slave_appl_ptr %= dmix->shmptr->s.boundary; dmix_down_sem(dmix); for (;;) { snd_pcm_uframes_t transfer = size; if (appl_ptr + transfer > pcm->buffer_size) transfer = pcm->buffer_size - appl_ptr; if (slave_appl_ptr + transfer > slave_bsize) transfer = slave_bsize - slave_appl_ptr; mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer); size -= transfer; if (! size) break; slave_appl_ptr += transfer; slave_appl_ptr %= slave_bsize; appl_ptr += transfer; appl_ptr %= pcm->buffer_size; } dmix_up_sem(dmix); }
/* * synchronize shm ring buffer with hardware */ static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size; snd_pcm_uframes_t appl_ptr, size, transfer; const snd_pcm_channel_area_t *src_areas, *dst_areas; /* calculate the size to transfer */ /* check the available size in the local buffer * last_appl_ptr keeps the last updated position */ size = dmix->appl_ptr - dmix->last_appl_ptr; if (! size) return; if (size >= pcm->boundary / 2) size = pcm->boundary - size; /* the slave_app_ptr can be far behind the slave_hw_ptr */ /* reduce mixing and errors here - just skip not catched writes */ if (dmix->slave_hw_ptr <= dmix->slave_appl_ptr) slave_size = dmix->slave_appl_ptr - dmix->slave_hw_ptr; else slave_size = dmix->slave_appl_ptr + (dmix->slave_boundary - dmix->slave_hw_ptr); if (slave_size > dmix->slave_buffer_size) { transfer = dmix->slave_buffer_size - slave_size; if (transfer > size) transfer = size; dmix->last_appl_ptr += transfer; dmix->last_appl_ptr %= pcm->boundary; dmix->slave_appl_ptr += transfer; dmix->slave_appl_ptr %= dmix->slave_boundary; size = dmix->appl_ptr - dmix->last_appl_ptr; if (! size) return; if (size >= pcm->boundary / 2) size = pcm->boundary - size; } /* check the available size in the slave PCM buffer */ slave_hw_ptr = dmix->slave_hw_ptr; /* don't write on the last active period - this area may be cleared * by the driver during mix operation... */ slave_hw_ptr -= slave_hw_ptr % dmix->slave_period_size; slave_hw_ptr += dmix->slave_buffer_size; if (slave_hw_ptr >= dmix->slave_boundary) slave_hw_ptr -= dmix->slave_boundary; if (slave_hw_ptr < dmix->slave_appl_ptr) slave_size = slave_hw_ptr + (dmix->slave_boundary - dmix->slave_appl_ptr); else slave_size = slave_hw_ptr - dmix->slave_appl_ptr; if (slave_size < size) size = slave_size; if (! size) return; /* add sample areas here */ src_areas = snd_pcm_mmap_areas(pcm); dst_areas = snd_pcm_mmap_areas(dmix->spcm); appl_ptr = dmix->last_appl_ptr % pcm->buffer_size; dmix->last_appl_ptr += size; dmix->last_appl_ptr %= pcm->boundary; slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size; dmix->slave_appl_ptr += size; dmix->slave_appl_ptr %= dmix->slave_boundary; dmix_down_sem(dmix); for (;;) { transfer = size; if (appl_ptr + transfer > pcm->buffer_size) transfer = pcm->buffer_size - appl_ptr; if (slave_appl_ptr + transfer > dmix->slave_buffer_size) transfer = dmix->slave_buffer_size - slave_appl_ptr; mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer); size -= transfer; if (! size) break; slave_appl_ptr += transfer; slave_appl_ptr %= dmix->slave_buffer_size; appl_ptr += transfer; appl_ptr %= pcm->buffer_size; } dmix_up_sem(dmix); }