WaveEncoderPlugin.cxx 5.69 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

20
#include "config.h"
21
#include "WaveEncoderPlugin.hxx"
22
#include "../EncoderAPI.hxx"
23
#include "system/ByteOrder.hxx"
24 25
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx"
26 27 28 29

#include <assert.h>
#include <string.h>

30 31
static constexpr uint16_t WAVE_FORMAT_PCM = 1;

32
struct WaveEncoder {
33
	Encoder encoder;
34 35
	unsigned bits;

36
	Manual<DynamicFifoBuffer<uint8_t>> buffer;
37

38
	WaveEncoder():encoder(wave_encoder_plugin) {}
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
};

struct wave_header {
	uint32_t id_riff;
	uint32_t riff_size;
	uint32_t id_wave;
	uint32_t id_fmt;
	uint32_t fmt_size;
	uint16_t format;
	uint16_t channels;
	uint32_t freq;
	uint32_t byterate;
	uint16_t blocksize;
	uint16_t bits;
	uint32_t id_data;
	uint32_t data_size;
};

static void
58
fill_wave_header(struct wave_header *header, int channels, int bits,
59 60 61 62 63
		int freq, int block_size)
{
	int data_size = 0x0FFFFFFF;

	/* constants */
64 65 66 67
	header->id_riff = ToLE32(0x46464952);
	header->id_wave = ToLE32(0x45564157);
	header->id_fmt = ToLE32(0x20746d66);
	header->id_data = ToLE32(0x61746164);
68

69
	/* wave format */
70
	header->format = ToLE16(WAVE_FORMAT_PCM);
71 72 73 74 75
	header->channels = ToLE16(channels);
	header->bits = ToLE16(bits);
	header->freq = ToLE32(freq);
	header->blocksize = ToLE16(block_size);
	header->byterate = ToLE32(freq * block_size);
76

77
	/* chunk sizes (fake data length) */
78 79 80
	header->fmt_size = ToLE32(16);
	header->data_size = ToLE32(data_size);
	header->riff_size = ToLE32(4 + (8 + 16) + (8 + data_size));
81 82
}

83
static Encoder *
84
wave_encoder_init(gcc_unused const config_param &param,
85
		  gcc_unused Error &error)
86
{
87
	WaveEncoder *encoder = new WaveEncoder();
88 89 90 91
	return &encoder->encoder;
}

static void
92
wave_encoder_finish(Encoder *_encoder)
93
{
94
	WaveEncoder *encoder = (WaveEncoder *)_encoder;
95

96
	delete encoder;
97 98 99
}

static bool
100
wave_encoder_open(Encoder *_encoder,
101
		  AudioFormat &audio_format,
102
		  gcc_unused Error &error)
103
{
104
	WaveEncoder *encoder = (WaveEncoder *)_encoder;
105

106
	assert(audio_format.IsValid());
107

108 109
	switch (audio_format.format) {
	case SampleFormat::S8:
110 111 112
		encoder->bits = 8;
		break;

113
	case SampleFormat::S16:
114 115 116
		encoder->bits = 16;
		break;

117
	case SampleFormat::S24_P32:
118 119 120
		encoder->bits = 24;
		break;

121
	case SampleFormat::S32:
122 123 124 125
		encoder->bits = 32;
		break;

	default:
126
		audio_format.format = SampleFormat::S16;
127 128 129
		encoder->bits = 16;
		break;
	}
130

131 132 133 134 135
	encoder->buffer.Construct(8192);

	auto range = encoder->buffer->Write();
	assert(range.size >= sizeof(wave_header));
	wave_header *header = (wave_header *)range.data;
136

137
	/* create PCM wave header in initial buffer */
138
	fill_wave_header(header,
139
			 audio_format.channels,
140
			 encoder->bits,
141 142
			 audio_format.sample_rate,
			 (encoder->bits / 8) * audio_format.channels);
143 144

	encoder->buffer->Append(sizeof(*header));
145 146 147 148

	return true;
}

149
static void
150
wave_encoder_close(Encoder *_encoder)
151
{
152
	WaveEncoder *encoder = (WaveEncoder *)_encoder;
153

154
	encoder->buffer.Destruct();
155 156
}

157
static size_t
158
pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
159
{
160 161
	size_t cnt = length >> 1;
	while (cnt > 0) {
162
		*dst16++ = ToLE16(*src16++);
163
		cnt--;
164 165 166 167
	}
	return length;
}

168
static size_t
169
pcm32_to_wave(uint32_t *dst32, const uint32_t *src32, size_t length)
170
{
171 172
	size_t cnt = length >> 2;
	while (cnt > 0){
173
		*dst32++ = ToLE32(*src32++);
174
		cnt--;
175 176 177 178
	}
	return length;
}

179
static size_t
180
pcm24_to_wave(uint8_t *dst8, const uint32_t *src32, size_t length)
181
{
182 183
	uint32_t value;
	uint8_t *dst_old = dst8;
184

185 186
	length = length >> 2;
	while (length > 0){
187 188 189 190
		value = *src32++;
		*dst8++ = (value) & 0xFF;
		*dst8++ = (value >> 8) & 0xFF;
		*dst8++ = (value >> 16) & 0xFF;
191
		length--;
192 193
	}
	//correct buffer length
194
	return (dst8 - dst_old);
195 196 197
}

static bool
198
wave_encoder_write(Encoder *_encoder,
199
		   const void *src, size_t length,
200
		   gcc_unused Error &error)
201
{
202
	WaveEncoder *encoder = (WaveEncoder *)_encoder;
203

204
	uint8_t *dst = encoder->buffer->Write(length);
205

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
	if (IsLittleEndian()) {
		switch (encoder->bits) {
		case 8:
		case 16:
		case 32:// optimized cases
			memcpy(dst, src, length);
			break;
		case 24:
			length = pcm24_to_wave(dst, (const uint32_t *)src, length);
			break;
		}
	} else {
		switch (encoder->bits) {
		case 8:
			memcpy(dst, src, length);
			break;
		case 16:
			length = pcm16_to_wave((uint16_t *)dst,
					       (const uint16_t *)src, length);
			break;
		case 24:
			length = pcm24_to_wave(dst, (const uint32_t *)src, length);
			break;
		case 32:
			length = pcm32_to_wave((uint32_t *)dst,
					       (const uint32_t *)src, length);
			break;
		}
234 235
	}

236
	encoder->buffer->Append(length);
237 238 239 240
	return true;
}

static size_t
241
wave_encoder_read(Encoder *_encoder, void *dest, size_t length)
242
{
243
	WaveEncoder *encoder = (WaveEncoder *)_encoder;
244

245
	return encoder->buffer->Read((uint8_t *)dest, length);
246 247
}

248
static const char *
249
wave_encoder_get_mime_type(gcc_unused Encoder *_encoder)
250
{
251
	return "audio/wav";
252 253
}

254
const EncoderPlugin wave_encoder_plugin = {
255 256 257 258 259 260 261 262 263 264 265 266
	"wave",
	wave_encoder_init,
	wave_encoder_finish,
	wave_encoder_open,
	wave_encoder_close,
	nullptr,
	nullptr,
	nullptr,
	nullptr,
	wave_encoder_write,
	wave_encoder_read,
	wave_encoder_get_mime_type,
267
};