mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-14 10:36:34 +01:00
Manual merge of the backend (peakfile reading) part of waveview branch.
Summary:
* use mmap() for the whole peakfile instead of lots of small seek/reads
* cache the computed peaks
* where possible, open files with O_NOATIME.
This commit is contained in:
parent
1199fe2f90
commit
fb8bc4e8c9
2 changed files with 95 additions and 80 deletions
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include <glibmm/threads.h>
|
#include <glibmm/threads.h>
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/scoped_array.hpp>
|
||||||
|
|
||||||
#include "ardour/source.h"
|
#include "ardour/source.h"
|
||||||
#include "ardour/ardour.h"
|
#include "ardour/ardour.h"
|
||||||
|
|
@ -164,6 +165,12 @@ class LIBARDOUR_API AudioSource : virtual public Source,
|
||||||
framecnt_t peak_leftover_size;
|
framecnt_t peak_leftover_size;
|
||||||
Sample* peak_leftovers;
|
Sample* peak_leftovers;
|
||||||
framepos_t peak_leftover_frame;
|
framepos_t peak_leftover_frame;
|
||||||
|
|
||||||
|
mutable bool _first_run;
|
||||||
|
mutable double _last_scale;
|
||||||
|
mutable off_t _last_map_off;
|
||||||
|
mutable size_t _last_raw_map_length;
|
||||||
|
mutable boost::scoped_array<PeakData> peak_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,11 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gstdio.h>
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
#include <boost/scoped_array.hpp>
|
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
#include <glibmm/fileutils.h>
|
#include <glibmm/fileutils.h>
|
||||||
|
|
@ -78,6 +79,10 @@ AudioSource::AudioSource (Session& s, string name)
|
||||||
, peak_leftover_cnt (0)
|
, peak_leftover_cnt (0)
|
||||||
, peak_leftover_size (0)
|
, peak_leftover_size (0)
|
||||||
, peak_leftovers (0)
|
, peak_leftovers (0)
|
||||||
|
, _first_run (true)
|
||||||
|
, _last_scale (0.0)
|
||||||
|
, _last_map_off (0)
|
||||||
|
, _last_raw_map_length (0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,6 +95,10 @@ AudioSource::AudioSource (Session& s, const XMLNode& node)
|
||||||
, peak_leftover_cnt (0)
|
, peak_leftover_cnt (0)
|
||||||
, peak_leftover_size (0)
|
, peak_leftover_size (0)
|
||||||
, peak_leftovers (0)
|
, peak_leftovers (0)
|
||||||
|
, _first_run (true)
|
||||||
|
, _last_scale (0.0)
|
||||||
|
, _last_map_off (0)
|
||||||
|
, _last_raw_map_length (0)
|
||||||
{
|
{
|
||||||
if (set_state (node, Stateful::loading_state_version)) {
|
if (set_state (node, Stateful::loading_state_version)) {
|
||||||
throw failed_constructor();
|
throw failed_constructor();
|
||||||
|
|
@ -331,10 +340,14 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
|
||||||
PeakData::PeakDatum xmax;
|
PeakData::PeakDatum xmax;
|
||||||
PeakData::PeakDatum xmin;
|
PeakData::PeakDatum xmin;
|
||||||
int32_t to_read;
|
int32_t to_read;
|
||||||
ssize_t nread;
|
const int bufsize = sysconf(_SC_PAGESIZE);
|
||||||
framecnt_t zero_fill = 0;
|
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) {
|
if (sfd < 0) {
|
||||||
error << string_compose (_("Cannot open peakfile @ %1 for reading (%2)"), peakpath, strerror (errno)) << endmsg;
|
error << string_compose (_("Cannot open peakfile @ %1 for reading (%2)"), peakpath, strerror (errno)) << endmsg;
|
||||||
|
|
@ -383,39 +396,45 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scale == 1.0) {
|
if (scale == 1.0) {
|
||||||
|
|
||||||
off_t offset = 0;
|
|
||||||
off_t first_peak_byte = (start / samples_per_file_peak) * sizeof (PeakData);
|
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 */
|
/* open, read, close */
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Peaks, "DIRECT PEAKS\n");
|
DEBUG_TRACE (DEBUG::Peaks, "DIRECT PEAKS\n");
|
||||||
|
|
||||||
offset = lseek (sfd, first_peak_byte, SEEK_SET);
|
off_t map_off = first_peak_byte;
|
||||||
|
off_t read_map_off = map_off & ~(bufsize - 1);
|
||||||
|
off_t map_delta = map_off - read_map_off;
|
||||||
|
size_t map_length = bytes_to_read + map_delta;
|
||||||
|
|
||||||
if (offset != first_peak_byte) {
|
if (_first_run || (_last_scale != samples_per_visual_peak) || (_last_map_off != map_off) || (_last_raw_map_length < bytes_to_read)) {
|
||||||
error << string_compose(_("AudioSource: could not seek to correct location in peak file \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
|
peak_cache.reset (new PeakData[npeaks]);
|
||||||
return -1;
|
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 << string_compose (_("map failed - could not mmap peakfile %1."), peakpath) << endmsg;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy ((void*)peak_cache.get(), (void*)(addr + map_delta), bytes_to_read);
|
||||||
|
munmap (addr, map_length);
|
||||||
|
|
||||||
|
if (zero_fill) {
|
||||||
|
memset (&peak_cache[npeaks], 0, sizeof (PeakData) * zero_fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
_first_run = false;
|
||||||
|
_last_scale = samples_per_visual_peak;
|
||||||
|
_last_map_off = map_off;
|
||||||
|
_last_raw_map_length = bytes_to_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
nread = ::read (sfd, peaks, bytes_to_read);
|
memcpy ((void*)peaks, (void*)peak_cache.get(), npeaks * sizeof(PeakData));
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
framecnt_t tnp;
|
|
||||||
|
|
||||||
if (scale < 1.0) {
|
if (scale < 1.0) {
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Peaks, "DOWNSAMPLE\n");
|
DEBUG_TRACE (DEBUG::Peaks, "DOWNSAMPLE\n");
|
||||||
|
|
@ -430,20 +449,16 @@ 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
|
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);
|
const framecnt_t chunksize = (framecnt_t) expected_peaks; // we read all the peaks we need in one hit.
|
||||||
|
|
||||||
boost::scoped_array<PeakData> staging(new PeakData[chunksize]);
|
|
||||||
|
|
||||||
/* compute the rounded up frame position */
|
/* compute the rounded up frame position */
|
||||||
|
|
||||||
framepos_t current_frame = start;
|
framepos_t current_stored_peak = (framepos_t) ceil (start / (double) samples_per_file_peak);
|
||||||
framepos_t current_stored_peak = (framepos_t) ceil (current_frame / (double) samples_per_file_peak);
|
framepos_t next_visual_peak = (framepos_t) ceil (start / samples_per_visual_peak);
|
||||||
framepos_t next_visual_peak = (framepos_t) ceil (current_frame / samples_per_visual_peak);
|
|
||||||
double next_visual_peak_frame = next_visual_peak * 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;
|
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 nvisual_peaks = 0;
|
||||||
framecnt_t stored_peaks_read = 0;
|
uint32_t i = 0;
|
||||||
framecnt_t i = 0;
|
|
||||||
|
|
||||||
/* handle the case where the initial visual peak is on a pixel boundary */
|
/* handle the case where the initial visual peak is on a pixel boundary */
|
||||||
|
|
||||||
|
|
@ -451,67 +466,60 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
|
||||||
|
|
||||||
/* open ... close during out: handling */
|
/* open ... close during out: handling */
|
||||||
|
|
||||||
while (nvisual_peaks < npeaks) {
|
off_t map_off = (uint32_t) (ceil (start / (double) samples_per_file_peak)) * sizeof(PeakData);
|
||||||
|
off_t read_map_off = map_off & ~(bufsize - 1);
|
||||||
|
off_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;
|
||||||
|
|
||||||
if (i == stored_peaks_read) {
|
if (_first_run || (_last_scale != samples_per_visual_peak) || (_last_map_off != map_off) || (_last_raw_map_length < raw_map_length)) {
|
||||||
|
peak_cache.reset (new PeakData[npeaks]);
|
||||||
|
boost::scoped_array<PeakData> staging (new PeakData[chunksize]);
|
||||||
|
|
||||||
uint32_t start_byte = current_stored_peak * sizeof(PeakData);
|
char* addr;
|
||||||
tnp = min ((framecnt_t)(_length/samples_per_file_peak - current_stored_peak), (framecnt_t) expected_peaks);
|
addr = (char*) mmap (0, map_length, PROT_READ, MAP_PRIVATE, sfd, read_map_off);
|
||||||
to_read = min (chunksize, tnp);
|
if (addr == MAP_FAILED) {
|
||||||
ssize_t bytes_to_read = sizeof (PeakData) * to_read;
|
error << string_compose (_("map failed - could not mmap peakfile %1."), peakpath) << endmsg;
|
||||||
|
return -1;
|
||||||
DEBUG_TRACE (DEBUG::Peaks, string_compose ("reading %1 bytes from peakfile @ %2\n"
|
|
||||||
, bytes_to_read, start_byte));
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xmax = -1.0;
|
memcpy ((void*)staging.get(), (void*)(addr + map_delta), raw_map_length);
|
||||||
xmin = 1.0;
|
munmap (addr, map_length);
|
||||||
|
|
||||||
while ((i < stored_peaks_read) && (current_stored_peak <= stored_peak_before_next_visual_peak)) {
|
while (nvisual_peaks < npeaks) {
|
||||||
|
|
||||||
xmax = max (xmax, staging[i].max);
|
xmax = -1.0;
|
||||||
xmin = min (xmin, staging[i].min);
|
xmin = 1.0;
|
||||||
++i;
|
|
||||||
++current_stored_peak;
|
while ((current_stored_peak <= stored_peak_before_next_visual_peak) && (i < raw_map_length)) {
|
||||||
--expected_peaks;
|
|
||||||
|
xmax = max (xmax, staging[i].max);
|
||||||
|
xmin = min (xmin, staging[i].min);
|
||||||
|
++i;
|
||||||
|
++current_stored_peak;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
peaks[nvisual_peaks].max = xmax;
|
if (zero_fill) {
|
||||||
peaks[nvisual_peaks].min = xmin;
|
cerr << "Zero fill end of peaks (@ " << npeaks << " with " << zero_fill << endl;
|
||||||
++nvisual_peaks;
|
memset (&peak_cache[npeaks], 0, sizeof (PeakData) * zero_fill);
|
||||||
++next_visual_peak;
|
}
|
||||||
|
|
||||||
//next_visual_peak_frame = min ((next_visual_peak * samples_per_visual_peak), (next_visual_peak_frame+samples_per_visual_peak) );
|
_first_run = false;
|
||||||
next_visual_peak_frame = min ((double) start+cnt, (next_visual_peak_frame+samples_per_visual_peak) );
|
_last_scale = samples_per_visual_peak;
|
||||||
stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak;
|
_last_map_off = map_off;
|
||||||
|
_last_raw_map_length = raw_map_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zero_fill) {
|
memcpy ((void*)peaks, (void*)peak_cache.get(), npeaks * sizeof(PeakData));
|
||||||
cerr << "Zero fill end of peaks (@ " << npeaks << " with " << zero_fill << endl;
|
|
||||||
memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Peaks, "UPSAMPLE\n");
|
DEBUG_TRACE (DEBUG::Peaks, "UPSAMPLE\n");
|
||||||
|
|
||||||
/* the caller wants
|
/* the caller wants
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue