mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-10-29 05:40:25 -04:00 
			
		
		
		
	PCM loopback API improved and added back the functionality.
Some mmap() changes for the OSS emulation.
This commit is contained in:
		
							parent
							
								
									3239ca9940
								
							
						
					
					
						commit
						f5e461caa3
					
				
					 4 changed files with 182 additions and 34 deletions
				
			
		|  | @ -6,7 +6,7 @@ | |||
|  ****************************************************************************/ | ||||
| 
 | ||||
| typedef struct snd_mixer_callbacks { | ||||
| 	void *private_data;	/* should be used by an application */ | ||||
| 	void *private_data;	/* should be used with an application */ | ||||
| 	void (*rebuild) (void *private_data); | ||||
| 	void (*element) (void *private_data, int cmd, snd_mixer_eid_t *eid); | ||||
| 	void (*group) (void *private_data, int cmd, snd_mixer_gid_t *gid); | ||||
|  |  | |||
|  | @ -144,13 +144,23 @@ int snd_pcm_plugin_build_voices(int src_format, int src_voices, | |||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subchn, int mode); | ||||
| typedef struct snd_pcm_loopback_callbacks { | ||||
| 	void *private_data;		/* should be used with an application */ | ||||
| 	size_t max_buffer_size;		/* zero = default (64kB) */ | ||||
| 	void (*data) (void *private_data, char *buffer, size_t count); | ||||
| 	void (*position_change) (void *private_data, unsigned int pos); | ||||
| 	void (*format_change) (void *private_data, snd_pcm_format_t *format); | ||||
| 	char *reserved[32];		/* reserved for the future use - must be NULL!!! */ | ||||
| } snd_pcm_loopback_callbacks_t; | ||||
| 
 | ||||
| int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subdev, int mode); | ||||
| int snd_pcm_loopback_close(snd_pcm_loopback_t *handle); | ||||
| int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *handle); | ||||
| int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *handle, int enable); | ||||
| int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *handle, int mode); | ||||
| int snd_pcm_loopback_format(snd_pcm_loopback_t *handle, snd_pcm_format_t * format); | ||||
| ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *handle, void *buffer, size_t size); | ||||
| int snd_pcm_loopback_status(snd_pcm_loopback_t *handle, snd_pcm_loopback_status_t * status); | ||||
| ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *handle, snd_pcm_loopback_callbacks_t * callbacks); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  |  | |||
|  | @ -29,13 +29,16 @@ | |||
| #include "asoundlib.h" | ||||
| 
 | ||||
| #define SND_FILE_PCM_LB		"/proc/asound/%i/pcmloopD%iS%i%s" | ||||
| #define SND_PCM_LB_VERSION_MAX	SND_PROTOCOL_VERSION( 1, 0, 0 ) | ||||
| #define SND_PCM_LB_VERSION_MAX	SND_PROTOCOL_VERSION(2, 0, 0) | ||||
| 
 | ||||
| struct snd_pcm_loopback { | ||||
| 	int card; | ||||
| 	int device; | ||||
| 	int fd; | ||||
| } ; | ||||
| 	long mode; | ||||
| 	size_t buffer_size; | ||||
| 	char *buffer; | ||||
| }; | ||||
| 
 | ||||
| int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subchn, int mode) | ||||
| { | ||||
|  | @ -70,39 +73,35 @@ int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int | |||
| 	lb->card = card; | ||||
| 	lb->device = device; | ||||
| 	lb->fd = fd; | ||||
| 	lb->mode = SND_PCM_LB_STREAM_MODE_RAW; | ||||
| 	*handle = lb; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_loopback_close(snd_pcm_loopback_t *handle) | ||||
| int snd_pcm_loopback_close(snd_pcm_loopback_t *lb) | ||||
| { | ||||
| 	snd_pcm_loopback_t *lb; | ||||
| 	int res; | ||||
| 
 | ||||
| 	lb = handle; | ||||
| 	if (!lb) | ||||
| 		return -EINVAL; | ||||
| 	if (lb->buffer) | ||||
| 		free(lb->buffer); | ||||
| 	res = close(lb->fd) < 0 ? -errno : 0; | ||||
| 	free(lb); | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *handle) | ||||
| int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *lb) | ||||
| { | ||||
| 	snd_pcm_loopback_t *lb; | ||||
| 
 | ||||
| 	lb = handle; | ||||
| 	if (!lb) | ||||
| 		return -EINVAL; | ||||
| 	return lb->fd; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *handle, int enable) | ||||
| int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *lb, int enable) | ||||
| { | ||||
| 	snd_pcm_loopback_t *lb; | ||||
| 	long flags; | ||||
| 
 | ||||
| 	lb = handle; | ||||
| 	if (!lb) | ||||
| 		return -EINVAL; | ||||
| 	if ((flags = fcntl(lb->fd, F_GETFL)) < 0) | ||||
|  | @ -116,24 +115,19 @@ int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *handle, int enable) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *handle, int mode) | ||||
| int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *lb, int mode) | ||||
| { | ||||
| 	snd_pcm_loopback_t *lb; | ||||
| 	long lmode = mode; | ||||
| 
 | ||||
| 	lb = handle; | ||||
| 	if (!lb) | ||||
| 	if (!lb || (mode != SND_PCM_LB_STREAM_MODE_RAW && | ||||
| 	            mode != SND_PCM_LB_STREAM_MODE_PACKET)) | ||||
| 		return -EINVAL; | ||||
| 	if (ioctl(lb->fd, SND_PCM_LB_IOCTL_STREAM_MODE, &lmode) < 0) | ||||
| 	lb->mode = mode; | ||||
| 	if (ioctl(lb->fd, SND_PCM_LB_IOCTL_STREAM_MODE, &lb->mode) < 0) | ||||
| 		return -errno; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int snd_pcm_loopback_format(snd_pcm_loopback_t *handle, snd_pcm_format_t * format) | ||||
| int snd_pcm_loopback_format(snd_pcm_loopback_t *lb, snd_pcm_format_t * format) | ||||
| { | ||||
| 	snd_pcm_loopback_t *lb; | ||||
| 
 | ||||
| 	lb = handle; | ||||
| 	if (!lb) | ||||
| 		return -EINVAL; | ||||
| 	if (ioctl(lb->fd, SND_PCM_LB_IOCTL_FORMAT, format) < 0) | ||||
|  | @ -141,16 +135,101 @@ int snd_pcm_loopback_format(snd_pcm_loopback_t *handle, snd_pcm_format_t * forma | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *handle, void *buffer, size_t size) | ||||
| int snd_pcm_loopback_status(snd_pcm_loopback_t *lb, snd_pcm_loopback_status_t * status) | ||||
| { | ||||
| 	snd_pcm_loopback_t *lb; | ||||
| 	ssize_t result; | ||||
| 
 | ||||
| 	lb = handle; | ||||
| 	if (!lb) | ||||
| 		return -EINVAL; | ||||
| 	result = read(lb->fd, buffer, size); | ||||
| 	if (result < 0) | ||||
| 	if (ioctl(lb->fd, SND_PCM_LB_IOCTL_FORMAT, status) < 0) | ||||
| 		return -errno; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *lb, snd_pcm_loopback_callbacks_t *callbacks) | ||||
| { | ||||
| 	ssize_t result = 0, res, count; | ||||
| 	size_t size; | ||||
| 	char *buf; | ||||
| 	snd_pcm_loopback_header_t header; | ||||
| 
 | ||||
| 	if (!lb || !callbacks) | ||||
| 		return -EINVAL; | ||||
| 	if (callbacks->max_buffer_size == 0) | ||||
| 		size = 64 * 1024; | ||||
| 	else | ||||
| 		size = callbacks->max_buffer_size < 16 ? 16 : callbacks->max_buffer_size; | ||||
| 	while (1) { | ||||
| 		if (lb->mode == SND_PCM_LB_STREAM_MODE_RAW) { | ||||
| 			header.size = size; | ||||
| 			header.type = SND_PCM_LB_TYPE_DATA; | ||||
| 		} else { | ||||
| 			res = read(lb->fd, &header, sizeof(header)); | ||||
| 			if (res < 0) | ||||
| 				return -errno; | ||||
| 			if (res == 0) | ||||
| 				break; | ||||
| 			if (res != sizeof(header)) | ||||
| 				return -EBADFD; | ||||
| 			result += res; | ||||
| 		} | ||||
| 		switch (header.type) { | ||||
| 		case SND_PCM_LB_TYPE_DATA: | ||||
| 			if (lb->buffer_size < size) { | ||||
| 				buf = (char *) realloc(lb->buffer, size); | ||||
| 				if (buf == NULL) | ||||
| 					return -ENOMEM; | ||||
| 				lb->buffer = buf; | ||||
| 				lb->buffer_size = size; | ||||
| 			} else { | ||||
| 				buf = lb->buffer; | ||||
| 			} | ||||
| 			while (header.size > 0) { | ||||
| 				count = header.size; | ||||
| 				if (count > size) | ||||
| 					count = size; | ||||
| 				res = read(lb->fd, buf, count); | ||||
| 				if (res < 0) | ||||
| 					return -errno; | ||||
| 				result += res; | ||||
| 				if (lb->mode == SND_PCM_LB_STREAM_MODE_PACKET && res != count) | ||||
| 					return -EBADFD; | ||||
| 				if (res == 0) | ||||
| 					break; | ||||
| 				if (callbacks->data) | ||||
| 					callbacks->data(callbacks->private_data, buf, res); | ||||
| 				if (res < count && lb->mode == SND_PCM_LB_STREAM_MODE_RAW) | ||||
| 					break; | ||||
| 				header.size -= res; | ||||
| 			} | ||||
| 			break; | ||||
| 		case SND_PCM_LB_TYPE_FORMAT: | ||||
| 			{ | ||||
| 				snd_pcm_format_t format; | ||||
| 				 | ||||
| 				res = read(lb->fd, &format, sizeof(format)); | ||||
| 				if (res < 0) | ||||
| 					return -errno; | ||||
| 				result += res; | ||||
| 				if (res != sizeof(format)) | ||||
| 					return -EBADFD; | ||||
| 				if (callbacks->format_change) | ||||
| 					callbacks->format_change(callbacks->private_data, &format); | ||||
| 			} | ||||
| 			break; | ||||
| 		case SND_PCM_LB_TYPE_POSITION: | ||||
| 			{ | ||||
| 				unsigned int pos; | ||||
| 				 | ||||
| 				res = read(lb->fd, &pos, sizeof(pos)); | ||||
| 				if (res < 0) | ||||
| 					return -errno; | ||||
| 				result += res; | ||||
| 				if (res != sizeof(pos)) | ||||
| 					return -EBADFD; | ||||
| 				if (callbacks->position_change) | ||||
| 					callbacks->position_change(callbacks->private_data, pos); | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  |  | |||
|  | @ -3,16 +3,75 @@ | |||
| #include <string.h> | ||||
| #include "../include/asoundlib.h" | ||||
| 
 | ||||
| #define CARD	0 | ||||
| #define DEVICE	0 | ||||
| #define SUBDEV	0 | ||||
| #define MODE	SND_PCM_LB_STREAM_MODE_PACKET | ||||
| 
 | ||||
| static void show_format1(const char *prefix, snd_pcm_format_t *format) | ||||
| { | ||||
| 	printf("%sinterleave = %i, rate = %i, voices = %i, format = %i\n", | ||||
| 			prefix, | ||||
| 			format->interleave ? 1 : 0, | ||||
| 			format->rate, | ||||
| 			format->voices, | ||||
| 			format->format); | ||||
| } | ||||
| 
 | ||||
| static void show_format(snd_pcm_loopback_t *handle) | ||||
| { | ||||
| 	snd_pcm_format_t format; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = snd_pcm_loopback_format(handle, &format); | ||||
| 	if (err < 0) { | ||||
| 		fprintf(stderr, "format failed: %s\n", snd_strerror(err)); | ||||
| 		exit(0); | ||||
| 	} | ||||
| 	show_format1("Format: ", &format); | ||||
| } | ||||
| 
 | ||||
| static void data(void *private_data, char *buf, size_t count) | ||||
| { | ||||
| 	printf("DATA> count = %li\n", (long)count); | ||||
| } | ||||
| 
 | ||||
| static void format_change(void *private_data, snd_pcm_format_t *format) | ||||
| { | ||||
| 	show_format1("Format change> ", format); | ||||
| } | ||||
| 
 | ||||
| static void position_change(void *private_data, unsigned int pos) | ||||
| { | ||||
| 	printf("Position change> %u\n", pos); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int err; | ||||
| 	ssize_t res; | ||||
| 	snd_pcm_loopback_t *handle; | ||||
| 	snd_pcm_loopback_callbacks_t callbacks; | ||||
| 
 | ||||
| 	err = snd_pcm_loopback_open(&handle, 0, 0, SND_PCM_LB_OPEN_PLAYBACK); | ||||
| 	err = snd_pcm_loopback_open(&handle, CARD, DEVICE, SUBDEV, SND_PCM_LB_OPEN_PLAYBACK); | ||||
| 	if (err < 0) { | ||||
| 		fprintf(stderr, "open error: %s\n", snd_strerror(err)); | ||||
| 		exit(0); | ||||
| 	} | ||||
| 	err = snd_pcm_loopback_stream_mode(handle, MODE); | ||||
| 	if (err < 0) { | ||||
| 		fprintf(stderr, "stream mode setup failed: %s\n", snd_strerror(err)); | ||||
| 		exit(0); | ||||
| 	} | ||||
| 	show_format(handle); | ||||
| 	memset(&callbacks, 0, sizeof(callbacks)); | ||||
| 	callbacks.data = data; | ||||
| 	callbacks.format_change = format_change;	 | ||||
| 	callbacks.position_change = position_change; | ||||
| 	while ((res = snd_pcm_loopback_read(handle, &callbacks)) >= 0) { | ||||
| 		if (res > 0) | ||||
| 			printf("Read ok.. - %i\n", res); | ||||
| 	} | ||||
| 	snd_pcm_loopback_close(handle); | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jaroslav Kysela
						Jaroslav Kysela