1d5ac70f0Sopenharmony_ci/** 2d5ac70f0Sopenharmony_ci * \file pcm/pcm_meter.c 3d5ac70f0Sopenharmony_ci * \brief Helper functions for #SND_PCM_TYPE_METER PCM scopes 4d5ac70f0Sopenharmony_ci * \author Abramo Bagnara <abramo@alsa-project.org> 5d5ac70f0Sopenharmony_ci * \date 2001 6d5ac70f0Sopenharmony_ci * 7d5ac70f0Sopenharmony_ci * Helper functions for #SND_PCM_TYPE_METER PCM scopes 8d5ac70f0Sopenharmony_ci */ 9d5ac70f0Sopenharmony_ci/* 10d5ac70f0Sopenharmony_ci * PCM - Meter plugin 11d5ac70f0Sopenharmony_ci * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org> 12d5ac70f0Sopenharmony_ci * 13d5ac70f0Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 14d5ac70f0Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as 15d5ac70f0Sopenharmony_ci * published by the Free Software Foundation; either version 2.1 of 16d5ac70f0Sopenharmony_ci * the License, or (at your option) any later version. 17d5ac70f0Sopenharmony_ci * 18d5ac70f0Sopenharmony_ci * This program is distributed in the hope that it will be useful, 19d5ac70f0Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 20d5ac70f0Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21d5ac70f0Sopenharmony_ci * GNU Lesser General Public License for more details. 22d5ac70f0Sopenharmony_ci * 23d5ac70f0Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public 24d5ac70f0Sopenharmony_ci * License along with this library; if not, write to the Free Software 25d5ac70f0Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 26d5ac70f0Sopenharmony_ci * 27d5ac70f0Sopenharmony_ci */ 28d5ac70f0Sopenharmony_ci 29d5ac70f0Sopenharmony_ci 30d5ac70f0Sopenharmony_ci#include "pcm_local.h" 31d5ac70f0Sopenharmony_ci#include "pcm_plugin.h" 32d5ac70f0Sopenharmony_ci#include "bswap.h" 33d5ac70f0Sopenharmony_ci#include <time.h> 34d5ac70f0Sopenharmony_ci#include <pthread.h> 35d5ac70f0Sopenharmony_ci#include <dlfcn.h> 36d5ac70f0Sopenharmony_ci 37d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN 38d5ac70f0Sopenharmony_ci#define atomic_read(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST ) 39d5ac70f0Sopenharmony_ci#define atomic_add(ptr, n) __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST) 40d5ac70f0Sopenharmony_ci#define atomic_dec(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST) 41d5ac70f0Sopenharmony_ci#endif 42d5ac70f0Sopenharmony_ci 43d5ac70f0Sopenharmony_ci#ifndef PIC 44d5ac70f0Sopenharmony_ci/* entry for static linking */ 45d5ac70f0Sopenharmony_ciconst char *_snd_module_pcm_meter = ""; 46d5ac70f0Sopenharmony_ci#endif 47d5ac70f0Sopenharmony_ci 48d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN 49d5ac70f0Sopenharmony_ci#define FREQUENCY 50 50d5ac70f0Sopenharmony_ci 51d5ac70f0Sopenharmony_cistruct _snd_pcm_scope { 52d5ac70f0Sopenharmony_ci int enabled; 53d5ac70f0Sopenharmony_ci char *name; 54d5ac70f0Sopenharmony_ci const snd_pcm_scope_ops_t *ops; 55d5ac70f0Sopenharmony_ci void *private_data; 56d5ac70f0Sopenharmony_ci struct list_head list; 57d5ac70f0Sopenharmony_ci}; 58d5ac70f0Sopenharmony_ci 59d5ac70f0Sopenharmony_citypedef struct _snd_pcm_meter { 60d5ac70f0Sopenharmony_ci snd_pcm_generic_t gen; 61d5ac70f0Sopenharmony_ci snd_pcm_uframes_t rptr; 62d5ac70f0Sopenharmony_ci snd_pcm_uframes_t buf_size; 63d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *buf_areas; 64d5ac70f0Sopenharmony_ci snd_pcm_uframes_t now; 65d5ac70f0Sopenharmony_ci unsigned char *buf; 66d5ac70f0Sopenharmony_ci struct list_head scopes; 67d5ac70f0Sopenharmony_ci int closed; 68d5ac70f0Sopenharmony_ci int running; 69d5ac70f0Sopenharmony_ci int reset; 70d5ac70f0Sopenharmony_ci pthread_t thread; 71d5ac70f0Sopenharmony_ci pthread_mutex_t update_mutex; 72d5ac70f0Sopenharmony_ci pthread_mutex_t running_mutex; 73d5ac70f0Sopenharmony_ci pthread_cond_t running_cond; 74d5ac70f0Sopenharmony_ci struct timespec delay; 75d5ac70f0Sopenharmony_ci void *dl_handle; 76d5ac70f0Sopenharmony_ci} snd_pcm_meter_t; 77d5ac70f0Sopenharmony_ci 78d5ac70f0Sopenharmony_cistatic void snd_pcm_meter_add_frames(snd_pcm_t *pcm, 79d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *areas, 80d5ac70f0Sopenharmony_ci snd_pcm_uframes_t ptr, 81d5ac70f0Sopenharmony_ci snd_pcm_uframes_t frames) 82d5ac70f0Sopenharmony_ci{ 83d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 84d5ac70f0Sopenharmony_ci if (frames > pcm->buffer_size) 85d5ac70f0Sopenharmony_ci frames = pcm->buffer_size; 86d5ac70f0Sopenharmony_ci while (frames > 0) { 87d5ac70f0Sopenharmony_ci snd_pcm_uframes_t n = frames; 88d5ac70f0Sopenharmony_ci snd_pcm_uframes_t dst_offset = ptr % meter->buf_size; 89d5ac70f0Sopenharmony_ci snd_pcm_uframes_t src_offset = ptr % pcm->buffer_size; 90d5ac70f0Sopenharmony_ci snd_pcm_uframes_t dst_cont = meter->buf_size - dst_offset; 91d5ac70f0Sopenharmony_ci snd_pcm_uframes_t src_cont = pcm->buffer_size - src_offset; 92d5ac70f0Sopenharmony_ci if (n > dst_cont) 93d5ac70f0Sopenharmony_ci n = dst_cont; 94d5ac70f0Sopenharmony_ci if (n > src_cont) 95d5ac70f0Sopenharmony_ci n = src_cont; 96d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(meter->buf_areas, dst_offset, 97d5ac70f0Sopenharmony_ci areas, src_offset, 98d5ac70f0Sopenharmony_ci pcm->channels, n, pcm->format); 99d5ac70f0Sopenharmony_ci frames -= n; 100d5ac70f0Sopenharmony_ci ptr += n; 101d5ac70f0Sopenharmony_ci if (ptr == pcm->boundary) 102d5ac70f0Sopenharmony_ci ptr = 0; 103d5ac70f0Sopenharmony_ci } 104d5ac70f0Sopenharmony_ci} 105d5ac70f0Sopenharmony_ci 106d5ac70f0Sopenharmony_cistatic void snd_pcm_meter_update_main(snd_pcm_t *pcm) 107d5ac70f0Sopenharmony_ci{ 108d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 109d5ac70f0Sopenharmony_ci snd_pcm_sframes_t frames; 110d5ac70f0Sopenharmony_ci snd_pcm_uframes_t rptr, old_rptr; 111d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *areas; 112d5ac70f0Sopenharmony_ci int locked; 113d5ac70f0Sopenharmony_ci locked = (pthread_mutex_trylock(&meter->update_mutex) >= 0); 114d5ac70f0Sopenharmony_ci areas = snd_pcm_mmap_areas(pcm); 115d5ac70f0Sopenharmony_ci rptr = *pcm->hw.ptr; 116d5ac70f0Sopenharmony_ci old_rptr = meter->rptr; 117d5ac70f0Sopenharmony_ci meter->rptr = rptr; 118d5ac70f0Sopenharmony_ci frames = rptr - old_rptr; 119d5ac70f0Sopenharmony_ci if (frames < 0) 120d5ac70f0Sopenharmony_ci frames += pcm->boundary; 121d5ac70f0Sopenharmony_ci if (frames > 0) { 122d5ac70f0Sopenharmony_ci assert((snd_pcm_uframes_t) frames <= pcm->buffer_size); 123d5ac70f0Sopenharmony_ci snd_pcm_meter_add_frames(pcm, areas, old_rptr, 124d5ac70f0Sopenharmony_ci (snd_pcm_uframes_t) frames); 125d5ac70f0Sopenharmony_ci } 126d5ac70f0Sopenharmony_ci if (locked) 127d5ac70f0Sopenharmony_ci pthread_mutex_unlock(&meter->update_mutex); 128d5ac70f0Sopenharmony_ci} 129d5ac70f0Sopenharmony_ci 130d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_update_scope(snd_pcm_t *pcm) 131d5ac70f0Sopenharmony_ci{ 132d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 133d5ac70f0Sopenharmony_ci snd_pcm_sframes_t frames; 134d5ac70f0Sopenharmony_ci snd_pcm_uframes_t rptr, old_rptr; 135d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *areas; 136d5ac70f0Sopenharmony_ci int reset = 0; 137d5ac70f0Sopenharmony_ci /* Wait main thread */ 138d5ac70f0Sopenharmony_ci pthread_mutex_lock(&meter->update_mutex); 139d5ac70f0Sopenharmony_ci areas = snd_pcm_mmap_areas(pcm); 140d5ac70f0Sopenharmony_ci _again: 141d5ac70f0Sopenharmony_ci rptr = *pcm->hw.ptr; 142d5ac70f0Sopenharmony_ci old_rptr = meter->rptr; 143d5ac70f0Sopenharmony_ci if (atomic_read(&meter->reset)) { 144d5ac70f0Sopenharmony_ci reset = 1; 145d5ac70f0Sopenharmony_ci atomic_dec(&meter->reset); 146d5ac70f0Sopenharmony_ci goto _again; 147d5ac70f0Sopenharmony_ci } 148d5ac70f0Sopenharmony_ci meter->rptr = rptr; 149d5ac70f0Sopenharmony_ci frames = rptr - old_rptr; 150d5ac70f0Sopenharmony_ci if (frames < 0) 151d5ac70f0Sopenharmony_ci frames += pcm->boundary; 152d5ac70f0Sopenharmony_ci if (frames > 0) { 153d5ac70f0Sopenharmony_ci assert((snd_pcm_uframes_t) frames <= pcm->buffer_size); 154d5ac70f0Sopenharmony_ci snd_pcm_meter_add_frames(pcm, areas, old_rptr, 155d5ac70f0Sopenharmony_ci (snd_pcm_uframes_t) frames); 156d5ac70f0Sopenharmony_ci } 157d5ac70f0Sopenharmony_ci pthread_mutex_unlock(&meter->update_mutex); 158d5ac70f0Sopenharmony_ci return reset; 159d5ac70f0Sopenharmony_ci} 160d5ac70f0Sopenharmony_ci 161d5ac70f0Sopenharmony_cistatic int snd_pcm_scope_remove(snd_pcm_scope_t *scope) 162d5ac70f0Sopenharmony_ci{ 163d5ac70f0Sopenharmony_ci free(scope->name); 164d5ac70f0Sopenharmony_ci scope->ops->close(scope); 165d5ac70f0Sopenharmony_ci list_del(&scope->list); 166d5ac70f0Sopenharmony_ci free(scope); 167d5ac70f0Sopenharmony_ci return 0; 168d5ac70f0Sopenharmony_ci} 169d5ac70f0Sopenharmony_ci 170d5ac70f0Sopenharmony_cistatic int snd_pcm_scope_enable(snd_pcm_scope_t *scope) 171d5ac70f0Sopenharmony_ci{ 172d5ac70f0Sopenharmony_ci int err; 173d5ac70f0Sopenharmony_ci assert(!scope->enabled); 174d5ac70f0Sopenharmony_ci err = scope->ops->enable(scope); 175d5ac70f0Sopenharmony_ci scope->enabled = (err >= 0); 176d5ac70f0Sopenharmony_ci return err; 177d5ac70f0Sopenharmony_ci} 178d5ac70f0Sopenharmony_ci 179d5ac70f0Sopenharmony_cistatic int snd_pcm_scope_disable(snd_pcm_scope_t *scope) 180d5ac70f0Sopenharmony_ci{ 181d5ac70f0Sopenharmony_ci assert(scope->enabled); 182d5ac70f0Sopenharmony_ci scope->ops->disable(scope); 183d5ac70f0Sopenharmony_ci scope->enabled = 0; 184d5ac70f0Sopenharmony_ci return 0; 185d5ac70f0Sopenharmony_ci} 186d5ac70f0Sopenharmony_ci 187d5ac70f0Sopenharmony_cistatic void *snd_pcm_meter_thread(void *data) 188d5ac70f0Sopenharmony_ci{ 189d5ac70f0Sopenharmony_ci snd_pcm_t *pcm = data; 190d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 191d5ac70f0Sopenharmony_ci snd_pcm_t *spcm = meter->gen.slave; 192d5ac70f0Sopenharmony_ci struct list_head *pos; 193d5ac70f0Sopenharmony_ci snd_pcm_scope_t *scope; 194d5ac70f0Sopenharmony_ci int reset; 195d5ac70f0Sopenharmony_ci list_for_each(pos, &meter->scopes) { 196d5ac70f0Sopenharmony_ci scope = list_entry(pos, snd_pcm_scope_t, list); 197d5ac70f0Sopenharmony_ci snd_pcm_scope_enable(scope); 198d5ac70f0Sopenharmony_ci } 199d5ac70f0Sopenharmony_ci while (!meter->closed) { 200d5ac70f0Sopenharmony_ci snd_pcm_sframes_t now; 201d5ac70f0Sopenharmony_ci snd_pcm_status_t status; 202d5ac70f0Sopenharmony_ci int err; 203d5ac70f0Sopenharmony_ci pthread_mutex_lock(&meter->running_mutex); 204d5ac70f0Sopenharmony_ci err = snd_pcm_status(spcm, &status); 205d5ac70f0Sopenharmony_ci assert(err >= 0); 206d5ac70f0Sopenharmony_ci if (status.state != SND_PCM_STATE_RUNNING && 207d5ac70f0Sopenharmony_ci (status.state != SND_PCM_STATE_DRAINING || 208d5ac70f0Sopenharmony_ci spcm->stream != SND_PCM_STREAM_PLAYBACK)) { 209d5ac70f0Sopenharmony_ci if (meter->running) { 210d5ac70f0Sopenharmony_ci list_for_each(pos, &meter->scopes) { 211d5ac70f0Sopenharmony_ci scope = list_entry(pos, snd_pcm_scope_t, list); 212d5ac70f0Sopenharmony_ci scope->ops->stop(scope); 213d5ac70f0Sopenharmony_ci } 214d5ac70f0Sopenharmony_ci meter->running = 0; 215d5ac70f0Sopenharmony_ci } 216d5ac70f0Sopenharmony_ci pthread_cond_wait(&meter->running_cond, 217d5ac70f0Sopenharmony_ci &meter->running_mutex); 218d5ac70f0Sopenharmony_ci pthread_mutex_unlock(&meter->running_mutex); 219d5ac70f0Sopenharmony_ci continue; 220d5ac70f0Sopenharmony_ci } 221d5ac70f0Sopenharmony_ci pthread_mutex_unlock(&meter->running_mutex); 222d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 223d5ac70f0Sopenharmony_ci now = status.appl_ptr - status.delay; 224d5ac70f0Sopenharmony_ci if (now < 0) 225d5ac70f0Sopenharmony_ci now += pcm->boundary; 226d5ac70f0Sopenharmony_ci } else { 227d5ac70f0Sopenharmony_ci now = status.appl_ptr + status.delay; 228d5ac70f0Sopenharmony_ci if ((snd_pcm_uframes_t) now >= pcm->boundary) 229d5ac70f0Sopenharmony_ci now -= pcm->boundary; 230d5ac70f0Sopenharmony_ci } 231d5ac70f0Sopenharmony_ci meter->now = now; 232d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_CAPTURE) 233d5ac70f0Sopenharmony_ci reset = snd_pcm_meter_update_scope(pcm); 234d5ac70f0Sopenharmony_ci else { 235d5ac70f0Sopenharmony_ci reset = 0; 236d5ac70f0Sopenharmony_ci while (atomic_read(&meter->reset)) { 237d5ac70f0Sopenharmony_ci reset = 1; 238d5ac70f0Sopenharmony_ci atomic_dec(&meter->reset); 239d5ac70f0Sopenharmony_ci } 240d5ac70f0Sopenharmony_ci } 241d5ac70f0Sopenharmony_ci if (reset) { 242d5ac70f0Sopenharmony_ci list_for_each(pos, &meter->scopes) { 243d5ac70f0Sopenharmony_ci scope = list_entry(pos, snd_pcm_scope_t, list); 244d5ac70f0Sopenharmony_ci if (scope->enabled) 245d5ac70f0Sopenharmony_ci scope->ops->reset(scope); 246d5ac70f0Sopenharmony_ci } 247d5ac70f0Sopenharmony_ci continue; 248d5ac70f0Sopenharmony_ci } 249d5ac70f0Sopenharmony_ci if (!meter->running) { 250d5ac70f0Sopenharmony_ci list_for_each(pos, &meter->scopes) { 251d5ac70f0Sopenharmony_ci scope = list_entry(pos, snd_pcm_scope_t, list); 252d5ac70f0Sopenharmony_ci if (scope->enabled) 253d5ac70f0Sopenharmony_ci scope->ops->start(scope); 254d5ac70f0Sopenharmony_ci } 255d5ac70f0Sopenharmony_ci meter->running = 1; 256d5ac70f0Sopenharmony_ci } 257d5ac70f0Sopenharmony_ci list_for_each(pos, &meter->scopes) { 258d5ac70f0Sopenharmony_ci scope = list_entry(pos, snd_pcm_scope_t, list); 259d5ac70f0Sopenharmony_ci if (scope->enabled) 260d5ac70f0Sopenharmony_ci scope->ops->update(scope); 261d5ac70f0Sopenharmony_ci } 262d5ac70f0Sopenharmony_ci nanosleep(&meter->delay, NULL); 263d5ac70f0Sopenharmony_ci } 264d5ac70f0Sopenharmony_ci list_for_each(pos, &meter->scopes) { 265d5ac70f0Sopenharmony_ci scope = list_entry(pos, snd_pcm_scope_t, list); 266d5ac70f0Sopenharmony_ci if (scope->enabled) 267d5ac70f0Sopenharmony_ci snd_pcm_scope_disable(scope); 268d5ac70f0Sopenharmony_ci } 269d5ac70f0Sopenharmony_ci return NULL; 270d5ac70f0Sopenharmony_ci} 271d5ac70f0Sopenharmony_ci 272d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_close(snd_pcm_t *pcm) 273d5ac70f0Sopenharmony_ci{ 274d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 275d5ac70f0Sopenharmony_ci struct list_head *pos, *npos; 276d5ac70f0Sopenharmony_ci int err = 0; 277d5ac70f0Sopenharmony_ci pthread_mutex_destroy(&meter->update_mutex); 278d5ac70f0Sopenharmony_ci pthread_mutex_destroy(&meter->running_mutex); 279d5ac70f0Sopenharmony_ci pthread_cond_destroy(&meter->running_cond); 280d5ac70f0Sopenharmony_ci if (meter->gen.close_slave) 281d5ac70f0Sopenharmony_ci err = snd_pcm_close(meter->gen.slave); 282d5ac70f0Sopenharmony_ci list_for_each_safe(pos, npos, &meter->scopes) { 283d5ac70f0Sopenharmony_ci snd_pcm_scope_t *scope; 284d5ac70f0Sopenharmony_ci scope = list_entry(pos, snd_pcm_scope_t, list); 285d5ac70f0Sopenharmony_ci snd_pcm_scope_remove(scope); 286d5ac70f0Sopenharmony_ci } 287d5ac70f0Sopenharmony_ci if (meter->dl_handle) 288d5ac70f0Sopenharmony_ci snd_dlclose(meter->dl_handle); 289d5ac70f0Sopenharmony_ci free(meter); 290d5ac70f0Sopenharmony_ci return err; 291d5ac70f0Sopenharmony_ci} 292d5ac70f0Sopenharmony_ci 293d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_prepare(snd_pcm_t *pcm) 294d5ac70f0Sopenharmony_ci{ 295d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 296d5ac70f0Sopenharmony_ci int err; 297d5ac70f0Sopenharmony_ci atomic_add(&meter->reset, 1); 298d5ac70f0Sopenharmony_ci err = snd_pcm_prepare(meter->gen.slave); 299d5ac70f0Sopenharmony_ci if (err >= 0) { 300d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) 301d5ac70f0Sopenharmony_ci meter->rptr = *pcm->appl.ptr; 302d5ac70f0Sopenharmony_ci else 303d5ac70f0Sopenharmony_ci meter->rptr = *pcm->hw.ptr; 304d5ac70f0Sopenharmony_ci } 305d5ac70f0Sopenharmony_ci return err; 306d5ac70f0Sopenharmony_ci} 307d5ac70f0Sopenharmony_ci 308d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_reset(snd_pcm_t *pcm) 309d5ac70f0Sopenharmony_ci{ 310d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 311d5ac70f0Sopenharmony_ci int err = snd_pcm_reset(meter->gen.slave); 312d5ac70f0Sopenharmony_ci if (err >= 0) { 313d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) 314d5ac70f0Sopenharmony_ci meter->rptr = *pcm->appl.ptr; 315d5ac70f0Sopenharmony_ci } 316d5ac70f0Sopenharmony_ci return err; 317d5ac70f0Sopenharmony_ci} 318d5ac70f0Sopenharmony_ci 319d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_start(snd_pcm_t *pcm) 320d5ac70f0Sopenharmony_ci{ 321d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 322d5ac70f0Sopenharmony_ci int err; 323d5ac70f0Sopenharmony_ci pthread_mutex_lock(&meter->running_mutex); 324d5ac70f0Sopenharmony_ci err = snd_pcm_start(meter->gen.slave); 325d5ac70f0Sopenharmony_ci if (err >= 0) 326d5ac70f0Sopenharmony_ci pthread_cond_signal(&meter->running_cond); 327d5ac70f0Sopenharmony_ci pthread_mutex_unlock(&meter->running_mutex); 328d5ac70f0Sopenharmony_ci return err; 329d5ac70f0Sopenharmony_ci} 330d5ac70f0Sopenharmony_ci 331d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_meter_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 332d5ac70f0Sopenharmony_ci{ 333d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 334d5ac70f0Sopenharmony_ci snd_pcm_sframes_t err = snd_pcm_rewind(meter->gen.slave, frames); 335d5ac70f0Sopenharmony_ci if (err > 0 && pcm->stream == SND_PCM_STREAM_PLAYBACK) 336d5ac70f0Sopenharmony_ci meter->rptr = *pcm->appl.ptr; 337d5ac70f0Sopenharmony_ci return err; 338d5ac70f0Sopenharmony_ci} 339d5ac70f0Sopenharmony_ci 340d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_meter_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 341d5ac70f0Sopenharmony_ci{ 342d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 343d5ac70f0Sopenharmony_ci snd_pcm_sframes_t err = INTERNAL(snd_pcm_forward)(meter->gen.slave, frames); 344d5ac70f0Sopenharmony_ci if (err > 0 && pcm->stream == SND_PCM_STREAM_PLAYBACK) 345d5ac70f0Sopenharmony_ci meter->rptr = *pcm->appl.ptr; 346d5ac70f0Sopenharmony_ci return err; 347d5ac70f0Sopenharmony_ci} 348d5ac70f0Sopenharmony_ci 349d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_meter_mmap_commit(snd_pcm_t *pcm, 350d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset, 351d5ac70f0Sopenharmony_ci snd_pcm_uframes_t size) 352d5ac70f0Sopenharmony_ci{ 353d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 354d5ac70f0Sopenharmony_ci snd_pcm_uframes_t old_rptr = *pcm->appl.ptr; 355d5ac70f0Sopenharmony_ci snd_pcm_sframes_t result = snd_pcm_mmap_commit(meter->gen.slave, offset, size); 356d5ac70f0Sopenharmony_ci if (result <= 0) 357d5ac70f0Sopenharmony_ci return result; 358d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 359d5ac70f0Sopenharmony_ci snd_pcm_meter_add_frames(pcm, snd_pcm_mmap_areas(pcm), old_rptr, result); 360d5ac70f0Sopenharmony_ci meter->rptr = *pcm->appl.ptr; 361d5ac70f0Sopenharmony_ci } 362d5ac70f0Sopenharmony_ci return result; 363d5ac70f0Sopenharmony_ci} 364d5ac70f0Sopenharmony_ci 365d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_meter_avail_update(snd_pcm_t *pcm) 366d5ac70f0Sopenharmony_ci{ 367d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 368d5ac70f0Sopenharmony_ci snd_pcm_sframes_t result = snd_pcm_avail_update(meter->gen.slave); 369d5ac70f0Sopenharmony_ci if (result <= 0) 370d5ac70f0Sopenharmony_ci return result; 371d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_CAPTURE) 372d5ac70f0Sopenharmony_ci snd_pcm_meter_update_main(pcm); 373d5ac70f0Sopenharmony_ci return result; 374d5ac70f0Sopenharmony_ci} 375d5ac70f0Sopenharmony_ci 376d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params) 377d5ac70f0Sopenharmony_ci{ 378d5ac70f0Sopenharmony_ci int err; 379d5ac70f0Sopenharmony_ci snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM }; 380d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, 381d5ac70f0Sopenharmony_ci &access_mask); 382d5ac70f0Sopenharmony_ci if (err < 0) 383d5ac70f0Sopenharmony_ci return err; 384d5ac70f0Sopenharmony_ci params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); 385d5ac70f0Sopenharmony_ci return 0; 386d5ac70f0Sopenharmony_ci} 387d5ac70f0Sopenharmony_ci 388d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams) 389d5ac70f0Sopenharmony_ci{ 390d5ac70f0Sopenharmony_ci snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP }; 391d5ac70f0Sopenharmony_ci _snd_pcm_hw_params_any(sparams); 392d5ac70f0Sopenharmony_ci _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, 393d5ac70f0Sopenharmony_ci &saccess_mask); 394d5ac70f0Sopenharmony_ci return 0; 395d5ac70f0Sopenharmony_ci} 396d5ac70f0Sopenharmony_ci 397d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, 398d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 399d5ac70f0Sopenharmony_ci{ 400d5ac70f0Sopenharmony_ci int err; 401d5ac70f0Sopenharmony_ci unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS; 402d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_params_refine(sparams, links, params); 403d5ac70f0Sopenharmony_ci if (err < 0) 404d5ac70f0Sopenharmony_ci return err; 405d5ac70f0Sopenharmony_ci return 0; 406d5ac70f0Sopenharmony_ci} 407d5ac70f0Sopenharmony_ci 408d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, 409d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 410d5ac70f0Sopenharmony_ci{ 411d5ac70f0Sopenharmony_ci int err; 412d5ac70f0Sopenharmony_ci unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS; 413d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_params_refine(params, links, sparams); 414d5ac70f0Sopenharmony_ci if (err < 0) 415d5ac70f0Sopenharmony_ci return err; 416d5ac70f0Sopenharmony_ci return 0; 417d5ac70f0Sopenharmony_ci} 418d5ac70f0Sopenharmony_ci 419d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 420d5ac70f0Sopenharmony_ci{ 421d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 422d5ac70f0Sopenharmony_ci return snd_pcm_hw_refine(meter->gen.slave, params); 423d5ac70f0Sopenharmony_ci} 424d5ac70f0Sopenharmony_ci 425d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 426d5ac70f0Sopenharmony_ci{ 427d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 428d5ac70f0Sopenharmony_ci return _snd_pcm_hw_params_internal(meter->gen.slave, params); 429d5ac70f0Sopenharmony_ci} 430d5ac70f0Sopenharmony_ci 431d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 432d5ac70f0Sopenharmony_ci{ 433d5ac70f0Sopenharmony_ci return snd_pcm_hw_refine_slave(pcm, params, 434d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_refine_cprepare, 435d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_refine_cchange, 436d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_refine_sprepare, 437d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_refine_schange, 438d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_refine_slave); 439d5ac70f0Sopenharmony_ci} 440d5ac70f0Sopenharmony_ci 441d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) 442d5ac70f0Sopenharmony_ci{ 443d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 444d5ac70f0Sopenharmony_ci unsigned int channel; 445d5ac70f0Sopenharmony_ci snd_pcm_t *slave = meter->gen.slave; 446d5ac70f0Sopenharmony_ci size_t buf_size_bytes; 447d5ac70f0Sopenharmony_ci int err; 448d5ac70f0Sopenharmony_ci err = snd_pcm_hw_params_slave(pcm, params, 449d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_refine_cchange, 450d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_refine_sprepare, 451d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_refine_schange, 452d5ac70f0Sopenharmony_ci snd_pcm_meter_hw_params_slave); 453d5ac70f0Sopenharmony_ci if (err < 0) 454d5ac70f0Sopenharmony_ci return err; 455d5ac70f0Sopenharmony_ci /* more than 1 second of buffer */ 456d5ac70f0Sopenharmony_ci meter->buf_size = slave->buffer_size; 457d5ac70f0Sopenharmony_ci while (meter->buf_size < slave->rate) 458d5ac70f0Sopenharmony_ci meter->buf_size *= 2; 459d5ac70f0Sopenharmony_ci buf_size_bytes = snd_pcm_frames_to_bytes(slave, meter->buf_size); 460d5ac70f0Sopenharmony_ci assert(!meter->buf); 461d5ac70f0Sopenharmony_ci meter->buf = malloc(buf_size_bytes); 462d5ac70f0Sopenharmony_ci if (!meter->buf) 463d5ac70f0Sopenharmony_ci return -ENOMEM; 464d5ac70f0Sopenharmony_ci meter->buf_areas = malloc(sizeof(*meter->buf_areas) * slave->channels); 465d5ac70f0Sopenharmony_ci if (!meter->buf_areas) { 466d5ac70f0Sopenharmony_ci free(meter->buf); 467d5ac70f0Sopenharmony_ci return -ENOMEM; 468d5ac70f0Sopenharmony_ci } 469d5ac70f0Sopenharmony_ci for (channel = 0; channel < slave->channels; ++channel) { 470d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *a = &meter->buf_areas[channel]; 471d5ac70f0Sopenharmony_ci a->addr = meter->buf + buf_size_bytes / slave->channels * channel; 472d5ac70f0Sopenharmony_ci a->first = 0; 473d5ac70f0Sopenharmony_ci a->step = slave->sample_bits; 474d5ac70f0Sopenharmony_ci } 475d5ac70f0Sopenharmony_ci meter->closed = 0; 476d5ac70f0Sopenharmony_ci err = pthread_create(&meter->thread, NULL, snd_pcm_meter_thread, pcm); 477d5ac70f0Sopenharmony_ci assert(err == 0); 478d5ac70f0Sopenharmony_ci return 0; 479d5ac70f0Sopenharmony_ci} 480d5ac70f0Sopenharmony_ci 481d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_hw_free(snd_pcm_t *pcm) 482d5ac70f0Sopenharmony_ci{ 483d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 484d5ac70f0Sopenharmony_ci int err; 485d5ac70f0Sopenharmony_ci meter->closed = 1; 486d5ac70f0Sopenharmony_ci pthread_mutex_lock(&meter->running_mutex); 487d5ac70f0Sopenharmony_ci pthread_cond_signal(&meter->running_cond); 488d5ac70f0Sopenharmony_ci pthread_mutex_unlock(&meter->running_mutex); 489d5ac70f0Sopenharmony_ci err = pthread_join(meter->thread, 0); 490d5ac70f0Sopenharmony_ci assert(err == 0); 491d5ac70f0Sopenharmony_ci free(meter->buf); 492d5ac70f0Sopenharmony_ci free(meter->buf_areas); 493d5ac70f0Sopenharmony_ci meter->buf = NULL; 494d5ac70f0Sopenharmony_ci meter->buf_areas = NULL; 495d5ac70f0Sopenharmony_ci return snd_pcm_hw_free(meter->gen.slave); 496d5ac70f0Sopenharmony_ci} 497d5ac70f0Sopenharmony_ci 498d5ac70f0Sopenharmony_cistatic void snd_pcm_meter_dump(snd_pcm_t *pcm, snd_output_t *out) 499d5ac70f0Sopenharmony_ci{ 500d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 501d5ac70f0Sopenharmony_ci snd_output_printf(out, "Meter PCM\n"); 502d5ac70f0Sopenharmony_ci if (pcm->setup) { 503d5ac70f0Sopenharmony_ci snd_output_printf(out, "Its setup is:\n"); 504d5ac70f0Sopenharmony_ci snd_pcm_dump_setup(pcm, out); 505d5ac70f0Sopenharmony_ci } 506d5ac70f0Sopenharmony_ci snd_output_printf(out, "Slave: "); 507d5ac70f0Sopenharmony_ci snd_pcm_dump(meter->gen.slave, out); 508d5ac70f0Sopenharmony_ci} 509d5ac70f0Sopenharmony_ci 510d5ac70f0Sopenharmony_cistatic const snd_pcm_ops_t snd_pcm_meter_ops = { 511d5ac70f0Sopenharmony_ci .close = snd_pcm_meter_close, 512d5ac70f0Sopenharmony_ci .info = snd_pcm_generic_info, 513d5ac70f0Sopenharmony_ci .hw_refine = snd_pcm_meter_hw_refine, 514d5ac70f0Sopenharmony_ci .hw_params = snd_pcm_meter_hw_params, 515d5ac70f0Sopenharmony_ci .hw_free = snd_pcm_meter_hw_free, 516d5ac70f0Sopenharmony_ci .sw_params = snd_pcm_generic_sw_params, 517d5ac70f0Sopenharmony_ci .channel_info = snd_pcm_generic_channel_info, 518d5ac70f0Sopenharmony_ci .dump = snd_pcm_meter_dump, 519d5ac70f0Sopenharmony_ci .nonblock = snd_pcm_generic_nonblock, 520d5ac70f0Sopenharmony_ci .async = snd_pcm_generic_async, 521d5ac70f0Sopenharmony_ci .mmap = snd_pcm_generic_mmap, 522d5ac70f0Sopenharmony_ci .munmap = snd_pcm_generic_munmap, 523d5ac70f0Sopenharmony_ci .query_chmaps = snd_pcm_generic_query_chmaps, 524d5ac70f0Sopenharmony_ci .get_chmap = snd_pcm_generic_get_chmap, 525d5ac70f0Sopenharmony_ci .set_chmap = snd_pcm_generic_set_chmap, 526d5ac70f0Sopenharmony_ci}; 527d5ac70f0Sopenharmony_ci 528d5ac70f0Sopenharmony_cistatic const snd_pcm_fast_ops_t snd_pcm_meter_fast_ops = { 529d5ac70f0Sopenharmony_ci .status = snd_pcm_generic_status, 530d5ac70f0Sopenharmony_ci .state = snd_pcm_generic_state, 531d5ac70f0Sopenharmony_ci .hwsync = snd_pcm_generic_hwsync, 532d5ac70f0Sopenharmony_ci .delay = snd_pcm_generic_delay, 533d5ac70f0Sopenharmony_ci .prepare = snd_pcm_meter_prepare, 534d5ac70f0Sopenharmony_ci .reset = snd_pcm_meter_reset, 535d5ac70f0Sopenharmony_ci .start = snd_pcm_meter_start, 536d5ac70f0Sopenharmony_ci .drop = snd_pcm_generic_drop, 537d5ac70f0Sopenharmony_ci .drain = snd_pcm_generic_drain, 538d5ac70f0Sopenharmony_ci .pause = snd_pcm_generic_pause, 539d5ac70f0Sopenharmony_ci .rewindable = snd_pcm_generic_rewindable, 540d5ac70f0Sopenharmony_ci .rewind = snd_pcm_meter_rewind, 541d5ac70f0Sopenharmony_ci .forwardable = snd_pcm_generic_forwardable, 542d5ac70f0Sopenharmony_ci .forward = snd_pcm_meter_forward, 543d5ac70f0Sopenharmony_ci .resume = snd_pcm_generic_resume, 544d5ac70f0Sopenharmony_ci .writei = snd_pcm_mmap_writei, 545d5ac70f0Sopenharmony_ci .writen = snd_pcm_mmap_writen, 546d5ac70f0Sopenharmony_ci .readi = snd_pcm_mmap_readi, 547d5ac70f0Sopenharmony_ci .readn = snd_pcm_mmap_readn, 548d5ac70f0Sopenharmony_ci .avail_update = snd_pcm_meter_avail_update, 549d5ac70f0Sopenharmony_ci .mmap_commit = snd_pcm_meter_mmap_commit, 550d5ac70f0Sopenharmony_ci .htimestamp = snd_pcm_generic_htimestamp, 551d5ac70f0Sopenharmony_ci .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, 552d5ac70f0Sopenharmony_ci .poll_descriptors = snd_pcm_generic_poll_descriptors, 553d5ac70f0Sopenharmony_ci .poll_revents = snd_pcm_generic_poll_revents, 554d5ac70f0Sopenharmony_ci .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min, 555d5ac70f0Sopenharmony_ci}; 556d5ac70f0Sopenharmony_ci 557d5ac70f0Sopenharmony_ci/** 558d5ac70f0Sopenharmony_ci * \brief Creates a new Meter PCM 559d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle 560d5ac70f0Sopenharmony_ci * \param name Name of PCM 561d5ac70f0Sopenharmony_ci * \param frequency Update frequency 562d5ac70f0Sopenharmony_ci * \param slave Slave PCM handle 563d5ac70f0Sopenharmony_ci * \param close_slave When set, the slave PCM handle is closed with copy PCM 564d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code 565d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense 566d5ac70f0Sopenharmony_ci * of compatibility reasons. The prototype might be freely 567d5ac70f0Sopenharmony_ci * changed in future. 568d5ac70f0Sopenharmony_ci */ 569d5ac70f0Sopenharmony_ciint snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, unsigned int frequency, 570d5ac70f0Sopenharmony_ci snd_pcm_t *slave, int close_slave) 571d5ac70f0Sopenharmony_ci{ 572d5ac70f0Sopenharmony_ci snd_pcm_t *pcm; 573d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 574d5ac70f0Sopenharmony_ci int err; 575d5ac70f0Sopenharmony_ci assert(pcmp); 576d5ac70f0Sopenharmony_ci meter = calloc(1, sizeof(snd_pcm_meter_t)); 577d5ac70f0Sopenharmony_ci if (!meter) 578d5ac70f0Sopenharmony_ci return -ENOMEM; 579d5ac70f0Sopenharmony_ci meter->gen.slave = slave; 580d5ac70f0Sopenharmony_ci meter->gen.close_slave = close_slave; 581d5ac70f0Sopenharmony_ci meter->delay.tv_sec = 0; 582d5ac70f0Sopenharmony_ci meter->delay.tv_nsec = 1000000000 / frequency; 583d5ac70f0Sopenharmony_ci INIT_LIST_HEAD(&meter->scopes); 584d5ac70f0Sopenharmony_ci 585d5ac70f0Sopenharmony_ci err = snd_pcm_new(&pcm, SND_PCM_TYPE_METER, name, slave->stream, slave->mode); 586d5ac70f0Sopenharmony_ci if (err < 0) { 587d5ac70f0Sopenharmony_ci free(meter); 588d5ac70f0Sopenharmony_ci return err; 589d5ac70f0Sopenharmony_ci } 590d5ac70f0Sopenharmony_ci pcm->mmap_rw = 1; 591d5ac70f0Sopenharmony_ci pcm->mmap_shadow = 1; 592d5ac70f0Sopenharmony_ci pcm->ops = &snd_pcm_meter_ops; 593d5ac70f0Sopenharmony_ci pcm->fast_ops = &snd_pcm_meter_fast_ops; 594d5ac70f0Sopenharmony_ci pcm->private_data = meter; 595d5ac70f0Sopenharmony_ci pcm->poll_fd = slave->poll_fd; 596d5ac70f0Sopenharmony_ci pcm->poll_events = slave->poll_events; 597d5ac70f0Sopenharmony_ci pcm->tstamp_type = slave->tstamp_type; 598d5ac70f0Sopenharmony_ci snd_pcm_link_hw_ptr(pcm, slave); 599d5ac70f0Sopenharmony_ci snd_pcm_link_appl_ptr(pcm, slave); 600d5ac70f0Sopenharmony_ci *pcmp = pcm; 601d5ac70f0Sopenharmony_ci 602d5ac70f0Sopenharmony_ci pthread_mutex_init(&meter->update_mutex, NULL); 603d5ac70f0Sopenharmony_ci pthread_mutex_init(&meter->running_mutex, NULL); 604d5ac70f0Sopenharmony_ci pthread_cond_init(&meter->running_cond, NULL); 605d5ac70f0Sopenharmony_ci return 0; 606d5ac70f0Sopenharmony_ci} 607d5ac70f0Sopenharmony_ci 608d5ac70f0Sopenharmony_ci 609d5ac70f0Sopenharmony_cistatic int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name, 610d5ac70f0Sopenharmony_ci snd_config_t *root, snd_config_t *conf) 611d5ac70f0Sopenharmony_ci{ 612d5ac70f0Sopenharmony_ci char buf[256], errbuf[256]; 613d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 614d5ac70f0Sopenharmony_ci const char *id; 615d5ac70f0Sopenharmony_ci const char *lib = NULL, *open_name = NULL, *str = NULL; 616d5ac70f0Sopenharmony_ci snd_config_t *c, *type_conf = NULL; 617d5ac70f0Sopenharmony_ci int (*open_func)(snd_pcm_t *, const char *, 618d5ac70f0Sopenharmony_ci snd_config_t *, snd_config_t *) = NULL; 619d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = pcm->private_data; 620d5ac70f0Sopenharmony_ci void *h = NULL; 621d5ac70f0Sopenharmony_ci int err; 622d5ac70f0Sopenharmony_ci 623d5ac70f0Sopenharmony_ci if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) { 624d5ac70f0Sopenharmony_ci SNDERR("Invalid type for scope %s", str); 625d5ac70f0Sopenharmony_ci err = -EINVAL; 626d5ac70f0Sopenharmony_ci goto _err; 627d5ac70f0Sopenharmony_ci } 628d5ac70f0Sopenharmony_ci err = snd_config_search(conf, "type", &c); 629d5ac70f0Sopenharmony_ci if (err < 0) { 630d5ac70f0Sopenharmony_ci SNDERR("type is not defined"); 631d5ac70f0Sopenharmony_ci goto _err; 632d5ac70f0Sopenharmony_ci } 633d5ac70f0Sopenharmony_ci err = snd_config_get_id(c, &id); 634d5ac70f0Sopenharmony_ci if (err < 0) { 635d5ac70f0Sopenharmony_ci SNDERR("unable to get id"); 636d5ac70f0Sopenharmony_ci goto _err; 637d5ac70f0Sopenharmony_ci } 638d5ac70f0Sopenharmony_ci err = snd_config_get_string(c, &str); 639d5ac70f0Sopenharmony_ci if (err < 0) { 640d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 641d5ac70f0Sopenharmony_ci goto _err; 642d5ac70f0Sopenharmony_ci } 643d5ac70f0Sopenharmony_ci err = snd_config_search_definition(root, "pcm_scope_type", str, &type_conf); 644d5ac70f0Sopenharmony_ci if (err >= 0) { 645d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, type_conf) { 646d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 647d5ac70f0Sopenharmony_ci const char *id; 648d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 649d5ac70f0Sopenharmony_ci continue; 650d5ac70f0Sopenharmony_ci if (strcmp(id, "comment") == 0) 651d5ac70f0Sopenharmony_ci continue; 652d5ac70f0Sopenharmony_ci if (strcmp(id, "lib") == 0) { 653d5ac70f0Sopenharmony_ci err = snd_config_get_string(n, &lib); 654d5ac70f0Sopenharmony_ci if (err < 0) { 655d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 656d5ac70f0Sopenharmony_ci goto _err; 657d5ac70f0Sopenharmony_ci } 658d5ac70f0Sopenharmony_ci continue; 659d5ac70f0Sopenharmony_ci } 660d5ac70f0Sopenharmony_ci if (strcmp(id, "open") == 0) { 661d5ac70f0Sopenharmony_ci err = snd_config_get_string(n, &open_name); 662d5ac70f0Sopenharmony_ci if (err < 0) { 663d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 664d5ac70f0Sopenharmony_ci goto _err; 665d5ac70f0Sopenharmony_ci } 666d5ac70f0Sopenharmony_ci continue; 667d5ac70f0Sopenharmony_ci } 668d5ac70f0Sopenharmony_ci SNDERR("Unknown field %s", id); 669d5ac70f0Sopenharmony_ci err = -EINVAL; 670d5ac70f0Sopenharmony_ci goto _err; 671d5ac70f0Sopenharmony_ci } 672d5ac70f0Sopenharmony_ci } 673d5ac70f0Sopenharmony_ci if (!open_name) { 674d5ac70f0Sopenharmony_ci open_name = buf; 675d5ac70f0Sopenharmony_ci snprintf(buf, sizeof(buf), "_snd_pcm_scope_%s_open", str); 676d5ac70f0Sopenharmony_ci } 677d5ac70f0Sopenharmony_ci h = INTERNAL(snd_dlopen)(lib, RTLD_NOW, errbuf, sizeof(errbuf)); 678d5ac70f0Sopenharmony_ci open_func = h ? dlsym(h, open_name) : NULL; 679d5ac70f0Sopenharmony_ci err = 0; 680d5ac70f0Sopenharmony_ci if (!h) { 681d5ac70f0Sopenharmony_ci SNDERR("Cannot open shared library %s (%s)", lib, errbuf); 682d5ac70f0Sopenharmony_ci err = -ENOENT; 683d5ac70f0Sopenharmony_ci } else if (!open_func) { 684d5ac70f0Sopenharmony_ci SNDERR("symbol %s is not defined inside %s", open_name, lib); 685d5ac70f0Sopenharmony_ci snd_dlclose(h); 686d5ac70f0Sopenharmony_ci err = -ENXIO; 687d5ac70f0Sopenharmony_ci } 688d5ac70f0Sopenharmony_ci _err: 689d5ac70f0Sopenharmony_ci if (type_conf) 690d5ac70f0Sopenharmony_ci snd_config_delete(type_conf); 691d5ac70f0Sopenharmony_ci if (! err) { 692d5ac70f0Sopenharmony_ci err = open_func(pcm, name, root, conf); 693d5ac70f0Sopenharmony_ci if (err < 0) 694d5ac70f0Sopenharmony_ci snd_dlclose(h); 695d5ac70f0Sopenharmony_ci else 696d5ac70f0Sopenharmony_ci meter->dl_handle = h; 697d5ac70f0Sopenharmony_ci } 698d5ac70f0Sopenharmony_ci return err; 699d5ac70f0Sopenharmony_ci} 700d5ac70f0Sopenharmony_ci 701d5ac70f0Sopenharmony_ci/*! \page pcm_plugins 702d5ac70f0Sopenharmony_ci 703d5ac70f0Sopenharmony_ci\section pcm_plugins_meter Plugin: Meter 704d5ac70f0Sopenharmony_ci 705d5ac70f0Sopenharmony_ciShow meter (visual waveform representation). 706d5ac70f0Sopenharmony_ci 707d5ac70f0Sopenharmony_ci\code 708d5ac70f0Sopenharmony_cipcm_scope_type.NAME { 709d5ac70f0Sopenharmony_ci [lib STR] # Library file (default libasound.so) 710d5ac70f0Sopenharmony_ci [open STR] # Open function (default _snd_pcm_scope_NAME_open) 711d5ac70f0Sopenharmony_ci} 712d5ac70f0Sopenharmony_ci 713d5ac70f0Sopenharmony_cipcm_scope.name { 714d5ac70f0Sopenharmony_ci type STR # Scope type 715d5ac70f0Sopenharmony_ci ... 716d5ac70f0Sopenharmony_ci} 717d5ac70f0Sopenharmony_ci 718d5ac70f0Sopenharmony_cipcm.name { 719d5ac70f0Sopenharmony_ci type meter # Meter PCM 720d5ac70f0Sopenharmony_ci slave STR # Slave name 721d5ac70f0Sopenharmony_ci # or 722d5ac70f0Sopenharmony_ci slave { # Slave definition 723d5ac70f0Sopenharmony_ci pcm STR # Slave PCM name 724d5ac70f0Sopenharmony_ci # or 725d5ac70f0Sopenharmony_ci pcm { } # Slave PCM definition 726d5ac70f0Sopenharmony_ci } 727d5ac70f0Sopenharmony_ci [frequency INT] # Updates per second 728d5ac70f0Sopenharmony_ci scopes { 729d5ac70f0Sopenharmony_ci ID STR # Scope name (see pcm_scope) 730d5ac70f0Sopenharmony_ci # or 731d5ac70f0Sopenharmony_ci ID { } # Scope definition (see pcm_scope) 732d5ac70f0Sopenharmony_ci } 733d5ac70f0Sopenharmony_ci} 734d5ac70f0Sopenharmony_ci\endcode 735d5ac70f0Sopenharmony_ci 736d5ac70f0Sopenharmony_ci\subsection pcm_plugins_meter_funcref Function reference 737d5ac70f0Sopenharmony_ci 738d5ac70f0Sopenharmony_ci<UL> 739d5ac70f0Sopenharmony_ci <LI>snd_pcm_meter_open() 740d5ac70f0Sopenharmony_ci <LI>_snd_pcm_meter_open() 741d5ac70f0Sopenharmony_ci</UL> 742d5ac70f0Sopenharmony_ci 743d5ac70f0Sopenharmony_ci*/ 744d5ac70f0Sopenharmony_ci 745d5ac70f0Sopenharmony_ci/** 746d5ac70f0Sopenharmony_ci * \brief Creates a new Meter PCM 747d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle 748d5ac70f0Sopenharmony_ci * \param name Name of PCM 749d5ac70f0Sopenharmony_ci * \param root Root configuration node 750d5ac70f0Sopenharmony_ci * \param conf Configuration node with Meter PCM description 751d5ac70f0Sopenharmony_ci * \param stream Stream type 752d5ac70f0Sopenharmony_ci * \param mode Stream mode 753d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code 754d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense 755d5ac70f0Sopenharmony_ci * of compatibility reasons. The prototype might be freely 756d5ac70f0Sopenharmony_ci * changed in future. 757d5ac70f0Sopenharmony_ci */ 758d5ac70f0Sopenharmony_ciint _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, 759d5ac70f0Sopenharmony_ci snd_config_t *root, snd_config_t *conf, 760d5ac70f0Sopenharmony_ci snd_pcm_stream_t stream, int mode) 761d5ac70f0Sopenharmony_ci{ 762d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 763d5ac70f0Sopenharmony_ci int err; 764d5ac70f0Sopenharmony_ci snd_pcm_t *spcm; 765d5ac70f0Sopenharmony_ci snd_config_t *slave = NULL, *sconf; 766d5ac70f0Sopenharmony_ci long frequency = -1; 767d5ac70f0Sopenharmony_ci snd_config_t *scopes = NULL; 768d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, conf) { 769d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 770d5ac70f0Sopenharmony_ci const char *id; 771d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 772d5ac70f0Sopenharmony_ci continue; 773d5ac70f0Sopenharmony_ci if (snd_pcm_conf_generic_id(id)) 774d5ac70f0Sopenharmony_ci continue; 775d5ac70f0Sopenharmony_ci if (strcmp(id, "slave") == 0) { 776d5ac70f0Sopenharmony_ci slave = n; 777d5ac70f0Sopenharmony_ci continue; 778d5ac70f0Sopenharmony_ci } 779d5ac70f0Sopenharmony_ci if (strcmp(id, "frequency") == 0) { 780d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &frequency); 781d5ac70f0Sopenharmony_ci if (err < 0) { 782d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 783d5ac70f0Sopenharmony_ci return -EINVAL; 784d5ac70f0Sopenharmony_ci } 785d5ac70f0Sopenharmony_ci continue; 786d5ac70f0Sopenharmony_ci } 787d5ac70f0Sopenharmony_ci if (strcmp(id, "scopes") == 0) { 788d5ac70f0Sopenharmony_ci if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { 789d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 790d5ac70f0Sopenharmony_ci return -EINVAL; 791d5ac70f0Sopenharmony_ci } 792d5ac70f0Sopenharmony_ci scopes = n; 793d5ac70f0Sopenharmony_ci continue; 794d5ac70f0Sopenharmony_ci } 795d5ac70f0Sopenharmony_ci SNDERR("Unknown field %s", id); 796d5ac70f0Sopenharmony_ci return -EINVAL; 797d5ac70f0Sopenharmony_ci } 798d5ac70f0Sopenharmony_ci if (!slave) { 799d5ac70f0Sopenharmony_ci SNDERR("slave is not defined"); 800d5ac70f0Sopenharmony_ci return -EINVAL; 801d5ac70f0Sopenharmony_ci } 802d5ac70f0Sopenharmony_ci err = snd_pcm_slave_conf(root, slave, &sconf, 0); 803d5ac70f0Sopenharmony_ci if (err < 0) 804d5ac70f0Sopenharmony_ci return err; 805d5ac70f0Sopenharmony_ci err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf); 806d5ac70f0Sopenharmony_ci snd_config_delete(sconf); 807d5ac70f0Sopenharmony_ci if (err < 0) 808d5ac70f0Sopenharmony_ci return err; 809d5ac70f0Sopenharmony_ci err = snd_pcm_meter_open(pcmp, name, frequency > 0 ? (unsigned int) frequency : FREQUENCY, spcm, 1); 810d5ac70f0Sopenharmony_ci if (err < 0) { 811d5ac70f0Sopenharmony_ci snd_pcm_close(spcm); 812d5ac70f0Sopenharmony_ci return err; 813d5ac70f0Sopenharmony_ci } 814d5ac70f0Sopenharmony_ci if (!scopes) 815d5ac70f0Sopenharmony_ci return 0; 816d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, scopes) { 817d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 818d5ac70f0Sopenharmony_ci const char *id, *str; 819d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 820d5ac70f0Sopenharmony_ci continue; 821d5ac70f0Sopenharmony_ci if (snd_config_get_string(n, &str) >= 0) { 822d5ac70f0Sopenharmony_ci err = snd_config_search_definition(root, "pcm_scope", str, &n); 823d5ac70f0Sopenharmony_ci if (err < 0) { 824d5ac70f0Sopenharmony_ci SNDERR("unknown pcm_scope %s", str); 825d5ac70f0Sopenharmony_ci } else { 826d5ac70f0Sopenharmony_ci err = snd_pcm_meter_add_scope_conf(*pcmp, id, root, n); 827d5ac70f0Sopenharmony_ci snd_config_delete(n); 828d5ac70f0Sopenharmony_ci } 829d5ac70f0Sopenharmony_ci } else 830d5ac70f0Sopenharmony_ci err = snd_pcm_meter_add_scope_conf(*pcmp, id, root, n); 831d5ac70f0Sopenharmony_ci if (err < 0) { 832d5ac70f0Sopenharmony_ci snd_pcm_close(*pcmp); 833d5ac70f0Sopenharmony_ci return err; 834d5ac70f0Sopenharmony_ci } 835d5ac70f0Sopenharmony_ci } 836d5ac70f0Sopenharmony_ci return 0; 837d5ac70f0Sopenharmony_ci} 838d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_pcm_meter_open, SND_PCM_DLSYM_VERSION); 839d5ac70f0Sopenharmony_ci 840d5ac70f0Sopenharmony_ci#endif 841d5ac70f0Sopenharmony_ci 842d5ac70f0Sopenharmony_ci/** 843d5ac70f0Sopenharmony_ci * \brief Add a scope to a #SND_PCM_TYPE_METER PCM 844d5ac70f0Sopenharmony_ci * \param pcm PCM handle 845d5ac70f0Sopenharmony_ci * \param scope Scope handle 846d5ac70f0Sopenharmony_ci * \return 0 on success otherwise a negative error code 847d5ac70f0Sopenharmony_ci */ 848d5ac70f0Sopenharmony_ciint snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope) 849d5ac70f0Sopenharmony_ci{ 850d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 851d5ac70f0Sopenharmony_ci assert(pcm->type == SND_PCM_TYPE_METER); 852d5ac70f0Sopenharmony_ci meter = pcm->private_data; 853d5ac70f0Sopenharmony_ci list_add_tail(&scope->list, &meter->scopes); 854d5ac70f0Sopenharmony_ci return 0; 855d5ac70f0Sopenharmony_ci} 856d5ac70f0Sopenharmony_ci 857d5ac70f0Sopenharmony_ci/** 858d5ac70f0Sopenharmony_ci * \brief Search an installed scope inside a #SND_PCM_TYPE_METER PCM 859d5ac70f0Sopenharmony_ci * \param pcm PCM handle 860d5ac70f0Sopenharmony_ci * \param name scope name 861d5ac70f0Sopenharmony_ci * \return pointer to found scope or NULL if none is found 862d5ac70f0Sopenharmony_ci */ 863d5ac70f0Sopenharmony_cisnd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name) 864d5ac70f0Sopenharmony_ci{ 865d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 866d5ac70f0Sopenharmony_ci struct list_head *pos; 867d5ac70f0Sopenharmony_ci assert(pcm->type == SND_PCM_TYPE_METER); 868d5ac70f0Sopenharmony_ci meter = pcm->private_data; 869d5ac70f0Sopenharmony_ci list_for_each(pos, &meter->scopes) { 870d5ac70f0Sopenharmony_ci snd_pcm_scope_t *scope; 871d5ac70f0Sopenharmony_ci scope = list_entry(pos, snd_pcm_scope_t, list); 872d5ac70f0Sopenharmony_ci if (scope->name && strcmp(scope->name, name) == 0) 873d5ac70f0Sopenharmony_ci return scope; 874d5ac70f0Sopenharmony_ci } 875d5ac70f0Sopenharmony_ci return NULL; 876d5ac70f0Sopenharmony_ci} 877d5ac70f0Sopenharmony_ci 878d5ac70f0Sopenharmony_ci/** 879d5ac70f0Sopenharmony_ci * \brief Get meter buffer size from a #SND_PCM_TYPE_METER PCM 880d5ac70f0Sopenharmony_ci * \param pcm PCM handle 881d5ac70f0Sopenharmony_ci * \return meter buffer size in frames 882d5ac70f0Sopenharmony_ci */ 883d5ac70f0Sopenharmony_cisnd_pcm_uframes_t snd_pcm_meter_get_bufsize(snd_pcm_t *pcm) 884d5ac70f0Sopenharmony_ci{ 885d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 886d5ac70f0Sopenharmony_ci assert(pcm->type == SND_PCM_TYPE_METER); 887d5ac70f0Sopenharmony_ci meter = pcm->private_data; 888d5ac70f0Sopenharmony_ci assert(meter->gen.slave->setup); 889d5ac70f0Sopenharmony_ci return meter->buf_size; 890d5ac70f0Sopenharmony_ci} 891d5ac70f0Sopenharmony_ci 892d5ac70f0Sopenharmony_ci/** 893d5ac70f0Sopenharmony_ci * \brief Get meter channels from a #SND_PCM_TYPE_METER PCM 894d5ac70f0Sopenharmony_ci * \param pcm PCM handle 895d5ac70f0Sopenharmony_ci * \return meter channels count 896d5ac70f0Sopenharmony_ci */ 897d5ac70f0Sopenharmony_ciunsigned int snd_pcm_meter_get_channels(snd_pcm_t *pcm) 898d5ac70f0Sopenharmony_ci{ 899d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 900d5ac70f0Sopenharmony_ci assert(pcm->type == SND_PCM_TYPE_METER); 901d5ac70f0Sopenharmony_ci meter = pcm->private_data; 902d5ac70f0Sopenharmony_ci assert(meter->gen.slave->setup); 903d5ac70f0Sopenharmony_ci return meter->gen.slave->channels; 904d5ac70f0Sopenharmony_ci} 905d5ac70f0Sopenharmony_ci 906d5ac70f0Sopenharmony_ci/** 907d5ac70f0Sopenharmony_ci * \brief Get meter rate from a #SND_PCM_TYPE_METER PCM 908d5ac70f0Sopenharmony_ci * \param pcm PCM handle 909d5ac70f0Sopenharmony_ci * \return approximate rate 910d5ac70f0Sopenharmony_ci */ 911d5ac70f0Sopenharmony_ciunsigned int snd_pcm_meter_get_rate(snd_pcm_t *pcm) 912d5ac70f0Sopenharmony_ci{ 913d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 914d5ac70f0Sopenharmony_ci assert(pcm->type == SND_PCM_TYPE_METER); 915d5ac70f0Sopenharmony_ci meter = pcm->private_data; 916d5ac70f0Sopenharmony_ci assert(meter->gen.slave->setup); 917d5ac70f0Sopenharmony_ci return meter->gen.slave->rate; 918d5ac70f0Sopenharmony_ci} 919d5ac70f0Sopenharmony_ci 920d5ac70f0Sopenharmony_ci/** 921d5ac70f0Sopenharmony_ci * \brief Get meter "now" frame pointer from a #SND_PCM_TYPE_METER PCM 922d5ac70f0Sopenharmony_ci * \param pcm PCM handle 923d5ac70f0Sopenharmony_ci * \return "now" frame pointer in frames (0 ... boundary - 1) see #snd_pcm_meter_get_boundary 924d5ac70f0Sopenharmony_ci */ 925d5ac70f0Sopenharmony_cisnd_pcm_uframes_t snd_pcm_meter_get_now(snd_pcm_t *pcm) 926d5ac70f0Sopenharmony_ci{ 927d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 928d5ac70f0Sopenharmony_ci assert(pcm->type == SND_PCM_TYPE_METER); 929d5ac70f0Sopenharmony_ci meter = pcm->private_data; 930d5ac70f0Sopenharmony_ci assert(meter->gen.slave->setup); 931d5ac70f0Sopenharmony_ci return meter->now; 932d5ac70f0Sopenharmony_ci} 933d5ac70f0Sopenharmony_ci 934d5ac70f0Sopenharmony_ci/** 935d5ac70f0Sopenharmony_ci * \brief Get boundary for frame pointers from a #SND_PCM_TYPE_METER PCM 936d5ac70f0Sopenharmony_ci * \param pcm PCM handle 937d5ac70f0Sopenharmony_ci * \return boundary in frames 938d5ac70f0Sopenharmony_ci */ 939d5ac70f0Sopenharmony_cisnd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm) 940d5ac70f0Sopenharmony_ci{ 941d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 942d5ac70f0Sopenharmony_ci assert(pcm->type == SND_PCM_TYPE_METER); 943d5ac70f0Sopenharmony_ci meter = pcm->private_data; 944d5ac70f0Sopenharmony_ci assert(meter->gen.slave->setup); 945d5ac70f0Sopenharmony_ci return meter->gen.slave->boundary; 946d5ac70f0Sopenharmony_ci} 947d5ac70f0Sopenharmony_ci 948d5ac70f0Sopenharmony_ci/** 949d5ac70f0Sopenharmony_ci * \brief Set name of a #SND_PCM_TYPE_METER PCM scope 950d5ac70f0Sopenharmony_ci * \param scope PCM meter scope 951d5ac70f0Sopenharmony_ci * \param val scope name 952d5ac70f0Sopenharmony_ci */ 953d5ac70f0Sopenharmony_civoid snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val) 954d5ac70f0Sopenharmony_ci{ 955d5ac70f0Sopenharmony_ci scope->name = strdup(val); 956d5ac70f0Sopenharmony_ci} 957d5ac70f0Sopenharmony_ci 958d5ac70f0Sopenharmony_ci/** 959d5ac70f0Sopenharmony_ci * \brief Get name of a #SND_PCM_TYPE_METER PCM scope 960d5ac70f0Sopenharmony_ci * \param scope PCM meter scope 961d5ac70f0Sopenharmony_ci * \return scope name 962d5ac70f0Sopenharmony_ci */ 963d5ac70f0Sopenharmony_ciconst char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope) 964d5ac70f0Sopenharmony_ci{ 965d5ac70f0Sopenharmony_ci return scope->name; 966d5ac70f0Sopenharmony_ci} 967d5ac70f0Sopenharmony_ci 968d5ac70f0Sopenharmony_ci/** 969d5ac70f0Sopenharmony_ci * \brief Set callbacks for a #SND_PCM_TYPE_METER PCM scope 970d5ac70f0Sopenharmony_ci * \param scope PCM meter scope 971d5ac70f0Sopenharmony_ci * \param val callbacks 972d5ac70f0Sopenharmony_ci */ 973d5ac70f0Sopenharmony_civoid snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, const snd_pcm_scope_ops_t *val) 974d5ac70f0Sopenharmony_ci{ 975d5ac70f0Sopenharmony_ci scope->ops = val; 976d5ac70f0Sopenharmony_ci} 977d5ac70f0Sopenharmony_ci 978d5ac70f0Sopenharmony_ci/** 979d5ac70f0Sopenharmony_ci * \brief Get callbacks private value for a #SND_PCM_TYPE_METER PCM scope 980d5ac70f0Sopenharmony_ci * \param scope PCM meter scope 981d5ac70f0Sopenharmony_ci * \return Private data value 982d5ac70f0Sopenharmony_ci */ 983d5ac70f0Sopenharmony_civoid *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope) 984d5ac70f0Sopenharmony_ci{ 985d5ac70f0Sopenharmony_ci return scope->private_data; 986d5ac70f0Sopenharmony_ci} 987d5ac70f0Sopenharmony_ci 988d5ac70f0Sopenharmony_ci/** 989d5ac70f0Sopenharmony_ci * \brief Get callbacks private value for a #SND_PCM_TYPE_METER PCM scope 990d5ac70f0Sopenharmony_ci * \param scope PCM meter scope 991d5ac70f0Sopenharmony_ci * \param val Private data value 992d5ac70f0Sopenharmony_ci */ 993d5ac70f0Sopenharmony_civoid snd_pcm_scope_set_callback_private(snd_pcm_scope_t *scope, void *val) 994d5ac70f0Sopenharmony_ci{ 995d5ac70f0Sopenharmony_ci scope->private_data = val; 996d5ac70f0Sopenharmony_ci} 997d5ac70f0Sopenharmony_ci 998d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN 999d5ac70f0Sopenharmony_citypedef struct _snd_pcm_scope_s16 { 1000d5ac70f0Sopenharmony_ci snd_pcm_t *pcm; 1001d5ac70f0Sopenharmony_ci snd_pcm_adpcm_state_t *adpcm_states; 1002d5ac70f0Sopenharmony_ci unsigned int index; 1003d5ac70f0Sopenharmony_ci snd_pcm_uframes_t old; 1004d5ac70f0Sopenharmony_ci int16_t *buf; 1005d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *buf_areas; 1006d5ac70f0Sopenharmony_ci} snd_pcm_scope_s16_t; 1007d5ac70f0Sopenharmony_ci 1008d5ac70f0Sopenharmony_cistatic int s16_enable(snd_pcm_scope_t *scope) 1009d5ac70f0Sopenharmony_ci{ 1010d5ac70f0Sopenharmony_ci snd_pcm_scope_s16_t *s16 = scope->private_data; 1011d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = s16->pcm->private_data; 1012d5ac70f0Sopenharmony_ci snd_pcm_t *spcm = meter->gen.slave; 1013d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *a; 1014d5ac70f0Sopenharmony_ci unsigned int c; 1015d5ac70f0Sopenharmony_ci int idx; 1016d5ac70f0Sopenharmony_ci if (spcm->format == SND_PCM_FORMAT_S16 && 1017d5ac70f0Sopenharmony_ci spcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) { 1018d5ac70f0Sopenharmony_ci s16->buf = (int16_t *) meter->buf; 1019d5ac70f0Sopenharmony_ci return -EINVAL; 1020d5ac70f0Sopenharmony_ci } 1021d5ac70f0Sopenharmony_ci switch (spcm->format) { 1022d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_A_LAW: 1023d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_MU_LAW: 1024d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_IMA_ADPCM: 1025d5ac70f0Sopenharmony_ci idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S16); 1026d5ac70f0Sopenharmony_ci break; 1027d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_S8: 1028d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_S16_LE: 1029d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_S16_BE: 1030d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_S24_LE: 1031d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_S24_BE: 1032d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_S32_LE: 1033d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_S32_BE: 1034d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_U8: 1035d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_U16_LE: 1036d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_U16_BE: 1037d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_U24_LE: 1038d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_U24_BE: 1039d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_U32_LE: 1040d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_U32_BE: 1041d5ac70f0Sopenharmony_ci idx = snd_pcm_linear_convert_index(spcm->format, SND_PCM_FORMAT_S16); 1042d5ac70f0Sopenharmony_ci break; 1043d5ac70f0Sopenharmony_ci default: 1044d5ac70f0Sopenharmony_ci return -EINVAL; 1045d5ac70f0Sopenharmony_ci } 1046d5ac70f0Sopenharmony_ci s16->index = idx; 1047d5ac70f0Sopenharmony_ci if (spcm->format == SND_PCM_FORMAT_IMA_ADPCM) { 1048d5ac70f0Sopenharmony_ci s16->adpcm_states = calloc(spcm->channels, sizeof(*s16->adpcm_states)); 1049d5ac70f0Sopenharmony_ci if (!s16->adpcm_states) 1050d5ac70f0Sopenharmony_ci return -ENOMEM; 1051d5ac70f0Sopenharmony_ci } 1052d5ac70f0Sopenharmony_ci s16->buf = malloc(meter->buf_size * 2 * spcm->channels); 1053d5ac70f0Sopenharmony_ci if (!s16->buf) { 1054d5ac70f0Sopenharmony_ci free(s16->adpcm_states); 1055d5ac70f0Sopenharmony_ci return -ENOMEM; 1056d5ac70f0Sopenharmony_ci } 1057d5ac70f0Sopenharmony_ci a = calloc(spcm->channels, sizeof(*a)); 1058d5ac70f0Sopenharmony_ci if (!a) { 1059d5ac70f0Sopenharmony_ci free(s16->buf); 1060d5ac70f0Sopenharmony_ci free(s16->adpcm_states); 1061d5ac70f0Sopenharmony_ci return -ENOMEM; 1062d5ac70f0Sopenharmony_ci } 1063d5ac70f0Sopenharmony_ci s16->buf_areas = a; 1064d5ac70f0Sopenharmony_ci for (c = 0; c < spcm->channels; c++, a++) { 1065d5ac70f0Sopenharmony_ci a->addr = s16->buf + c * meter->buf_size; 1066d5ac70f0Sopenharmony_ci a->first = 0; 1067d5ac70f0Sopenharmony_ci a->step = 16; 1068d5ac70f0Sopenharmony_ci } 1069d5ac70f0Sopenharmony_ci return 0; 1070d5ac70f0Sopenharmony_ci} 1071d5ac70f0Sopenharmony_ci 1072d5ac70f0Sopenharmony_cistatic void s16_disable(snd_pcm_scope_t *scope) 1073d5ac70f0Sopenharmony_ci{ 1074d5ac70f0Sopenharmony_ci snd_pcm_scope_s16_t *s16 = scope->private_data; 1075d5ac70f0Sopenharmony_ci free(s16->adpcm_states); 1076d5ac70f0Sopenharmony_ci s16->adpcm_states = NULL; 1077d5ac70f0Sopenharmony_ci free(s16->buf); 1078d5ac70f0Sopenharmony_ci s16->buf = NULL; 1079d5ac70f0Sopenharmony_ci free(s16->buf_areas); 1080d5ac70f0Sopenharmony_ci s16->buf_areas = 0; 1081d5ac70f0Sopenharmony_ci} 1082d5ac70f0Sopenharmony_ci 1083d5ac70f0Sopenharmony_cistatic void s16_close(snd_pcm_scope_t *scope) 1084d5ac70f0Sopenharmony_ci{ 1085d5ac70f0Sopenharmony_ci snd_pcm_scope_s16_t *s16 = scope->private_data; 1086d5ac70f0Sopenharmony_ci free(s16); 1087d5ac70f0Sopenharmony_ci} 1088d5ac70f0Sopenharmony_ci 1089d5ac70f0Sopenharmony_cistatic void s16_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED) 1090d5ac70f0Sopenharmony_ci{ 1091d5ac70f0Sopenharmony_ci} 1092d5ac70f0Sopenharmony_ci 1093d5ac70f0Sopenharmony_cistatic void s16_stop(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED) 1094d5ac70f0Sopenharmony_ci{ 1095d5ac70f0Sopenharmony_ci} 1096d5ac70f0Sopenharmony_ci 1097d5ac70f0Sopenharmony_cistatic void s16_update(snd_pcm_scope_t *scope) 1098d5ac70f0Sopenharmony_ci{ 1099d5ac70f0Sopenharmony_ci snd_pcm_scope_s16_t *s16 = scope->private_data; 1100d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = s16->pcm->private_data; 1101d5ac70f0Sopenharmony_ci snd_pcm_t *spcm = meter->gen.slave; 1102d5ac70f0Sopenharmony_ci snd_pcm_sframes_t size; 1103d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset; 1104d5ac70f0Sopenharmony_ci size = meter->now - s16->old; 1105d5ac70f0Sopenharmony_ci if (size < 0) 1106d5ac70f0Sopenharmony_ci size += spcm->boundary; 1107d5ac70f0Sopenharmony_ci if (size > (snd_pcm_sframes_t)s16->pcm->buffer_size) 1108d5ac70f0Sopenharmony_ci size = s16->pcm->buffer_size; 1109d5ac70f0Sopenharmony_ci offset = s16->old % meter->buf_size; 1110d5ac70f0Sopenharmony_ci while (size > 0) { 1111d5ac70f0Sopenharmony_ci snd_pcm_uframes_t frames = size; 1112d5ac70f0Sopenharmony_ci snd_pcm_uframes_t cont = meter->buf_size - offset; 1113d5ac70f0Sopenharmony_ci if (frames > cont) 1114d5ac70f0Sopenharmony_ci frames = cont; 1115d5ac70f0Sopenharmony_ci switch (spcm->format) { 1116d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_A_LAW: 1117d5ac70f0Sopenharmony_ci snd_pcm_alaw_decode(s16->buf_areas, offset, 1118d5ac70f0Sopenharmony_ci meter->buf_areas, offset, 1119d5ac70f0Sopenharmony_ci spcm->channels, frames, 1120d5ac70f0Sopenharmony_ci s16->index); 1121d5ac70f0Sopenharmony_ci break; 1122d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_MU_LAW: 1123d5ac70f0Sopenharmony_ci snd_pcm_mulaw_decode(s16->buf_areas, offset, 1124d5ac70f0Sopenharmony_ci meter->buf_areas, offset, 1125d5ac70f0Sopenharmony_ci spcm->channels, frames, 1126d5ac70f0Sopenharmony_ci s16->index); 1127d5ac70f0Sopenharmony_ci break; 1128d5ac70f0Sopenharmony_ci case SND_PCM_FORMAT_IMA_ADPCM: 1129d5ac70f0Sopenharmony_ci snd_pcm_adpcm_decode(s16->buf_areas, offset, 1130d5ac70f0Sopenharmony_ci meter->buf_areas, offset, 1131d5ac70f0Sopenharmony_ci spcm->channels, frames, 1132d5ac70f0Sopenharmony_ci s16->index, 1133d5ac70f0Sopenharmony_ci s16->adpcm_states); 1134d5ac70f0Sopenharmony_ci break; 1135d5ac70f0Sopenharmony_ci default: 1136d5ac70f0Sopenharmony_ci snd_pcm_linear_convert(s16->buf_areas, offset, 1137d5ac70f0Sopenharmony_ci meter->buf_areas, offset, 1138d5ac70f0Sopenharmony_ci spcm->channels, frames, 1139d5ac70f0Sopenharmony_ci s16->index); 1140d5ac70f0Sopenharmony_ci break; 1141d5ac70f0Sopenharmony_ci } 1142d5ac70f0Sopenharmony_ci if (frames == cont) 1143d5ac70f0Sopenharmony_ci offset = 0; 1144d5ac70f0Sopenharmony_ci else 1145d5ac70f0Sopenharmony_ci offset += frames; 1146d5ac70f0Sopenharmony_ci size -= frames; 1147d5ac70f0Sopenharmony_ci } 1148d5ac70f0Sopenharmony_ci s16->old = meter->now; 1149d5ac70f0Sopenharmony_ci} 1150d5ac70f0Sopenharmony_ci 1151d5ac70f0Sopenharmony_cistatic void s16_reset(snd_pcm_scope_t *scope) 1152d5ac70f0Sopenharmony_ci{ 1153d5ac70f0Sopenharmony_ci snd_pcm_scope_s16_t *s16 = scope->private_data; 1154d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter = s16->pcm->private_data; 1155d5ac70f0Sopenharmony_ci s16->old = meter->now; 1156d5ac70f0Sopenharmony_ci} 1157d5ac70f0Sopenharmony_ci 1158d5ac70f0Sopenharmony_cistatic const snd_pcm_scope_ops_t s16_ops = { 1159d5ac70f0Sopenharmony_ci .enable = s16_enable, 1160d5ac70f0Sopenharmony_ci .disable = s16_disable, 1161d5ac70f0Sopenharmony_ci .close = s16_close, 1162d5ac70f0Sopenharmony_ci .start = s16_start, 1163d5ac70f0Sopenharmony_ci .stop = s16_stop, 1164d5ac70f0Sopenharmony_ci .update = s16_update, 1165d5ac70f0Sopenharmony_ci .reset = s16_reset, 1166d5ac70f0Sopenharmony_ci}; 1167d5ac70f0Sopenharmony_ci 1168d5ac70f0Sopenharmony_ci#endif 1169d5ac70f0Sopenharmony_ci 1170d5ac70f0Sopenharmony_ci/** 1171d5ac70f0Sopenharmony_ci * \brief Add a s16 pseudo scope to a #SND_PCM_TYPE_METER PCM 1172d5ac70f0Sopenharmony_ci * \param pcm The pcm handle 1173d5ac70f0Sopenharmony_ci * \param name Scope name 1174d5ac70f0Sopenharmony_ci * \param scopep Pointer to newly created and added scope 1175d5ac70f0Sopenharmony_ci * \return 0 on success otherwise a negative error code 1176d5ac70f0Sopenharmony_ci * 1177d5ac70f0Sopenharmony_ci * s16 pseudo scope convert #SND_PCM_TYPE_METER PCM frames in CPU endian 1178d5ac70f0Sopenharmony_ci * 16 bit frames for use with other scopes. Don't forget to insert it before 1179d5ac70f0Sopenharmony_ci * and to not insert it more time (see #snd_pcm_meter_search_scope) 1180d5ac70f0Sopenharmony_ci */ 1181d5ac70f0Sopenharmony_ciint snd_pcm_scope_s16_open(snd_pcm_t *pcm, const char *name, 1182d5ac70f0Sopenharmony_ci snd_pcm_scope_t **scopep) 1183d5ac70f0Sopenharmony_ci{ 1184d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 1185d5ac70f0Sopenharmony_ci snd_pcm_scope_t *scope; 1186d5ac70f0Sopenharmony_ci snd_pcm_scope_s16_t *s16; 1187d5ac70f0Sopenharmony_ci assert(pcm->type == SND_PCM_TYPE_METER); 1188d5ac70f0Sopenharmony_ci meter = pcm->private_data; 1189d5ac70f0Sopenharmony_ci scope = calloc(1, sizeof(*scope)); 1190d5ac70f0Sopenharmony_ci if (!scope) 1191d5ac70f0Sopenharmony_ci return -ENOMEM; 1192d5ac70f0Sopenharmony_ci s16 = calloc(1, sizeof(*s16)); 1193d5ac70f0Sopenharmony_ci if (!s16) { 1194d5ac70f0Sopenharmony_ci free(scope); 1195d5ac70f0Sopenharmony_ci return -ENOMEM; 1196d5ac70f0Sopenharmony_ci } 1197d5ac70f0Sopenharmony_ci if (name) 1198d5ac70f0Sopenharmony_ci scope->name = strdup(name); 1199d5ac70f0Sopenharmony_ci s16->pcm = pcm; 1200d5ac70f0Sopenharmony_ci scope->ops = &s16_ops; 1201d5ac70f0Sopenharmony_ci scope->private_data = s16; 1202d5ac70f0Sopenharmony_ci list_add_tail(&scope->list, &meter->scopes); 1203d5ac70f0Sopenharmony_ci *scopep = scope; 1204d5ac70f0Sopenharmony_ci return 0; 1205d5ac70f0Sopenharmony_ci} 1206d5ac70f0Sopenharmony_ci 1207d5ac70f0Sopenharmony_ci/** 1208d5ac70f0Sopenharmony_ci * \brief Get s16 pseudo scope frames buffer for a channel 1209d5ac70f0Sopenharmony_ci * \param scope s16 pseudo scope handle 1210d5ac70f0Sopenharmony_ci * \param channel Channel 1211d5ac70f0Sopenharmony_ci * \return Pointer to channel buffer 1212d5ac70f0Sopenharmony_ci */ 1213d5ac70f0Sopenharmony_ciint16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope, 1214d5ac70f0Sopenharmony_ci unsigned int channel) 1215d5ac70f0Sopenharmony_ci{ 1216d5ac70f0Sopenharmony_ci snd_pcm_scope_s16_t *s16; 1217d5ac70f0Sopenharmony_ci snd_pcm_meter_t *meter; 1218d5ac70f0Sopenharmony_ci assert(scope->ops == &s16_ops); 1219d5ac70f0Sopenharmony_ci s16 = scope->private_data; 1220d5ac70f0Sopenharmony_ci meter = s16->pcm->private_data; 1221d5ac70f0Sopenharmony_ci assert(meter->gen.slave->setup); 1222d5ac70f0Sopenharmony_ci assert(s16->buf_areas); 1223d5ac70f0Sopenharmony_ci assert(channel < meter->gen.slave->channels); 1224d5ac70f0Sopenharmony_ci return s16->buf_areas[channel].addr; 1225d5ac70f0Sopenharmony_ci} 1226d5ac70f0Sopenharmony_ci 1227d5ac70f0Sopenharmony_ci/** 1228d5ac70f0Sopenharmony_ci * \brief allocate an invalid #snd_pcm_scope_t using standard malloc 1229d5ac70f0Sopenharmony_ci * \param ptr returned pointer 1230d5ac70f0Sopenharmony_ci * \return 0 on success otherwise negative error code 1231d5ac70f0Sopenharmony_ci */ 1232d5ac70f0Sopenharmony_ciint snd_pcm_scope_malloc(snd_pcm_scope_t **ptr) 1233d5ac70f0Sopenharmony_ci{ 1234d5ac70f0Sopenharmony_ci assert(ptr); 1235d5ac70f0Sopenharmony_ci *ptr = calloc(1, sizeof(snd_pcm_scope_t)); 1236d5ac70f0Sopenharmony_ci if (!*ptr) 1237d5ac70f0Sopenharmony_ci return -ENOMEM; 1238d5ac70f0Sopenharmony_ci return 0; 1239d5ac70f0Sopenharmony_ci} 1240d5ac70f0Sopenharmony_ci 1241