/* === S Y N F I G ========================================================= */
/*! \file widget_sound.cpp
** \brief Widget Sound Implementation File
**
** $Id$
**
** \legal
** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**
** This package 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 2 of
** the License, or (at your option) any later version.
**
** This package 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.
** \endlegal
*/
/* ========================================================================= */
/* === H E A D E R S ======================================================= */
#ifdef USING_PCH
# include "pch.h"
#else
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <gtkmm/adjustment.h>
#include <ETL/clock>
#include <ETL/misc>
#include <synfig/general.h>
#include "audiocontainer.h"
#include "widget_sound.h"
#include <gui/localization.h>
#endif
/* === U S I N G =========================================================== */
using namespace synfig;
using namespace studio;
/* === M A C R O S ========================================================= */
/* === G L O B A L S ======================================================= */
/* === P R O C E D U R E S ================================================= */
/* === M E T H O D S ======================================================= */
/* === E N T R Y P O I N T ================================================= */
Widget_Sound::Widget_Sound()
{ }
Widget_Sound::~Widget_Sound()
{ }
void
Widget_Sound::set_position(double t)
{
//info("Setting position to %.2lf s", t);
if(adj_timescale && t != adj_timescale->get_value()) {
float upper = adj_timescale->get_upper();
float lower = adj_timescale->get_lower();
float framesize = upper - lower;
if (t < lower) {
lower -= ceil((lower-t)/framesize)*framesize;
upper = lower + framesize;
GlibFreezeNotify freeze(adj_timescale);
adj_timescale->set_lower(lower);
adj_timescale->set_upper(upper);
adj_timescale->set_value(t);
} else
if (t > upper) {
lower += ceil((t-upper)/framesize)*framesize;
upper = lower + framesize;
GlibFreezeNotify freeze(adj_timescale);
adj_timescale->set_lower(lower);
adj_timescale->set_upper(upper);
adj_timescale->set_value(t);
} else {
adj_timescale->set_value(t);
}
}
}
double
Widget_Sound::get_position() const
{
if(adj_timescale)
{
return adj_timescale->get_value();
}
return 0;
}
bool
Widget_Sound::set_profile(etl::handle<AudioProfile> p)
{
clear();
//set the profile
audioprof = p;
if (!audioprof) {
clear();
return false;
}
return true;
}
etl::handle<AudioProfile>
Widget_Sound::get_profile() const
{ return audioprof; }
void
Widget_Sound::clear()
{ audioprof.detach(); }
void
Widget_Sound::draw()
{ queue_draw(); }
bool
Widget_Sound::on_draw(const Cairo::RefPtr<Cairo::Context> &cr)
{
Gdk::RGBA c("#3f3f3f");
int w = get_width();
int baseline = get_height()/2;
cr->set_source_rgb(c.get_red(), c.get_green(), c.get_blue());
cr->rectangle(0.0, 0.0, w, get_height());
cr->fill();
//draw the base line, set up the color to be blue
cr->set_source_rgb(0.0, 0.5, 1.0);
cr->move_to(0,baseline);
cr->line_to(w,baseline);
cr->stroke();
//redraw all the samples from begin to end, but only if we have samples to draw (or there is no space to draw)
//warning("Ok rendered everything, now must render actual sound wave");
if(!audioprof || !adj_timescale || !w)
return true;
//draw you fool!
float framesize = adj_timescale->get_upper() - adj_timescale->get_lower();
if(framesize)
{
float delta=0,cum=0;
//position in sample space
int begin=0,end=0;
int cur=0,maxs=0,mins=0;
//etl::clock check; check.reset();
float position = adj_timescale->get_value();
float samplerate = audioprof->get_samplerate();
int posi = 0;
//enforce position inside of frame size
{
float offset = audioprof->get_offset();
//clamp begin and end to framesize
float beginf = adj_timescale->get_lower();
float endf = adj_timescale->get_upper();
posi = etl::round_to_int((position-beginf)*w/framesize);
//posi = (int)((position-beginf)*w/framesize);
//calculate in sample space from seconds
begin = etl::round_to_int((beginf - offset)*samplerate);
end = etl::round_to_int((endf - offset)*samplerate);
//begin = (int)((beginf - offset)*samplerate);
//end = (int)((endf - offset)*samplerate);
}
delta = (end - begin)/(float)w; //samples per pixel
//warning("Rendering a framesize of %f secs from [%d,%d) samples to %d samples, took %f sec",
// framesize, begin, end, w, check());
cur = begin;
cum = 0;
for(int i=0;i<w;++i)
{
//get the maximum of the collected samples
maxs = 0;
mins = 0;
for(;cum < delta; ++cum, ++cur)
{
maxs = std::max(maxs,(int)(*audioprof)[cur]);
mins = std::min(mins,(int)(*audioprof)[cur]);
}
cum -= delta;
//draw spike if not needed be
if(maxs||mins)
{
int top = maxs * baseline / 64;
int bot = mins * baseline / 64;
cr->set_source_rgb(0.0, 0.5, 1.0);
cr->move_to(i,baseline+bot);
cr->line_to(i,baseline+top);
cr->stroke();
}
}
//warning("Drawing audio line");
cr->set_source_rgb(1.0, 0.0, 0.0);
cr->move_to(posi,0);
cr->line_to(posi,get_height());
cr->stroke();
}
return true;
}
//--- Handle the single clicking and dragging for scrubbing
bool
Widget_Sound::on_motion_notify_event(GdkEventMotion* event)
{
Gdk::ModifierType mod = Gdk::ModifierType(event->state);
//if we are scrubbing
if(mod & Gdk::BUTTON1_MASK)
{
//Can't do this if we don't have a time frame (heheh...)
if(!adj_timescale) return false;
double beg = adj_timescale->get_lower(), end = adj_timescale->get_upper();
//find event position in time
double t = beg + event->x * (end-beg) / get_width();
//signal that we are scrubbing to this new value...
signal_scrub()(t);
// We should be able to just call
// Widget_Timeslider::on_motion_notify_event(),
// but that seems to cause the program to halt
// for some reason. So for now, let's do the job ourselves
//adj_timescale->set_value(t);
//return true;
}
return Widget_Timeslider::on_motion_notify_event(event);
}
bool
Widget_Sound::on_button_press_event(GdkEventButton *event)
{
//Assume button PRESS
//if we are starting... using left click
if(event->button == 1)
{
if(!adj_timescale) return false;
double beg = adj_timescale->get_lower(), end = adj_timescale->get_upper();
//find event position in time
double t = beg + event->x * (end-beg) / get_width();
//signal the attached scrubbing devices...
signal_start_scrubbing()(t);
return true;
}
return Widget_Timeslider::on_button_press_event(event);
}
bool
Widget_Sound::on_button_release_event(GdkEventButton *event)
{
//Assume button RELEASE
//if we are ending... using left click
if (event->button == 1) {
//signal the scrubbing device... to stop
signal_stop_scrubbing()();
return true;
}
return Widget_Timeslider::on_button_release_event(event);
}