Compare commits

...

18 Commits

Author SHA1 Message Date
nick_m eec505621a Don't cache peakfile reads, but still cache the generated peak data.
Clean up some other cruft.
2015-02-24 04:57:33 +11:00
nick_m 1990839728 Restore waveview.cc and audiosource.cc to pre-debugging state. 2015-02-24 02:29:29 +11:00
nick_m 8a0404a545 Leave chunksize at expected peaks. 2015-02-24 01:24:35 +11:00
nick_m 027f068d43 Fix silly omission in a while loop. 2015-02-24 01:02:26 +11:00
nick_m 0e8c07cbe9 No cached disk read (staging).
Still testing.
2015-02-24 00:36:12 +11:00
nick_m 7c9cb6615c Don't open with O_NONBLOCK. 2015-02-24 00:07:57 +11:00
nick_m e1ccb6d5d4 No O_NOATIME on Windows.
Remove unused variable.
Revert f79238bebc for testing with the_CLA.
2015-02-23 23:53:36 +11:00
nick_m 621c905590 Robin's patch for the correct way to apply a low pass filter to peak data. 2015-02-23 22:40:09 +11:00
nick_m f79238bebc Don't draw any of the source outside the region bounds.
Helps a bit with stacked mode / playhead interaction
where start/end is visible.
2015-02-22 03:46:17 +11:00
nick_m d6ded04b1d Fix thinko wrt "square wave" handling (only draw the joining line once).
Implement Paul's request for outline colour to be the same as the wave colour
when only the outline is to be drawn.
Offset the dark outline in the same direction for top and bottom.
This part is a real wtf as it looks good to me (using a magnifying glass),
but the code seems wrong.
2015-02-21 05:09:12 +11:00
nick_m b34f7d0ea0 Draw high amplitude high frequency waveforms better. 2015-02-21 03:26:25 +11:00
nick_m 8549c99444 Fix breakage caused by removing rounded wave tips. 2015-02-21 02:40:38 +11:00
nick_m 6f01cc6232 Clear up some uncertainty about negative numbers (i think).
Don't draw diagonal lines on the canvas.
Don't antialias any alpha images.
Don't use rounded line caps for waveform.
2015-02-21 01:12:27 +11:00
nick_m 959ef3e762 Merge branch 'master' into waveview 2015-02-21 00:53:17 +11:00
nick_m bf5bb1a07f Merge branch 'master' into waveview 2015-02-18 23:26:34 +11:00
nick_m 404fdae8af Merge branch 'master' into waveview 2015-02-13 03:58:23 +11:00
nick_m dea2eb2326 Cache the calculated peaks.
Interestingly, keeping the last peakfile read around
(staging) seems to "wind up" the vm cache on linux nicely.
Don't use 8 times the amount of needed memory (!).
Restore the waveview cache to previous size for comparison to master.

Works very well for scrolling regions whose source length is less than
the screen width at current zoom.
Tested on a large session with 72% cpu load (while playing.
2015-02-13 02:36:51 +11:00
nick_m c72df7e24c Just a hackish test to see the effect of disk reads on waveview rendering speed. 2015-02-11 02:43:21 +11:00
5 changed files with 146 additions and 140 deletions

View File

@ -28,6 +28,7 @@
#include <glibmm/threads.h>
#include <boost/function.hpp>
#include <boost/scoped_array.hpp>
#include "ardour/source.h"
#include "ardour/ardour.h"
@ -164,6 +165,12 @@ class LIBARDOUR_API AudioSource : virtual public Source,
framecnt_t peak_leftover_size;
Sample* peak_leftovers;
framepos_t peak_leftover_frame;
mutable uint32_t _last_map_off;
mutable size_t _last_raw_map_length;
mutable bool _first_run;
mutable double _last_scale;
mutable boost::scoped_array<PeakData> peak_cache;
};
}

View File

@ -34,10 +34,11 @@
#include <algorithm>
#include <vector>
#include <sys/mman.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <boost/scoped_array.hpp>
#include <boost/scoped_ptr.hpp>
#include <glibmm/fileutils.h>
@ -53,7 +54,7 @@
#include "i18n.h"
#include "ardour/debug.h"
const int BUFSIZE = sysconf(_SC_PAGESIZE);
using namespace std;
using namespace ARDOUR;
using namespace PBD;
@ -78,6 +79,8 @@ AudioSource::AudioSource (Session& s, string name)
, peak_leftover_cnt (0)
, peak_leftover_size (0)
, peak_leftovers (0)
, _first_run (true)
, _last_scale (0.0)
{
}
@ -90,6 +93,8 @@ AudioSource::AudioSource (Session& s, const XMLNode& node)
, peak_leftover_cnt (0)
, peak_leftover_size (0)
, peak_leftovers (0)
, _first_run (true)
, _last_scale (0.0)
{
if (set_state (node, Stateful::loading_state_version)) {
throw failed_constructor();
@ -331,10 +336,13 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
PeakData::PeakDatum xmax;
PeakData::PeakDatum xmin;
int32_t to_read;
ssize_t nread;
framecnt_t zero_fill = 0;
ScopedFileDescriptor sfd (::open (peakpath.c_str(), O_RDONLY));
#ifdef PLATFORM_WINDOWS
ScopedFileDescriptor sfd (::open (peakpath.c_str(), O_RDONLY | O_NONBLOCK));
#else
ScopedFileDescriptor sfd (::open (peakpath.c_str(), O_RDONLY | O_NOATIME | O_NONBLOCK));
#endif
if (sfd < 0) {
error << string_compose (_("Cannot open peakfile @ %1 for reading (%2)"), peakpath, strerror (errno)) << endmsg;
@ -383,39 +391,44 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
}
if (scale == 1.0) {
off_t offset = 0;
off_t first_peak_byte = (start / samples_per_file_peak) * sizeof (PeakData);
ssize_t bytes_to_read = sizeof (PeakData)* npeaks;
size_t bytes_to_read = sizeof (PeakData)* npeaks;
/* open, read, close */
DEBUG_TRACE (DEBUG::Peaks, "DIRECT PEAKS\n");
offset = lseek (sfd, first_peak_byte, SEEK_SET);
uint32_t map_off = first_peak_byte;
uint32_t read_map_off = map_off & ~(BUFSIZE - 1);
uint32_t map_delta = map_off - read_map_off;
size_t map_length = bytes_to_read + map_delta;
if (_first_run || (_last_map_off != map_off) || (_last_scale != samples_per_visual_peak) || (_last_raw_map_length < bytes_to_read)) {
peak_cache.reset (new PeakData[npeaks]);
boost::scoped_array<PeakData> staging (new PeakData[npeaks]);
char* addr = (char*) mmap (0, map_length, PROT_READ, MAP_PRIVATE, sfd, read_map_off);
if (addr == MAP_FAILED) {
error << _("map failed - could not mmap peakfile.") << endmsg;
return -1;
}
memcpy ((void*)peak_cache.get(), (void*)(addr + map_delta), bytes_to_read);
munmap (addr, map_length);
_last_map_off = map_off;
_last_raw_map_length = bytes_to_read;
_first_run = false;
if (zero_fill) {
memset (&peak_cache[npeaks], 0, sizeof (PeakData) * zero_fill);
}
if (offset != first_peak_byte) {
error << string_compose(_("AudioSource: could not seek to correct location in peak file \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
return -1;
}
nread = ::read (sfd, peaks, bytes_to_read);
if (nread != bytes_to_read) {
DEBUG_TRACE (DEBUG::Peaks, string_compose ("[%1]: Cannot read peaks from peakfile! (read only %2 not %3 at sample %4 = byte %5 )\n"
, _name, nread, npeaks, start, first_peak_byte));
return -1;
}
if (zero_fill) {
memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
}
_last_scale = samples_per_visual_peak;
memcpy ((void*)peaks, (void*)peak_cache.get(), npeaks * sizeof(PeakData));
return 0;
}
framecnt_t tnp;
if (scale < 1.0) {
DEBUG_TRACE (DEBUG::Peaks, "DOWNSAMPLE\n");
@ -430,88 +443,77 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
to avoid confusion, I'll refer to the requested peaks as visual_peaks and the peakfile peaks as stored_peaks
*/
const framecnt_t chunksize = (framecnt_t) min (expected_peaks, 65536.0);
boost::scoped_array<PeakData> staging(new PeakData[chunksize]);
/* compute the rounded up frame position */
framepos_t current_frame = start;
framepos_t current_stored_peak = (framepos_t) ceil (current_frame / (double) samples_per_file_peak);
framepos_t next_visual_peak = (framepos_t) ceil (current_frame / samples_per_visual_peak);
framepos_t current_stored_peak = (framepos_t) ceil (start / (double) samples_per_file_peak);
framepos_t next_visual_peak = (framepos_t) ceil (start / samples_per_visual_peak);
double next_visual_peak_frame = next_visual_peak * samples_per_visual_peak;
framepos_t stored_peak_before_next_visual_peak = (framepos_t) next_visual_peak_frame / samples_per_file_peak;
framecnt_t nvisual_peaks = 0;
framecnt_t stored_peaks_read = 0;
framecnt_t i = 0;
uint32_t i = 0;
framecnt_t chunksize = (framecnt_t) expected_peaks; // we read all the peaks we need in one hit.
/* handle the case where the initial visual peak is on a pixel boundary */
current_stored_peak = min (current_stored_peak, stored_peak_before_next_visual_peak);
/* open ... close during out: handling */
uint32_t map_off = (uint32_t) (ceil (start / (double) samples_per_file_peak)) * sizeof(PeakData);
uint32_t read_map_off = map_off & ~(BUFSIZE - 1);
uint32_t map_delta = map_off - read_map_off;
size_t raw_map_length = chunksize * sizeof(PeakData);
size_t map_length = (chunksize * sizeof(PeakData)) + map_delta;
while (nvisual_peaks < npeaks) {
if (_first_run || (_last_map_off != map_off) || (_last_scale != samples_per_visual_peak) || (_last_raw_map_length < raw_map_length)) {
peak_cache.reset (new PeakData[npeaks]);
boost::scoped_array<PeakData> staging (new PeakData[chunksize]);
if (i == stored_peaks_read) {
char* addr;
addr = (char*) mmap (0, map_length, PROT_READ, MAP_PRIVATE, sfd, read_map_off);
if (addr == MAP_FAILED) {
error << _("map failed - could not mmap peakfile.") << endmsg;
return -1;
}
memcpy ((void*)staging.get(), (void*)(addr + map_delta), raw_map_length);
munmap (addr, map_length);
uint32_t start_byte = current_stored_peak * sizeof(PeakData);
tnp = min ((framecnt_t)(_length/samples_per_file_peak - current_stored_peak), (framecnt_t) expected_peaks);
to_read = min (chunksize, tnp);
ssize_t bytes_to_read = sizeof (PeakData) * to_read;
_last_map_off = map_off;
_last_raw_map_length = raw_map_length;
_first_run = false;
_last_scale = samples_per_visual_peak;
while (nvisual_peaks < npeaks) {
DEBUG_TRACE (DEBUG::Peaks, string_compose ("reading %1 bytes from peakfile @ %2\n"
, bytes_to_read, start_byte));
xmax = -1.0;
xmin = 1.0;
while ((current_stored_peak <= stored_peak_before_next_visual_peak) && (i < raw_map_length)) {
off_t offset = lseek (sfd, start_byte, SEEK_SET);
if (offset != start_byte) {
error << string_compose(_("AudioSource: could not seek to correct location in peak file \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
return -1;
xmax = max (xmax, staging[i].max);
xmin = min (xmin, staging[i].min);
++i;
++current_stored_peak;
}
if ((nread = ::read (sfd, staging.get(), bytes_to_read)) != bytes_to_read) {
off_t fend = lseek (sfd, 0, SEEK_END);
DEBUG_TRACE (DEBUG::Peaks, string_compose ("[%1]: cannot read peak data from peakfile (%2 peaks instead of %3) (%4) at start_byte = %5 _length = %6 versus len = %7 expected maxpeaks = %8 npeaks was %9"
, _name, (nread / sizeof(PeakData)), to_read, g_strerror (errno), start_byte, _length, fend, ((_length - current_frame)/samples_per_file_peak), npeaks));
return -1;
}
i = 0;
stored_peaks_read = nread / sizeof(PeakData);
peak_cache[nvisual_peaks].max = xmax;
peak_cache[nvisual_peaks].min = xmin;
++nvisual_peaks;
next_visual_peak_frame = min ((double) start + cnt, (next_visual_peak_frame + samples_per_visual_peak));
stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak;
}
xmax = -1.0;
xmin = 1.0;
while ((i < stored_peaks_read) && (current_stored_peak <= stored_peak_before_next_visual_peak)) {
xmax = max (xmax, staging[i].max);
xmin = min (xmin, staging[i].min);
++i;
++current_stored_peak;
--expected_peaks;
if (zero_fill) {
cerr << "Zero fill end of peaks (@ " << npeaks << " with " << zero_fill << endl;
memset (&peak_cache[npeaks], 0, sizeof (PeakData) * zero_fill);
}
peaks[nvisual_peaks].max = xmax;
peaks[nvisual_peaks].min = xmin;
++nvisual_peaks;
++next_visual_peak;
//next_visual_peak_frame = min ((next_visual_peak * samples_per_visual_peak), (next_visual_peak_frame+samples_per_visual_peak) );
next_visual_peak_frame = min ((double) start+cnt, (next_visual_peak_frame+samples_per_visual_peak) );
stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak;
}
if (zero_fill) {
cerr << "Zero fill end of peaks (@ " << npeaks << " with " << zero_fill << endl;
memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
}
memcpy ((void*)peaks, (void*)peak_cache.get(), npeaks * sizeof(PeakData));
return 0;
} else {
DEBUG_TRACE (DEBUG::Peaks, "UPSAMPLE\n");
/* the caller wants
@ -683,7 +685,7 @@ AudioSource::done_with_peakfile_writes (bool done)
_peaks_built = true;
PeaksReady (); /* EMIT SIGNAL */
}
close (_peakfile_fd);
_peakfile_fd = -1;
}

View File

@ -232,8 +232,8 @@ Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
for (Points::const_iterator p = _points.begin(); p != _points.end(); ++p) {
Duple window_space (item_to_window (*p));
context->arc (window_space.x, window_space.y, 5.0, 0.0, 2 * M_PI);
context->stroke ();
}
context->stroke ();
#endif
}

View File

@ -384,6 +384,10 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
Cairo::RefPtr<Cairo::Context> outline_context = Cairo::Context::create (images.outline);
Cairo::RefPtr<Cairo::Context> clip_context = Cairo::Context::create (images.clip);
Cairo::RefPtr<Cairo::Context> zero_context = Cairo::Context::create (images.zero);
wave_context->set_antialias (Cairo::ANTIALIAS_NONE);
outline_context->set_antialias (Cairo::ANTIALIAS_NONE);
clip_context->set_antialias (Cairo::ANTIALIAS_NONE);
zero_context->set_antialias (Cairo::ANTIALIAS_NONE);
boost::scoped_array<LineTips> tips (new LineTips[n_peaks]);
@ -414,16 +418,17 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
tips[i].top = y_extent (p, false);
tips[i].spread = p * (_height - 1.0);
if (fabs (_peaks[i].max) >= clip_level) {
if (_peaks[i].max >= clip_level) {
tips[i].clip_max = true;
}
if (fabs (_peaks[i].min) >= clip_level) {
if (-(_peaks[i].min) >= clip_level) {
tips[i].clip_min = true;
}
}
} else {for (int i = 0; i < n_peaks; ++i) {
} else {
for (int i = 0; i < n_peaks; ++i) {
tips[i].bot = height() - 1.0;
const double p = max(fabs (_peaks[i].max), fabs (_peaks[i].min));
@ -443,22 +448,11 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
double top = _peaks[i].max;
double bot = _peaks[i].min;
if (_peaks[i].max > 0 && _peaks[i].min > 0) {
if (fabs (_peaks[i].max) >= clip_level) {
if (_peaks[i].max >= clip_level) {
tips[i].clip_max = true;
}
}
else if (_peaks[i].max < 0 && _peaks[i].min < 0) {
if (fabs (_peaks[i].min) >= clip_level) {
tips[i].clip_min = true;
}
} else {
if (fabs (_peaks[i].max) >= clip_level) {
tips[i].clip_max = true;
}
if (fabs (_peaks[i].min) >= clip_level) {
tips[i].clip_min = true;
}
if (-(_peaks[i].min) >= clip_level) {
tips[i].clip_min = true;
}
if (top > 0.0) {
@ -479,32 +473,21 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
tips[i].top = y_extent (top, false);
tips[i].bot = y_extent (bot, true);
tips[i].spread = fabs (tips[i].top - tips[i].bot);
tips[i].spread = tips[i].bot - tips[i].top;
}
} else {
for (int i = 0; i < n_peaks; ++i) {
if (_peaks[i].max > 0 && _peaks[i].min > 0) {
if (fabs (_peaks[i].max) >= clip_level) {
tips[i].clip_max = true;
}
if (_peaks[i].max >= clip_level) {
tips[i].clip_max = true;
}
else if (_peaks[i].max < 0 && _peaks[i].min < 0) {
if (fabs (_peaks[i].min) >= clip_level) {
tips[i].clip_min = true;
}
} else {
if (fabs (_peaks[i].max) >= clip_level) {
tips[i].clip_max = true;
}
if (fabs (_peaks[i].min) >= clip_level) {
tips[i].clip_min = true;
}
if (-(_peaks[i].min) >= clip_level) {
tips[i].clip_min = true;
}
tips[i].top = y_extent (_peaks[i].max, false);
tips[i].bot = y_extent (_peaks[i].min, true);
tips[i].spread = fabs (tips[i].top - tips[i].bot);
tips[i].spread = tips[i].bot - tips[i].top;
}
}
@ -521,7 +504,6 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
wave_context->set_line_width (1.0);
wave_context->translate (0.5, +0.5);
outline_context->set_line_cap (Cairo::LINE_CAP_ROUND);
outline_context->set_line_width (1.0);
outline_context->translate (0.5, +0.5);
@ -536,6 +518,7 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
*/
const double clip_height = min (7.0, ceil (_height * 0.05));
bool draw_outline_as_wave = false;
/* There are 3 possible components to draw at each x-axis position: the
waveform "line", the zero line and an outline/clip indicator. We
@ -594,19 +577,23 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
for (int i = 0; i < n_peaks; ++i) {
/* waveform line */
if (tips[i].spread >= 2.0) {
wave_context->move_to (i, tips[i].top);
wave_context->line_to (i, tips[i].bot);
}
/* draw square waves and other discontiguous points clearly */
if (i > 0) {
if (tips[i-1].top + 2 < tips[i].bot) {
if (tips[i-1].top + 2 < tips[i].top) {
wave_context->move_to (i-1, tips[i-1].top);
wave_context->line_to (i, tips[i].bot);
}
else if (tips[i-1].bot > tips[i].top + 2) {
wave_context->move_to (i-1, tips[i-1].bot);
wave_context->line_to (i-1, (tips[i].bot + tips[i-1].top)/2);
wave_context->move_to (i, (tips[i].bot + tips[i-1].top)/2);
wave_context->line_to (i, tips[i].top);
} else if (tips[i-1].bot > tips[i].bot + 2) {
wave_context->move_to (i-1, tips[i-1].bot);
wave_context->line_to (i-1, (tips[i].top + tips[i-1].bot)/2);
wave_context->move_to (i, (tips[i].top + tips[i-1].bot)/2);
wave_context->line_to (i, tips[i].bot);
}
}
@ -618,6 +605,7 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
}
if (tips[i].spread > 1.0) {
draw_outline_as_wave = false;
/* lower outline/clip indicator */
if (_global_show_waveform_clipping && tips[i].clip_min) {
clip_context->move_to (i, tips[i].bot);
@ -625,11 +613,12 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
const double sign = tips[i].bot > height_2 ? -1 : 1;
clip_context->rel_line_to (0, sign * min (clip_height, ceil (tips[i].spread + .5)));
} else {
outline_context->move_to (i, tips[i].bot);
outline_context->move_to (i, tips[i].bot + 0.5);
/* normal lower terminal dot */
outline_context->close_path ();
outline_context->rel_line_to (0, -0.5);
}
} else {
draw_outline_as_wave = true;
if (tips[i].clip_min) {
// make sure we draw the clip
tips[i].clip_max = true;
@ -643,9 +632,17 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
const double sign = tips[i].top > height_2 ? -1 : 1;
clip_context->rel_line_to (0, sign * min(clip_height, ceil(tips[i].spread + .5)));
} else {
outline_context->move_to (i, tips[i].top);
/* normal upper terminal dot */
outline_context->close_path ();
if (draw_outline_as_wave) {
wave_context->move_to (i, tips[i].top + 0.5);
/* special case where outline only is drawn.
is this correct? too short by 0.5?
*/
wave_context->rel_line_to (0, -0.5);
} else {
outline_context->move_to (i, tips[i].top + 0.5);
/* normal upper terminal dot */
outline_context->rel_line_to (0, -0.5);
}
}
}
@ -757,8 +754,8 @@ WaveView::get_image (Cairo::RefPtr<Cairo::ImageSurface>& image, framepos_t start
/* we can request data from anywhere in the Source, between 0 and its length
*/
framepos_t sample_start = max ((framepos_t) 0, (center - canvas_samples));
framepos_t sample_end = min (center + canvas_samples, _region->source_length (0));
framepos_t sample_start = max ((framepos_t) _region->start(), (center - canvas_samples));
framepos_t sample_end = min (center + canvas_samples, _region->start() + _region->length());
const int n_peaks = llrintf ((sample_end - sample_start)/ (double) _samples_per_pixel);

View File

@ -241,13 +241,6 @@ XFadeCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) co
#define IS (_xfadeposition == Start)
/* fill primary fade */
context->begin_new_path ();
context->append_path (IS ? *path_in : *path_out);
close_path(draw, context, IS ?_in : _out, false);
set_source_rgba (context, _fill_color);
context->fill ();
/* fill background fade */
context->save ();
context->begin_new_path ();
@ -263,6 +256,13 @@ XFadeCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) co
context->fill ();
context->restore ();
/* fill primary fade */
context->begin_new_path ();
context->append_path (IS ? *path_in : *path_out);
close_path(draw, context, IS ?_in : _out, false);
set_source_rgba (context, _fill_color);
context->fill ();
/* draw lines over fills */
set_source_rgba (context, IS ? _outline_color : outline_shaded);
context->set_line_width (IS ? 1.0 : .5);