Draw rulers

This commit is contained in:
Alexander Kojevnikov 2012-08-16 00:36:30 -07:00
parent f41b33fbac
commit 144d1fa2cf
5 changed files with 118 additions and 94 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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 ();
}
}
}