volume.c 11.9 KB
Newer Older
Warren Dukes's avatar
Warren Dukes committed
1
/* the Music Player Daemon (MPD)
2
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
Warren Dukes's avatar
Warren Dukes committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 * This project's homepage is: http://www.musicpd.org
 *
 * 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 "volume.h"

#include "command.h"
#include "conf.h"
#include "log.h"
23
#include "player_control.h"
24 25
#include "gcc.h"
#include "utils.h"
Max Kellermann's avatar
Max Kellermann committed
26
#include "ack.h"
27
#include "os_compat.h"
Warren Dukes's avatar
Warren Dukes committed
28

Max Kellermann's avatar
Max Kellermann committed
29 30
#include "../config.h"

31
#ifdef HAVE_OSS
Warren Dukes's avatar
Warren Dukes committed
32 33 34 35 36 37
#include <sys/soundcard.h>
#endif
#ifdef HAVE_ALSA
#include <alsa/asoundlib.h>
#endif

38 39 40
#define VOLUME_MIXER_TYPE_SOFTWARE		0
#define VOLUME_MIXER_TYPE_OSS			1
#define VOLUME_MIXER_TYPE_ALSA			2
Warren Dukes's avatar
Warren Dukes committed
41

42 43 44
#define VOLUME_MIXER_SOFTWARE_DEFAULT		""
#define VOLUME_MIXER_OSS_DEFAULT		"/dev/mixer"
#define VOLUME_MIXER_ALSA_DEFAULT		"default"
45
#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT	"PCM"
46
#define SW_VOLUME_STATE                         "sw_volume: "
Warren Dukes's avatar
Warren Dukes committed
47

48
#ifdef HAVE_OSS
49 50 51 52 53 54 55 56 57 58 59 60
#define VOLUME_MIXER_TYPE_DEFAULT               VOLUME_MIXER_TYPE_OSS
#define VOLUME_MIXER_DEVICE_DEFAULT             VOLUME_MIXER_OSS_DEFAULT
#else
#ifdef HAVE_ALSA
#define VOLUME_MIXER_TYPE_DEFAULT               VOLUME_MIXER_TYPE_ALSA
#define VOLUME_MIXER_DEVICE_DEFAULT             VOLUME_MIXER_ALSA_DEFAULT
#else
#define VOLUME_MIXER_TYPE_DEFAULT               VOLUME_MIXER_TYPE_SOFTWARE
#define VOLUME_MIXER_DEVICE_DEFAULT             VOLUME_MIXER_SOFTWARE_DEFAULT
#endif
#endif

61
static int volume_mixerType = VOLUME_MIXER_TYPE_DEFAULT;
Max Kellermann's avatar
Max Kellermann committed
62
static const char *volume_mixerDevice = VOLUME_MIXER_DEVICE_DEFAULT;
Warren Dukes's avatar
Warren Dukes committed
63

64
static int volume_softwareSet = 100;
Warren Dukes's avatar
Warren Dukes committed
65

66
#ifdef HAVE_OSS
67
static int volume_ossFd = -1;
68
static int volume_ossControl = SOUND_MIXER_PCM;
Warren Dukes's avatar
Warren Dukes committed
69 70 71
#endif

#ifdef HAVE_ALSA
72
static snd_mixer_t *volume_alsaMixerHandle;
Avuton Olrich's avatar
Avuton Olrich committed
73
static snd_mixer_elem_t *volume_alsaElem;
74 75 76
static long volume_alsaMin;
static long volume_alsaMax;
static int volume_alsaSet = -1;
Warren Dukes's avatar
Warren Dukes committed
77 78
#endif

79
#ifdef HAVE_OSS
80 81 82

static void closeOssMixer(void)
{
Avuton Olrich's avatar
Avuton Olrich committed
83
	while (close(volume_ossFd) && errno == EINTR) ;
84 85 86
	volume_ossFd = -1;
}

Max Kellermann's avatar
Max Kellermann committed
87
static int prepOssMixer(const char *device)
Avuton Olrich's avatar
Avuton Olrich committed
88 89
{
	ConfigParam *param;
Warren Dukes's avatar
Warren Dukes committed
90

Avuton Olrich's avatar
Avuton Olrich committed
91 92
	if ((volume_ossFd = open(device, O_RDONLY)) < 0) {
		WARNING("unable to open oss mixer \"%s\"\n", device);
Warren Dukes's avatar
Warren Dukes committed
93 94 95
		return -1;
	}

Avuton Olrich's avatar
Avuton Olrich committed
96
	param = getConfigParam(CONF_MIXER_CONTROL);
97

Avuton Olrich's avatar
Avuton Olrich committed
98
	if (param) {
Max Kellermann's avatar
Max Kellermann committed
99
		const char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
Max Kellermann's avatar
Max Kellermann committed
100
		char *duplicated;
Avuton Olrich's avatar
Avuton Olrich committed
101
		int i, j;
102
		int devmask = 0;
Warren Dukes's avatar
Warren Dukes committed
103

Avuton Olrich's avatar
Avuton Olrich committed
104
		if (ioctl(volume_ossFd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
105
			WARNING("errors getting read_devmask for oss mixer\n");
106
			closeOssMixer();
Warren Dukes's avatar
Warren Dukes committed
107 108 109
			return -1;
		}

Avuton Olrich's avatar
Avuton Olrich committed
110
		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
Max Kellermann's avatar
Max Kellermann committed
111
			duplicated = xstrdup(labels[i]);
Warren Dukes's avatar
Warren Dukes committed
112
			/* eliminate spaces at the end */
Max Kellermann's avatar
Max Kellermann committed
113 114 115 116 117
			j = strlen(duplicated) - 1;
			while (j >= 0 && duplicated[j] == ' ')
				duplicated[j--] = '\0';
			if (strcasecmp(duplicated, param->value) == 0) {
				free(duplicated);
Warren Dukes's avatar
Warren Dukes committed
118 119
				break;
			}
Max Kellermann's avatar
Max Kellermann committed
120
			free(duplicated);
Warren Dukes's avatar
Warren Dukes committed
121 122
		}

Avuton Olrich's avatar
Avuton Olrich committed
123
		if (i >= SOUND_MIXER_NRDEVICES) {
124
			WARNING("mixer control \"%s\" not found at line %i\n",
Avuton Olrich's avatar
Avuton Olrich committed
125
				param->value, param->line);
126
			closeOssMixer();
Warren Dukes's avatar
Warren Dukes committed
127
			return -1;
Avuton Olrich's avatar
Avuton Olrich committed
128
		} else if (!((1 << i) & devmask)) {
129
			WARNING("mixer control \"%s\" not usable at line %i\n",
Avuton Olrich's avatar
Avuton Olrich committed
130
				param->value, param->line);
131
			closeOssMixer();
Warren Dukes's avatar
Warren Dukes committed
132 133 134 135 136 137 138 139 140
			return -1;
		}

		volume_ossControl = i;
	}

	return 0;
}

141 142 143 144 145
static int ensure_oss_open(void)
{
	if ((volume_ossFd < 0 && prepOssMixer(volume_mixerDevice) < 0))
		return -1;
	return 0;
Warren Dukes's avatar
Warren Dukes committed
146 147
}

Avuton Olrich's avatar
Avuton Olrich committed
148 149
static int getOssVolumeLevel(void)
{
Warren Dukes's avatar
Warren Dukes committed
150 151
	int left, right, level;

152 153 154
	if (ensure_oss_open() < 0)
		return -1;

Avuton Olrich's avatar
Avuton Olrich committed
155
	if (ioctl(volume_ossFd, MIXER_READ(volume_ossControl), &level) < 0) {
156
		closeOssMixer();
157
		WARNING("unable to read volume\n");
Warren Dukes's avatar
Warren Dukes committed
158 159 160 161 162 163
		return -1;
	}

	left = level & 0xff;
	right = (level & 0xff00) >> 8;

Avuton Olrich's avatar
Avuton Olrich committed
164
	if (left != right) {
165
		WARNING("volume for left and right is not the same, \"%i\" and "
Avuton Olrich's avatar
Avuton Olrich committed
166
			"\"%i\"\n", left, right);
Warren Dukes's avatar
Warren Dukes committed
167 168 169 170 171
	}

	return left;
}

172
static int changeOssVolumeLevel(int fd, int change, int rel)
Avuton Olrich's avatar
Avuton Olrich committed
173
{
Warren Dukes's avatar
Warren Dukes committed
174 175 176 177 178
	int current;
	int new;
	int level;

	if (rel) {
Avuton Olrich's avatar
Avuton Olrich committed
179
		if ((current = getOssVolumeLevel()) < 0) {
180
			commandError(fd, ACK_ERROR_SYSTEM,
181
				     "problem getting current volume");
Warren Dukes's avatar
Warren Dukes committed
182 183 184
			return -1;
		}

Avuton Olrich's avatar
Avuton Olrich committed
185
		new = current + change;
186 187 188 189
	} else {
		if (ensure_oss_open() < 0)
			return -1;
		new = change;
Warren Dukes's avatar
Warren Dukes committed
190 191
	}

Avuton Olrich's avatar
Avuton Olrich committed
192 193 194 195
	if (new < 0)
		new = 0;
	else if (new > 100)
		new = 100;
Warren Dukes's avatar
Warren Dukes committed
196 197 198

	level = (new << 8) + new;

Avuton Olrich's avatar
Avuton Olrich committed
199
	if (ioctl(volume_ossFd, MIXER_WRITE(volume_ossControl), &level) < 0) {
200
		closeOssMixer();
201
		commandError(fd, ACK_ERROR_SYSTEM, "problems setting volume");
Warren Dukes's avatar
Warren Dukes committed
202 203 204 205 206 207 208 209
		return -1;
	}

	return 0;
}
#endif

#ifdef HAVE_ALSA
Avuton Olrich's avatar
Avuton Olrich committed
210 211
static void closeAlsaMixer(void)
{
212 213 214 215
	snd_mixer_close(volume_alsaMixerHandle);
	volume_alsaMixerHandle = NULL;
}

Max Kellermann's avatar
Max Kellermann committed
216
static int prepAlsaMixer(const char *card)
Avuton Olrich's avatar
Avuton Olrich committed
217
{
Warren Dukes's avatar
Warren Dukes committed
218
	int err;
Avuton Olrich's avatar
Avuton Olrich committed
219
	snd_mixer_elem_t *elem;
Max Kellermann's avatar
Max Kellermann committed
220
	const char *controlName = VOLUME_MIXER_ALSA_CONTROL_DEFAULT;
Avuton Olrich's avatar
Avuton Olrich committed
221
	ConfigParam *param;
Warren Dukes's avatar
Warren Dukes committed
222

Avuton Olrich's avatar
Avuton Olrich committed
223
	err = snd_mixer_open(&volume_alsaMixerHandle, 0);
224 225
	snd_config_update_free_global();
	if (err < 0) {
Avuton Olrich's avatar
Avuton Olrich committed
226
		WARNING("problems opening alsa mixer: %s\n", snd_strerror(err));
Warren Dukes's avatar
Warren Dukes committed
227 228
		return -1;
	}
229

Avuton Olrich's avatar
Avuton Olrich committed
230
	if ((err = snd_mixer_attach(volume_alsaMixerHandle, card)) < 0) {
231
		closeAlsaMixer();
Avuton Olrich's avatar
Avuton Olrich committed
232
		WARNING("problems attaching alsa mixer: %s\n",
Warren Dukes's avatar
Warren Dukes committed
233 234 235
			snd_strerror(err));
		return -1;
	}
236

Avuton Olrich's avatar
Avuton Olrich committed
237 238 239
	if ((err =
	     snd_mixer_selem_register(volume_alsaMixerHandle, NULL,
				      NULL)) < 0) {
240
		closeAlsaMixer();
241
		WARNING("problems snd_mixer_selem_register'ing: %s\n",
Warren Dukes's avatar
Warren Dukes committed
242 243 244
			snd_strerror(err));
		return -1;
	}
245

Avuton Olrich's avatar
Avuton Olrich committed
246
	if ((err = snd_mixer_load(volume_alsaMixerHandle)) < 0) {
247
		closeAlsaMixer();
248
		WARNING("problems snd_mixer_selem_register'ing: %s\n",
Warren Dukes's avatar
Warren Dukes committed
249 250 251 252 253
			snd_strerror(err));
		return -1;
	}

	elem = snd_mixer_first_elem(volume_alsaMixerHandle);
254

255
	param = getConfigParam(CONF_MIXER_CONTROL);
256

Avuton Olrich's avatar
Avuton Olrich committed
257
	if (param) {
258
		controlName = param->value;
259 260
	}

Avuton Olrich's avatar
Avuton Olrich committed
261 262 263 264
	while (elem) {
		if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE) {
			if (strcasecmp(controlName,
				       snd_mixer_selem_get_name(elem)) == 0) {
265
				break;
Warren Dukes's avatar
Warren Dukes committed
266 267
			}
		}
268
		elem = snd_mixer_elem_next(elem);
Warren Dukes's avatar
Warren Dukes committed
269
	}
270

Avuton Olrich's avatar
Avuton Olrich committed
271
	if (elem) {
Warren Dukes's avatar
Warren Dukes committed
272
		volume_alsaElem = elem;
Avuton Olrich's avatar
Avuton Olrich committed
273 274 275
		snd_mixer_selem_get_playback_volume_range(volume_alsaElem,
							  &volume_alsaMin,
							  &volume_alsaMax);
276
		return 0;
Warren Dukes's avatar
Warren Dukes committed
277 278
	}

Avuton Olrich's avatar
Avuton Olrich committed
279
	WARNING("can't find alsa mixer_control \"%s\"\n", controlName);
280

281
	closeAlsaMixer();
Warren Dukes's avatar
Warren Dukes committed
282 283 284
	return -1;
}

285 286 287 288 289
static int prep_alsa_get_level(long *level)
{
	const char *cmd;
	int err;

Avuton Olrich's avatar
Avuton Olrich committed
290
	if (!volume_alsaMixerHandle && prepAlsaMixer(volume_mixerDevice) < 0)
291 292 293 294 295 296
		return -1;

	if ((err = snd_mixer_handle_events(volume_alsaMixerHandle)) < 0) {
		cmd = "handle_events";
		goto error;
	}
Avuton Olrich's avatar
Avuton Olrich committed
297 298 299
	if ((err = snd_mixer_selem_get_playback_volume(volume_alsaElem,
						       SND_MIXER_SCHN_FRONT_LEFT,
						       level)) < 0) {
300 301 302 303
		cmd = "selem_get_playback_volume";
		goto error;
	}
	return 0;
304 305

error:
306
	WARNING("problems getting alsa volume: %s (snd_mixer_%s)\n",
Avuton Olrich's avatar
Avuton Olrich committed
307
		snd_strerror(err), cmd);
308 309
	closeAlsaMixer();
	return -1;
Warren Dukes's avatar
Warren Dukes committed
310 311
}

Avuton Olrich's avatar
Avuton Olrich committed
312 313
static int getAlsaVolumeLevel(void)
{
Warren Dukes's avatar
Warren Dukes committed
314 315 316 317
	int ret;
	long level;
	long max = volume_alsaMax;
	long min = volume_alsaMin;
Warren Dukes's avatar
Warren Dukes committed
318

319
	if (prep_alsa_get_level(&level) < 0)
Warren Dukes's avatar
Warren Dukes committed
320 321
		return -1;

Avuton Olrich's avatar
Avuton Olrich committed
322 323
	ret = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5;
	if (volume_alsaSet > 0 && ret == level) {
Warren Dukes's avatar
Warren Dukes committed
324
		ret = volume_alsaSet;
Avuton Olrich's avatar
Avuton Olrich committed
325 326
	} else
		ret = (int)(100 * (((float)(level - min)) / (max - min)) + 0.5);
Warren Dukes's avatar
Warren Dukes committed
327 328 329 330

	return ret;
}

331
static int changeAlsaVolumeLevel(int fd, int change, int rel)
Avuton Olrich's avatar
Avuton Olrich committed
332
{
Warren Dukes's avatar
Warren Dukes committed
333 334
	float vol;
	long level;
Warren Dukes's avatar
Warren Dukes committed
335
	long test;
Warren Dukes's avatar
Warren Dukes committed
336 337 338 339
	long max = volume_alsaMax;
	long min = volume_alsaMin;
	int err;

340
	if (prep_alsa_get_level(&level) < 0)
Warren Dukes's avatar
Warren Dukes committed
341 342 343
		return -1;

	if (rel) {
Avuton Olrich's avatar
Avuton Olrich committed
344 345
		test = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5;
		if (volume_alsaSet >= 0 && level == test) {
Warren Dukes's avatar
Warren Dukes committed
346
			vol = volume_alsaSet;
Avuton Olrich's avatar
Avuton Olrich committed
347 348 349 350
		} else
			vol = 100.0 * (((float)(level - min)) / (max - min));
		vol += change;
	} else
Warren Dukes's avatar
Warren Dukes committed
351 352
		vol = change;

Avuton Olrich's avatar
Avuton Olrich committed
353 354 355
	volume_alsaSet = vol + 0.5;
	volume_alsaSet = volume_alsaSet > 100 ? 100 :
	    (volume_alsaSet < 0 ? 0 : volume_alsaSet);
Warren Dukes's avatar
Warren Dukes committed
356

Avuton Olrich's avatar
Avuton Olrich committed
357 358 359
	level = (long)(((vol / 100.0) * (max - min) + min) + 0.5);
	level = level > max ? max : level;
	level = level < min ? min : level;
Warren Dukes's avatar
Warren Dukes committed
360

Avuton Olrich's avatar
Avuton Olrich committed
361 362 363
	if ((err =
	     snd_mixer_selem_set_playback_volume_all(volume_alsaElem,
						     level)) < 0) {
364
		commandError(fd, ACK_ERROR_SYSTEM, "problems setting volume");
Avuton Olrich's avatar
Avuton Olrich committed
365 366
		WARNING("problems setting alsa volume: %s\n",
			snd_strerror(err));
367
		closeAlsaMixer();
Warren Dukes's avatar
Warren Dukes committed
368 369 370 371 372 373 374
		return -1;
	}

	return 0;
}
#endif

Max Kellermann's avatar
Max Kellermann committed
375
static int prepMixer(const char *device)
Avuton Olrich's avatar
Avuton Olrich committed
376 377
{
	switch (volume_mixerType) {
Warren Dukes's avatar
Warren Dukes committed
378 379 380 381
#ifdef HAVE_ALSA
	case VOLUME_MIXER_TYPE_ALSA:
		return prepAlsaMixer(device);
#endif
382
#ifdef HAVE_OSS
Warren Dukes's avatar
Warren Dukes committed
383 384 385 386 387 388 389 390
	case VOLUME_MIXER_TYPE_OSS:
		return prepOssMixer(device);
#endif
	}

	return 0;
}

Avuton Olrich's avatar
Avuton Olrich committed
391 392 393
void finishVolume(void)
{
	switch (volume_mixerType) {
Warren Dukes's avatar
Warren Dukes committed
394 395 396 397 398
#ifdef HAVE_ALSA
	case VOLUME_MIXER_TYPE_ALSA:
		closeAlsaMixer();
		break;
#endif
399
#ifdef HAVE_OSS
Warren Dukes's avatar
Warren Dukes committed
400 401 402 403 404 405 406
	case VOLUME_MIXER_TYPE_OSS:
		closeOssMixer();
		break;
#endif
	}
}

Avuton Olrich's avatar
Avuton Olrich committed
407 408 409
void initVolume(void)
{
	ConfigParam *param = getConfigParam(CONF_MIXER_TYPE);
410

Avuton Olrich's avatar
Avuton Olrich committed
411 412
	if (param) {
		if (0) ;
Warren Dukes's avatar
Warren Dukes committed
413
#ifdef HAVE_ALSA
Avuton Olrich's avatar
Avuton Olrich committed
414
		else if (strcmp(param->value, VOLUME_MIXER_ALSA) == 0) {
415 416 417
			volume_mixerType = VOLUME_MIXER_TYPE_ALSA;
			volume_mixerDevice = VOLUME_MIXER_ALSA_DEFAULT;
		}
Warren Dukes's avatar
Warren Dukes committed
418
#endif
419
#ifdef HAVE_OSS
Avuton Olrich's avatar
Avuton Olrich committed
420
		else if (strcmp(param->value, VOLUME_MIXER_OSS) == 0) {
421 422 423
			volume_mixerType = VOLUME_MIXER_TYPE_OSS;
			volume_mixerDevice = VOLUME_MIXER_OSS_DEFAULT;
		}
Warren Dukes's avatar
Warren Dukes committed
424
#endif
Avuton Olrich's avatar
Avuton Olrich committed
425
		else if (strcmp(param->value, VOLUME_MIXER_SOFTWARE) == 0) {
426 427
			volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
			volume_mixerDevice = VOLUME_MIXER_SOFTWARE_DEFAULT;
Avuton Olrich's avatar
Avuton Olrich committed
428
		} else {
429
			FATAL("unknown mixer type %s at line %i\n",
Avuton Olrich's avatar
Avuton Olrich committed
430
			      param->value, param->line);
431
		}
Warren Dukes's avatar
Warren Dukes committed
432
	}
433 434

	param = getConfigParam(CONF_MIXER_DEVICE);
435

Avuton Olrich's avatar
Avuton Olrich committed
436
	if (param) {
437
		volume_mixerDevice = param->value;
Warren Dukes's avatar
Warren Dukes committed
438 439 440
	}
}

Avuton Olrich's avatar
Avuton Olrich committed
441 442 443
void openVolumeDevice(void)
{
	if (prepMixer(volume_mixerDevice) < 0) {
444
		WARNING("using software volume\n");
Warren Dukes's avatar
Warren Dukes committed
445 446 447 448
		volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
	}
}

Avuton Olrich's avatar
Avuton Olrich committed
449 450
static int getSoftwareVolume(void)
{
451
	return volume_softwareSet;
Warren Dukes's avatar
Warren Dukes committed
452 453
}

Avuton Olrich's avatar
Avuton Olrich committed
454 455 456
int getVolumeLevel(void)
{
	switch (volume_mixerType) {
Warren Dukes's avatar
Warren Dukes committed
457 458 459 460
#ifdef HAVE_ALSA
	case VOLUME_MIXER_TYPE_ALSA:
		return getAlsaVolumeLevel();
#endif
461
#ifdef HAVE_OSS
Warren Dukes's avatar
Warren Dukes committed
462 463 464 465 466 467 468 469 470 471
	case VOLUME_MIXER_TYPE_OSS:
		return getOssVolumeLevel();
#endif
	case VOLUME_MIXER_TYPE_SOFTWARE:
		return getSoftwareVolume();
	default:
		return -1;
	}
}

472
static int changeSoftwareVolume(mpd_unused int fd, int change, int rel)
Avuton Olrich's avatar
Avuton Olrich committed
473
{
Warren Dukes's avatar
Warren Dukes committed
474 475
	int new = change;

Avuton Olrich's avatar
Avuton Olrich committed
476 477
	if (rel)
		new += volume_softwareSet;
Warren Dukes's avatar
Warren Dukes committed
478

Avuton Olrich's avatar
Avuton Olrich committed
479 480 481 482
	if (new > 100)
		new = 100;
	else if (new < 0)
		new = 0;
Warren Dukes's avatar
Warren Dukes committed
483

Warren Dukes's avatar
Warren Dukes committed
484 485
	volume_softwareSet = new;

Avuton Olrich's avatar
Avuton Olrich committed
486 487 488 489 490 491 492 493
	/*new = 100.0*(exp(new/50.0)-1)/(M_E*M_E-1)+0.5; */
	if (new >= 100)
		new = 1000;
	else if (new <= 0)
		new = 0;
	else
		new =
		    1000.0 * (exp(new / 25.0) - 1) / (54.5981500331F - 1) + 0.5;
Warren Dukes's avatar
Warren Dukes committed
494 495 496 497 498 499

	setPlayerSoftwareVolume(new);

	return 0;
}

500
int changeVolumeLevel(int fd, int change, int rel)
Avuton Olrich's avatar
Avuton Olrich committed
501 502
{
	switch (volume_mixerType) {
Warren Dukes's avatar
Warren Dukes committed
503 504
#ifdef HAVE_ALSA
	case VOLUME_MIXER_TYPE_ALSA:
505
		return changeAlsaVolumeLevel(fd, change, rel);
Warren Dukes's avatar
Warren Dukes committed
506
#endif
507
#ifdef HAVE_OSS
Warren Dukes's avatar
Warren Dukes committed
508
	case VOLUME_MIXER_TYPE_OSS:
509
		return changeOssVolumeLevel(fd, change, rel);
Warren Dukes's avatar
Warren Dukes committed
510 511
#endif
	case VOLUME_MIXER_TYPE_SOFTWARE:
512
		return changeSoftwareVolume(fd, change, rel);
Warren Dukes's avatar
Warren Dukes committed
513
	default:
514
		return 0;
Warren Dukes's avatar
Warren Dukes committed
515 516
	}
}
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547

void read_sw_volume_state(FILE *fp)
{
	/* strlen(SW_VOLUME_STATE) + strlen('100') + '\0' */
	#define bufsize 16
	char buf[bufsize];
	const size_t len = strlen(SW_VOLUME_STATE);
	char *end = NULL;
	long int sv;

	if (volume_mixerType != VOLUME_MIXER_TYPE_SOFTWARE)
		return;
	while (myFgets(buf, bufsize, fp)) {
		if (strncmp(buf, SW_VOLUME_STATE, len))
			continue;
		sv = strtol(buf + len, &end, 10);
		if (mpd_likely(!*end))
			changeSoftwareVolume(STDERR_FILENO, sv, 0);
		else
			ERROR("Can't parse software volume: %s\n", buf);
		return;
	}
	#undef bufsize
}

void save_sw_volume_state(FILE *fp)
{
	if (volume_mixerType == VOLUME_MIXER_TYPE_SOFTWARE)
		fprintf(fp, SW_VOLUME_STATE "%d\n", volume_softwareSet);
}