mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-10-29 05:40:25 -04:00 
			
		
		
		
	Exported ERR macros. Completed scopes architecture. Added defaults for {pcm,ctl,rawmidi,seq}type configuration. Fixed bogus dlclose. Implemented ncurses levelmeter (temporarily placed in src/pcm/scopes)
This commit is contained in:
		
							parent
							
								
									8f3ae2cf2e
								
							
						
					
					
						commit
						46b52167df
					
				
					 14 changed files with 928 additions and 353 deletions
				
			
		|  | @ -33,8 +33,17 @@ | |||
| #include <fcntl.h> | ||||
| #include <assert.h> | ||||
| #include <sys/poll.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #ifndef ATTRIBUTE_UNUSED | ||||
| #define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) | ||||
| #endif | ||||
| 
 | ||||
| #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) | ||||
| #define SNDERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__) | ||||
| #define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__) | ||||
| #else | ||||
| #define SNDERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args) | ||||
| #define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args) | ||||
| #endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -42,15 +42,6 @@ | |||
| #define _snd_hwdep_info sndrv_hwdep_info | ||||
| 
 | ||||
| #include "asoundlib.h" | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) | ||||
| #define SNDERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__) | ||||
| #define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__) | ||||
| #else | ||||
| #define SNDERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args) | ||||
| #define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args) | ||||
| #endif | ||||
| 
 | ||||
| enum _snd_set_mode { | ||||
| 	SND_CHANGE, | ||||
|  |  | |||
|  | @ -209,6 +209,8 @@ typedef struct _snd_pcm_channel_area { | |||
| 	unsigned int step;		/* samples distance in bits */ | ||||
| } snd_pcm_channel_area_t; | ||||
| 
 | ||||
| typedef struct _snd_pcm_scope snd_pcm_scope_t; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | @ -331,6 +333,34 @@ ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, snd_pcm_sframes_t frames); | |||
| int snd_pcm_bytes_to_samples(snd_pcm_t *pcm, ssize_t bytes); | ||||
| ssize_t snd_pcm_samples_to_bytes(snd_pcm_t *pcm, int samples); | ||||
| 
 | ||||
| /* meter */ | ||||
| typedef struct _snd_pcm_scope_ops { | ||||
| 	int (*enable)(snd_pcm_scope_t *scope); | ||||
| 	void (*disable)(snd_pcm_scope_t *scope); | ||||
| 	void (*start)(snd_pcm_scope_t *scope); | ||||
| 	void (*stop)(snd_pcm_scope_t *scope); | ||||
| 	void (*update)(snd_pcm_scope_t *scope); | ||||
| 	void (*reset)(snd_pcm_scope_t *scope); | ||||
| 	void (*close)(snd_pcm_scope_t *scope); | ||||
| } snd_pcm_scope_ops_t; | ||||
| 
 | ||||
| snd_pcm_uframes_t snd_pcm_meter_get_bufsize(snd_pcm_t *pcm); | ||||
| unsigned int snd_pcm_meter_get_channels(snd_pcm_t *pcm); | ||||
| unsigned int snd_pcm_meter_get_rate(snd_pcm_t *pcm); | ||||
| snd_pcm_uframes_t snd_pcm_meter_get_now(snd_pcm_t *pcm); | ||||
| snd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm); | ||||
| int snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope); | ||||
| snd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name); | ||||
| int snd_pcm_scope_malloc(snd_pcm_scope_t **ptr); | ||||
| void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, snd_pcm_scope_ops_t *val); | ||||
| void snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val); | ||||
| const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope); | ||||
| void *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope); | ||||
| void snd_pcm_scope_set_callback_private(snd_pcm_scope_t *scope, void *val); | ||||
| int snd_pcm_scope_s16_open(snd_pcm_t *pcm, const char *name, | ||||
| 			   snd_pcm_scope_t **scopep); | ||||
| int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope, | ||||
| 					      unsigned int channel); | ||||
| 
 | ||||
| /* misc */ | ||||
| 
 | ||||
|  |  | |||
|  | @ -197,6 +197,7 @@ int snd_ctl_wait(snd_ctl_t *ctl, int timeout) | |||
| int snd_ctl_open(snd_ctl_t **ctlp, const char *name) | ||||
| { | ||||
| 	const char *str; | ||||
| 	char buf[256]; | ||||
| 	int err; | ||||
| 	snd_config_t *ctl_conf, *conf, *type_conf; | ||||
| 	snd_config_iterator_t i, next; | ||||
|  | @ -229,36 +230,45 @@ int snd_ctl_open(snd_ctl_t **ctlp, const char *name) | |||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	err = snd_config_searchv(snd_config, &type_conf, "ctltype", str, 0); | ||||
| 	snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 			err = snd_config_get_string(n, &lib); | ||||
| 			if (err < 0) | ||||
| 				return -EINVAL; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (strcmp(id, "open") == 0) { | ||||
| 			err = snd_config_get_string(n, &open); | ||||
| 			if (err < 0) | ||||
| 				return -EINVAL; | ||||
| 			continue; | ||||
| 	if (err >= 0) { | ||||
| 		snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 				err = snd_config_get_string(n, &lib); | ||||
| 				if (err < 0) | ||||
| 					return -EINVAL; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (strcmp(id, "open") == 0) { | ||||
| 				err = snd_config_get_string(n, &open); | ||||
| 				if (err < 0) | ||||
| 					return -EINVAL; | ||||
| 				continue; | ||||
| 			} | ||||
| 			SNDERR("Unknown field %s", id); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!open) | ||||
| 		return -EINVAL; | ||||
| 	if (!open) { | ||||
| 		open = buf; | ||||
| 		snprintf(buf, sizeof(buf), "_snd_ctl_%s_open", str); | ||||
| 	} | ||||
| 	if (!lib) | ||||
| 		lib = "libasound.so"; | ||||
| 	h = dlopen(lib, RTLD_NOW); | ||||
| 	if (!h) | ||||
| 	if (!h) { | ||||
| 		SNDERR("Cannot open shared library %s", lib); | ||||
| 		return -ENOENT; | ||||
| 	} | ||||
| 	open_func = dlsym(h, open); | ||||
| 	dlclose(h); | ||||
| 	if (!open_func) | ||||
| 	if (!open_func) { | ||||
| 		SNDERR("symbol %s is not defined inside %s", open, lib); | ||||
| 		dlclose(h); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 	return open_func(ctlp, name, ctl_conf); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ libpcm_la_SOURCES = atomic.c mask.c interval.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 pcm_meter.h | ||||
| 	         interval.h interval_inline.h plugin_ops.h | ||||
| 
 | ||||
| all: libpcm.la | ||||
| 
 | ||||
|  |  | |||
|  | @ -555,8 +555,9 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name, | |||
| 		 snd_pcm_stream_t stream, int mode) | ||||
| { | ||||
| 	const char *str; | ||||
| 	char buf[256]; | ||||
| 	int err; | ||||
| 	snd_config_t *pcm_conf, *conf, *type_conf; | ||||
| 	snd_config_t *pcm_conf, *conf, *type_conf = NULL; | ||||
| 	snd_config_iterator_t i, next; | ||||
| 	const char *lib = NULL, *open = NULL; | ||||
| 	int (*open_func)(snd_pcm_t **pcmp, const char *name, snd_config_t *conf,  | ||||
|  | @ -638,37 +639,35 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name, | |||
| 		return err; | ||||
| 	} | ||||
| 	err = snd_config_searchv(snd_config, &type_conf, "pcmtype", str, 0); | ||||
| 	if (err < 0) { | ||||
| 		SNDERR("Unknown PCM type %s", str); | ||||
| 		return err; | ||||
| 	} | ||||
| 	snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 			err = snd_config_get_string(n, &lib); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 	if (err >= 0) { | ||||
| 		snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 				err = snd_config_get_string(n, &lib); | ||||
| 				if (err < 0) { | ||||
| 					SNDERR("Invalid type for %s", id); | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (strcmp(id, "open") == 0) { | ||||
| 			err = snd_config_get_string(n, &open); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 			if (strcmp(id, "open") == 0) { | ||||
| 				err = snd_config_get_string(n, &open); | ||||
| 				if (err < 0) { | ||||
| 					SNDERR("Invalid type for %s", id); | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			continue; | ||||
| 			SNDERR("Unknown field %s", id); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!open) { | ||||
| 		SNDERR("open is not defined"); | ||||
| 		return -EINVAL; | ||||
| 		open = buf; | ||||
| 		snprintf(buf, sizeof(buf), "_snd_pcm_%s_open", str); | ||||
| 	} | ||||
| 	if (!lib) | ||||
| 		lib = "libasound.so"; | ||||
|  | @ -678,9 +677,9 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name, | |||
| 		return -ENOENT; | ||||
| 	} | ||||
| 	open_func = dlsym(h, open); | ||||
| 	dlclose(h); | ||||
| 	if (!open_func) { | ||||
| 		SNDERR("symbol %s is not defined inside %s", open, lib); | ||||
| 		dlclose(h); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 	return open_func(pcmp, name, pcm_conf, stream, mode); | ||||
|  |  | |||
|  | @ -20,189 +20,41 @@ | |||
|    | ||||
| #include <byteswap.h> | ||||
| #include <time.h> | ||||
| #include "pcm_meter.h" | ||||
| #include <pthread.h> | ||||
| #include <asm/atomic.h> | ||||
| #include <dlfcn.h> | ||||
| #include "list.h" | ||||
| #include "pcm_local.h" | ||||
| #include "pcm_plugin.h" | ||||
| 
 | ||||
| #define FREQUENCY 50 | ||||
| 
 | ||||
| typedef struct _snd_pcm_meter_s16 { | ||||
| 	snd_pcm_adpcm_state_t *adpcm_states; | ||||
| 	unsigned int index; | ||||
| 	snd_pcm_uframes_t old; | ||||
| } snd_pcm_meter_s16_t; | ||||
| 
 | ||||
| int s16_open(snd_pcm_meter_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter = scope->pcm->private_data; | ||||
| 	snd_pcm_t *spcm = meter->slave; | ||||
| 	snd_pcm_channel_area_t *a; | ||||
| 	unsigned int c; | ||||
| 	snd_pcm_meter_s16_t *s16; | ||||
| 	int index; | ||||
| 	if (spcm->format == SND_PCM_FORMAT_S16 && | ||||
| 	    spcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) { | ||||
| 		meter->buf16 = (int16_t *) meter->buf; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	switch (spcm->format) { | ||||
| 	case SND_PCM_FORMAT_A_LAW: | ||||
| 	case SND_PCM_FORMAT_MU_LAW: | ||||
| 	case SND_PCM_FORMAT_IMA_ADPCM: | ||||
| 		index = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S16); | ||||
| 		break; | ||||
| 	case SND_PCM_FORMAT_S8: | ||||
| 	case SND_PCM_FORMAT_S16_LE: | ||||
| 	case SND_PCM_FORMAT_S16_BE: | ||||
| 	case SND_PCM_FORMAT_S24_LE: | ||||
| 	case SND_PCM_FORMAT_S24_BE: | ||||
| 	case SND_PCM_FORMAT_S32_LE: | ||||
| 	case SND_PCM_FORMAT_S32_BE: | ||||
| 	case SND_PCM_FORMAT_U8: | ||||
| 	case SND_PCM_FORMAT_U16_LE: | ||||
| 	case SND_PCM_FORMAT_U16_BE: | ||||
| 	case SND_PCM_FORMAT_U24_LE: | ||||
| 	case SND_PCM_FORMAT_U24_BE: | ||||
| 	case SND_PCM_FORMAT_U32_LE: | ||||
| 	case SND_PCM_FORMAT_U32_BE: | ||||
| 		index = snd_pcm_linear_convert_index(spcm->format, SND_PCM_FORMAT_S16); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	s16 = calloc(1, sizeof(*s16)); | ||||
| 	if (!s16) | ||||
| 		return -ENOMEM; | ||||
| 	s16->index = index; | ||||
| 	if (spcm->format == SND_PCM_FORMAT_IMA_ADPCM) { | ||||
| 		s16->adpcm_states = calloc(spcm->channels, sizeof(*s16->adpcm_states)); | ||||
| 		if (!s16->adpcm_states) { | ||||
| 			free(s16); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 	} | ||||
| 	meter->buf16 = malloc(meter->buf_size * 2 * spcm->channels); | ||||
| 	if (!meter->buf16) { | ||||
| 		if (s16->adpcm_states) | ||||
| 			free(s16->adpcm_states); | ||||
| 		free(s16); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	a = calloc(spcm->channels, sizeof(*a)); | ||||
| 	if (!a) { | ||||
| 		free(meter->buf16); | ||||
| 		if (s16->adpcm_states) | ||||
| 			free(s16->adpcm_states); | ||||
| 		free(s16); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	meter->buf16_areas = a; | ||||
| 	for (c = 0; c < spcm->channels; c++, a++) { | ||||
| 		a->addr = meter->buf16 + c * meter->buf_size; | ||||
| 		a->first = 0; | ||||
| 		a->step = 16; | ||||
| 	} | ||||
| 	scope->private_data = s16; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void s16_close(snd_pcm_meter_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter = scope->pcm->private_data; | ||||
| 	snd_pcm_meter_s16_t *s16 = scope->private_data; | ||||
| 	if (s16->adpcm_states) | ||||
| 		free(s16->adpcm_states); | ||||
| 	free(s16); | ||||
| 	free(meter->buf16); | ||||
| 	meter->buf16 = NULL; | ||||
| 	free(meter->buf16_areas); | ||||
| } | ||||
| 
 | ||||
| void s16_start(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void s16_stop(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void s16_update(snd_pcm_meter_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter = scope->pcm->private_data; | ||||
| 	snd_pcm_meter_s16_t *s16 = scope->private_data; | ||||
| 	snd_pcm_t *spcm = meter->slave; | ||||
| 	snd_pcm_sframes_t size; | ||||
| 	snd_pcm_uframes_t offset; | ||||
| 	size = meter->now - s16->old; | ||||
| 	if (size < 0) | ||||
| 		size += spcm->boundary; | ||||
| 	offset = s16->old % meter->buf_size; | ||||
| 	while (size > 0) { | ||||
| 		snd_pcm_uframes_t frames = size; | ||||
| 		snd_pcm_uframes_t cont = meter->buf_size - offset; | ||||
| 		if (frames > cont) | ||||
| 			frames = cont; | ||||
| 		switch (spcm->format) { | ||||
| 		case SND_PCM_FORMAT_A_LAW: | ||||
| 			snd_pcm_alaw_decode(meter->buf16_areas, offset, | ||||
| 					    meter->buf_areas, offset, | ||||
| 					    spcm->channels, frames, | ||||
| 					    s16->index); | ||||
| 			break; | ||||
| 		case SND_PCM_FORMAT_MU_LAW: | ||||
| 			snd_pcm_mulaw_decode(meter->buf16_areas, offset, | ||||
| 					     meter->buf_areas, offset, | ||||
| 					     spcm->channels, frames, | ||||
| 					     s16->index); | ||||
| 			break; | ||||
| 		case SND_PCM_FORMAT_IMA_ADPCM: | ||||
| 			snd_pcm_adpcm_decode(meter->buf16_areas, offset, | ||||
| 					     meter->buf_areas, offset, | ||||
| 					     spcm->channels, frames, | ||||
| 					     s16->index, | ||||
| 					     s16->adpcm_states); | ||||
| 			break; | ||||
| 		default: | ||||
| 			snd_pcm_linear_convert(meter->buf16_areas, offset, | ||||
| 					       meter->buf_areas, offset, | ||||
| 					       spcm->channels, frames, | ||||
| 					       s16->index); | ||||
| 			break; | ||||
| 		} | ||||
| 		if (frames == cont) | ||||
| 			offset = 0; | ||||
| 		else | ||||
| 			offset += frames; | ||||
| 		size -= frames; | ||||
| 	} | ||||
| 	s16->old = meter->now; | ||||
| } | ||||
| 
 | ||||
| void s16_reset(snd_pcm_meter_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter = scope->pcm->private_data; | ||||
| 	snd_pcm_meter_s16_t *s16 = scope->private_data; | ||||
| 	s16->old = meter->now; | ||||
| } | ||||
| 
 | ||||
| snd_pcm_meter_scope_t s16_scope = { | ||||
| 	name: "s16", | ||||
| 	open: s16_open, | ||||
| 	start: s16_start, | ||||
| 	stop: s16_stop, | ||||
| 	update: s16_update, | ||||
| 	reset: s16_reset, | ||||
| 	close: s16_close, | ||||
| 	pcm: NULL, | ||||
| 	active: 0, | ||||
| 	list: { 0, 0 }, | ||||
| 	private_data: NULL, | ||||
| struct _snd_pcm_scope { | ||||
| 	int enabled; | ||||
| 	const char *name; | ||||
| 	snd_pcm_scope_ops_t *ops; | ||||
| 	void *private_data; | ||||
| 	struct list_head list; | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 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; | ||||
| 	unsigned char *buf; | ||||
| 	struct list_head scopes; | ||||
| 	int closed; | ||||
| 	int running; | ||||
| 	atomic_t reset; | ||||
| 	pthread_t thread; | ||||
| 	pthread_mutex_t update_mutex; | ||||
| 	pthread_mutex_t running_mutex; | ||||
| 	pthread_cond_t running_cond; | ||||
| 	struct timespec delay; | ||||
| } snd_pcm_meter_t; | ||||
| 
 | ||||
| void snd_pcm_meter_add_frames(snd_pcm_t *pcm, | ||||
| 			      const snd_pcm_channel_area_t *areas, | ||||
|  | @ -284,18 +136,59 @@ static int snd_pcm_meter_update_scope(snd_pcm_t *pcm) | |||
| 	return reset; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_scope_remove(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	if (scope->name) | ||||
| 		free((void *)scope->name); | ||||
| 	scope->ops->close(scope); | ||||
| 	list_del(&scope->list); | ||||
| 	free(scope); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_scope_enable(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	int err; | ||||
| 	assert(!scope->enabled); | ||||
| 	err = scope->ops->enable(scope); | ||||
| 	scope->enabled = (err >= 0); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_scope_disable(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	assert(scope->enabled); | ||||
| 	scope->ops->disable(scope); | ||||
| 	scope->enabled = 0; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| snd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	struct list_head *pos; | ||||
| 	assert(pcm->type == SND_PCM_TYPE_METER); | ||||
| 	meter = pcm->private_data; | ||||
| 	list_for_each(pos, &meter->scopes) { | ||||
| 		snd_pcm_scope_t *scope; | ||||
| 		scope = list_entry(pos, snd_pcm_scope_t, list); | ||||
| 		if (scope->name && strcmp(scope->name, name) == 0) | ||||
| 			return scope; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 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 err, reset; | ||||
| 	snd_pcm_scope_t *scope; | ||||
| 	int reset; | ||||
| 	list_for_each(pos, &meter->scopes) { | ||||
| 		scope = list_entry(pos, snd_pcm_meter_scope_t, list); | ||||
| 		err = scope->open(scope); | ||||
| 		scope->active = (err >= 0); | ||||
| 		scope = list_entry(pos, snd_pcm_scope_t, list); | ||||
| 		snd_pcm_scope_enable(scope); | ||||
| 	} | ||||
| 	while (!meter->closed) { | ||||
| 		snd_pcm_sframes_t now; | ||||
|  | @ -309,8 +202,8 @@ static void *snd_pcm_meter_thread(void *data) | |||
| 		     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); | ||||
| 					scope = list_entry(pos, snd_pcm_scope_t, list); | ||||
| 					scope->ops->stop(scope); | ||||
| 				} | ||||
| 				meter->running = 0; | ||||
| 			} | ||||
|  | @ -341,45 +234,50 @@ static void *snd_pcm_meter_thread(void *data) | |||
| 		} | ||||
| 		if (reset) { | ||||
| 			list_for_each(pos, &meter->scopes) { | ||||
| 				scope = list_entry(pos, snd_pcm_meter_scope_t, list); | ||||
| 				if (scope->active) | ||||
| 					scope->reset(scope); | ||||
| 				scope = list_entry(pos, snd_pcm_scope_t, list); | ||||
| 				if (scope->enabled) | ||||
| 					scope->ops->reset(scope); | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!meter->running) { | ||||
| 			list_for_each(pos, &meter->scopes) { | ||||
| 				scope = list_entry(pos, snd_pcm_meter_scope_t, list); | ||||
| 				if (scope->active) | ||||
| 					scope->start(scope); | ||||
| 				scope = list_entry(pos, snd_pcm_scope_t, list); | ||||
| 				if (scope->enabled) | ||||
| 					scope->ops->start(scope); | ||||
| 			} | ||||
| 			meter->running = 1; | ||||
| 		} | ||||
| 		list_for_each(pos, &meter->scopes) { | ||||
| 			scope = list_entry(pos, snd_pcm_meter_scope_t, list); | ||||
| 			if (scope->active) | ||||
| 				scope->update(scope); | ||||
| 			scope = list_entry(pos, snd_pcm_scope_t, list); | ||||
| 			if (scope->enabled) | ||||
| 				scope->ops->update(scope); | ||||
| 		} | ||||
| 	        nanosleep(&meter->delay, NULL); | ||||
| 	} | ||||
| 	list_for_each(pos, &meter->scopes) { | ||||
| 		scope = list_entry(pos, snd_pcm_meter_scope_t, list); | ||||
| 		if (scope->active) | ||||
| 			scope->close(scope); | ||||
| 		scope = list_entry(pos, snd_pcm_scope_t, list); | ||||
| 		if (scope->enabled) | ||||
| 			snd_pcm_scope_disable(scope); | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int snd_pcm_meter_close(snd_pcm_t *pcm) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter = pcm->private_data; | ||||
| 	struct list_head *pos, *npos; | ||||
| 	int err = 0; | ||||
| 	pthread_mutex_destroy(&meter->update_mutex); | ||||
| 	pthread_mutex_destroy(&meter->running_mutex); | ||||
| 	pthread_cond_destroy(&meter->running_cond); | ||||
| 	if (meter->close_slave) | ||||
| 		err = snd_pcm_close(meter->slave); | ||||
| 	list_for_each_safe(pos, npos, &meter->scopes) { | ||||
| 		snd_pcm_scope_t *scope; | ||||
| 		scope = list_entry(pos, snd_pcm_scope_t, list); | ||||
| 		snd_pcm_scope_remove(scope); | ||||
| 	} | ||||
| 	free(meter); | ||||
| 	return err; | ||||
| } | ||||
|  | @ -735,21 +633,89 @@ int snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, unsigned int frequenc | |||
| 	pthread_mutex_init(&meter->update_mutex, NULL); | ||||
| 	pthread_mutex_init(&meter->running_mutex, NULL); | ||||
| 	pthread_cond_init(&meter->running_cond, NULL); | ||||
| #if 1 | ||||
| 	snd_pcm_meter_add_scope(pcm, &s16_scope); | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name, | ||||
| 				 snd_config_t *conf) | ||||
| { | ||||
| 	char buf[256]; | ||||
| 	snd_config_iterator_t i, next; | ||||
| 	const char *lib = NULL, *open = NULL, *str = NULL; | ||||
| 	snd_config_t *c, *type_conf; | ||||
| 	int (*open_func)(snd_pcm_t *pcm, const char *name, | ||||
| 			 snd_config_t *conf); | ||||
| 	void *h; | ||||
| 	int err; | ||||
| 	err = snd_config_search(conf, "type", &c); | ||||
| 	if (err < 0) { | ||||
| 		SNDERR("type is not defined"); | ||||
| 		return err; | ||||
| 	} | ||||
| 	err = snd_config_get_string(c, &str); | ||||
| 	if (err < 0) { | ||||
| 		SNDERR("Invalid type for %s", snd_config_get_id(c)); | ||||
| 		return err; | ||||
| 	} | ||||
| 	err = snd_config_searchv(snd_config, &type_conf, "scopetype", str, 0); | ||||
| 	if (err >= 0) { | ||||
| 		snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 				err = snd_config_get_string(n, &lib); | ||||
| 				if (err < 0) { | ||||
| 					SNDERR("Invalid type for %s", id); | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (strcmp(id, "open") == 0) { | ||||
| 				err = snd_config_get_string(n, &open); | ||||
| 				if (err < 0) { | ||||
| 					SNDERR("Invalid type for %s", id); | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			SNDERR("Unknown field %s", id); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!open) { | ||||
| 		open = buf; | ||||
| 		snprintf(buf, sizeof(buf), "_snd_pcm_scope_%s_open", str); | ||||
| 	} | ||||
| 	if (!lib) | ||||
| 		lib = "libasound.so"; | ||||
| 	h = dlopen(lib, RTLD_NOW); | ||||
| 	if (!h) { | ||||
| 		SNDERR("Cannot open shared library %s", lib); | ||||
| 		return -ENOENT; | ||||
| 	} | ||||
| 	open_func = dlsym(h, open); | ||||
| 	if (!open_func) { | ||||
| 		SNDERR("symbol %s is not defined inside %s", open, lib); | ||||
| 		dlclose(h); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 	return open_func(pcm, name, conf); | ||||
| } | ||||
| 
 | ||||
| 			 | ||||
| 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_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; | ||||
| 	long frequency = -1; | ||||
| 	snd_config_t *scopes = NULL; | ||||
| 	snd_config_for_each(i, next, conf) { | ||||
| 		snd_config_t *n = snd_config_iterator_entry(i); | ||||
| 		const char *id = snd_config_get_id(n); | ||||
|  | @ -773,6 +739,14 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, | |||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (strcmp(id, "scope") == 0) { | ||||
| 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
| 			scopes = n; | ||||
| 			continue; | ||||
| 		} | ||||
| 		SNDERR("Unknown field %s", id); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | @ -792,5 +766,315 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, | |||
| 	err = snd_pcm_meter_open(pcmp, name, frequency < 0 ? FREQUENCY : frequency, spcm, 1); | ||||
| 	if (err < 0) | ||||
| 		snd_pcm_close(spcm); | ||||
| 	return err; | ||||
| 	if (!scopes) | ||||
| 		return 0; | ||||
| 	snd_config_for_each(i, next, scopes) { | ||||
| 		snd_config_t *n = snd_config_iterator_entry(i); | ||||
| 		const char *id = snd_config_get_id(n); | ||||
| 		err = snd_pcm_meter_add_scope_conf(*pcmp, id, n); | ||||
| 		if (err < 0) { | ||||
| 			snd_pcm_close(*pcmp); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	assert(pcm->type == SND_PCM_TYPE_METER); | ||||
| 	meter = pcm->private_data; | ||||
| 	list_add_tail(&scope->list, &meter->scopes); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| snd_pcm_uframes_t snd_pcm_meter_get_bufsize(snd_pcm_t *pcm) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	assert(pcm->type == SND_PCM_TYPE_METER); | ||||
| 	meter = pcm->private_data; | ||||
| 	return meter->buf_size; | ||||
| } | ||||
| 
 | ||||
| unsigned int snd_pcm_meter_get_channels(snd_pcm_t *pcm) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	assert(pcm->type == SND_PCM_TYPE_METER); | ||||
| 	meter = pcm->private_data; | ||||
| 	assert(meter->slave->setup); | ||||
| 	return meter->slave->channels; | ||||
| } | ||||
| 
 | ||||
| unsigned int snd_pcm_meter_get_rate(snd_pcm_t *pcm) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	assert(pcm->type == SND_PCM_TYPE_METER); | ||||
| 	meter = pcm->private_data; | ||||
| 	assert(meter->slave->setup); | ||||
| 	return meter->slave->rate; | ||||
| } | ||||
| 
 | ||||
| snd_pcm_uframes_t snd_pcm_meter_get_now(snd_pcm_t *pcm) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	assert(pcm->type == SND_PCM_TYPE_METER); | ||||
| 	meter = pcm->private_data; | ||||
| 	assert(meter->slave->setup); | ||||
| 	return meter->now; | ||||
| } | ||||
| 
 | ||||
| snd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	assert(pcm->type == SND_PCM_TYPE_METER); | ||||
| 	meter = pcm->private_data; | ||||
| 	assert(meter->slave->setup); | ||||
| 	return meter->slave->boundary; | ||||
| } | ||||
| 
 | ||||
| void snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val) | ||||
| { | ||||
| 	scope->name = val; | ||||
| } | ||||
| 
 | ||||
| const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	return scope->name; | ||||
| } | ||||
| 
 | ||||
| void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, snd_pcm_scope_ops_t *val) | ||||
| { | ||||
| 	scope->ops = val; | ||||
| } | ||||
| 
 | ||||
| void *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	return scope->private_data; | ||||
| } | ||||
| 
 | ||||
| void snd_pcm_scope_set_callback_private(snd_pcm_scope_t *scope, void *val) | ||||
| { | ||||
| 	scope->private_data = val; | ||||
| } | ||||
| 
 | ||||
| typedef struct _snd_pcm_scope_s16 { | ||||
| 	snd_pcm_t *pcm; | ||||
| 	snd_pcm_adpcm_state_t *adpcm_states; | ||||
| 	unsigned int index; | ||||
| 	snd_pcm_uframes_t old; | ||||
| 	int16_t *buf; | ||||
| 	snd_pcm_channel_area_t *buf_areas; | ||||
| } snd_pcm_scope_s16_t; | ||||
| 
 | ||||
| static int s16_enable(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_s16_t *s16 = scope->private_data; | ||||
| 	snd_pcm_meter_t *meter = s16->pcm->private_data; | ||||
| 	snd_pcm_t *spcm = meter->slave; | ||||
| 	snd_pcm_channel_area_t *a; | ||||
| 	unsigned int c; | ||||
| 	int index; | ||||
| 	if (spcm->format == SND_PCM_FORMAT_S16 && | ||||
| 	    spcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) { | ||||
| 		s16->buf = (int16_t *) meter->buf; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	switch (spcm->format) { | ||||
| 	case SND_PCM_FORMAT_A_LAW: | ||||
| 	case SND_PCM_FORMAT_MU_LAW: | ||||
| 	case SND_PCM_FORMAT_IMA_ADPCM: | ||||
| 		index = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S16); | ||||
| 		break; | ||||
| 	case SND_PCM_FORMAT_S8: | ||||
| 	case SND_PCM_FORMAT_S16_LE: | ||||
| 	case SND_PCM_FORMAT_S16_BE: | ||||
| 	case SND_PCM_FORMAT_S24_LE: | ||||
| 	case SND_PCM_FORMAT_S24_BE: | ||||
| 	case SND_PCM_FORMAT_S32_LE: | ||||
| 	case SND_PCM_FORMAT_S32_BE: | ||||
| 	case SND_PCM_FORMAT_U8: | ||||
| 	case SND_PCM_FORMAT_U16_LE: | ||||
| 	case SND_PCM_FORMAT_U16_BE: | ||||
| 	case SND_PCM_FORMAT_U24_LE: | ||||
| 	case SND_PCM_FORMAT_U24_BE: | ||||
| 	case SND_PCM_FORMAT_U32_LE: | ||||
| 	case SND_PCM_FORMAT_U32_BE: | ||||
| 		index = snd_pcm_linear_convert_index(spcm->format, SND_PCM_FORMAT_S16); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	s16->index = index; | ||||
| 	if (spcm->format == SND_PCM_FORMAT_IMA_ADPCM) { | ||||
| 		s16->adpcm_states = calloc(spcm->channels, sizeof(*s16->adpcm_states)); | ||||
| 		if (!s16->adpcm_states) | ||||
| 			return -ENOMEM; | ||||
| 	} | ||||
| 	s16->buf = malloc(meter->buf_size * 2 * spcm->channels); | ||||
| 	if (!s16->buf) { | ||||
| 		if (s16->adpcm_states) | ||||
| 			free(s16->adpcm_states); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	a = calloc(spcm->channels, sizeof(*a)); | ||||
| 	if (!a) { | ||||
| 		free(s16->buf); | ||||
| 		if (s16->adpcm_states) | ||||
| 			free(s16->adpcm_states); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	s16->buf_areas = a; | ||||
| 	for (c = 0; c < spcm->channels; c++, a++) { | ||||
| 		a->addr = s16->buf + c * meter->buf_size; | ||||
| 		a->first = 0; | ||||
| 		a->step = 16; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void s16_disable(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_s16_t *s16 = scope->private_data; | ||||
| 	if (s16->adpcm_states) { | ||||
| 		free(s16->adpcm_states); | ||||
| 		s16->adpcm_states = NULL; | ||||
| 	} | ||||
| 	free(s16->buf); | ||||
| 	s16->buf = NULL; | ||||
| 	free(s16->buf_areas); | ||||
| 	s16->buf_areas = 0; | ||||
| } | ||||
| 
 | ||||
| static void s16_close(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_s16_t *s16 = scope->private_data; | ||||
| 	free(s16); | ||||
| } | ||||
| 
 | ||||
| static void s16_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void s16_stop(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void s16_update(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_s16_t *s16 = scope->private_data; | ||||
| 	snd_pcm_meter_t *meter = s16->pcm->private_data; | ||||
| 	snd_pcm_t *spcm = meter->slave; | ||||
| 	snd_pcm_sframes_t size; | ||||
| 	snd_pcm_uframes_t offset; | ||||
| 	size = meter->now - s16->old; | ||||
| 	if (size < 0) | ||||
| 		size += spcm->boundary; | ||||
| 	offset = s16->old % meter->buf_size; | ||||
| 	while (size > 0) { | ||||
| 		snd_pcm_uframes_t frames = size; | ||||
| 		snd_pcm_uframes_t cont = meter->buf_size - offset; | ||||
| 		if (frames > cont) | ||||
| 			frames = cont; | ||||
| 		switch (spcm->format) { | ||||
| 		case SND_PCM_FORMAT_A_LAW: | ||||
| 			snd_pcm_alaw_decode(s16->buf_areas, offset, | ||||
| 					    meter->buf_areas, offset, | ||||
| 					    spcm->channels, frames, | ||||
| 					    s16->index); | ||||
| 			break; | ||||
| 		case SND_PCM_FORMAT_MU_LAW: | ||||
| 			snd_pcm_mulaw_decode(s16->buf_areas, offset, | ||||
| 					     meter->buf_areas, offset, | ||||
| 					     spcm->channels, frames, | ||||
| 					     s16->index); | ||||
| 			break; | ||||
| 		case SND_PCM_FORMAT_IMA_ADPCM: | ||||
| 			snd_pcm_adpcm_decode(s16->buf_areas, offset, | ||||
| 					     meter->buf_areas, offset, | ||||
| 					     spcm->channels, frames, | ||||
| 					     s16->index, | ||||
| 					     s16->adpcm_states); | ||||
| 			break; | ||||
| 		default: | ||||
| 			snd_pcm_linear_convert(s16->buf_areas, offset, | ||||
| 					       meter->buf_areas, offset, | ||||
| 					       spcm->channels, frames, | ||||
| 					       s16->index); | ||||
| 			break; | ||||
| 		} | ||||
| 		if (frames == cont) | ||||
| 			offset = 0; | ||||
| 		else | ||||
| 			offset += frames; | ||||
| 		size -= frames; | ||||
| 	} | ||||
| 	s16->old = meter->now; | ||||
| } | ||||
| 
 | ||||
| static void s16_reset(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_s16_t *s16 = scope->private_data; | ||||
| 	snd_pcm_meter_t *meter = s16->pcm->private_data; | ||||
| 	s16->old = meter->now; | ||||
| } | ||||
| 
 | ||||
| snd_pcm_scope_ops_t s16_ops = { | ||||
| 	enable: s16_enable, | ||||
| 	disable: s16_disable, | ||||
| 	close: s16_close, | ||||
| 	start: s16_start, | ||||
| 	stop: s16_stop, | ||||
| 	update: s16_update, | ||||
| 	reset: s16_reset, | ||||
| }; | ||||
| 
 | ||||
| int snd_pcm_scope_s16_open(snd_pcm_t *pcm, const char *name, | ||||
| 			   snd_pcm_scope_t **scopep) | ||||
| { | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	snd_pcm_scope_t *scope; | ||||
| 	snd_pcm_scope_s16_t *s16; | ||||
| 	assert(pcm->type == SND_PCM_TYPE_METER); | ||||
| 	meter = pcm->private_data; | ||||
| 	scope = calloc(1, sizeof(*scope)); | ||||
| 	if (!scope) | ||||
| 		return -ENOMEM; | ||||
| 	s16 = calloc(1, sizeof(*s16)); | ||||
| 	if (!s16) { | ||||
| 		free(scope); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	if (name) | ||||
| 		scope->name = strdup(name); | ||||
| 	s16->pcm = pcm; | ||||
| 	scope->ops = &s16_ops; | ||||
| 	scope->private_data = s16; | ||||
| 	list_add_tail(&scope->list, &meter->scopes); | ||||
| 	*scopep = scope; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope, | ||||
| 					      unsigned int channel) | ||||
| { | ||||
| 	snd_pcm_scope_s16_t *s16; | ||||
| 	snd_pcm_meter_t *meter; | ||||
| 	assert(scope->ops == &s16_ops); | ||||
| 	s16 = scope->private_data; | ||||
| 	meter = s16->pcm->private_data; | ||||
| 	assert(meter->slave->setup); | ||||
| 	assert(s16->buf_areas); | ||||
| 	assert(channel < meter->slave->channels); | ||||
| 	return s16->buf_areas[channel].addr; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_scope_malloc(snd_pcm_scope_t **ptr) | ||||
| { | ||||
| 	assert(ptr); | ||||
| 	*ptr = calloc(1, sizeof(snd_pcm_scope_t)); | ||||
| 	if (!*ptr) | ||||
| 		return -ENOMEM; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,46 +18,4 @@ | |||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <pthread.h> | ||||
| #include <asm/atomic.h> | ||||
| #include "list.h" | ||||
| #include "pcm_local.h" | ||||
| #include "pcm_plugin.h" | ||||
| 
 | ||||
| typedef struct _snd_pcm_meter_scope snd_pcm_meter_scope_t; | ||||
| 
 | ||||
| struct _snd_pcm_meter_scope { | ||||
| 	snd_pcm_t *pcm; | ||||
| 	int active; | ||||
| 	char *name; | ||||
| 	int (*open)(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; | ||||
| 	unsigned char *buf; | ||||
| 	struct list_head scopes; | ||||
| 	int closed; | ||||
| 	int running; | ||||
| 	atomic_t reset; | ||||
| 	pthread_t thread; | ||||
| 	pthread_mutex_t update_mutex; | ||||
| 	pthread_mutex_t running_mutex; | ||||
| 	pthread_cond_t running_cond; | ||||
| 	int16_t *buf16; | ||||
| 	snd_pcm_channel_area_t *buf16_areas; | ||||
| 	struct timespec delay; | ||||
| } snd_pcm_meter_t; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										7
									
								
								src/pcm/scopes/Makefile.am
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/pcm/scopes/Makefile.am
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| 
 | ||||
| CFLAGS = -g -O2 -W -Wall | ||||
| 
 | ||||
| lib_LTLIBRARIES = liblevel.la | ||||
| 
 | ||||
| liblevel_la_SOURCES = level.c | ||||
| liblevel_la_LIBADD = -lncurses | ||||
							
								
								
									
										9
									
								
								src/pcm/scopes/configure.in
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/pcm/scopes/configure.in
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| AC_INIT(level.c) | ||||
| AM_INIT_AUTOMAKE(scopes, 0.9) | ||||
| 
 | ||||
| AC_PROG_CC | ||||
| AC_PROG_INSTALL | ||||
| AC_PROG_LN_S  | ||||
| AM_PROG_LIBTOOL | ||||
| 
 | ||||
| AC_OUTPUT(Makefile) | ||||
							
								
								
									
										11
									
								
								src/pcm/scopes/cvscompile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/pcm/scopes/cvscompile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| #!/bin/bash | ||||
| 
 | ||||
| aclocal $ACLOCAL_FLAGS | ||||
| automake --foreign --add-missing | ||||
| autoconf | ||||
| export CFLAGS='-O2 -Wall -W -pipe -g' | ||||
| echo "CFLAGS=$CFLAGS" | ||||
| echo "./configure $@" | ||||
| ./configure $@ | ||||
| unset CFLAGS | ||||
| make | ||||
							
								
								
									
										269
									
								
								src/pcm/scopes/level.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								src/pcm/scopes/level.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,269 @@ | |||
| /*
 | ||||
|  *  PCM - Meter level plugin (ncurses) | ||||
|  *  Copyright (c) 2001 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 <curses.h> | ||||
| #include <errno.h> | ||||
| #include <sys/asoundlib.h> | ||||
| 
 | ||||
| #define BAR_WIDTH 70 | ||||
| /* milliseconds to go from 32767 to 0 */ | ||||
| #define DECAY_MS 400 | ||||
| /* milliseconds for peak to disappear */ | ||||
| #define PEAK_MS 800 | ||||
| 
 | ||||
| typedef struct _snd_pcm_scope_level_channel { | ||||
| 	int16_t level; | ||||
| 	int16_t peak; | ||||
| 	unsigned int peak_age; | ||||
| } snd_pcm_scope_level_channel_t; | ||||
| 
 | ||||
| typedef struct _snd_pcm_scope_level { | ||||
| 	snd_pcm_t *pcm; | ||||
| 	snd_pcm_scope_t *s16; | ||||
| 	snd_pcm_scope_level_channel_t *channels; | ||||
| 	snd_pcm_uframes_t old; | ||||
| 	int top; | ||||
| 	WINDOW *win; | ||||
| 	unsigned int bar_width; | ||||
| 	unsigned int decay_ms; | ||||
| 	unsigned int peak_ms; | ||||
| } snd_pcm_scope_level_t; | ||||
| 
 | ||||
| static int level_enable(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope); | ||||
| 	int y, x; | ||||
| 	level->channels = calloc(snd_pcm_meter_get_channels(level->pcm), sizeof(*level->channels)); | ||||
| 	if (!level->channels) { | ||||
| 		free(level); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	snd_pcm_scope_set_callback_private(scope, level); | ||||
| 	level->win = initscr(); | ||||
| 	winsdelln(level->win, snd_pcm_meter_get_channels(level->pcm)); | ||||
|         getyx(level->win, y, x); | ||||
| 	level->top = y; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void level_disable(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope); | ||||
| 	endwin(); | ||||
| 	free(level->channels); | ||||
| } | ||||
| 
 | ||||
| static void level_close(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope); | ||||
| 	free(level); | ||||
| } | ||||
| 
 | ||||
| static void level_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void level_stop(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope); | ||||
| 	unsigned int c; | ||||
| 	for (c = 0; c < snd_pcm_meter_get_channels(level->pcm); c++) { | ||||
| 		move(level->top + c, 0); | ||||
| 		clrtoeol(); | ||||
| 	} | ||||
| 	move(level->top, 0); | ||||
| 	refresh(); | ||||
| } | ||||
| 
 | ||||
| static void level_update(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope); | ||||
| 	snd_pcm_t *pcm = level->pcm; | ||||
| 	snd_pcm_sframes_t size; | ||||
| 	snd_pcm_uframes_t size1, size2; | ||||
| 	snd_pcm_uframes_t offset, cont; | ||||
| 	unsigned int c, channels; | ||||
| 	unsigned int ms; | ||||
| 	static char bar[256] = { [0 ... 255] = '#' }; | ||||
| 	int max_decay; | ||||
| 	size = snd_pcm_meter_get_now(pcm) - level->old; | ||||
| 	if (size < 0) | ||||
| 		size += snd_pcm_meter_get_boundary(pcm); | ||||
| 	offset = level->old % snd_pcm_meter_get_bufsize(pcm); | ||||
| 	cont = snd_pcm_meter_get_bufsize(pcm) - offset; | ||||
| 	size1 = size; | ||||
| 	if (size1 > cont) | ||||
| 		size1 = cont; | ||||
| 	size2 = size - size1; | ||||
| 	ms = size * 1000 / snd_pcm_meter_get_rate(pcm); | ||||
| 	max_decay = 32768 * ms / level->decay_ms; | ||||
| 	channels = snd_pcm_meter_get_channels(pcm); | ||||
| 	for (c = 0; c < channels; c++) { | ||||
| 		int16_t *ptr; | ||||
| 		int s, lev = 0; | ||||
| 		snd_pcm_uframes_t n; | ||||
| 		snd_pcm_scope_level_channel_t *l; | ||||
| 		unsigned int lev_pos, peak_pos; | ||||
| 		l = &level->channels[c]; | ||||
| 		ptr = snd_pcm_scope_s16_get_channel_buffer(level->s16, c) + offset; | ||||
| 		for (n = size1; n > 0; n--) { | ||||
| 			s = *ptr; | ||||
| 			if (s < 0) | ||||
| 				s = -s; | ||||
| 			if (s > lev) | ||||
| 				lev = s; | ||||
| 			ptr++; | ||||
| 		} | ||||
| 		ptr = snd_pcm_scope_s16_get_channel_buffer(level->s16, c); | ||||
| 		for (n = size2; n > 0; n--) { | ||||
| 			s = *ptr; | ||||
| 			if (s < 0) | ||||
| 				s = -s; | ||||
| 			if (s > lev) | ||||
| 				lev = s; | ||||
| 			ptr++; | ||||
| 		} | ||||
| 		l->level = lev; | ||||
| 		l->peak_age += ms; | ||||
| 		if (l->peak_age >= level->peak_ms || | ||||
| 		    lev >= l->peak) { | ||||
| 			l->peak = lev; | ||||
| 			l->peak_age = 0; | ||||
| 		} | ||||
| 		if (lev < l->level - max_decay) | ||||
| 			lev = l->level - max_decay; | ||||
| 		move(level->top + c, 0); | ||||
| 		lev_pos = lev * level->bar_width / 32768; | ||||
| 		peak_pos = l->peak * level->bar_width / 32768; | ||||
| 		addnstr(bar, lev_pos); | ||||
| 		clrtoeol(); | ||||
| 		mvaddch(level->top + c, peak_pos - 1, '#'); | ||||
| 	} | ||||
| 	move(level->top, 0); | ||||
| 	refresh(); | ||||
| 	level->old = snd_pcm_meter_get_now(pcm); | ||||
| } | ||||
| 
 | ||||
| static void level_reset(snd_pcm_scope_t *scope) | ||||
| { | ||||
| 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope); | ||||
| 	snd_pcm_t *pcm = level->pcm; | ||||
| 	memset(level->channels, 0, snd_pcm_meter_get_channels(pcm) * sizeof(*level->channels)); | ||||
| 	level->old = snd_pcm_meter_get_now(pcm); | ||||
| } | ||||
| 
 | ||||
| snd_pcm_scope_ops_t level_ops = { | ||||
| 	enable: level_enable, | ||||
| 	disable: level_disable, | ||||
| 	close: level_close, | ||||
| 	start: level_start, | ||||
| 	stop: level_stop, | ||||
| 	update: level_update, | ||||
| 	reset: level_reset, | ||||
| }; | ||||
| 
 | ||||
| int snd_pcm_scope_level_open(snd_pcm_t *pcm, const char *name, | ||||
| 			     unsigned int bar_width, unsigned int decay_ms, | ||||
| 			     unsigned int peak_ms, | ||||
| 			     snd_pcm_scope_t **scopep) | ||||
| { | ||||
| 	snd_pcm_scope_t *scope, *s16; | ||||
| 	snd_pcm_scope_level_t *level; | ||||
| 	int err = snd_pcm_scope_malloc(&scope); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	level = calloc(1, sizeof(*level)); | ||||
| 	if (!level) { | ||||
| 		free(scope); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	level->pcm = pcm; | ||||
| 	level->bar_width = bar_width; | ||||
| 	level->decay_ms = decay_ms; | ||||
| 	level->peak_ms = peak_ms; | ||||
| 	s16 = snd_pcm_meter_search_scope(pcm, "s16"); | ||||
| 	if (!s16) { | ||||
| 		err = snd_pcm_scope_s16_open(pcm, "s16", &s16); | ||||
| 		if (err < 0) { | ||||
| 			free(scope); | ||||
| 			free(level); | ||||
| 			return err; | ||||
| 		} | ||||
| 	} | ||||
| 	level->s16 = s16; | ||||
| 	snd_pcm_scope_set_ops(scope, &level_ops); | ||||
| 	snd_pcm_scope_set_callback_private(scope, level); | ||||
| 	if (name) | ||||
| 		snd_pcm_scope_set_name(scope, strdup(name)); | ||||
| 	snd_pcm_meter_add_scope(pcm, scope); | ||||
| 	*scopep = scope; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int _snd_pcm_scope_level_open(snd_pcm_t *pcm, const char *name, | ||||
| 			      snd_config_t *conf) | ||||
| { | ||||
| 	snd_config_iterator_t i, next; | ||||
| 	snd_pcm_scope_t *scope; | ||||
| 	long bar_width = -1, decay_ms = -1, peak_ms = -1; | ||||
| 	int err; | ||||
| 	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, "bar_width") == 0) { | ||||
| 			err = snd_config_get_integer(n, &bar_width); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (strcmp(id, "decay_ms") == 0) { | ||||
| 			err = snd_config_get_integer(n, &decay_ms); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (strcmp(id, "peak_ms") == 0) { | ||||
| 			err = snd_config_get_integer(n, &peak_ms); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		SNDERR("Unknown field %s", id); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (bar_width < 0) | ||||
| 		bar_width = BAR_WIDTH; | ||||
| 	if (decay_ms < 0) | ||||
| 		decay_ms = DECAY_MS; | ||||
| 	if (peak_ms < 0) | ||||
| 		peak_ms = PEAK_MS; | ||||
| 	return snd_pcm_scope_level_open(pcm, name, bar_width, decay_ms, peak_ms, | ||||
| 					&scope); | ||||
| } | ||||
|  | @ -169,6 +169,7 @@ int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, | |||
| 		     const char *name, int mode) | ||||
| { | ||||
| 	const char *str; | ||||
| 	char buf[256]; | ||||
| 	int err; | ||||
| 	snd_config_t *rawmidi_conf, *conf, *type_conf; | ||||
| 	snd_config_iterator_t i, next; | ||||
|  | @ -208,37 +209,35 @@ int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, | |||
| 		return err; | ||||
| 	} | ||||
| 	err = snd_config_searchv(snd_config, &type_conf, "rawmiditype", str, 0); | ||||
| 	if (err < 0) { | ||||
| 		SNDERR("Unknown RAWMIDI type %s", str); | ||||
| 		return err; | ||||
| 	} | ||||
| 	snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 			err = snd_config_get_string(n, &lib); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 	if (err >= 0) { | ||||
| 		snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 				err = snd_config_get_string(n, &lib); | ||||
| 				if (err < 0) { | ||||
| 					SNDERR("Invalid type for %s", id); | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (strcmp(id, "open") == 0) { | ||||
| 			err = snd_config_get_string(n, &open); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 			if (strcmp(id, "open") == 0) { | ||||
| 				err = snd_config_get_string(n, &open); | ||||
| 				if (err < 0) { | ||||
| 					SNDERR("Invalid type for %s", id); | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			continue; | ||||
| 			SNDERR("Unknown field %s", id); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!open) { | ||||
| 		SNDERR("open is not defined"); | ||||
| 		return -EINVAL; | ||||
| 		open = buf; | ||||
| 		snprintf(buf, sizeof(buf), "_snd_rawmidi_%s_open", str); | ||||
| 	} | ||||
| 	if (!lib) | ||||
| 		lib = "libasound.so"; | ||||
|  | @ -248,9 +247,9 @@ int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, | |||
| 		return -ENOENT; | ||||
| 	} | ||||
| 	open_func = dlsym(h, open); | ||||
| 	dlclose(h); | ||||
| 	if (!open_func) { | ||||
| 		SNDERR("symbol %s is not defined inside %s", open, lib); | ||||
| 		dlclose(h); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 	err = open_func(inputp, outputp, name, rawmidi_conf, mode); | ||||
|  |  | |||
|  | @ -40,6 +40,7 @@ int snd_seq_open(snd_seq_t **seqp, const char *name, | |||
| 		 int streams, int mode) | ||||
| { | ||||
| 	const char *str; | ||||
| 	char buf[256]; | ||||
| 	int err; | ||||
| 	snd_config_t *seq_conf, *conf, *type_conf; | ||||
| 	snd_config_iterator_t i, next; | ||||
|  | @ -73,37 +74,35 @@ int snd_seq_open(snd_seq_t **seqp, const char *name, | |||
| 		return err; | ||||
| 	} | ||||
| 	err = snd_config_searchv(snd_config, &type_conf, "seqtype", str, 0); | ||||
| 	if (err < 0) { | ||||
| 		SNDERR("Unknown SEQ type %s", str); | ||||
| 		return err; | ||||
| 	} | ||||
| 	snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 			err = snd_config_get_string(n, &lib); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 	if (err >= 0) { | ||||
| 		snd_config_for_each(i, next, type_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, "lib") == 0) { | ||||
| 				err = snd_config_get_string(n, &lib); | ||||
| 				if (err < 0) { | ||||
| 					SNDERR("Invalid type for %s", id); | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (strcmp(id, "open") == 0) { | ||||
| 			err = snd_config_get_string(n, &open); | ||||
| 			if (err < 0) { | ||||
| 				SNDERR("Invalid type for %s", id); | ||||
| 				return -EINVAL; | ||||
| 			if (strcmp(id, "open") == 0) { | ||||
| 				err = snd_config_get_string(n, &open); | ||||
| 				if (err < 0) { | ||||
| 					SNDERR("Invalid type for %s", id); | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			continue; | ||||
| 			SNDERR("Unknown field %s", id); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!open) { | ||||
| 		SNDERR("open is not defined"); | ||||
| 		return -EINVAL; | ||||
| 		open = buf; | ||||
| 		snprintf(buf, sizeof(buf), "_snd_seq_%s_open", str); | ||||
| 	} | ||||
| 	if (!lib) | ||||
| 		lib = "libasound.so"; | ||||
|  | @ -113,9 +112,9 @@ int snd_seq_open(snd_seq_t **seqp, const char *name, | |||
| 		return -ENOENT; | ||||
| 	} | ||||
| 	open_func = dlsym(h, open); | ||||
| 	dlclose(h); | ||||
| 	if (!open_func) { | ||||
| 		SNDERR("symbol %s is not defined inside %s", open, lib); | ||||
| 		dlclose(h); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 	return open_func(seqp, name, seq_conf, streams, mode); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Abramo Bagnara
						Abramo Bagnara