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