spek

Acoustic spectrum analyser
git clone http://git.hanabi.in/repos/spek.git
Log | Files | Refs | README

commit 3ecdad345927a15e2c39cddab1bd843287cd0802
parent 063d0433c0dd2e92cd9e1f84b3006a29a105fe1e
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date:   Mon, 13 Aug 2012 21:38:48 -0700

Paint the palette

Diffstat:
Msrc/Makefile.am | 2++
Asrc/spek-spectrogram.cc | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/spek-spectrogram.hh | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/spek-spectrogram.vala | 124-------------------------------------------------------------------------------
Msrc/spek-window.cc | 12+++++++++++-
Msrc/spek-window.hh | 6+++++-
Msrc/spek-window.vala | 16----------------
Msrc/spek.cc | 8++++++--
8 files changed, 239 insertions(+), 144 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am @@ -10,6 +10,8 @@ spek_SOURCES = \ spek-platform.hh \ spek-preferences.cc \ spek-preferences.hh \ + spek-spectrogram.cc \ + spek-spectrogram.hh \ spek-window.cc \ spek-window.hh diff --git a/src/spek-spectrogram.cc b/src/spek-spectrogram.cc @@ -0,0 +1,170 @@ +/* spek-spectrogram.cc + * + * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com> + * + * Spek 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 3 of the License, or + * (at your option) any later version. + * + * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <wx/dcbuffer.h> + +#include "spek-spectrogram.hh" + +BEGIN_EVENT_TABLE(SpekSpectrogram, wxPanel) + EVT_PAINT(SpekSpectrogram::OnPaint) +END_EVENT_TABLE() + +enum +{ + THRESHOLD = -92, + NFFT = 2048, + BANDS = NFFT / 2 + 1, + LPAD = 60, + TPAD = 60, + RPAD = 80, + BPAD = 40, + GAP = 10, + RULER = 10, +}; + +SpekSpectrogram::SpekSpectrogram(wxFrame *parent) : + wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), + palette(RULER, BANDS) +{ + SetBackgroundStyle(wxBG_STYLE_CUSTOM); + + // Pre-draw the palette. + for (int y = 0; y < BANDS; y++) { + uint32_t color = GetColor(y / (double) BANDS); + this->palette.SetRGB( + wxRect(0, BANDS - y - 1, RULER, 1), + color >> 16, + (color >> 8) & 0xFF, + color & 0xFF + ); + } +} + +void SpekSpectrogram::Open(const wxString& path) +{ + this->path = path; + Start(); +} + +void SpekSpectrogram::Save(const wxString& path) +{ +} + +void SpekSpectrogram::OnPaint(wxPaintEvent& evt) +{ + wxAutoBufferedPaintDC dc(this); + Render(dc); +} + +void SpekSpectrogram::Render(wxDC& dc) +{ + wxSize size = GetClientSize(); + int w = size.GetWidth(); + int h = size.GetHeight(); + + // Initialise. + dc.SetBackground(*wxBLACK_BRUSH); + dc.SetBackgroundMode(wxTRANSPARENT); + dc.SetPen(*wxWHITE_PEN); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.SetTextForeground(wxColour(255, 255, 255)); + wxFont normal = wxFont(9, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); + wxFont large = wxFont(normal); + large.SetPointSize(10); + large.SetWeight(wxFONTWEIGHT_BOLD); + wxFont small = wxFont(normal); + small.SetPointSize(8); + dc.SetFont(normal); + int normal_height = dc.GetTextExtent(wxT("dummy")).GetHeight(); + dc.SetFont(large); + int large_height = dc.GetTextExtent(wxT("dummy")).GetHeight(); + + // Clean the background. + dc.Clear(); + + // Spek version + dc.SetFont(large); + wxString package_name(wxT(PACKAGE_NAME)); + dc.DrawText( + package_name, + w - RPAD + GAP, + TPAD - 2 * GAP - normal_height - large_height + ); + int package_name_width = dc.GetTextExtent(package_name + wxT(" ")).GetWidth(); + dc.SetFont(normal); + dc.DrawText( + wxT(PACKAGE_VERSION), + w - RPAD + GAP + package_name_width, + TPAD - 2 * GAP - 2 * normal_height + ); + + // Border around the spectrogram. + // TODO: check if this uses antialiasing + dc.DrawRectangle(LPAD, TPAD, w - LPAD - RPAD, h - TPAD - BPAD); + + // The palette. + wxBitmap bmp(this->palette.Scale(RULER, h - TPAD - BPAD + 1 /*TODO:, wxIMAGE_QUALITY_HIGH*/)); + dc.DrawBitmap(bmp, w - RPAD + GAP, TPAD); +} + +void SpekSpectrogram::Start() +{ +} + +// Modified version of Dan Bruton's algorithm: +// http://www.physics.sfasu.edu/astro/color/spectra.html +// TODO: Move out to a C function. +uint32_t SpekSpectrogram::GetColor(double level) +{ + level *= 0.6625; + double r = 0.0, g = 0.0, b = 0.0; + if (level >= 0 && level < 0.15) { + r = (0.15 - level) / (0.15 + 0.075); + g = 0.0; + b = 1.0; + } else if (level >= 0.15 && level < 0.275) { + r = 0.0; + g = (level - 0.15) / (0.275 - 0.15); + b = 1.0; + } else if (level >= 0.275 && level < 0.325) { + r = 0.0; + g = 1.0; + b = (0.325 - level) / (0.325 - 0.275); + } else if (level >= 0.325 && level < 0.5) { + r = (level - 0.325) / (0.5 - 0.325); + g = 1.0; + b = 0.0; + } else if (level >= 0.5 && level < 0.6625) { + r = 1.0; + g = (0.6625 - level) / (0.6625 - 0.5f); + b = 0.0; + } + + // Intensity correction. + double cf = 1.0; + if (level >= 0.0 && level < 0.1) { + cf = level / 0.1; + } + cf *= 255.0; + + // Pack RGB values into a 32-bit uint. + uint32_t rr = (uint32_t) (r * cf + 0.5); + uint32_t gg = (uint32_t) (g * cf + 0.5); + uint32_t bb = (uint32_t) (b * cf + 0.5); + return (rr << 16) + (gg << 8) + bb; +} diff --git a/src/spek-spectrogram.hh b/src/spek-spectrogram.hh @@ -0,0 +1,45 @@ +/* spek-spectrogram.hh + * + * Copyright (C) 2010-2012 Alexander Kojevnikov <alexander@kojevnikov.com> + * + * Spek 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 3 of the License, or + * (at your option) any later version. + * + * Spek 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 Spek. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef SPEK_SPECTROGRAM_HH_ +#define SPEK_SPECTROGRAM_HH_ + +#include <wx/wx.h> + +class SpekSpectrogram : public wxPanel +{ +public: + SpekSpectrogram(wxFrame *parent); + void Open(const wxString& path); + void Save(const wxString& path); + +private: + void OnPaint(wxPaintEvent& evt); + void Render(wxDC& dc); + + void Start(); + uint32_t GetColor(double level); + + wxString path; + wxString info; + wxImage palette; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/src/spek-spectrogram.vala b/src/spek-spectrogram.vala @@ -24,42 +24,11 @@ using Pango; namespace Spek { class Spectrogram : DrawingArea { - public string file_name { get; private set; } private Pipeline pipeline; - private string info; - private const int THRESHOLD = -92; - private const int NFFT = 2048; - private const int BANDS = NFFT / 2 + 1; private ImageSurface image; - private ImageSurface palette; - - private const int LPAD = 60; - private const int TPAD = 60; - private const int RPAD = 80; - private const int BPAD = 40; - private const int GAP = 10; - private const int RULER = 10; - private double FONT_SCALE = Platform.get_font_scale (); public Spectrogram () { - // Pre-draw the palette. - palette = new ImageSurface (Format.RGB24, RULER, BANDS); - for (int y = 0; y < BANDS; y++) { - var color = get_color (y / (double) BANDS); - for (int x = 0; x < RULER; x++) { - put_pixel (palette, x, y, color); - } - } - show_all (); - } - - public void open (string file_name) { - this.file_name = file_name; - this.info = ""; - - start (); - } public void save (string file_name) { Allocation allocation; @@ -128,37 +97,8 @@ namespace Spek { } private void draw (Cairo.Context cr) { - Allocation allocation; - get_allocation (out allocation); - double w = allocation.width; - double h = allocation.height; int text_width, text_height; - // Clean the background. - cr.set_source_rgb (0, 0, 0); - cr.paint (); - - // Spek version - cr.set_source_rgb (1, 1, 1); - var layout = cairo_create_layout (cr); - layout.set_font_description (FontDescription.from_string ( - "Sans " + (9 * FONT_SCALE).to_string ())); - layout.set_width (RPAD * Pango.SCALE); - layout.set_text ("dummy", -1); - layout.get_pixel_size (out text_width, out text_height); - int line_height = text_height; - layout.set_font_description (FontDescription.from_string ( - "Sans Bold " + (10 * FONT_SCALE).to_string ())); - layout.set_text (Config.PACKAGE_NAME + " ", -1); - layout.get_pixel_size (out text_width, out text_height); - cr.move_to (w - RPAD + GAP, TPAD - 2 * GAP - line_height); - cairo_show_layout_line (cr, layout.get_line (0)); - layout.set_font_description (FontDescription.from_string ( - "Sans " + (9 * FONT_SCALE).to_string ())); - layout.set_text (Config.PACKAGE_VERSION, -1); - cr.rel_move_to (text_width, 0); - cairo_show_layout_line (cr, layout.get_line (0)); - if (image != null) { // Draw the spectrogram. cr.translate (LPAD, h - BPAD); @@ -228,20 +168,6 @@ namespace Spek { cairo_show_layout_line (cr, layout.get_line (0)); } - // Border around the spectrogram. - cr.set_source_rgb (1, 1, 1); - cr.set_line_width (1); - cr.set_antialias (Antialias.NONE); - cr.rectangle (LPAD, TPAD, w - LPAD - RPAD, h - TPAD - BPAD); - cr.stroke (); - - // The palette. - cr.translate (w - RPAD + GAP, h - BPAD); - cr.scale (1, -(h - TPAD - BPAD + 1) / palette.get_height ()); - cr.set_source_surface (palette, 0, 0); - cr.paint (); - cr.identity_matrix (); - // Prepare to draw the ruler. cr.set_source_rgb (1, 1, 1); cr.set_line_width (1); @@ -265,55 +191,5 @@ namespace Spek { density_ruler.draw (cr, layout); cr.identity_matrix (); } - - private void put_pixel (ImageSurface surface, int x, int y, uint32 color) { - var i = y * surface.get_stride () + x * 4; - unowned uchar[] data = surface.get_data (); - - // Translate uchar* to uint32* to avoid dealing with endianness. - uint32 *p = (uint32 *) (&data[i]); - *p = color; - } - - // Modified version of Dan Bruton's algorithm: - // http://www.physics.sfasu.edu/astro/color/spectra.html - private uint32 get_color (double level) { - level *= 0.6625; - double r = 0.0, g = 0.0, b = 0.0; - if (level >= 0 && level < 0.15) { - r = (0.15 - level) / (0.15 + 0.075); - g = 0.0; - b = 1.0; - } else if (level >= 0.15 && level < 0.275) { - r = 0.0; - g = (level - 0.15) / (0.275 - 0.15); - b = 1.0; - } else if (level >= 0.275 && level < 0.325) { - r = 0.0; - g = 1.0; - b = (0.325 - level) / (0.325 - 0.275); - } else if (level >= 0.325 && level < 0.5) { - r = (level - 0.325) / (0.5 - 0.325); - g = 1.0; - b = 0.0; - } else if (level >= 0.5 && level < 0.6625) { - r = 1.0; - g = (0.6625 - level) / (0.6625 - 0.5f); - b = 0.0; - } - - // Intensity correction. - double cf = 1.0; - if (level >= 0.0 && level < 0.1) { - cf = level / 0.1; - } - cf *= 255.0; - - // Pack RGB values into Cairo-happy format. - uint32 rr = (uint32) (r * cf + 0.5); - uint32 gg = (uint32) (g * cf + 0.5); - uint32 bb = (uint32) (b * cf + 0.5); - return (rr << 16) + (gg << 8) + bb; - } } } diff --git a/src/spek-window.cc b/src/spek-window.cc @@ -19,6 +19,8 @@ #include <wx/artprov.h> #include <wx/filename.h> +#include "spek-spectrogram.hh" + #include "spek-window.hh" BEGIN_EVENT_TABLE(SpekWindow, wxFrame) @@ -29,7 +31,7 @@ BEGIN_EVENT_TABLE(SpekWindow, wxFrame) EVT_MENU(wxID_ABOUT, SpekWindow::OnAbout) END_EVENT_TABLE() -SpekWindow::SpekWindow() : wxFrame(NULL, -1, wxEmptyString) +SpekWindow::SpekWindow(const wxString& path) : wxFrame(NULL, -1, wxEmptyString) { SetTitle(_("Spek - Acoustic Spectrum Analyser")); // TODO: test on all platforms @@ -75,6 +77,12 @@ SpekWindow::SpekWindow() : wxFrame(NULL, -1, wxEmptyString) wxArtProvider::GetBitmap(wxART_FILE_SAVE) ); toolbar->Realize(); + + this->spectrogram = new SpekSpectrogram(this); + + if (!path.IsEmpty()) { + Open(path); + } } // TODO: s/audio/media/ @@ -176,5 +184,7 @@ void SpekWindow::Open(const wxString& path) wxString title = wxString::Format(_("Spek - %s"), full_name.c_str()); // TODO: make sure the above works on all platforms, both in x32 and x64. SetTitle(title); + + this->spectrogram->Open(path); } } diff --git a/src/spek-window.hh b/src/spek-window.hh @@ -21,10 +21,12 @@ #include <wx/wx.h> +class SpekSpectrogram; + class SpekWindow : public wxFrame { public: - SpekWindow(); + SpekWindow(const wxString& path); private: void OnOpen(wxCommandEvent& event); @@ -35,6 +37,8 @@ private: void Open(const wxString& path); + SpekSpectrogram *spectrogram; + DECLARE_EVENT_TABLE() }; diff --git a/src/spek-window.vala b/src/spek-window.vala @@ -50,17 +50,13 @@ namespace Spek { info_bar.message_type = MessageType.INFO; info_bar.response.connect(() => info_bar.hide()); - spectrogram = new Spectrogram (); - filter_png = new FileFilter (); filter_png.set_name (_("PNG images")); filter_png.add_pattern ("*.png"); var vbox = new VBox (false, 0); vbox.pack_start (info_bar, false, true, 0); - vbox.pack_start (spectrogram, true, true, 0); add (vbox); - spectrogram.show_all (); vbox.show (); show (); @@ -69,10 +65,6 @@ namespace Spek { drag_dest_set (this, DestDefaults.ALL, DEST_TARGET_ENTRIES, DragAction.COPY); drag_data_received.connect (on_dropped); - if (file_name != null) { - open_file (file_name); - } - try { Thread.create<void*> (check_version, false); } catch (ThreadError e) { @@ -93,10 +85,6 @@ namespace Spek { drag_finish (cx, false, false, time); } - private void open_file (string file_name) { - spectrogram.open (file_name); - } - private void on_file_save () { var chooser = new FileChooserDialog ( _("Save Spectrogram"), this, FileChooserAction.SAVE, @@ -119,10 +107,6 @@ namespace Spek { chooser.destroy (); } - private void on_file_quit () { - destroy (); - } - private void on_edit_preferences () { var dlg = new PreferencesDialog (); dlg.transient_for = this; diff --git a/src/spek.cc b/src/spek.cc @@ -26,10 +26,12 @@ class Spek: public wxApp { -protected: +private: virtual bool OnInit(); virtual void OnInitCmdLine(wxCmdLineParser& parser); virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + + wxString path; }; IMPLEMENT_APP(Spek) @@ -43,7 +45,7 @@ bool Spek::OnInit() return false; } - SpekWindow *window = new SpekWindow(); + SpekWindow *window = new SpekWindow(this->path); window->Show(true); SetTopWindow(window); return true; @@ -89,5 +91,7 @@ bool Spek::OnCmdLineParsed(wxCmdLineParser& parser) return false; } + this->path = parser.GetParam(); + return true; }