spek

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

commit 144d1fa2cf2c83d491d1c3774a3e9c3941193253
parent f41b33fbac5143b0af4c357539c9a2460f1b76df
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date:   Thu, 16 Aug 2012 00:36:30 -0700

Draw rulers

Diffstat:
Msrc/spek-ruler.cc | 37+++++++++++++++++++++++--------------
Msrc/spek-ruler.hh | 17++++++++++++-----
Msrc/spek-spectrogram.cc | 82++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/spek-spectrogram.hh | 2++
Msrc/spek-spectrogram.vala | 74--------------------------------------------------------------------------
5 files changed, 118 insertions(+), 94 deletions(-)

diff --git a/src/spek-ruler.cc b/src/spek-ruler.cc @@ -16,10 +16,18 @@ * along with Spek. If not, see <http://www.gnu.org/licenses/>. */ +#include <cmath> + #include "spek-ruler.hh" -SpekRuler::SpekRuler(Position pos, wxString sample_label, int *factors, int units, double spacing) : - pos(pos), sample_label(sample_label), factors(factors), units(units), spacing(spacing) +SpekRuler::SpekRuler( + int x, int y, Position pos, wxString sample_label, + int *factors, int units, double spacing, + double scale, double offset, formatter_cb formatter) + : + x(x), y(y), pos(pos), sample_label(sample_label), + factors(factors), units(units), spacing(spacing), + scale(scale), offset(offset), formatter(formatter) { } @@ -32,7 +40,7 @@ void SpekRuler::draw(wxDC& dc) // Select the factor to use, we want some space between the labels. int factor = 0; for (int i = 0; factors[i]; ++i) { - if (this->measure(factors[i]) >= this->spacing * len) { + if (fabs(this->scale * factors[i]) >= this->spacing * len) { factor = factors[i]; break; } @@ -44,7 +52,7 @@ void SpekRuler::draw(wxDC& dc) if (factor > 0) { for (int tick = factor; tick < units; tick += factor) { - if (this->measure(units - tick) < len * 1.2) { + if (fabs(this->scale * (units - tick)) < len * 1.2) { break; } this->draw_tick(dc, tick); @@ -57,29 +65,30 @@ void SpekRuler::draw_tick(wxDC& dc, int tick) double GAP = 10; double TICK_LEN = 4; - wxString label = format(tick); - double p = place(measure(this->pos == TOP || this->pos == BOTTOM ? tick : this->units - tick)); + wxString label = this->formatter(tick); + int value = this->pos == TOP || this->pos == BOTTOM ? tick : this->units - tick; + double p = this->offset + this->scale * value; wxSize size = dc.GetTextExtent(label); int w = size.GetWidth(); int h = size.GetHeight(); if (this->pos == TOP) { - dc.DrawText(label, p - w / 2, -GAP - h); + dc.DrawText(label, this->x + p - w / 2, this->y - GAP - h); } else if (this->pos == RIGHT){ - dc.DrawText(label, GAP, p + h / 4); + dc.DrawText(label, this->x + GAP, this->y + p - h / 2); } else if (this->pos == BOTTOM) { - dc.DrawText(label, p - w / 2, GAP + h); + dc.DrawText(label, this->x + p - w / 2, this->y + GAP); } else if (this->pos == LEFT){ - dc.DrawText(label, -w - GAP, p + h / 4); + dc.DrawText(label, this->x - w - GAP, this->y + p - h / 2); } if (this->pos == TOP) { - dc.DrawLine(p, 0, p, -TICK_LEN); + dc.DrawLine(this->x + p, this->y, this->x + p, this->y - TICK_LEN); } else if (this->pos == RIGHT) { - dc.DrawLine(0, p, TICK_LEN, p); + dc.DrawLine(this->x, this->y + p, this->x + TICK_LEN, this->y + p); } else if (this->pos == BOTTOM) { - dc.DrawLine(p, 0, p, TICK_LEN); + dc.DrawLine(this->x + p, this->y, this->x + p, this->y + TICK_LEN); } else if (this->pos == LEFT) { - dc.DrawLine(0, p, -TICK_LEN, p); + dc.DrawLine(this->x, this->y + p, this->x - TICK_LEN, this->y + p); } } diff --git a/src/spek-ruler.hh b/src/spek-ruler.hh @@ -33,22 +33,29 @@ public: LEFT }; - SpekRuler(Position pos, wxString sample_label, int *factors, int units, double spacing); + typedef wxString (*formatter_cb)(int unit); + + SpekRuler( + int x, int y, Position pos, wxString sample_label, + int *factors, int units, double spacing, + double scale, double offset, formatter_cb formatter + ); void draw(wxDC& dc); protected: - virtual double measure(int unit) = 0; - virtual double place(double p) = 0; - virtual wxString format(int unit) = 0; - void draw_tick(wxDC& dc, int tick); + int x; + int y; Position pos; wxString sample_label; int *factors; int units; double spacing; + double scale; + double offset; + formatter_cb formatter; }; #endif diff --git a/src/spek-spectrogram.cc b/src/spek-spectrogram.cc @@ -19,8 +19,10 @@ #include <cmath> #include <wx/dcbuffer.h> +#include "spek-audio.h" #include "spek-audio-desc.hh" #include "spek-pipeline.h" +#include "spek-ruler.hh" #include "spek-spectrogram.hh" @@ -46,6 +48,7 @@ enum SpekSpectrogram::SpekSpectrogram(wxFrame *parent) : wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), pipeline(NULL), + properties(NULL), palette(RULER, BANDS), image(1, 1), prev_width(-1) @@ -96,6 +99,22 @@ void SpekSpectrogram::on_size(wxSizeEvent& evt) } } +static wxString time_formatter(int unit) +{ + // TODO: i18n + return wxString::Format(wxT("%d:%02d"), unit / 60, unit % 60); +} + +static wxString freq_formatter(int unit) +{ + return wxString::Format(_("%d kHz"), unit / 1000); +} + +static wxString density_formatter(int unit) +{ + return wxString::Format(_("%d dB"), -unit); +} + void SpekSpectrogram::render(wxDC& dc) { wxSize size = GetClientSize(); @@ -152,6 +171,45 @@ void SpekSpectrogram::render(wxDC& dc) dc.SetFont(normal_font); // TODO: ellipsize dc.DrawText(this->desc, LPAD, TPAD - GAP - normal_height); + + // Prepare to draw the rulers. + dc.SetFont(small_font); + + // Time ruler. + double duration = this->properties->duration; + int time_factors[] = {1, 2, 5, 10, 20, 30, 1*60, 2*60, 5*60, 10*60, 20*60, 30*60, 0}; + SpekRuler time_ruler( + LPAD, + h - BPAD, + SpekRuler::BOTTOM, + // TODO: i18n + wxT("00:00"), + time_factors, + (int)duration, + 1.5, + (w - LPAD - RPAD) / duration, + 0.0, + time_formatter + ); + time_ruler.draw(dc); + + // Frequency ruler. + int freq = this->properties->sample_rate / 2; + int freq_factors[] = {1000, 2000, 5000, 10000, 20000, 0}; + SpekRuler freq_ruler( + LPAD, + TPAD, + SpekRuler::LEFT, + // TRANSLATORS: keep "00" unchanged, it's used to calc the text width + _("00 kHz"), + freq_factors, + freq, + 3.0, + (h - TPAD - BPAD) / (double)freq, + 0.0, + freq_formatter + ); + freq_ruler.draw(dc); } // Border around the spectrogram. @@ -161,6 +219,26 @@ void SpekSpectrogram::render(wxDC& dc) // The palette. wxBitmap bmp(this->palette.Scale(RULER, h - TPAD - BPAD + 1 /*TODO:, wxIMAGE_QUALITY_HIGH*/)); dc.DrawBitmap(bmp, w - RPAD + GAP, TPAD); + + // Prepare to draw the ruler. + dc.SetFont(small_font); + + // Spectral density. + int density_factors[] = {1, 2, 5, 10, 20, 50, 0}; + SpekRuler density_ruler( + w - RPAD + GAP + RULER, + TPAD, + SpekRuler::RIGHT, + // TRANSLATORS: keep "-00" unchanged, it's used to calc the text width + _("-00 dB"), + density_factors, + -THRESHOLD, + 3.0, + (h - TPAD - BPAD) / (double)THRESHOLD, + h - TPAD - BPAD, + density_formatter + ); + density_ruler.draw(dc); } void SpekSpectrogram::pipeline_cb(int sample, float *values, void *cb_data) @@ -191,6 +269,7 @@ void SpekSpectrogram::start() if (this->pipeline) { spek_pipeline_close(this->pipeline); this->pipeline = NULL; + this->properties = NULL; } // The number of samples is the number of pixels available for the image. @@ -209,7 +288,8 @@ void SpekSpectrogram::start() this ); spek_pipeline_start(this->pipeline); - this->desc = spek_audio_desc(spek_pipeline_properties(this->pipeline)); + this->properties = spek_pipeline_properties(this->pipeline); + this->desc = spek_audio_desc(this->properties); } else { this->image.Create(1, 1); } diff --git a/src/spek-spectrogram.hh b/src/spek-spectrogram.hh @@ -22,6 +22,7 @@ #include <stdint.h> #include <wx/wx.h> +struct spek_audio_properties; struct spek_pipeline; class SpekSpectrogram : public wxPanel @@ -42,6 +43,7 @@ private: static uint32_t get_color(double level); spek_pipeline *pipeline; + const spek_audio_properties *properties; wxString path; wxString desc; wxImage palette; diff --git a/src/spek-spectrogram.vala b/src/spek-spectrogram.vala @@ -24,10 +24,6 @@ using Pango; namespace Spek { class Spectrogram : DrawingArea { - private ImageSurface image; - - public Spectrogram () { - public void save (string file_name) { Allocation allocation; get_allocation (out allocation); @@ -35,75 +31,5 @@ namespace Spek { draw (new Cairo.Context (surface)); surface.write_to_png (file_name); } - - private void draw (Cairo.Context cr) { - int text_width, text_height; - - if (image != null) { - // Prepare to draw the rulers. - cr.set_source_rgb (1, 1, 1); - cr.set_line_width (1); - cr.set_antialias (Antialias.NONE); - layout.set_font_description (FontDescription.from_string ( - "Sans " + (8 * FONT_SCALE).to_string ())); - layout.set_width (-1); - - // Time ruler. - var duration_seconds = (int) pipeline.duration; - var time_ruler = new Ruler ( - Ruler.Position.BOTTOM, - // TODO: i18n - "00:00", - {1, 2, 5, 10, 20, 30, 1*60, 2*60, 5*60, 10*60, 20*60, 30*60}, - duration_seconds, - 1.5, - unit => (w - LPAD - RPAD) * unit / duration_seconds, - p => p, - // TODO: i18n - unit => "%d:%02d".printf (unit / 60, unit % 60)); - cr.translate (LPAD, h - BPAD); - time_ruler.draw (cr, layout); - cr.identity_matrix (); - - // Frequency ruler. - var freq = pipeline.sample_rate / 2; - var rate_ruler = new Ruler ( - Ruler.Position.LEFT, - // TRANSLATORS: keep "00" unchanged, it's used to calc the text width - _("00 kHz"), - {1000, 2000, 5000, 10000, 20000}, - freq, - 3.0, - unit => (h - TPAD - BPAD) * unit / freq, - p => p, - unit => _("%d kHz").printf (unit / 1000)); - cr.translate (LPAD, TPAD); - rate_ruler.draw (cr, layout); - cr.identity_matrix (); - } - - // Prepare to draw the ruler. - cr.set_source_rgb (1, 1, 1); - cr.set_line_width (1); - cr.set_antialias (Antialias.NONE); - layout.set_font_description (FontDescription.from_string ( - "Sans " + (8 * FONT_SCALE).to_string ())); - layout.set_width (-1); - - // Spectral density. - var density_ruler = new Ruler ( - Ruler.Position.RIGHT, - // TRANSLATORS: keep "-00" unchanged, it's used to calc the text width - _("-00 dB"), - {1, 2, 5, 10, 20, 50}, - -THRESHOLD, - 3.0, - unit => -(h - TPAD - BPAD) * unit / THRESHOLD, - p => h - TPAD - BPAD - p, - unit => _("%d dB").printf (-unit)); - cr.translate (w - RPAD + GAP + RULER, TPAD); - density_ruler.draw (cr, layout); - cr.identity_matrix (); - } } }