audioOutput.c 5.66 KB
Newer Older
1
#include "audioOutput.h"
Warren Dukes's avatar
Warren Dukes committed
2

3 4
#include "list.h"
#include "log.h"
5
#include "pcm_utils.h"
6 7 8 9 10

#include <string.h> 

#define AUDIO_OUTPUT_TYPE	"type"
#define AUDIO_OUTPUT_NAME	"name"
11
#define AUDIO_OUTPUT_FORMAT	"format"
Warren Dukes's avatar
Warren Dukes committed
12 13 14 15

static List * audioOutputPluginList;

void loadAudioOutputPlugin(AudioOutputPlugin * audioOutputPlugin) {
16
	if(!audioOutputPlugin->name) return;
Warren Dukes's avatar
Warren Dukes committed
17 18 19 20 21
	insertInList(audioOutputPluginList, audioOutputPlugin->name,
			audioOutputPlugin);
}

void unloadAudioOutputPlugin(AudioOutputPlugin * audioOutputPlugin) {
22
	if(!audioOutputPlugin->name) return;
Warren Dukes's avatar
Warren Dukes committed
23 24 25 26
	deleteFromList(audioOutputPluginList, audioOutputPlugin->name);
}

void initAudioOutputPlugins() {
27
	audioOutputPluginList = makeList(NULL, 0);
Warren Dukes's avatar
Warren Dukes committed
28 29 30 31 32 33
}

void finishAudioOutputPlugins() {
	freeList(audioOutputPluginList);
}

34
#define getBlockParam(name, str, force) { \
35
	bp = getBlockParam(param, name); \
36
	if(force && bp == NULL) { \
37 38 39 40 41
		ERROR("couldn't find parameter \"%s\" in audio output " \
				"definition begining at %i\n", \
				name, param->line); \
		exit(EXIT_FAILURE); \
	} \
42
	if(bp) str = bp->value; \
43 44 45
}

AudioOutput * newAudioOutput(ConfigParam * param) {
Warren Dukes's avatar
Warren Dukes committed
46 47
	AudioOutput * ret = NULL;
	void * data = NULL;
48
	char * name = NULL;
49
	char * format = NULL;
50
	char * type = NULL;
51 52 53 54 55 56
	BlockParam * bp = NULL;
	AudioOutputPlugin * plugin = NULL;

	if(param) {
		getBlockParam(AUDIO_OUTPUT_NAME, name, 1);
		getBlockParam(AUDIO_OUTPUT_TYPE, type, 1);
57
		getBlockParam(AUDIO_OUTPUT_FORMAT, format, 0);
58 59 60 61 62 63 64

		if(!findInList(audioOutputPluginList, type, &data)) {
			ERROR("couldn't find audio output plugin for type "
					"\"%s\" at line %i\n", type, 
					param->line);
			exit(EXIT_FAILURE);
		}
65

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
		plugin = (AudioOutputPlugin *) data;
	}
	else {
		ListNode * node = audioOutputPluginList->firstNode;

		WARNING("No \"%s\" defined in config file\n", 
				CONF_AUDIO_OUTPUT);
		WARNING("Attempt to detect audio output device\n");

		while(node) {
			plugin = (AudioOutputPlugin *) node->data;
			if(plugin->testDefaultDeviceFunc) {
				WARNING("Attempting to detect a %s audio "
						"device\n", plugin->name);
				if(plugin->testDefaultDeviceFunc() == 0) {
					WARNING("Successfully detected a %s "
							"audio device\n",
							plugin->name);
					break;
				}
			}
			node = node->nextNode;
88 89
		}

90 91 92
		if(!node) {
			WARNING("Unable to detect an audio device\n");
			return NULL;
93
		}
94 95 96

		name = "default detected output";
		type = plugin->name;
Warren Dukes's avatar
Warren Dukes committed
97
	}
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

	ret = malloc(sizeof(AudioOutput));
	ret->name = strdup(name);
	ret->type = strdup(type);
	ret->finishDriverFunc = plugin->finishDriverFunc;
	ret->openDeviceFunc = plugin->openDeviceFunc;
	ret->playFunc = plugin->playFunc;
	ret->dropBufferedAudioFunc = plugin->dropBufferedAudioFunc;
	ret->closeDeviceFunc = plugin->closeDeviceFunc;
	ret->sendMetdataFunc = plugin->sendMetdataFunc;
	ret->open = 0;

	ret->convertAudioFormat = 0;
	ret->sameInAndOutFormats = 0;
	ret->convBuffer = NULL;
	ret->convBufferLen = 0;

	memset(&ret->inAudioFormat, 0, sizeof(AudioFormat));
	memset(&ret->outAudioFormat, 0, sizeof(AudioFormat));
	memset(&ret->reqAudioFormat, 0, sizeof(AudioFormat));

119 120 121 122 123 124 125 126 127 128 129 130 131
	if(format) {
		ret->convertAudioFormat = 1;

		if(0 != parseAudioConfig(&ret->reqAudioFormat, format))
		{
			ERROR("error parsing format at line %i\n", 
					bp->line);
			exit(EXIT_FAILURE);
		}

		copyAudioFormat(&ret->outAudioFormat, &ret->reqAudioFormat);
	}

132 133 134
	if(plugin->initDriverFunc(ret, param) != 0) {
		free(ret);
		ret = NULL;
135
	}
Warren Dukes's avatar
Warren Dukes committed
136 137 138 139

	return ret;
}

140
int openAudioOutput(AudioOutput * audioOutput, AudioFormat * audioFormat) {
141 142
	int ret;
	
143 144 145 146 147 148 149 150 151 152 153 154
	if(audioOutput->open) {
		if(cmpAudioFormat(audioFormat, &audioOutput->inAudioFormat) 
				== 0) 
		{
			return 0;
		}
		closeAudioOutput(audioOutput);
	}

	copyAudioFormat(&audioOutput->inAudioFormat, audioFormat);

	if(audioOutput->convertAudioFormat) {
155 156
		copyAudioFormat(&audioOutput->outAudioFormat,
				&audioOutput->reqAudioFormat);
157 158 159 160 161 162
	}
	else {
		copyAudioFormat(&audioOutput->outAudioFormat, 
				&audioOutput->inAudioFormat);
	}

163 164 165 166 167 168 169 170 171 172
	ret = audioOutput->openDeviceFunc(audioOutput);

	if(cmpAudioFormat(&audioOutput->inAudioFormat, 
			  &audioOutput->outAudioFormat) == 0)
	{
		audioOutput->sameInAndOutFormats = 1;
	}
	else audioOutput->sameInAndOutFormats = 0;

	return ret;
173 174
}

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
static void convertAudioFormat(AudioOutput * audioOutput, char ** chunkArgPtr,
		int * sizeArgPtr)
{
	int size = pcm_sizeOfOutputBufferForAudioFormatConversion(
			&(audioOutput->inAudioFormat), *sizeArgPtr, 
			&(audioOutput->outAudioFormat));

	if(size > audioOutput->convBufferLen) {
		audioOutput->convBuffer = 
				realloc(audioOutput->convBuffer, size);
		audioOutput->convBufferLen = size;
	}

	pcm_convertAudioFormat(&(audioOutput->inAudioFormat), *chunkArgPtr, 
			*sizeArgPtr, &(audioOutput->outAudioFormat), 
			audioOutput->convBuffer);
	
	*sizeArgPtr = size;
	*chunkArgPtr = audioOutput->convBuffer;
}

196
int playAudioOutput(AudioOutput * audioOutput, char * playChunk, int size) {
197 198
	int ret;

Warren Dukes's avatar
Warren Dukes committed
199
	if(!audioOutput->open) return -1;
200 201 202 203

	if(!audioOutput->sameInAndOutFormats) {
		convertAudioFormat(audioOutput, &playChunk, &size);
	}
204 205 206 207 208


	ret = audioOutput->playFunc(audioOutput, playChunk, size);

	return ret;
209 210
}

211 212 213 214
void dropBufferedAudioOutput(AudioOutput * audioOutput) {
	if(audioOutput->open) audioOutput->dropBufferedAudioFunc(audioOutput);
}

215
void closeAudioOutput(AudioOutput * audioOutput) {
Warren Dukes's avatar
Warren Dukes committed
216
	if(audioOutput->open) audioOutput->closeDeviceFunc(audioOutput);
217 218 219
}

void finishAudioOutput(AudioOutput * audioOutput) {
Warren Dukes's avatar
Warren Dukes committed
220
	closeAudioOutput(audioOutput);
221
	audioOutput->finishDriverFunc(audioOutput);
222
	if(audioOutput->convBuffer) free(audioOutput->convBuffer);
223 224
	free(audioOutput->type);
	free(audioOutput->name);
225 226
	free(audioOutput);
}
227 228

void sendMetadataToAudioOutput(AudioOutput * audioOutput, MpdTag * tag) {
229
	if(!audioOutput->sendMetdataFunc) return;
230 231
	audioOutput->sendMetdataFunc(audioOutput, tag);
}