mirror of
https://github.com/alexkay/spek.git
synced 2025-04-16 16:42:19 +03:00
Draw rulers
This commit is contained in:
parent
f41b33fbac
commit
144d1fa2cf
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user