2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Helge Norberg, helge.norberg@svt.se
22 #include "../../stdafx.h"
24 #include "audio_util.h"
26 #include <boost/algorithm/string/split.hpp>
27 #include <boost/algorithm/string.hpp>
28 #include <boost/thread/mutex.hpp>
29 #include <boost/foreach.hpp>
30 #include <boost/assign.hpp>
31 #include <boost/lexical_cast.hpp>
32 #include <boost/property_tree/exceptions.hpp>
34 namespace caspar { namespace core {
36 bool channel_layout::operator==(const channel_layout& other) const
38 return channel_names == other.channel_names
39 && num_channels == other.num_channels;
42 int channel_layout::channel_index(const std::wstring& channel_name) const
45 std::find(channel_names.begin(), channel_names.end(), channel_name);
47 if (iter == channel_names.end())
50 return iter - channel_names.begin();
53 bool channel_layout::has_channel(const std::wstring& channel_name) const
55 return channel_index(channel_name) != -1;
58 bool channel_layout::no_channel_names() const
60 return channel_names.empty();
63 bool needs_rearranging(
64 const channel_layout& source, const channel_layout& destination)
66 if ((source.no_channel_names() || destination.no_channel_names())
67 && source.num_channels == destination.num_channels)
70 return !(source == destination);
73 struct channel_layout_repository::impl
75 std::map<std::wstring, const channel_layout> layouts;
79 channel_layout_repository::channel_layout_repository()
84 channel_layout_repository::~channel_layout_repository()
88 void channel_layout_repository::register_layout(const channel_layout& layout)
90 boost::unique_lock<boost::mutex> lock(impl_->mutex);
92 impl_->layouts.erase(layout.name);
93 impl_->layouts.insert(std::make_pair(layout.name, layout));
96 const channel_layout& channel_layout_repository::get_by_name(
97 const std::wstring& layout_name) const
99 boost::unique_lock<boost::mutex> lock(impl_->mutex);
101 auto iter = impl_->layouts.find(layout_name);
103 if (iter == impl_->layouts.end())
104 BOOST_THROW_EXCEPTION(invalid_argument()
105 << msg_info(narrow(layout_name) + " not found"));
110 channel_layout create_layout_from_string(
111 const std::wstring& name,
112 const std::wstring& layout_type,
114 const std::wstring& channels)
116 channel_layout layout;
118 layout.name = boost::to_upper_copy(name);
119 layout.layout_type = boost::to_upper_copy(layout_type);
120 auto upper_channels = boost::to_upper_copy(channels);
122 if (channels.length() > 0)
124 layout.channel_names,
126 boost::is_any_of(L"\t "),
127 boost::token_compress_on);
129 layout.num_channels = num_channels == -1
130 ? layout.channel_names.size() : num_channels;
135 channel_layout create_unspecified_layout(int num_channels)
137 channel_layout layout;
139 layout.name = L"UNORDERED" + boost::lexical_cast<std::wstring>(
140 num_channels) + L"CH";
141 layout.layout_type = L"UNORDERED";
142 layout.num_channels = num_channels;
147 void register_default_channel_layouts(channel_layout_repository& repository)
149 repository.register_layout(create_layout_from_string(
150 L"mono", L"1.0", 1, L"C"));
151 repository.register_layout(create_layout_from_string(
152 L"stereo", L"2.0", 2, L"L R"));
153 repository.register_layout(create_layout_from_string(
154 L"dts", L"5.1", 6, L"C L R Ls Rs LFE"));
155 repository.register_layout(create_layout_from_string(
156 L"dolbye", L"5.1+stereomix", 8, L"L R C LFE Ls Rs Lmix Rmix"
158 repository.register_layout(create_layout_from_string(
159 L"dolbydigital", L"5.1", 6, L"L C R Ls Rs LFE"));
160 repository.register_layout(create_layout_from_string(
161 L"smpte", L"5.1", 6, L"L R C LFE Ls Rs"));
162 repository.register_layout(create_layout_from_string(
163 L"passthru", L"16ch", 16, L""));
166 void parse_channel_layouts(
167 channel_layout_repository& repository,
168 const boost::property_tree::wptree& layouts_element)
170 BOOST_FOREACH(auto& layout, layouts_element)
172 repository.register_layout(create_layout_from_string(
174 layout.second.get<std::wstring>(L"type"),
175 layout.second.get<int>(L"num-channels"),
176 layout.second.get<std::wstring>(L"channels")));
180 channel_layout_repository& default_channel_layout_repository()
182 static channel_layout_repository repository;
187 struct mix_config_repository::impl
189 std::map<std::wstring, std::map<std::wstring, const mix_config>> configs;
193 mix_config_repository::mix_config_repository()
198 mix_config_repository::~mix_config_repository()
202 void mix_config_repository::register_mix_config(const mix_config& config)
204 boost::unique_lock<boost::mutex> lock(impl_->mutex);
206 impl_->configs[config.from_layout_type].erase(config.to_layout_type);
207 impl_->configs[config.from_layout_type].insert(
208 std::make_pair(config.to_layout_type, config));
211 boost::optional<mix_config> mix_config_repository::get_mix_config(
212 const std::wstring& from_layout_type,
213 const std::wstring& to_layout_type) const
215 boost::unique_lock<boost::mutex> lock(impl_->mutex);
217 auto iter = impl_->configs[from_layout_type].find(to_layout_type);
219 if (iter == impl_->configs[from_layout_type].end())
220 return boost::optional<mix_config>();
225 mix_config create_mix_config_from_string(
226 const std::wstring& from_layout_type,
227 const std::wstring& to_layout_type,
228 mix_config::mix_strategy strategy,
229 const std::vector<std::wstring>& mappings)
232 config.from_layout_type = boost::to_upper_copy(from_layout_type);
233 config.to_layout_type = boost::to_upper_copy(to_layout_type);
234 config.strategy = strategy;
236 BOOST_FOREACH(auto& mapping, mappings)
238 auto upper_mapping = boost::to_upper_copy(mapping);
239 std::vector<std::wstring> words;
243 boost::is_any_of(L"\t "),
244 boost::token_compress_on);
246 if (words.size() != 3)
247 BOOST_THROW_EXCEPTION(invalid_argument() << msg_info(
248 "mix_config mapping string must have 3 tokens"));
250 auto from = words.at(0);
251 auto to = words.at(1);
252 auto influence = boost::lexical_cast<double>(words.at(2));
254 config.destination_ch_by_source_ch.insert(std::make_pair(
256 mix_config::destination(to, influence)));
262 void register_default_mix_configs(mix_config_repository& repository)
264 using namespace boost::assign;
267 repository.register_mix_config(create_mix_config_from_string(
268 L"1.0", L"2.0", mix_config::add, list_of
272 repository.register_mix_config(create_mix_config_from_string(
273 L"1.0", L"5.1", mix_config::add, list_of
277 repository.register_mix_config(create_mix_config_from_string(
278 L"1.0", L"5.1+stereomix", mix_config::add, list_of
285 repository.register_mix_config(create_mix_config_from_string(
286 L"2.0", L"1.0", mix_config::add, list_of
290 repository.register_mix_config(create_mix_config_from_string(
291 L"2.0", L"5.1", mix_config::add, list_of
295 repository.register_mix_config(create_mix_config_from_string(
296 L"2.0", L"5.1+stereomix", mix_config::add, list_of
303 repository.register_mix_config(create_mix_config_from_string(
304 L"5.1", L"1.0", mix_config::average, list_of
311 repository.register_mix_config(create_mix_config_from_string(
312 L"5.1", L"2.0", mix_config::average, list_of
320 repository.register_mix_config(create_mix_config_from_string(
321 L"5.1", L"5.1+stereomix", mix_config::average, list_of
336 // From 5.1+stereomix
337 repository.register_mix_config(create_mix_config_from_string(
338 L"5.1+stereomix", L"1.0", mix_config::add, list_of
342 repository.register_mix_config(create_mix_config_from_string(
343 L"5.1+stereomix", L"2.0", mix_config::add, list_of
347 repository.register_mix_config(create_mix_config_from_string(
348 L"5.1+stereomix", L"5.1", mix_config::add, list_of
358 void parse_mix_configs(
359 mix_config_repository& repository,
360 const boost::property_tree::wptree& channel_mixings_element)
362 BOOST_FOREACH(auto element, channel_mixings_element)
364 if (element.first != L"mix-config")
366 throw boost::property_tree::ptree_error(
367 "Expected mix-config element");
370 std::vector<std::wstring> mappings;
373 element.second.get_child(L"mappings"),
374 std::insert_iterator<std::vector<std::wstring>>(
375 mappings, mappings.begin()),
377 const std::pair<std::wstring,
378 boost::property_tree::wptree>& mapping)
380 return mapping.second.get_value<std::wstring>();
383 repository.register_mix_config(create_mix_config_from_string(
384 element.second.get<std::wstring>(L"from"),
385 element.second.get<std::wstring>(L"to"),
386 boost::to_upper_copy(element.second.get<std::wstring>(
387 L"mix", L"ADD")) == L"AVERAGE"
388 ? mix_config::average : mix_config::add,
393 mix_config_repository& default_mix_config_repository()
395 static mix_config_repository repository;
400 channel_layout create_custom_channel_layout(
401 const std::wstring& custom_channel_order,
402 const channel_layout_repository& repository)
404 std::vector<std::wstring> splitted;
407 custom_channel_order,
408 boost::is_any_of(L":"),
409 boost::token_compress_on);
411 if (splitted.size() == 1) // Named layout
415 return repository.get_by_name(splitted[0]);
417 catch (const std::exception&)
419 CASPAR_LOG_CURRENT_EXCEPTION();
420 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(
421 "CHANNEL_LAYOUT must be in a format like: "
422 "\"5.1:L R C LFE Ls Rs\" or like \"SMPTE\""));
426 if (splitted.size() != 2)
427 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(
428 "CHANNEL_LAYOUT must be in a format like: "
429 "\"5.1:L R C LFE Ls Rs\" or like \"SMPTE\""));
432 return create_layout_from_string(
433 custom_channel_order,