+ int w, h;
+ Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (get_pango_context ());
+
+ // calc geometry of numerics
+ const float dbfs = accurate_coefficient_to_dB (p->peak);
+ const float dbtp = accurate_coefficient_to_dB (p->truepeak);
+
+#define TXTSIZE(LINE, TXT, FONT) { \
+ layout->set_font_description (UIConfiguration::instance ().FONT ()); \
+ layout->set_text (TXT); \
+ layout->get_pixel_size (w, h); \
+ if (w > mnw) { mnw = w; } \
+ if (h > lin[LINE]) { lin[LINE] = h; } \
+}
+
+#define TXTWIDTH(TXT, FONT) { \
+ layout->set_font_description (UIConfiguration::instance ().FONT ()); \
+ layout->set_text (TXT); \
+ layout->get_pixel_size (w, h); \
+ if (w > mml) { mml = w; } \
+}
+
+ int m_r = 0; // right side
+ int mnh = 0; // mono height
+ int mnw = 0; // max numeric width
+ int anw = 0; // spectrum annotation text width
+
+ int lin[6] = { 0, 0, 0, 0, 0, 0 }; // max line height
+
+ TXTSIZE(0, _("(too short integration time)"), get_SmallFont);
+
+ TXTSIZE(0, _("-888"), get_SmallMonospaceFont);
+ anw = w;
+ m_r = anw + 10;
+ mnh = h + 1;
+
+ TXTSIZE(0, _("Peak:"), get_SmallFont);
+ TXTSIZE(1, string_compose (_("%1 dBFS"), std::setprecision (1), std::fixed, dbfs), get_LargeFont);
+ TXTSIZE(2, _("True Peak:"), get_SmallFont);
+ TXTSIZE(3, string_compose (_("%1 dBTP"), std::setprecision (1), std::fixed, dbtp), get_LargeFont);
+ TXTSIZE(4, _("Normalization Gain:"), get_SmallFont);
+ TXTSIZE(5, _("+888.88 dB"), get_SmallMonospaceFont);
+
+ TXTSIZE(0, _("Integrated Loudness:"), get_SmallFont);
+ TXTSIZE(1, string_compose (_("%1 LUFS"), std::setprecision (1), std::fixed, p->loudness), get_LargeFont);
+ TXTSIZE(2, _("Loudness Range:"), get_SmallFont);
+ TXTSIZE(3, string_compose (_("%1 LU"), std::setprecision (1), std::fixed, p->loudness_range), get_LargeFont);
+
+ mnw += 8;
+ const int ht = lin[0] * 1.25 + lin[1] * 1.25 + lin[2] * 1.25 + lin[3] *1.25 + lin[4] * 1.25 + lin[5];
+ const int hh = std::max (100, ht + 12);
+ const int htn = lin[0] * 1.25 + lin[1] * 1.25 + lin[2] * 1.25 + lin[3];
+ int m_l = 2 * mnw + /*hist-width*/ 540 + /*box spacing*/ 8 - /*peak-width*/ 800 - m_r; // margin left
+
+ int mml = 0; // min margin left -- ensure left margin is wide enough
+ TXTWIDTH (_("Time"), get_SmallFont);
+ TXTWIDTH (_("100"), get_SmallMonospaceFont);
+ m_l = (std::max(anw + mnh + 14, std::max (m_l, mml + 8)) + 3) & ~3;
+
+ mnw = (m_l - /*hist-width*/ 540 - /*box spacing*/ 8 + /*peak-width*/ 800 + m_r) / 2;
+ const int nw2 = mnw / 2; // nums, horizontal center
+
+ int y0[6];
+ if (p->normalized) {
+ y0[0] = (hh - ht) * .5;
+ } else {
+ y0[0] = (hh - htn) * .5;
+ }
+ y0[1] = y0[0] + lin[0] * 1.25;
+ y0[2] = y0[1] + lin[1] * 1.25;
+ y0[3] = y0[2] + lin[2] * 1.25;
+ y0[4] = y0[3] + lin[3] * 1.25;
+ y0[5] = y0[4] + lin[4] * 1.25;
+
+ /* calc heights & alignment of png-image */
+ const float specth = sizeof (p->spectrum[0]) / sizeof (float);
+ const float waveh2 = std::min (100, 8 * lin[0] / (int) p->n_channels);
+
+ Cairo::RefPtr<Cairo::ImageSurface> png_surface;
+ int png_w = 0;
+ int png_y0 = 0;
+
+ if (with_file && UIConfiguration::instance().get_save_export_analysis_image ()) { /*png image */
+ const int top_w = 540 + 2 * (mnw + 4); // 4px spacing
+ const int wav_w = m_l + m_r + 4 + sizeof (p->peaks) / sizeof (ARDOUR::PeakData::PeakDatum) / 4;
+ const int spc_w = m_l + m_r + 4 + sizeof (p->spectrum) / sizeof (float) / specth;
+ int ann_h = 0;
+ int linesp = 0;
+
+ if (channels > 0 && file_length > 0 && sample_rate > 0) {
+ layout->set_font_description (UIConfiguration::instance ().get_SmallMonospaceFont ());
+ layout->set_text (_("00:00:00.000"));
+ layout->get_pixel_size (w, h);
+ int height = h * 1.75;
+ ann_h = 4 + height /* Time Axis */;
+
+ layout->set_font_description (UIConfiguration::instance ().get_SmallFont ());
+ layout->set_text (_("0|A8"));
+ layout->get_pixel_size (w, h);
+ linesp = h * 1.5;
+ ann_h += 4 + 3 * linesp; /* File Info */;
+ }
+
+ const int png_h = hh + 4 + p->n_channels * (2 * waveh2 + 4) + ann_h + specth + 4;
+ png_w = std::max (std::max (top_w, wav_w), spc_w);
+
+ png_surface = Cairo::ImageSurface::create (Cairo::FORMAT_RGB24, png_w, png_h);
+ Cairo::RefPtr<Cairo::Context> pcx = Cairo::Context::create (png_surface);
+ pcx->set_source_rgb (.2, .2, .2);
+ pcx->paint ();
+
+ if (channels > 0 && file_length > 0 && sample_rate > 0) {
+ png_y0 += 4;
+ // Add file-name, format, duration, sample-rate & timecode
+ pcx->set_source_rgb (.7, .7, .7);
+ layout->set_font_description (UIConfiguration::instance ().get_SmallFont ());
+ layout->set_alignment (Pango::ALIGN_LEFT);
+
+#define IMGLABEL(X0, STR, VAL) { \
+ layout->set_text (STR); \
+ pcx->move_to (X0, png_y0); \
+ layout->get_pixel_size (w, h); \
+ layout->show_in_cairo_context (pcx); \
+ layout->set_text (VAL); \
+ pcx->move_to (X0 + w + 2, png_y0); \
+ layout->show_in_cairo_context (pcx); \
+}
+
+ // TODO get max width of labels per column, right-align labels, x-align 1/3, 2/3 columns
+ const int lx0 = m_l;
+ const int lx1 = m_l + png_w / 2;
+
+ IMGLABEL (lx0, _("File:"), Glib::path_get_basename (path));
+ IMGLABEL (lx1, _("Channels:"), string_compose ("%1", channels));
+ png_y0 += linesp;
+
+ IMGLABEL (lx0, _("Format:"), file_fmt);
+ IMGLABEL (lx1, _("Sample rate:"), string_compose (_("%1 Hz"), sample_rate));
+ png_y0 += linesp;
+
+ if (_session) {
+ Timecode::Time tct;
+ _session->sample_to_timecode (start_off, tct, false, false);
+ IMGLABEL (lx0, _("Timecode:"), Timecode::timecode_format_time (tct));
+ }
+ IMGLABEL (lx1, _("Duration:"), Timecode::timecode_format_sampletime (file_length, sample_rate, 1000, false));
+ png_y0 += linesp;
+ }
+ }
+
+ { /* peak, loudness and R128 histogram */
+ Cairo::RefPtr<Cairo::ImageSurface> nums = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, mnw, hh);
+ Cairo::RefPtr<Cairo::ImageSurface> ebur = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, mnw, hh);
+ Cairo::RefPtr<Cairo::ImageSurface> hist = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, 540, hh);
+
+ /* peak and true-peak numerics */
+ Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create (nums);
+ cr->set_source_rgba (0, 0, 0, 1.0);
+ cr->paint ();
+
+ layout->set_font_description (UIConfiguration::instance ().get_SmallFont ());
+ layout->set_alignment (Pango::ALIGN_LEFT);
+ layout->set_text (_("Peak:"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[0]);
+ cr->set_source_rgba (.7, .7, .7, 1.0);
+ layout->show_in_cairo_context (cr);
+
+ layout->set_font_description (UIConfiguration::instance ().get_LargeFont ());
+ layout->set_text (string_compose (_("%1 dBFS"), std::setprecision (1), std::fixed, dbfs));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[1]);
+ if (dbfs >= 0.f) { cr->set_source_rgba (1.0, .1, .1, 1.0); }
+ else if (dbfs > -1.f) { cr->set_source_rgba (1.0, .7, .0, 1.0); }
+ layout->show_in_cairo_context (cr);
+
+ if (p->have_dbtp) {
+ layout->set_font_description (UIConfiguration::instance ().get_SmallFont ());
+ layout->set_text (_("True Peak:"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[2]);
+ cr->set_source_rgba (.7, .7, .7, 1.0);
+ layout->show_in_cairo_context (cr);
+
+ layout->set_font_description (UIConfiguration::instance ().get_LargeFont ());
+ layout->set_text (string_compose (_("%1 dBTP"), std::setprecision (1), std::fixed, dbtp));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[3]);
+ if (dbtp >= 0.f) { cr->set_source_rgba (1.0, .1, .1, 1.0); }
+ else if (dbtp > -1.f) { cr->set_source_rgba (1.0, .7, .0, 1.0); }
+ layout->show_in_cairo_context (cr);
+ }
+
+ if (p->normalized) {
+ const float ndb = accurate_coefficient_to_dB (p->norm_gain_factor);
+ layout->set_font_description (UIConfiguration::instance ().get_SmallFont ());
+ layout->set_text (_("Normalization Gain:"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[4]);
+ cr->set_source_rgba (.7, .7, .7, 1.0);
+ layout->show_in_cairo_context (cr);
+
+ layout->set_font_description (UIConfiguration::instance ().get_SmallMonospaceFont ());
+ layout->set_text (string_compose (_("%1 dB"), std::setprecision (2), std::showpos, std::fixed, ndb));
+
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[5]);
+ // TODO tweak thresholds
+ if (p->norm_gain_factor < 1.0) {
+ cr->set_source_rgba (1.0, .7, .1, 1.0);
+ } else if (p->norm_gain_factor == 1.0) {
+ cr->set_source_rgba (.7, .7, .7, 1.0);
+ } else if (fabsf (ndb) < 12) {
+ cr->set_source_rgba (.1, 1.0, .1, 1.0);
+ } else if (fabsf (ndb) < 18) {
+ cr->set_source_rgba (1.0, .7, .1, 1.0);
+ } else {
+ cr->set_source_rgba (1.0, .1, .1, 1.0);
+ }
+ layout->show_in_cairo_context (cr);
+ }
+
+ nums->flush ();
+
+ /* EBU R128 numerics */
+ cr = Cairo::Context::create (ebur);
+ cr->set_source_rgba (0, 0, 0, 1.0);
+ cr->paint ();
+
+ cr->set_source_rgba (.7, .7, .7, 1.0);
+
+ if (!i->second->have_loudness) {
+ layout->set_alignment (Pango::ALIGN_CENTER);
+ layout->set_font_description (UIConfiguration::instance ().get_LargeFont ());
+ layout->set_text (_("Not\nAvailable"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), rint ((hh - h) * .5));
+ layout->show_in_cairo_context (cr);
+ }
+ else if (p->loudness == -200 && p->loudness_range == 0) {
+ layout->set_alignment (Pango::ALIGN_CENTER);
+ layout->set_font_description (UIConfiguration::instance ().get_LargeFont ());
+ layout->set_text (_("Not\nAvailable"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), rint (hh * .5 - h * .66));
+ layout->show_in_cairo_context (cr);
+ int yy = h * .5;
+
+ layout->set_font_description (UIConfiguration::instance ().get_SmallFont ());
+ layout->set_text (_("(too short integration time)"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), rint (hh * .5 + yy));
+ layout->show_in_cairo_context (cr);
+
+ } else {
+ layout->set_font_description (UIConfiguration::instance ().get_SmallFont ());
+ layout->set_alignment (Pango::ALIGN_LEFT);
+ layout->set_text (_("Integrated Loudness:"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[0]);
+ layout->show_in_cairo_context (cr);
+
+ layout->set_font_description (UIConfiguration::instance ().get_LargeFont ());
+ layout->set_text (string_compose (_("%1 LUFS"), std::setprecision (1), std::fixed, p->loudness));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[1]);
+ layout->show_in_cairo_context (cr);
+
+ layout->set_font_description (UIConfiguration::instance ().get_SmallFont ());
+ layout->set_text (_("Loudness Range:"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[2]);
+ layout->show_in_cairo_context (cr);
+
+ layout->set_font_description (UIConfiguration::instance ().get_LargeFont ());
+ layout->set_text (string_compose (_("%1 LU"), std::setprecision (1), std::fixed, p->loudness_range));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint (nw2 - w * .5), y0[3]);
+ layout->show_in_cairo_context (cr);
+ }
+ ebur->flush ();
+
+ /* draw loudness histogram */
+ cr = Cairo::Context::create (hist);
+ cr->set_source_rgba (0, 0, 0, 1.0);
+ cr->paint ();
+
+ cr->set_source_rgba (.7, .7, .7, 1.0);
+ cr->set_line_width (1.0);
+
+ if (p->loudness_hist_max > 0 && i->second->have_loudness) {
+ // draw data
+ for (size_t x = 0 ; x < 510; ++x) {
+ cr->move_to (x - .5, hh);
+ cr->line_to (x - .5, (float) hh * (1.0 - p->loudness_hist[x] / (float) p->loudness_hist_max));
+ cr->stroke ();
+ }
+
+ layout->set_font_description (UIConfiguration::instance ().get_SmallerFont ());
+ layout->set_alignment (Pango::ALIGN_CENTER);
+
+ // x-axis label
+ layout->set_text (_("LUFS\n(short)"));
+ layout->get_pixel_size (w, h);
+ Gtkmm2ext::rounded_rectangle (cr, 5, 5, w + 2, h + 2, 4);
+ cr->set_source_rgba (.1, .1, .1, 0.7);
+ cr->fill ();
+ cr->move_to (6, 6);
+ cr->set_source_rgba (.9, .9, .9, 1.0);
+ layout->show_in_cairo_context (cr);
+
+ // y-axis label
+ layout->set_text (_("Multiplicity"));
+ layout->get_pixel_size (w, h);
+ Gtkmm2ext::rounded_rectangle (cr, 5, hh - w - 3, h + 2, w + 2, 4);
+ cr->set_source_rgba (.1, .1, .1, 0.7);
+ cr->fill ();
+ cr->save ();
+ cr->move_to (6, hh - 2);
+ cr->set_source_rgba (.9, .9, .9, 1.0);
+ cr->rotate (M_PI / -2.0);
+ layout->show_in_cairo_context (cr);
+ cr->restore ();
+
+ // x-Axis labels
+ layout->set_font_description (UIConfiguration::instance ().get_SmallMonospaceFont ());
+ layout->set_alignment (Pango::ALIGN_LEFT);
+ for (int g = -53; g <= -8; g += 5) {
+ // grid-lines. [110] -59LUFS .. [650]: -5 LUFS
+ layout->set_text (string_compose ("%1", std::setw(3), std::setfill(' '), g));
+ layout->get_pixel_size (w, h);
+
+ cr->set_operator (Cairo::OPERATOR_OVER);
+ Gtkmm2ext::rounded_rectangle (cr,
+ rint ((g + 59.0) * 10.0 - h * .5), 5,
+ h + 2, w + 2, 4);
+ const float pk = (g + 59.0) / 54.0;
+ ArdourCanvas::Color c = ArdourCanvas::hsva_to_color (252 - 260 * pk, .9, .3 + pk * .4, .6);
+ ArdourCanvas::set_source_rgba (cr, c);
+ cr->fill ();
+
+ cr->save ();
+ cr->set_source_rgba (.9, .9, .9, 1.0);
+ cr->move_to (rint ((g + 59.0) * 10.0 - h * .5), w + 6.0);
+ cr->rotate (M_PI / -2.0);
+ layout->show_in_cairo_context (cr);
+ cr->restore ();
+
+ cr->set_operator (Cairo::OPERATOR_ADD);
+ cr->save ();
+ cr->set_source_rgba (.3, .3, .3, 1.0);
+ cr->set_dash (dashes, 1.0);
+ cr->set_line_cap (Cairo::LINE_CAP_ROUND);
+ cr->move_to (rint ((g + 59.0) * 10.0) + .5, w + 8.0);
+ cr->line_to (rint ((g + 59.0) * 10.0) + .5, hh);
+ cr->stroke ();
+ cr->restore ();
+ }
+
+ } else {
+ layout->set_alignment (Pango::ALIGN_CENTER);
+ layout->set_font_description (UIConfiguration::instance ().get_LargeFont ());
+ layout->set_text (_("Not\nAvailable"));
+ layout->get_pixel_size (w, h);
+ cr->move_to (rint ((510 - w) * .5), rint ((hh - h) * .5));
+ layout->show_in_cairo_context (cr);
+ }
+
+ hist->flush ();
+
+ if (png_surface) {
+ Cairo::RefPtr<Cairo::Context> pcx = Cairo::Context::create (png_surface);
+ pcx->set_source (nums, 0, png_y0);
+ pcx->paint ();
+ pcx->set_source (hist, (png_w - 540) / 2, png_y0);
+ pcx->paint ();
+ pcx->set_source (ebur, png_w - mnw, png_y0);
+ pcx->paint ();
+ png_y0 += hh + 4;
+ }
+
+ CimgArea *nu = manage (new CimgArea (nums));
+ CimgArea *eb = manage (new CimgArea (ebur));
+ CimgArea *hi = manage (new CimgArea (hist));
+ HBox *hb = manage (new HBox ());
+ hb->set_spacing (4);
+ hb->pack_start (*nu, false, false);
+ hb->pack_start (*hi, false, false);
+ hb->pack_start (*eb, false, false);
+
+ wtbl->attach (*hb, 0, 2, wrow, wrow + 1, SHRINK, SHRINK);
+ ++wrow;
+ }
+
+#define XAXISLABEL(POS, TXT) { \
+ const float yy = rint (POS); \
+ layout->set_text (TXT); \
+ layout->get_pixel_size (w, h); \
+ cr->move_to (m_l - 8 - w, rint ((POS) - h * .5)); \
+ cr->set_source_rgba (.9, .9, .9, 1.0); \
+ cr->set_operator (Cairo::OPERATOR_OVER); \
+ layout->show_in_cairo_context (cr); \
+ cr->move_to (m_l - 4, yy - .5); \
+ cr->line_to (m_l + width, yy - .5); \
+ cr->set_source_rgba (.3, .3, .3, 1.0); \
+ cr->set_operator (Cairo::OPERATOR_ADD); \
+ cr->stroke (); \
+}
+
+