Convert.cxx 3.59 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2021 The Music Player Daemon Project
3
 * http://www.musicpd.org
4 5 6 7 8 9 10 11 12 13
 *
 * 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.
14 15 16 17
 *
 * 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.
18 19
 */

20
#include "Convert.hxx"
21
#include "ConfiguredResampler.hxx"
22
#include "util/ConstBuffer.hxx"
23

24
#include <cassert>
25 26
#include <stdexcept>

27
void
28
pcm_convert_global_init(const ConfigData &config)
29
{
30
	pcm_resampler_global_init(config);
31 32
}

33
PcmConvert::PcmConvert(const AudioFormat _src_format,
34 35
		       const AudioFormat dest_format)
	:src_format(_src_format)
36
{
37 38
	assert(src_format.IsValid());
	assert(dest_format.IsValid());
39

40
	AudioFormat format = _src_format;
41 42
	if (format.format == SampleFormat::DSD) {
#ifdef ENABLE_DSD
43 44 45 46
		dsd2pcm_float = dest_format.format == SampleFormat::FLOAT;
		format.format = dsd2pcm_float
			? SampleFormat::FLOAT
			: SampleFormat::S24_P32;
47 48 49 50
#else
		throw std::runtime_error("DSD support is disabled");
#endif
	}
51

52
	enable_resampler = format.sample_rate != dest_format.sample_rate;
53
	if (enable_resampler) {
54
		resampler.Open(format, dest_format.sample_rate);
55 56

		format.format = resampler.GetOutputSampleFormat();
57
		format.sample_rate = dest_format.sample_rate;
58 59
	}

60
	enable_format = format.format != dest_format.format;
61 62 63
	if (enable_format) {
		try {
			format_converter.Open(format.format,
64
					      dest_format.format);
65 66 67 68 69
		} catch (...) {
			if (enable_resampler)
				resampler.Close();
			throw;
		}
70 71
	}

72
	format.format = dest_format.format;
73

74
	enable_channels = format.channels != dest_format.channels;
75 76 77
	if (enable_channels) {
		try {
			channels_converter.Open(format.format, format.channels,
78
						dest_format.channels);
79 80 81 82 83 84 85
		} catch (...) {
			if (enable_format)
				format_converter.Close();
			if (enable_resampler)
				resampler.Close();
			throw;
		}
86
	}
87 88
}

89
PcmConvert::~PcmConvert() noexcept
90
{
91
	if (enable_channels)
92
		channels_converter.Close();
93
	if (enable_format)
94
		format_converter.Close();
95 96
	if (enable_resampler)
		resampler.Close();
97

98
#ifdef ENABLE_DSD
Max Kellermann's avatar
Max Kellermann committed
99
	dsd.Reset();
100
#endif
101 102
}

103
void
Max Kellermann's avatar
Max Kellermann committed
104
PcmConvert::Reset() noexcept
105 106 107 108 109 110 111 112 113
{
	if (enable_resampler)
		resampler.Reset();

#ifdef ENABLE_DSD
	dsd.Reset();
#endif
}

114
ConstBuffer<void>
115
PcmConvert::Convert(ConstBuffer<void> buffer)
116
{
117
#ifdef ENABLE_DSD
118
	if (src_format.format == SampleFormat::DSD) {
119
		auto s = ConstBuffer<uint8_t>::FromVoid(buffer);
120 121 122
		auto d = dsd2pcm_float
			? dsd.ToFloat(src_format.channels, s).ToVoid()
			: dsd.ToS24(src_format.channels, s).ToVoid();
123 124
		if (d.IsNull())
			throw std::runtime_error("DSD to PCM conversion failed");
125

126
		buffer = d;
127
	}
128
#endif
129

130 131
	if (enable_resampler)
		buffer = resampler.Resample(buffer);
132

133 134
	if (enable_format)
		buffer = format_converter.Convert(buffer);
135

136 137
	if (enable_channels)
		buffer = channels_converter.Convert(buffer);
138

139
	return buffer;
140
}
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

ConstBuffer<void>
PcmConvert::Flush()
{
	if (enable_resampler) {
		auto buffer = resampler.Flush();
		if (!buffer.IsNull()) {
			if (enable_format)
				buffer = format_converter.Convert(buffer);

			if (enable_channels)
				buffer = channels_converter.Convert(buffer);

			return buffer;
		}
	}

	return nullptr;
}