2 Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic 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 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
21 /** @file src/wx/audio_mapping_view.cc
22 * @brief AudioMappingView class and helpers.
25 #include "audio_mapping_view.h"
27 #include "audio_gain_dialog.h"
28 #include "lib/audio_mapping.h"
30 #include <dcp/locale_convert.h>
31 #include <dcp/types.h>
33 #include <wx/renderer.h>
35 #include <wx/graphics.h>
36 #include <boost/foreach.hpp>
47 using boost::shared_ptr;
48 using boost::optional;
49 using dcp::locale_convert;
51 #define INDICATOR_SIZE 16
52 #define GRID_SPACING 24
53 #define LEFT_WIDTH (GRID_SPACING * 3)
54 #define TOP_HEIGHT (GRID_SPACING * 2)
63 AudioMappingView::AudioMappingView (wxWindow* parent)
64 : wxScrolledWindow (parent, wxID_ANY)
69 _menu->Append (ID_off, _("Off"));
70 _menu->Append (ID_full, _("Full"));
71 _menu->Append (ID_minus6dB, _("-6dB"));
72 _menu->Append (ID_edit, _("Edit..."));
74 Bind (wxEVT_PAINT, boost::bind(&AudioMappingView::paint, this));
75 Bind (wxEVT_MENU, boost::bind(&AudioMappingView::off, this), ID_off);
76 Bind (wxEVT_LEFT_DOWN, boost::bind(&AudioMappingView::left_down, this, _1));
77 Bind (wxEVT_RIGHT_DOWN, boost::bind(&AudioMappingView::right_down, this, _1));
78 Bind (wxEVT_MOTION, boost::bind(&AudioMappingView::motion, this, _1));
79 Bind (wxEVT_MENU, boost::bind(&AudioMappingView::full, this), ID_full);
80 Bind (wxEVT_MENU, boost::bind(&AudioMappingView::minus6dB, this), ID_minus6dB);
81 Bind (wxEVT_MENU, boost::bind(&AudioMappingView::edit, this), ID_edit);
83 SetScrollRate (GRID_SPACING, GRID_SPACING);
87 AudioMappingView::paint ()
91 wxGraphicsContext* gc = wxGraphicsContext::Create (dc);
97 GetViewStart (&sx, &sy);
98 gc->Translate (-sx * GRID_SPACING, -sy * GRID_SPACING);
99 dc.SetLogicalOrigin (sx * GRID_SPACING, sy * GRID_SPACING);
101 int const output_channels_width = _output_channels.size() * GRID_SPACING;
102 int const input_channels_height = _input_channels.size() * GRID_SPACING;
104 gc->SetAntialiasMode (wxANTIALIAS_DEFAULT);
105 wxGraphicsPath lines = gc->CreatePath ();
106 dc.SetFont (wxSWISS_FONT->Bold());
108 wxCoord label_height;
110 /* DCP label at the top */
112 dc.GetTextExtent (_("DCP"), &label_width, &label_height);
113 dc.DrawText (_("DCP"), LEFT_WIDTH + (output_channels_width - label_width) / 2, (GRID_SPACING - label_height) / 2);
115 /* Content label on the left */
117 dc.GetTextExtent (_("Content"), &label_width, &label_height);
120 (GRID_SPACING - label_height) / 2,
121 TOP_HEIGHT + (input_channels_height + label_width) / 2,
125 dc.SetFont (*wxSWISS_FONT);
126 gc->SetPen (*wxBLACK_PEN);
128 /* Column labels and some lines */
131 BOOST_FOREACH (string i, _output_channels) {
132 dc.GetTextExtent (std_to_wx(i), &label_width, &label_height);
133 dc.DrawText (std_to_wx(i), LEFT_WIDTH + GRID_SPACING * N + (GRID_SPACING - label_width) / 2, GRID_SPACING + (GRID_SPACING - label_height) / 2);
134 lines.MoveToPoint (LEFT_WIDTH + GRID_SPACING * N, GRID_SPACING);
135 lines.AddLineToPoint (LEFT_WIDTH + GRID_SPACING * N, TOP_HEIGHT + _input_channels.size() * GRID_SPACING);
138 lines.MoveToPoint (LEFT_WIDTH + GRID_SPACING * N, GRID_SPACING);
139 lines.AddLineToPoint (LEFT_WIDTH + GRID_SPACING * N, TOP_HEIGHT + _input_channels.size() * GRID_SPACING);
141 /* Horizontal lines at the top */
143 lines.MoveToPoint (LEFT_WIDTH, GRID_SPACING);
144 lines.AddLineToPoint (LEFT_WIDTH + output_channels_width, GRID_SPACING);
145 lines.MoveToPoint (LEFT_WIDTH, GRID_SPACING * 2);
146 lines.AddLineToPoint (LEFT_WIDTH + output_channels_width, GRID_SPACING * 2);
148 /* Row channel labels */
151 BOOST_FOREACH (string i, _input_channels) {
152 dc.GetTextExtent (std_to_wx(i), &label_width, &label_height);
153 dc.DrawText (std_to_wx(i), GRID_SPACING * 2 + (GRID_SPACING - label_width) / 2, TOP_HEIGHT + GRID_SPACING * N + (GRID_SPACING - label_height) / 2);
154 lines.MoveToPoint (GRID_SPACING * 2, TOP_HEIGHT + GRID_SPACING * N);
155 lines.AddLineToPoint (LEFT_WIDTH + output_channels_width, TOP_HEIGHT + GRID_SPACING * N);
158 lines.MoveToPoint (GRID_SPACING * 2, TOP_HEIGHT + GRID_SPACING * N);
159 lines.AddLineToPoint (LEFT_WIDTH + output_channels_width, TOP_HEIGHT + GRID_SPACING * N);
161 /* Vertical lines on the left */
163 for (int i = 1; i < 3; ++i) {
164 lines.MoveToPoint (GRID_SPACING * i, TOP_HEIGHT);
165 lines.AddLineToPoint (GRID_SPACING * i, TOP_HEIGHT + _input_channels.size() * GRID_SPACING);
168 /* Group labels and lines */
171 BOOST_FOREACH (Group i, _input_groups) {
172 dc.GetTextExtent (std_to_wx(i.name), &label_width, &label_height);
173 int const height = (i.to - i.from + 1) * GRID_SPACING;
176 GRID_SPACING + (GRID_SPACING - label_height) / 2,
177 y + (height + label_width) / 2,
180 lines.MoveToPoint (GRID_SPACING, y);
181 lines.AddLineToPoint (GRID_SPACING * 2, y);
185 lines.MoveToPoint (GRID_SPACING, y);
186 lines.AddLineToPoint (GRID_SPACING * 2, y);
188 gc->StrokePath (lines);
192 for (size_t x = 0; x < _output_channels.size(); ++x) {
193 for (size_t y = 0; y < _input_channels.size(); ++y) {
194 dc.SetBrush (*wxWHITE_BRUSH);
197 LEFT_WIDTH + x * GRID_SPACING + (GRID_SPACING - INDICATOR_SIZE) / 2,
198 TOP_HEIGHT + y * GRID_SPACING + (GRID_SPACING - INDICATOR_SIZE) / 2,
199 INDICATOR_SIZE, INDICATOR_SIZE
203 float const value_dB = 20 * log10 (_map.get(y, x));
204 int const range = 18;
206 if (value_dB > -range) {
207 height = INDICATOR_SIZE * (1 + value_dB / range);
210 dc.SetBrush (*wxTheBrushList->FindOrCreateBrush(wxColour (0, 255, 0), wxBRUSHSTYLE_SOLID));
213 LEFT_WIDTH + x * GRID_SPACING + (GRID_SPACING - INDICATOR_SIZE) / 2,
214 TOP_HEIGHT + y * GRID_SPACING + (GRID_SPACING - INDICATOR_SIZE) / 2 + INDICATOR_SIZE - height,
215 INDICATOR_SIZE, height
224 optional<pair<int, int> >
225 AudioMappingView::mouse_event_to_channels (wxMouseEvent& ev) const
228 GetViewStart (&sx, &sy);
229 int const x = ev.GetX() + sx * GRID_SPACING;
230 int const y = ev.GetY() + sy * GRID_SPACING;
232 if (x <= LEFT_WIDTH || y < TOP_HEIGHT) {
233 return optional<pair<int, int> >();
236 int const input = (y - TOP_HEIGHT) / GRID_SPACING;
237 int const output = (x - LEFT_WIDTH) / GRID_SPACING;
239 if (input >= int(_input_channels.size()) || output >= int(_output_channels.size())) {
240 return optional<pair<int, int> >();
243 return make_pair (input, output);
247 AudioMappingView::left_down (wxMouseEvent& ev)
249 optional<pair<int, int> > channels = mouse_event_to_channels (ev);
254 if (_map.get(channels->first, channels->second) > 0) {
255 _map.set (channels->first, channels->second, 0);
257 _map.set (channels->first, channels->second, 1);
260 map_values_changed ();
264 AudioMappingView::right_down (wxMouseEvent& ev)
266 optional<pair<int, int> > channels = mouse_event_to_channels (ev);
271 _menu_input = channels->first;
272 _menu_output = channels->second;
273 PopupMenu (_menu, ev.GetPosition());
276 /** Called when any gain value has changed */
278 AudioMappingView::map_values_changed ()
281 _last_tooltip_channels = optional<pair<int, int> >();
286 AudioMappingView::off ()
288 _map.set (_menu_input, _menu_output, 0);
289 map_values_changed ();
293 AudioMappingView::full ()
295 _map.set (_menu_input, _menu_output, 1);
296 map_values_changed ();
300 AudioMappingView::minus6dB ()
302 _map.set (_menu_input, _menu_output, pow (10, -6.0 / 20));
303 map_values_changed ();
307 AudioMappingView::edit ()
309 int const d = _menu_output - 1;
311 AudioGainDialog* dialog = new AudioGainDialog (this, _menu_input, _menu_output - 1, _map.get(_menu_input, d));
312 if (dialog->ShowModal() == wxID_OK) {
313 _map.set (_menu_input, d, dialog->value ());
314 map_values_changed ();
321 AudioMappingView::set_virtual_size ()
323 SetVirtualSize (LEFT_WIDTH + _output_channels.size() * GRID_SPACING, TOP_HEIGHT + _input_channels.size() * GRID_SPACING);
327 AudioMappingView::set (AudioMapping map)
334 AudioMappingView::set_input_channels (vector<string> const & names)
336 _input_channels = names;
342 AudioMappingView::set_output_channels (vector<string> const & names)
344 _output_channels = names;
350 AudioMappingView::motion (wxMouseEvent& ev)
352 optional<pair<int, int> > channels = mouse_event_to_channels (ev);
355 _last_tooltip_channels = channels;
359 if (channels != _last_tooltip_channels) {
361 float const gain = _map.get(channels->first, channels->second);
363 s = wxString::Format (_("No audio will be passed from content channel %d to DCP channel %d."), channels->first, channels->second);
364 } else if (gain == 1) {
365 s = wxString::Format (_("Audio will be passed from content channel %d to DCP channel %d unaltered."), channels->first, channels->second);
367 float const dB = 20 * log10 (gain);
368 s = wxString::Format (_("Audio will be passed from content channel %d to DCP channel %d with gain %.1fdB."), channels->first, channels->second, dB);
371 SetToolTip (s + " " + _("Right click to change gain."));
372 _last_tooltip_channels = channels;
379 AudioMappingView::set_input_groups (vector<Group> const & groups)
381 _input_groups = groups;