1 /*
2  *  Copyright (C) 2009-2014  Christian Heckendorf <heckendorfc@gmail.com>
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "alsautil.h"
19 #include "sndutil.h"
20 
snd_init(struct playerHandles * ph)21 int snd_init(struct playerHandles *ph){
22 	if(!ph->device)ph->device=strdup("default");
23 	if(snd_pcm_open(&ph->sndfd,ph->device,SND_PCM_STREAM_PLAYBACK,0)<0){
24 		fprintf(stderr,"sndfd open failed\n");
25 		return 1;
26 	}
27 	return 0;
28 }
29 
snd_param_init(struct playerHandles * ph,int * enc,int * channels,unsigned int * rate)30 int snd_param_init(struct playerHandles *ph, int *enc, int *channels, unsigned int *rate){
31 	snd_pcm_uframes_t bufsize=*rate/4;
32 	int x=0;
33 	*enc=SND_PCM_FORMAT_S16_LE;
34 	snd_pcm_drop(ph->sndfd);
35 	snd_pcm_hw_params_malloc(&ph->params);
36 	if(ph->params==NULL){
37 		fprintf(stderr,"can't malloc params\n");
38 		return 1;
39 	}
40 	if(snd_pcm_hw_params_any(ph->sndfd,ph->params)<0)fprintf(stderr,"can't init params\n");
41 	if(snd_pcm_hw_params_set_access(ph->sndfd,ph->params,SND_PCM_ACCESS_RW_INTERLEAVED)<0)fprintf(stderr,"no access\n");
42 	if(snd_pcm_hw_params_set_format(ph->sndfd,ph->params,*enc)<0)fprintf(stderr,"can't set fmt\n");
43 	if(snd_pcm_hw_params_set_channels(ph->sndfd,ph->params,*channels)<0)fprintf(stderr,"can't set channels\n");
44 	if(snd_pcm_hw_params_set_rate_near(ph->sndfd,ph->params,rate,0)<0)fprintf(stderr,"can't set rate\n");
45 	if(snd_pcm_hw_params_set_buffer_size_near(ph->sndfd,ph->params,&bufsize)<0)fprintf(stderr,"can't set buffer size\n");
46 	if((x=snd_pcm_hw_params(ph->sndfd,ph->params))<0)fprintf(stderr,"can't set parms: %s\n",snd_strerror(x));
47 	snd_pcm_hw_params_free(ph->params);
48 	return 0;
49 }
50 
changeVolume(struct playerHandles * ph,int mod)51 void changeVolume(struct playerHandles *ph, int mod){
52 	long val;
53 	int err;
54 	float range_p;
55 	long cur_vol,new_volume;
56 	long vmin,vmax;
57 	char tail[OUTPUT_TAIL_SIZE];
58 	snd_mixer_t *handle;
59 	snd_mixer_elem_t *elem;
60 	snd_mixer_selem_id_t *sid;
61 
62 	snd_mixer_selem_id_alloca(&sid);
63 	snd_mixer_selem_id_set_index(sid,0);
64 	snd_mixer_selem_id_set_name(sid,"PCM");
65 
66 	if(snd_mixer_open(&handle,0)<0)
67 		return;
68 
69 	if(snd_mixer_attach(handle,"default")<0){
70 		snd_mixer_close(handle);
71 		return;
72 	}
73 
74 	if(snd_mixer_selem_register(handle,NULL,NULL)<0){
75 		snd_mixer_close(handle);
76 		return;
77 	}
78 
79 	if(snd_mixer_load(handle)<0){
80 		snd_mixer_close(handle);
81 		return;
82 	}
83 
84 	elem=snd_mixer_find_selem(handle,sid);
85 	if(!elem){
86 		snd_mixer_close(handle);
87 		return;
88 	}
89 
90 	snd_mixer_selem_get_playback_volume_range(elem,&vmin,&vmax);
91 	range_p = (100.0f/(float)(vmax-vmin));
92 
93 	snd_mixer_selem_get_playback_volume(elem,0,&cur_vol);
94 	new_volume = cur_vol+vmin+mod/range_p;
95 	if(new_volume==cur_vol && mod!=0)new_volume+=(mod<0?-1:1);
96 	if(new_volume<vmin)new_volume=vmin;
97 	if(new_volume>vmax)new_volume=vmax;
98 	if(snd_mixer_selem_set_playback_volume(elem,0,new_volume)<0){
99 		snd_mixer_close(handle);
100 		return;
101 	}
102 
103 	sprintf(tail,"Volume: %d%%",(int)((float)range_p*(float)new_volume));
104 	addStatusTail(tail,ph->outdetail);
105 
106 	snd_mixer_selem_get_playback_volume(elem,1,&cur_vol);
107 	new_volume = cur_vol+vmin+mod/range_p;
108 	if(new_volume==cur_vol && mod!=0)new_volume+=(mod<0?-1:1);
109 	if(new_volume<vmin)new_volume=vmin;
110 	if(new_volume>vmax)new_volume=vmax;
111 	if(snd_mixer_selem_set_playback_volume(elem,1,new_volume)<0){
112 		snd_mixer_close(handle);
113 		return;
114 	}
115 
116 	snd_mixer_close(handle);
117 }
118 
toggleMute(struct playerHandles * ph,int * mute)119 void toggleMute(struct playerHandles *ph, int *mute){
120 	long val;
121 	int err;
122 	float range_p;
123 	long current=*mute;
124 	long vmin,vmax;
125 	char tail[OUTPUT_TAIL_SIZE];
126 	snd_mixer_t *handle;
127 	snd_mixer_elem_t *elem;
128 	snd_mixer_selem_id_t *sid;
129 
130 	snd_mixer_selem_id_alloca(&sid);
131 	snd_mixer_selem_id_set_index(sid,0);
132 	snd_mixer_selem_id_set_name(sid,"PCM");
133 
134 	if(snd_mixer_open(&handle,0)<0)
135 		return;
136 
137 	if(snd_mixer_attach(handle,"default")<0){
138 		snd_mixer_close(handle);
139 		return;
140 	}
141 
142 	if(snd_mixer_selem_register(handle,NULL,NULL)<0){
143 		snd_mixer_close(handle);
144 		return;
145 	}
146 
147 	if(snd_mixer_load(handle)<0){
148 		snd_mixer_close(handle);
149 		return;
150 	}
151 
152 	elem=snd_mixer_find_selem(handle,sid);
153 	if(!elem){
154 		snd_mixer_close(handle);
155 		return;
156 	}
157 
158 	snd_mixer_selem_get_playback_volume_range(elem,&vmin,&vmax);
159 	range_p = (100.0f/(float)(vmax-vmin));
160 	if(*mute>0){ // Unmute and perform volume change
161 		*mute=0;
162 		sprintf(tail,"Volume: %ld%%",current);
163 		addStatusTail(tail,ph->outdetail);
164 		current=current/range_p+vmin;
165 	}
166 	else{ // Mute
167 		snd_mixer_selem_get_playback_volume(elem,0,&current);
168 		*mute=current*range_p+vmin;
169 		current=0;
170 		addStatusTail("Volume Muted",ph->outdetail);
171 	}
172 	fflush(stdout);
173 
174 	if(snd_mixer_selem_set_playback_volume(elem,0,current)<0){
175 		snd_mixer_close(handle);
176 		return;
177 	}
178 	if(snd_mixer_selem_set_playback_volume(elem,1,current)<0){
179 		snd_mixer_close(handle);
180 		return;
181 	}
182 
183 	snd_mixer_close(handle);
184 }
185 
snd_clear(struct playerHandles * ph)186 void snd_clear(struct playerHandles *ph){
187 	snd_pcm_state_t st;
188 	int flag=1;
189 	while(flag==1){
190 		st=snd_pcm_state(ph->sndfd);
191 		switch(st){
192 			case SND_PCM_STATE_SETUP:
193 			case SND_PCM_STATE_XRUN:
194 			case SND_PCM_STATE_OPEN:
195 			case SND_PCM_STATE_PREPARED:usleep(50000);break; // TODO: fix possible infinite loop
196 			case SND_PCM_STATE_DRAINING:
197 			case SND_PCM_STATE_RUNNING:
198 			default:flag=0;break;
199 		}
200 	}
201 	snd_pcm_drop(ph->sndfd);
202 	snd_pcm_prepare(ph->sndfd);
203 }
204 
writei_snd(struct playerHandles * ph,const char * out,const unsigned int size)205 int writei_snd(struct playerHandles *ph, const char *out, const unsigned int size){
206 	int ret;
207 	if(ph->pflag->pause){
208 		snd_pcm_drop(ph->sndfd);
209 		do{
210 			usleep(100000); // 0.1 seconds
211 		}
212 		while(ph->pflag->pause);
213 		snd_pcm_prepare(ph->sndfd);
214 	}
215 	if(size==0){
216 		snd_pcm_drain(ph->sndfd);
217 		return 0;
218 	}
219 	ret=snd_pcm_writei(ph->sndfd,out,size);
220 	if(ret == -EAGAIN)return 0;
221 	if(ret<0){
222 		if(ret == -EPIPE){
223 			ret=snd_pcm_prepare(ph->sndfd);
224 			if(ret<0){
225 				snd_pcm_drain(ph->sndfd);
226 				snd_pcm_close(ph->sndfd);
227 				return -1;
228 			}
229 		}
230 		else if(ret == -ESTRPIPE){
231 			while((ret=snd_pcm_resume(ph->sndfd)) == -EAGAIN)
232 				sleep(1);
233 			if(ret<0){
234 				ret=snd_pcm_prepare(ph->sndfd);
235 				if(ret<0){
236 					snd_pcm_drain(ph->sndfd);
237 					snd_pcm_close(ph->sndfd);
238 					return -1;
239 				}
240 			}
241 		}
242 	}
243 	return 0;
244 }
245 
snd_close(struct playerHandles * ph)246 void snd_close(struct playerHandles *ph){
247 	snd_pcm_close(ph->sndfd);
248 }
249