summaryrefslogtreecommitdiff
path: root/alsaloop/alsaloop.h
blob: 366a2961f063cbe49e87b2b508ef1191107b0982 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 *  A simple PCM loopback utility
 *  Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include "aconfig.h"
#ifdef HAVE_SAMPLERATE_H
#define USE_SAMPLERATE
#include <samplerate.h>
#else
enum {
	SRC_SINC_BEST_QUALITY	= 0,
	SRC_SINC_MEDIUM_QUALITY	= 1,
	SRC_SINC_FASTEST	= 2,
	SRC_ZERO_ORDER_HOLD	= 3,
	SRC_LINEAR		= 4
};
#endif

#define MAX_ARGS	128
#define MAX_MIXERS	64

#if 0
#define FILE_PWRITE "/tmp/alsaloop.praw"
#define FILE_CWRITE "/tmp/alsaloop.craw"
#endif

typedef enum _sync_type {
	SYNC_TYPE_NONE = 0,
	SYNC_TYPE_SIMPLE,	/* add or remove samples */
	SYNC_TYPE_CAPTRATESHIFT,
	SYNC_TYPE_PLAYRATESHIFT,
	SYNC_TYPE_SAMPLERATE,
	SYNC_TYPE_AUTO,		/* order: CAPTRATESHIFT, PLAYRATESHIFT, */
				/*        SAMPLERATE, SIMPLE */
	SYNC_TYPE_LAST = SYNC_TYPE_AUTO
} sync_type_t;

typedef enum _slave_type {
	SLAVE_TYPE_AUTO = 0,
	SLAVE_TYPE_ON = 1,
	SLAVE_TYPE_OFF = 2,
	SLAVE_TYPE_LAST = SLAVE_TYPE_OFF
} slave_type_t;

struct loopback_control {
	snd_ctl_elem_id_t *id;
	snd_ctl_elem_info_t *info;
	snd_ctl_elem_value_t *value;
};

struct loopback_mixer {
	unsigned int skip:1;
	struct loopback_control src;
	struct loopback_control dst;
	struct loopback_mixer *next;
};

struct loopback_ossmixer {
	unsigned int skip:1;
	const char *alsa_id;
	int alsa_index;
	const char *oss_id;
	struct loopback_ossmixer *next;
};

struct loopback_handle {
	struct loopback *loopback;
	char *device;
	char *id;
	int card_number;
	snd_pcm_t *handle;
	snd_pcm_access_t access;
	snd_pcm_format_t format;
	unsigned int rate;
	unsigned int rate_req;
	unsigned int channels;
	unsigned int buffer_size;
	unsigned int period_size;
	unsigned int buffer_size_req;
	unsigned int period_size_req;
	unsigned int frame_size;
	unsigned int resample:1;	/* do resample */
	unsigned int nblock:1;		/* do block (period size) transfers */
	unsigned int xrun_pending:1;
	unsigned int pollfd_count;
	/* I/O job */
	char *buf;			/* I/O buffer */
	snd_pcm_uframes_t buf_pos;	/* I/O position */
	snd_pcm_uframes_t buf_count;	/* filled samples */
	snd_pcm_uframes_t buf_size;	/* buffer size in frames */
	snd_pcm_uframes_t buf_over;	/* capture buffer overflow */
	/* statistics */
	snd_pcm_uframes_t max;
	unsigned long long counter;
	unsigned long sync_point;	/* in samples */
	snd_pcm_sframes_t last_delay;
	double pitch;
	snd_pcm_uframes_t total_queued;
	/* control */
	snd_ctl_t *ctl;
	unsigned int ctl_pollfd_count;
	snd_ctl_elem_value_t *ctl_notify;
	snd_ctl_elem_value_t *ctl_rate_shift;
	snd_ctl_elem_value_t *ctl_active;
	snd_ctl_elem_value_t *ctl_format;
	snd_ctl_elem_value_t *ctl_rate;
	snd_ctl_elem_value_t *ctl_channels;
};

struct loopback {
	char *id;
	struct loopback_handle *capt;
	struct loopback_handle *play;
	snd_pcm_uframes_t latency;	/* final latency */
	unsigned int latency_req;	/* in frames / 2 */
	unsigned int latency_reqtime;	/* in us / 2 */
	unsigned long loop_time;	/* ~0 = unlimited (in seconds) */
	unsigned long long loop_limit;	/* ~0 = unlimited (in frames) */
	snd_output_t *output;
	int pollfd_count;
	int active_pollfd_count;
	unsigned int linked:1;		/* linked streams */
	unsigned int reinit:1;
	unsigned int running:1;
	sync_type_t sync;		/* type of sync */
	slave_type_t slave;
	int thread;			/* thread number */
	/* statistics */
	double pitch;
	double pitch_delta;
	snd_pcm_sframes_t pitch_diff;
	snd_pcm_sframes_t pitch_diff_min;
	snd_pcm_sframes_t pitch_diff_max;
	unsigned int total_queued_count;
	snd_timestamp_t tstamp_start;
	snd_timestamp_t tstamp_end;
	/* control mixer */
	struct loopback_mixer *controls;
	struct loopback_ossmixer *oss_controls;
	/* sample rate */
	unsigned int use_samplerate:1;
#ifdef USE_SAMPLERATE
	unsigned int src_enable:1;
	int src_converter_type;
	SRC_STATE *src_state;
	SRC_DATA src_data;
	unsigned int src_out_frames;
#endif
#ifdef FILE_CWRITE
	FILE *cfile;
#endif
#ifdef FILE_PWRITE
	FILE *pfile;
#endif
};

extern int verbose;
extern int use_syslog;

#define logit(priority, fmt, args...) do {		\
	if (use_syslog)					\
		syslog(priority, fmt, ##args);		\
	else						\
		fprintf(stderr, fmt, ##args);		\
} while (0)

int pcmjob_init(struct loopback *loop);
int pcmjob_done(struct loopback *loop);
int pcmjob_start(struct loopback *loop);
int pcmjob_stop(struct loopback *loop);
int pcmjob_pollfds_init(struct loopback *loop, struct pollfd *fds);
int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds);

int control_parse_id(const char *str, snd_ctl_elem_id_t *id);
int control_id_match(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2);
int control_init(struct loopback *loop);
int control_done(struct loopback *loop);
int control_event(struct loopback_handle *lhandle, snd_ctl_event_t *ev);