commit 144d1fa2cf2c83d491d1c3774a3e9c3941193253
parent f41b33fbac5143b0af4c357539c9a2460f1b76df
Author: Alexander Kojevnikov <alexander@kojevnikov.com>
Date: Thu, 16 Aug 2012 00:36:30 -0700
Draw rulers
Diffstat:
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 ();
- }
}
}