mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-11-03 09:01:52 -05:00 
			
		
		
		
	Added pcm_meter skeleton
This commit is contained in:
		
							parent
							
								
									bab4a552c8
								
							
						
					
					
						commit
						d87059867b
					
				
					 5 changed files with 707 additions and 19 deletions
				
			
		| 
						 | 
				
			
			@ -5,7 +5,7 @@ libpcm_la_SOURCES = atomic.c mask.c interval.c \
 | 
			
		|||
		    pcm.c pcm_m4.c pcm_hw.c pcm_plugin.c pcm_copy.c pcm_linear.c \
 | 
			
		||||
		    pcm_route.c pcm_mulaw.c pcm_alaw.c pcm_adpcm.c \
 | 
			
		||||
		    pcm_rate.c pcm_plug.c pcm_misc.c pcm_mmap.c pcm_multi.c \
 | 
			
		||||
	            pcm_shm.c pcm_file.c pcm_share.c pcm_null.c \
 | 
			
		||||
	            pcm_shm.c pcm_file.c pcm_share.c pcm_null.c pcm_meter.c \
 | 
			
		||||
		    pcm_params.c
 | 
			
		||||
noinst_HEADERS = atomic.h pcm_local.h pcm_plugin.h mask.h mask_inline.h \
 | 
			
		||||
	         interval.h interval_inline.h plugin_ops.h
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -166,7 +166,26 @@ struct _snd_pcm {
 | 
			
		|||
	void *private_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define ROUTE_PLUGIN_FLOAT 1
 | 
			
		||||
#define ROUTE_PLUGIN_RESOLUTION 16
 | 
			
		||||
 | 
			
		||||
#if ROUTE_PLUGIN_FLOAT
 | 
			
		||||
typedef float snd_pcm_route_ttable_entry_t;
 | 
			
		||||
#define HALF 0.5
 | 
			
		||||
#define FULL 1.0
 | 
			
		||||
#else
 | 
			
		||||
typedef int snd_pcm_route_ttable_entry_t;
 | 
			
		||||
#define HALF (ROUTE_PLUGIN_RESOLUTION / 2)
 | 
			
		||||
#define FULL ROUTE_PLUGIN_RESOLUTION
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int snd_pcm_hw_open(snd_pcm_t **pcm, const char *name, int card, int device, int subdevice, snd_pcm_stream_t stream, int mode);
 | 
			
		||||
int snd_pcm_plug_open(snd_pcm_t **pcmp,
 | 
			
		||||
		      const char *name,
 | 
			
		||||
		      snd_pcm_route_ttable_entry_t *ttable,
 | 
			
		||||
		      unsigned int tt_ssize,
 | 
			
		||||
		      unsigned int tt_cused, unsigned int tt_sused,
 | 
			
		||||
		      snd_pcm_t *slave, int close_slave);
 | 
			
		||||
int snd_pcm_plug_open_hw(snd_pcm_t **pcm, const char *name, int card, int device, int subdevice, snd_pcm_stream_t stream, int mode);
 | 
			
		||||
int snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name, const char *socket, const char *sname, snd_pcm_stream_t stream, int mode);
 | 
			
		||||
int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name, const char *fname, int fd, const char *fmt, snd_pcm_t *slave, int close_slave);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										686
									
								
								src/pcm/pcm_meter.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										686
									
								
								src/pcm/pcm_meter.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,686 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  PCM - Meter plugin
 | 
			
		||||
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *   This library is free software; you can redistribute it and/or modify
 | 
			
		||||
 *   it under the terms of the GNU Library General Public License as
 | 
			
		||||
 *   published by the Free Software Foundation; either version 2 of
 | 
			
		||||
 *   the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *   This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *   GNU Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *   You should have received a copy of the GNU Library General Public
 | 
			
		||||
 *   License along with this library; if not, write to the Free Software
 | 
			
		||||
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
  
 | 
			
		||||
#include <byteswap.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <asm/atomic.h>
 | 
			
		||||
#include "pcm_local.h"
 | 
			
		||||
#include "pcm_plugin.h"
 | 
			
		||||
#include "list.h"
 | 
			
		||||
 | 
			
		||||
#define FPS 50
 | 
			
		||||
 | 
			
		||||
typedef struct _snd_pcm_meter_scope snd_pcm_meter_scope_t;
 | 
			
		||||
 | 
			
		||||
struct _snd_pcm_meter_scope {
 | 
			
		||||
	snd_pcm_t *pcm;
 | 
			
		||||
	char *name;
 | 
			
		||||
	void (*init)(snd_pcm_meter_scope_t *scope);
 | 
			
		||||
	void (*start)(snd_pcm_meter_scope_t *scope);
 | 
			
		||||
	void (*stop)(snd_pcm_meter_scope_t *scope);
 | 
			
		||||
	void (*update)(snd_pcm_meter_scope_t *scope);
 | 
			
		||||
	void (*reset)(snd_pcm_meter_scope_t *scope);
 | 
			
		||||
	void (*close)(snd_pcm_meter_scope_t *scope);
 | 
			
		||||
	void *private_data;
 | 
			
		||||
	struct list_head list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct _snd_pcm_meter {
 | 
			
		||||
	snd_pcm_t *slave;
 | 
			
		||||
	int close_slave;
 | 
			
		||||
	snd_pcm_uframes_t rptr;
 | 
			
		||||
	snd_pcm_uframes_t buf_size;
 | 
			
		||||
	snd_pcm_channel_area_t *buf_areas;
 | 
			
		||||
	snd_pcm_uframes_t now;
 | 
			
		||||
	char *buf;
 | 
			
		||||
	pthread_t thread;
 | 
			
		||||
	int closed;
 | 
			
		||||
	struct list_head scopes;
 | 
			
		||||
	int running;
 | 
			
		||||
	atomic_t reset;
 | 
			
		||||
	pthread_mutex_t update_mutex;
 | 
			
		||||
	pthread_mutex_t running_mutex;
 | 
			
		||||
	pthread_cond_t running_cond;
 | 
			
		||||
} snd_pcm_meter_t;
 | 
			
		||||
 | 
			
		||||
void debug_init(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "init\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void debug_start(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "start\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void debug_stop(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "stop\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void debug_update(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = scope->pcm->private_data;
 | 
			
		||||
	fprintf(stderr, "update %ld\r", meter->now);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void debug_reset(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "reset\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void debug_close(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "close\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
snd_pcm_meter_scope_t debug_scope = {
 | 
			
		||||
	name: "debug",
 | 
			
		||||
	init: debug_init,
 | 
			
		||||
	start: debug_start,
 | 
			
		||||
	stop: debug_stop,
 | 
			
		||||
	update: debug_update,
 | 
			
		||||
	reset: debug_reset,
 | 
			
		||||
	close: debug_close,
 | 
			
		||||
	pcm: NULL,
 | 
			
		||||
	list: { 0, 0 },
 | 
			
		||||
	private_data: NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_meter_scope_t *scope)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	scope->pcm = pcm;
 | 
			
		||||
	list_add_tail(&scope->list, &meter->scopes);
 | 
			
		||||
	scope->init(scope);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void snd_pcm_meter_add_frames(snd_pcm_t *pcm,
 | 
			
		||||
			      const snd_pcm_channel_area_t *areas,
 | 
			
		||||
			      snd_pcm_uframes_t ptr,
 | 
			
		||||
			      snd_pcm_uframes_t frames)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	while (frames > 0) {
 | 
			
		||||
		snd_pcm_uframes_t n = frames;
 | 
			
		||||
		snd_pcm_uframes_t dst_offset = ptr % meter->buf_size;
 | 
			
		||||
		snd_pcm_uframes_t src_offset = ptr % pcm->buffer_size;
 | 
			
		||||
		snd_pcm_uframes_t dst_cont = meter->buf_size - dst_offset;
 | 
			
		||||
		snd_pcm_uframes_t src_cont = pcm->buffer_size - src_offset;
 | 
			
		||||
		if (n > dst_cont)
 | 
			
		||||
			n = dst_cont;
 | 
			
		||||
		if (n > src_cont)
 | 
			
		||||
			n = src_cont;
 | 
			
		||||
		snd_pcm_areas_copy(meter->buf_areas, dst_offset, 
 | 
			
		||||
				   areas, src_offset,
 | 
			
		||||
				   pcm->channels, n, pcm->format);
 | 
			
		||||
		frames -= n;
 | 
			
		||||
		ptr += n;
 | 
			
		||||
		if (ptr == pcm->boundary)
 | 
			
		||||
			ptr = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void snd_pcm_meter_update_main(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	snd_pcm_sframes_t frames;
 | 
			
		||||
	snd_pcm_uframes_t rptr, old_rptr;
 | 
			
		||||
	const snd_pcm_channel_area_t *areas;
 | 
			
		||||
	int locked;
 | 
			
		||||
	locked = (pthread_mutex_trylock(&meter->update_mutex) >= 0);
 | 
			
		||||
	areas = snd_pcm_mmap_areas(pcm);
 | 
			
		||||
	rptr = *pcm->hw_ptr;
 | 
			
		||||
	old_rptr = meter->rptr;
 | 
			
		||||
	meter->rptr = rptr;
 | 
			
		||||
	frames = rptr - old_rptr;
 | 
			
		||||
	if (frames < 0)
 | 
			
		||||
		frames += pcm->boundary;
 | 
			
		||||
	if (frames > 0) {
 | 
			
		||||
		assert((snd_pcm_uframes_t) frames <= pcm->buffer_size);
 | 
			
		||||
		snd_pcm_meter_add_frames(pcm, areas, old_rptr, frames);
 | 
			
		||||
	}
 | 
			
		||||
	if (locked)
 | 
			
		||||
		pthread_mutex_unlock(&meter->update_mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_update_scope(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	snd_pcm_sframes_t frames;
 | 
			
		||||
	snd_pcm_uframes_t rptr, old_rptr;
 | 
			
		||||
	const snd_pcm_channel_area_t *areas;
 | 
			
		||||
	int reset = 0;
 | 
			
		||||
	/* Wait main thread */
 | 
			
		||||
	pthread_mutex_lock(&meter->update_mutex);
 | 
			
		||||
	areas = snd_pcm_mmap_areas(pcm);
 | 
			
		||||
 _again:
 | 
			
		||||
	rptr = *pcm->hw_ptr;
 | 
			
		||||
	old_rptr = meter->rptr;
 | 
			
		||||
	rmb();
 | 
			
		||||
	if (atomic_read(&meter->reset)) {
 | 
			
		||||
		reset = 1;
 | 
			
		||||
		atomic_dec(&meter->reset);
 | 
			
		||||
		goto _again;
 | 
			
		||||
	}
 | 
			
		||||
	meter->rptr = rptr;
 | 
			
		||||
	frames = rptr - old_rptr;
 | 
			
		||||
	if (frames < 0)
 | 
			
		||||
		frames += pcm->boundary;
 | 
			
		||||
	if (frames > 0) {
 | 
			
		||||
		assert((snd_pcm_uframes_t) frames <= pcm->buffer_size);
 | 
			
		||||
		snd_pcm_meter_add_frames(pcm, areas, old_rptr, frames);
 | 
			
		||||
	}
 | 
			
		||||
	pthread_mutex_unlock(&meter->update_mutex);
 | 
			
		||||
	return reset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *snd_pcm_meter_thread(void *data)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_t *pcm = data;
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	snd_pcm_t *spcm = meter->slave;
 | 
			
		||||
	struct list_head *pos;
 | 
			
		||||
	snd_pcm_meter_scope_t *scope;
 | 
			
		||||
	int reset;
 | 
			
		||||
	struct timespec delay = {
 | 
			
		||||
		tv_sec: 0,
 | 
			
		||||
		tv_nsec: 1000000000 / FPS,
 | 
			
		||||
	};
 | 
			
		||||
	while (!meter->closed) {
 | 
			
		||||
		snd_pcm_sframes_t now;
 | 
			
		||||
		snd_pcm_status_t status;
 | 
			
		||||
		int err;
 | 
			
		||||
		pthread_mutex_lock(&meter->running_mutex);
 | 
			
		||||
		err = snd_pcm_status(spcm, &status);
 | 
			
		||||
		assert(err >= 0);
 | 
			
		||||
		if (status.state != SND_PCM_STATE_RUNNING &&
 | 
			
		||||
		    (status.state != SND_PCM_STATE_DRAINING ||
 | 
			
		||||
		     spcm->stream != SND_PCM_STREAM_PLAYBACK)) {
 | 
			
		||||
			if (meter->running) {
 | 
			
		||||
				list_for_each(pos, &meter->scopes) {
 | 
			
		||||
					scope = list_entry(pos, snd_pcm_meter_scope_t, list);
 | 
			
		||||
					scope->stop(scope);
 | 
			
		||||
				}
 | 
			
		||||
				meter->running = 0;
 | 
			
		||||
			}
 | 
			
		||||
			pthread_cond_wait(&meter->running_cond,
 | 
			
		||||
					  &meter->running_mutex);
 | 
			
		||||
			pthread_mutex_unlock(&meter->running_mutex);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		pthread_mutex_unlock(&meter->running_mutex);
 | 
			
		||||
		if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
 | 
			
		||||
			now = status.appl_ptr - status.delay;
 | 
			
		||||
			if (now < 0)
 | 
			
		||||
				now += pcm->boundary;
 | 
			
		||||
		} else {
 | 
			
		||||
			now = status.appl_ptr + status.delay;
 | 
			
		||||
			if ((snd_pcm_uframes_t) now >= pcm->boundary)
 | 
			
		||||
				now -= pcm->boundary;
 | 
			
		||||
		}
 | 
			
		||||
		meter->now = now % meter->buf_size;
 | 
			
		||||
		if (pcm->stream == SND_PCM_STREAM_CAPTURE)
 | 
			
		||||
			reset = snd_pcm_meter_update_scope(pcm);
 | 
			
		||||
		else {
 | 
			
		||||
			reset = 0;
 | 
			
		||||
			while (atomic_read(&meter->reset)) {
 | 
			
		||||
				reset = 1;
 | 
			
		||||
				atomic_dec(&meter->reset);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (!meter->running) {
 | 
			
		||||
			list_for_each(pos, &meter->scopes) {
 | 
			
		||||
				scope = list_entry(pos, snd_pcm_meter_scope_t, list);
 | 
			
		||||
				scope->start(scope);
 | 
			
		||||
			}
 | 
			
		||||
			meter->running = 1;
 | 
			
		||||
		} else if (reset) {
 | 
			
		||||
			list_for_each(pos, &meter->scopes) {
 | 
			
		||||
				scope = list_entry(pos, snd_pcm_meter_scope_t, list);
 | 
			
		||||
				scope->reset(scope);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		list_for_each(pos, &meter->scopes) {
 | 
			
		||||
			scope = list_entry(pos, snd_pcm_meter_scope_t, list);
 | 
			
		||||
			scope->update(scope);
 | 
			
		||||
		}
 | 
			
		||||
	        nanosleep(&delay, NULL);
 | 
			
		||||
	}
 | 
			
		||||
	list_for_each(pos, &meter->scopes) {
 | 
			
		||||
		scope = list_entry(pos, snd_pcm_meter_scope_t, list);
 | 
			
		||||
		scope->close(scope);
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_close(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	if (meter->close_slave)
 | 
			
		||||
		err = snd_pcm_close(meter->slave);
 | 
			
		||||
	meter->closed = 1;
 | 
			
		||||
	pthread_mutex_lock(&meter->running_mutex);
 | 
			
		||||
	pthread_cond_signal(&meter->running_cond);
 | 
			
		||||
	pthread_mutex_unlock(&meter->running_mutex);
 | 
			
		||||
	err = pthread_join(meter->thread, 0);
 | 
			
		||||
	assert(err == 0);
 | 
			
		||||
	pthread_mutex_destroy(&meter->update_mutex);
 | 
			
		||||
	pthread_mutex_destroy(&meter->running_mutex);
 | 
			
		||||
	pthread_cond_destroy(&meter->running_cond);
 | 
			
		||||
	free(meter);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_nonblock(snd_pcm_t *pcm, int nonblock)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_nonblock(meter->slave, nonblock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_async(snd_pcm_t *pcm, int sig, pid_t pid)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_async(meter->slave, sig, pid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_info(meter->slave, info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_channel_info(meter->slave, info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_status(meter->slave, status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static snd_pcm_state_t snd_pcm_meter_state(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_state(meter->slave);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_delay(meter->slave, delayp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_prepare(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	int err;
 | 
			
		||||
	atomic_inc(&meter->reset);
 | 
			
		||||
	err = snd_pcm_prepare(meter->slave);
 | 
			
		||||
	if (err >= 0) {
 | 
			
		||||
		if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
 | 
			
		||||
			meter->rptr = *pcm->appl_ptr;
 | 
			
		||||
		else
 | 
			
		||||
			meter->rptr = *pcm->hw_ptr;
 | 
			
		||||
	}
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_reset(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	int err = snd_pcm_reset(meter->slave);
 | 
			
		||||
	if (err >= 0) {
 | 
			
		||||
		if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
 | 
			
		||||
			meter->rptr = *pcm->appl_ptr;
 | 
			
		||||
	}
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_start(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	int err;
 | 
			
		||||
	pthread_mutex_lock(&meter->running_mutex);
 | 
			
		||||
	err = snd_pcm_start(meter->slave);
 | 
			
		||||
	if (err >= 0)
 | 
			
		||||
		pthread_cond_signal(&meter->running_cond);
 | 
			
		||||
	pthread_mutex_unlock(&meter->running_mutex);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_drop(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_drop(meter->slave);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_drain(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_drain(meter->slave);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_pause(snd_pcm_t *pcm, int enable)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_pause(meter->slave, enable);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static snd_pcm_sframes_t snd_pcm_meter_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	snd_pcm_sframes_t err = snd_pcm_rewind(meter->slave, frames);
 | 
			
		||||
	if (err > 0 && pcm->stream == SND_PCM_STREAM_PLAYBACK)
 | 
			
		||||
		meter->rptr = *pcm->appl_ptr;
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static snd_pcm_sframes_t snd_pcm_meter_mmap_forward(snd_pcm_t *pcm, snd_pcm_uframes_t size)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	snd_pcm_uframes_t old_rptr = *pcm->appl_ptr;
 | 
			
		||||
	snd_pcm_sframes_t result = snd_pcm_mmap_forward(meter->slave, size);
 | 
			
		||||
	if (result <= 0)
 | 
			
		||||
		return result;
 | 
			
		||||
	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
 | 
			
		||||
		snd_pcm_meter_add_frames(pcm, snd_pcm_mmap_areas(pcm), old_rptr, result);
 | 
			
		||||
		meter->rptr = *pcm->appl_ptr;
 | 
			
		||||
	}
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static snd_pcm_sframes_t snd_pcm_meter_avail_update(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	snd_pcm_sframes_t result = snd_pcm_avail_update(meter->slave);
 | 
			
		||||
	if (result <= 0)
 | 
			
		||||
		return result;
 | 
			
		||||
	if (pcm->stream == SND_PCM_STREAM_CAPTURE)
 | 
			
		||||
		snd_pcm_meter_update_main(pcm);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_PLUGIN };
 | 
			
		||||
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
 | 
			
		||||
					 &access_mask);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
 | 
			
		||||
	_snd_pcm_hw_params_any(sparams);
 | 
			
		||||
	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
 | 
			
		||||
				   &saccess_mask);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
 | 
			
		||||
					  snd_pcm_hw_params_t *sparams)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
	unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
 | 
			
		||||
	err = _snd_pcm_hw_params_refine(sparams, links, params);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
static int snd_pcm_meter_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
 | 
			
		||||
					  snd_pcm_hw_params_t *sparams)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
	unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
 | 
			
		||||
	err = _snd_pcm_hw_params_refine(params, links, sparams);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int snd_pcm_meter_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_hw_refine(meter->slave, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int snd_pcm_meter_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return _snd_pcm_hw_params(meter->slave, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 | 
			
		||||
{
 | 
			
		||||
	return snd_pcm_hw_refine_slave(pcm, params,
 | 
			
		||||
				       snd_pcm_meter_hw_refine_cprepare,
 | 
			
		||||
				       snd_pcm_meter_hw_refine_cchange,
 | 
			
		||||
				       snd_pcm_meter_hw_refine_sprepare,
 | 
			
		||||
				       snd_pcm_meter_hw_refine_schange,
 | 
			
		||||
				       snd_pcm_meter_hw_refine_slave);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	unsigned int channel;
 | 
			
		||||
	snd_pcm_t *slave = meter->slave;
 | 
			
		||||
	size_t buf_size_bytes;
 | 
			
		||||
	int err;
 | 
			
		||||
	err = snd_pcm_hw_params_slave(pcm, params,
 | 
			
		||||
				      snd_pcm_meter_hw_refine_cchange,
 | 
			
		||||
				      snd_pcm_meter_hw_refine_sprepare,
 | 
			
		||||
				      snd_pcm_meter_hw_refine_schange,
 | 
			
		||||
				      snd_pcm_meter_hw_params_slave);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	/* more than 1 second of buffer */
 | 
			
		||||
	meter->buf_size = slave->buffer_size;
 | 
			
		||||
	while (meter->buf_size < slave->rate)
 | 
			
		||||
		meter->buf_size *= 2;
 | 
			
		||||
	buf_size_bytes = snd_pcm_frames_to_bytes(slave, meter->buf_size);
 | 
			
		||||
	assert(!meter->buf);
 | 
			
		||||
	meter->buf = malloc(buf_size_bytes);
 | 
			
		||||
	meter->buf_areas = malloc(sizeof(*meter->buf_areas) * slave->channels);
 | 
			
		||||
	for (channel = 0; channel < slave->channels; ++channel) {
 | 
			
		||||
		snd_pcm_channel_area_t *a = &meter->buf_areas[channel];
 | 
			
		||||
		a->addr = meter->buf + buf_size_bytes / slave->channels * channel;
 | 
			
		||||
		a->first = 0;
 | 
			
		||||
		a->step = slave->sample_bits;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_hw_free(snd_pcm_t *pcm)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	if (meter->buf) {
 | 
			
		||||
		free(meter->buf);
 | 
			
		||||
		free(meter->buf_areas);
 | 
			
		||||
		meter->buf = 0;
 | 
			
		||||
		meter->buf_areas = 0;
 | 
			
		||||
	}
 | 
			
		||||
	return snd_pcm_hw_free(meter->slave);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	return snd_pcm_sw_params(meter->slave, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_pcm_meter_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void snd_pcm_meter_dump(snd_pcm_t *pcm, snd_output_t *out)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_meter_t *meter = pcm->private_data;
 | 
			
		||||
	snd_output_printf(out, "Meter PCM\n");
 | 
			
		||||
	if (pcm->setup) {
 | 
			
		||||
		snd_output_printf(out, "Its setup is:\n");
 | 
			
		||||
		snd_pcm_dump_setup(pcm, out);
 | 
			
		||||
	}
 | 
			
		||||
	snd_output_printf(out, "Slave: ");
 | 
			
		||||
	snd_pcm_dump(meter->slave, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
snd_pcm_ops_t snd_pcm_meter_ops = {
 | 
			
		||||
	close: snd_pcm_meter_close,
 | 
			
		||||
	info: snd_pcm_meter_info,
 | 
			
		||||
	hw_refine: snd_pcm_meter_hw_refine,
 | 
			
		||||
	hw_params: snd_pcm_meter_hw_params,
 | 
			
		||||
	hw_free: snd_pcm_meter_hw_free,
 | 
			
		||||
	sw_params: snd_pcm_meter_sw_params,
 | 
			
		||||
	channel_info: snd_pcm_meter_channel_info,
 | 
			
		||||
	dump: snd_pcm_meter_dump,
 | 
			
		||||
	nonblock: snd_pcm_meter_nonblock,
 | 
			
		||||
	async: snd_pcm_meter_async,
 | 
			
		||||
	mmap: snd_pcm_meter_mmap,
 | 
			
		||||
	munmap: snd_pcm_meter_munmap,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
snd_pcm_fast_ops_t snd_pcm_meter_fast_ops = {
 | 
			
		||||
	status: snd_pcm_meter_status,
 | 
			
		||||
	state: snd_pcm_meter_state,
 | 
			
		||||
	delay: snd_pcm_meter_delay,
 | 
			
		||||
	prepare: snd_pcm_meter_prepare,
 | 
			
		||||
	reset: snd_pcm_meter_reset,
 | 
			
		||||
	start: snd_pcm_meter_start,
 | 
			
		||||
	drop: snd_pcm_meter_drop,
 | 
			
		||||
	drain: snd_pcm_meter_drain,
 | 
			
		||||
	pause: snd_pcm_meter_pause,
 | 
			
		||||
	rewind: snd_pcm_meter_rewind,
 | 
			
		||||
	writei: snd_pcm_mmap_writei,
 | 
			
		||||
	writen: snd_pcm_mmap_writen,
 | 
			
		||||
	readi: snd_pcm_mmap_readi,
 | 
			
		||||
	readn: snd_pcm_mmap_readn,
 | 
			
		||||
	avail_update: snd_pcm_meter_avail_update,
 | 
			
		||||
	mmap_forward: snd_pcm_meter_mmap_forward,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
 | 
			
		||||
{
 | 
			
		||||
	snd_pcm_t *pcm;
 | 
			
		||||
	snd_pcm_meter_t *meter;
 | 
			
		||||
	int err;
 | 
			
		||||
	assert(pcmp);
 | 
			
		||||
	meter = calloc(1, sizeof(snd_pcm_meter_t));
 | 
			
		||||
	if (!meter)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	meter->slave = slave;
 | 
			
		||||
	meter->close_slave = close_slave;
 | 
			
		||||
	INIT_LIST_HEAD(&meter->scopes);
 | 
			
		||||
 | 
			
		||||
	pcm = calloc(1, sizeof(snd_pcm_t));
 | 
			
		||||
	if (!pcm) {
 | 
			
		||||
		free(meter);
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
	if (name)
 | 
			
		||||
		pcm->name = strdup(name);
 | 
			
		||||
	pcm->type = SND_PCM_TYPE_METER;
 | 
			
		||||
	pcm->stream = slave->stream;
 | 
			
		||||
	pcm->mode = slave->mode;
 | 
			
		||||
	pcm->mmap_rw = 1;
 | 
			
		||||
	pcm->ops = &snd_pcm_meter_ops;
 | 
			
		||||
	pcm->op_arg = pcm;
 | 
			
		||||
	pcm->fast_ops = &snd_pcm_meter_fast_ops;
 | 
			
		||||
	pcm->fast_op_arg = pcm;
 | 
			
		||||
	pcm->private_data = meter;
 | 
			
		||||
	pcm->poll_fd = slave->poll_fd;
 | 
			
		||||
	pcm->hw_ptr = slave->hw_ptr;
 | 
			
		||||
	pcm->appl_ptr = slave->appl_ptr;
 | 
			
		||||
	*pcmp = pcm;
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
	snd_pcm_meter_add_scope(pcm, &debug_scope);
 | 
			
		||||
#endif
 | 
			
		||||
	pthread_mutex_init(&meter->update_mutex, NULL);
 | 
			
		||||
	pthread_mutex_init(&meter->running_mutex, NULL);
 | 
			
		||||
	pthread_cond_init(&meter->running_cond, NULL);
 | 
			
		||||
	err = pthread_create(&meter->thread, NULL, snd_pcm_meter_thread, pcm);
 | 
			
		||||
	assert(err == 0);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
 | 
			
		||||
		       snd_config_t *conf, 
 | 
			
		||||
		       snd_pcm_stream_t stream, int mode)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
	const char *sname = NULL;
 | 
			
		||||
	int err;
 | 
			
		||||
	snd_pcm_t *spcm;
 | 
			
		||||
	snd_config_for_each(i, next, conf) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		const char *id = snd_config_get_id(n);
 | 
			
		||||
		if (strcmp(id, "comment") == 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (strcmp(id, "type") == 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (strcmp(id, "sname") == 0) {
 | 
			
		||||
			err = snd_config_get_string(n, &sname);
 | 
			
		||||
			if (err < 0) {
 | 
			
		||||
				ERR("Invalid type for %s", id);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		ERR("Unknown field %s", id);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	if (!sname) {
 | 
			
		||||
		ERR("sname is not defined");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* This is needed cause snd_config_update may destroy config */
 | 
			
		||||
	sname = strdup(sname);
 | 
			
		||||
	if (!sname)
 | 
			
		||||
		return  -ENOMEM;
 | 
			
		||||
	err = snd_pcm_open(&spcm, sname, stream, mode);
 | 
			
		||||
	free((void *) sname);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = snd_pcm_meter_open(pcmp, name, spcm, 1);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		snd_pcm_close(spcm);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -439,11 +439,7 @@ int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
 | 
			
		|||
	}
 | 
			
		||||
	status->appl_ptr = plugin->appl_ptr;
 | 
			
		||||
	status->hw_ptr = plugin->hw_ptr;
 | 
			
		||||
	err = snd_pcm_plugin_avail_update(pcm);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		status->avail = pcm->buffer_size;
 | 
			
		||||
	else
 | 
			
		||||
		status->avail = err;
 | 
			
		||||
	status->avail = pcm->buffer_size;
 | 
			
		||||
	snd_pcm_plugin_delay(pcm, &status->delay);
 | 
			
		||||
	if (!snd_atomic_read_ok(&ratom)) {
 | 
			
		||||
		snd_atomic_read_wait(&ratom);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,19 +98,6 @@ extern snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops;
 | 
			
		|||
#define RATE_MIN 4000
 | 
			
		||||
#define RATE_MAX 192000
 | 
			
		||||
 | 
			
		||||
#define ROUTE_PLUGIN_FLOAT 1
 | 
			
		||||
#define ROUTE_PLUGIN_RESOLUTION 16
 | 
			
		||||
 | 
			
		||||
#if ROUTE_PLUGIN_FLOAT
 | 
			
		||||
typedef float snd_pcm_route_ttable_entry_t;
 | 
			
		||||
#define HALF 0.5
 | 
			
		||||
#define FULL 1.0
 | 
			
		||||
#else
 | 
			
		||||
typedef int snd_pcm_route_ttable_entry_t;
 | 
			
		||||
#define HALF (ROUTE_PLUGIN_RESOLUTION / 2)
 | 
			
		||||
#define FULL ROUTE_PLUGIN_RESOLUTION
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int snd_pcm_linear_get_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
 | 
			
		||||
int snd_pcm_linear_put_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
 | 
			
		||||
int snd_pcm_linear_convert_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue