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,¤t);
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