Skip to content

Commit ff46a89

Browse files
committed
Ubuntu 18
1 parent df18acb commit ff46a89

12 files changed

+673
-387
lines changed

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ CFLAGS = -O3 -Wall -ffast-math -fexpensive-optimizations -fomit-frame-pointer -m
33
# CFLAGS += -DNDEBUG
44
# CFLAGS += -g -O0
55

6-
HDRS = timeliner_diagnostics.h timeliner_cache.h timeliner_util.h
6+
HDRS = timeliner_diagnostics.h timeliner_cache.h timeliner_util.h timeliner_util_threads.h timeliner_feature.h
77

88
OBJS = timeliner_util.o timeliner_diagnostics.o timeliner_cache.o
99
OBJS_PRE = $(OBJS) timeliner_pre.o
10-
OBJS_RUN = $(OBJS) timeliner_run.o alsa.o
10+
OBJS_RUN = $(OBJS) timeliner_run.o timeliner_util_threads.o timeliner_feature.o alsa.o
1111
OBJS_ALL = $(OBJS_RUN) $(OBJS_PRE)
1212

1313
LIBS_PRE = -lsndfile -lgsl -lblas

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ A browser for long audio recordings.
88

99
News story giving [historical context](http://www.ece.illinois.edu/mediacenter/article.asp?id=7568).
1010

11+
<!-- Ubuntu 18.04 needs at least libglew-dev libxmu-dev -->
12+
1113
### Building on Ubuntu 10.04 or 12.04
1214

1315
`sudo apt-get install g++ freeglut3-dev gsl-bin libgsl0-dev libglm-dev libsndfile1-dev libxi-dev libxmu-dev libasound2-dev audiofile-tools libglew-dev libpng12-dev`

timeliner_cache.cpp

+8-16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <limits>
77

88
#include "timeliner_cache.h"
9+
#include "timeliner_util.h"
910

1011
#undef VERBOSE
1112

@@ -273,9 +274,9 @@ CHello::CHello(const float* const aSrc, const long cs, const Float hzArg, const
273274
#ifndef _MSC_VER
274275
// VS2013 only got std::isnan in July 2013:
275276
// http://blogs.msdn.com/b/vcblog/archive/2013/07/19/c99-library-support-in-visual-studio-2013.aspx
276-
assert(!isnan(L[j+__+3]));
277-
assert(!isnan(L[j+__+4]));
278-
assert(!isnan(L[j+__+5]));
277+
assert(!std::isnan(L[j+__+3]));
278+
assert(!std::isnan(L[j+__+4]));
279+
assert(!std::isnan(L[j+__+5]));
279280
#endif
280281
assert(L[j+__+3] <= L[j+__+4] + epsilon); // min <= mean
281282
assert(L[k+__+3] <= L[k+__+4] + epsilon); // min <= mean
@@ -626,7 +627,7 @@ static inline const CQuartet merge_for_recurse(const CQuartet& a, const CQuartet
626627
(a[0]*a[2] + b[0]*b[2]) / numEls,
627628
std::max(a[3], b[3]));
628629

629-
static Float t[1+CQuartet_widthMax*3];
630+
Float t[1+CQuartet_widthMax*3]; // static would be cute, but not thread-safe. Just use the stack.
630631
t[0] = numEls;
631632
for (unsigned i=0; i<w; ++i) {
632633
const unsigned di = 1+3*i;
@@ -781,15 +782,6 @@ void RgbFromHsv(Float* a)
781782
a[0]=0.0; a[1]=0.0; a[2]=0.0;
782783
}
783784

784-
static inline Float sq(Float _)
785-
{ return _*_; }
786-
787-
static inline Float lerp(Float key, Float a, Float b)
788-
{
789-
// As key goes from 0 to 1, return from a to b.
790-
return key*b + (1.0F-key)*a;
791-
}
792-
793785
// Convert minmeanmax in-place to rgb.
794786
// Assumes min,mean,max all in [0,1].
795787
static void RgbFromMMM(Float* a, int iColormap)
@@ -868,13 +860,13 @@ static Float ByteFromMMM(const Float* a, int iColormap)
868860
return a[2]; // max
869861
case 3: // wavelet
870862
// max, and somewhat mean
871-
return lerp(0.8f, a[2], a[1]);
863+
return lerp(0.8, a[2], a[1]);
872864
case 0: // feaFB filterbank
873865
// max, and somewhat mean
874-
return lerp(0.7f, a[2], a[1]);
866+
return lerp(0.7, a[2], a[1]);
875867
case 1: // feaMFCC
876868
// max, and somewhat mean
877-
return lerp(0.6f, a[2], sq(a[1]));
869+
return lerp(0.6, a[2], sq(a[1]));
878870
default: // Saliency
879871
return a[2];
880872
}

timeliner_cache.h

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#pragma once
12
#include <vector>
23

34
#include <cassert>

timeliner_diagnostics.h

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#pragma once
12
#include <string>
23

34
extern std::string appname;

timeliner_feature.cpp

+232
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
#include "timeliner_feature.h"
2+
#include "timeliner_diagnostics.h"
3+
#include "timeliner_util.h"
4+
#include "timeliner_util_threads.h"
5+
#include <cstring>
6+
#include <cstdio>
7+
#include <cmath>
8+
9+
float gpuMBavailable()
10+
{
11+
// glerror GL_OUT_OF_MEMORY ?
12+
// kernel: [4391985.748987] NVRM: VM: nv_vm_malloc_pages: failed to allocate contiguous memory
13+
14+
// http://developer.download.nvidia.com/opengl/specs/GL_NVX_gpu_memory_info.txt
15+
// AMD/ATI equivalent: WGL_AMD_gpu_association wglGetGPUIDsAMD wglGetGPUIDsAMD wglGetGPUInfoAMD
16+
// www.opengl.org/registry/specs/ATI/meminfo.txt
17+
#if 0
18+
#define GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047
19+
GLint total_mem_kb = 0;
20+
glGetIntegerv(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &total_mem_kb);
21+
#endif
22+
#define GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
23+
GLint cur_avail_mem_kb = 0;
24+
glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &cur_avail_mem_kb);
25+
return cur_avail_mem_kb/1000.0F;
26+
27+
// A feature of width 65536 (almost 11 minutes at 100 samples/sec)
28+
// uses ~145 MB texture RAM.
29+
// That's 8 chunks of width 8192.
30+
// 1 chunk is 8192x1 texels * 2 for mipmap * 3 for GL_RGB * 62 vectorsize = 3047424 B = 2.9MB;
31+
// 8 chunks is 23 MB. But 145MB is used?! (RGBA not just RGB?)
32+
}
33+
34+
Feature::Feature(int /*iColormap*/, const std::string& filename, const std::string& dirname): m_fValid(false) {
35+
if (mb == mbUnknown) {
36+
mb = gpuMBavailable() > 0.0f ? mbPositive : mbZero;
37+
if (!hasGraphicsRAM())
38+
warn("Found no dedicated graphics RAM. Might run slowly.");
39+
}
40+
const Mmap marshaled_file(dirname + "/" + filename);
41+
if (!marshaled_file.valid())
42+
return;
43+
binaryload(marshaled_file.pch(), marshaled_file.cch()); // stuff many member variables
44+
makeMipmaps();
45+
m_fValid = true;
46+
// ~Mmap closes file
47+
}
48+
49+
void Feature::binaryload(const char* pch, long cch) {
50+
assert(4 == sizeof(float));
51+
strcpy(m_name, pch); pch += strlen(m_name);
52+
m_iColormap = int(*(float*)pch); pch += sizeof(float);
53+
m_period = *(float*)pch; pch += sizeof(float);
54+
const int slices = int(*(float*)pch); pch += sizeof(float);
55+
m_vectorsize = int(*(float*)pch); pch += sizeof(float);
56+
m_pz = (const float*)pch; // m_cz floats. Not doubles.
57+
m_cz = (cch - long(strlen(m_name) + 4*sizeof(float))) / 4;
58+
#ifndef NDEBUG
59+
printf("debugging feature: name %s, colormap %d, period %f, slices %d, width %d, cz %lu.\n", m_name, m_iColormap, m_period, slices, m_vectorsize, m_cz);
60+
#endif
61+
if (m_iColormap<0 || m_period<=0.0 || slices<=0 || m_vectorsize<=0) {
62+
quit("binaryload: feature '" + std::string(m_name) + "' has corrupt data");
63+
}
64+
assert(slices*m_vectorsize == m_cz);
65+
#ifdef NDEBUG
66+
_unused(slices);
67+
#else
68+
69+
#ifndef _MSC_VER
70+
// VS2013 only got std::isnormal and std::fpclassify in July 2013:
71+
// http://blogs.msdn.com/b/vcblog/archive/2013/07/19/c99-library-support-in-visual-studio-2013.aspx
72+
for (int i=0; i<m_cz; ++i) {
73+
if (!std::isnormal(m_pz[i]) && m_pz[i] != 0.0) {
74+
printf("binaryload: feature's float %d of %ld is bogus: class %d, value %f\n", i, m_cz, std::fpclassify(m_pz[i]), m_pz[i]);
75+
quit("");
76+
}
77+
}
78+
#endif
79+
#endif
80+
}
81+
82+
void prepTextureMipmap(const GLuint t)
83+
{
84+
glBindTexture(GL_TEXTURE_1D, t);
85+
assert(glIsTexture(t) == GL_TRUE);
86+
// Prevent bleeding onto opposite edges like a torus.
87+
#ifndef GL_CLAMP_TO_EDGE
88+
#define GL_CLAMP_TO_EDGE 0x812F
89+
#endif
90+
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
91+
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
92+
93+
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
94+
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
95+
//needed? glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_PRIORITY, 0.99);
96+
}
97+
98+
99+
arLock lockQueue;
100+
std::vector<QueueElement> queueChunk; // ;;;; rename queue to vector
101+
102+
void Feature::makeMipmaps() {
103+
// Adaptive subsample is too tricky, until I can better predict GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX.
104+
// (Adapt the prediction itself?? Allocate a few textures of various sizes, and measure reported GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX. But implement this only after getting 2 or 3 different PCs to test it on.)
105+
// Subsampling to coarser than 100 Hz would be pretty limiting.
106+
const char* pch = getenv("timeliner_zoom");
107+
unsigned subsample = pch ? atoi(pch) : 1;
108+
if (subsample < 1)
109+
subsample = 1;
110+
if (subsample > 1)
111+
printf("Subsampling %dx from environment variable timeliner_zoom.\n", subsample);
112+
113+
const int csample = samples();
114+
115+
// Smallest power of two that exceeds features' # of samples.
116+
unsigned width = 1;
117+
while (width < csample/subsample)
118+
width *= 2;
119+
//printf("feature has %d samples, for tex-chunks' width %d.\n", csample, width);
120+
121+
// Minimize cchunk to conserve RAM and increase FPS.
122+
{
123+
GLint widthLim; // often 2048..8192, rarely 16384, never greater.
124+
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &widthLim);
125+
assert(widthLim >= 0); // because width is unsigned
126+
if (width > unsigned(widthLim)) assert(width%widthLim==0); // everything is a power of two
127+
cchunk = width<unsigned(widthLim) ? 1 : width/widthLim;
128+
//printf("width = %d, cchunk = %d, widthLim = %d\n", width, cchunk, widthLim);
129+
if (width >= unsigned(widthLim))
130+
assert(GLint(width/cchunk) == widthLim);
131+
}
132+
133+
rgTex.resize(cchunk);
134+
for (int ichunk=0; ichunk<cchunk; ++ichunk) {
135+
glGenTextures(m_vectorsize, rgTex[ichunk].tex);
136+
for (int j=0; j < m_vectorsize; ++j)
137+
prepTextureMipmap(rgTex[ichunk].tex[j]);
138+
}
139+
140+
const CHello cacheHTK(m_pz, m_cz, 1.0f/m_period, subsample, m_vectorsize);
141+
glEnable(GL_TEXTURE_1D);
142+
const float mb0 = hasGraphicsRAM() ? gpuMBavailable() : 0.0f;
143+
// One pool for ALL Features would be slightly faster, but risks running out of memory.
144+
// However, any worker pool at all uses more memory.
145+
{
146+
WorkerPool pool;
147+
for (unsigned level=0; (width/cchunk)>>level >= 1; ++level) {
148+
//printf(" computing feature's mipmap level %d.\n", level);
149+
makeTextureMipmap(cacheHTK, level, width >> level);
150+
}
151+
}
152+
printf("finishing %lu chunks\n\n\n", queueChunk.size());
153+
for (std::vector<QueueElement>::iterator it = queueChunk.begin(); it != queueChunk.end(); ++it) {
154+
finishMipmap(*it);
155+
}
156+
157+
if (hasGraphicsRAM()) {
158+
const float mb1 = gpuMBavailable();
159+
printf("Feature used %.1f graphics MB; %.0f MB remaining.\n", mb0-mb1, mb1);
160+
if (mb1 < 50.0) {
161+
#ifdef _MSC_VER
162+
warn("Running out of graphics RAM. Try increasing the environment variable timeliner_zoom to 15 or so.");
163+
#else
164+
// Less than 50 MB free may hang X. Mouse responsive, Xorg 100% cpu, network up, console frozen.
165+
// Or, the next request may "go negative", mb0-mb1<0.
166+
warn("Running out of graphics RAM. Try export timeliner_zoom=15.");
167+
#endif
168+
}
169+
}
170+
}
171+
172+
extern double tShowBound[2];
173+
174+
const void Feature::makeTextureMipmapChunk(const CHello& cacheHTK, const int mipmaplevel, const int width, const int ichunk) const {
175+
unsigned char* bufByte = new unsigned char[vectorsize()*width];
176+
const double chunkL = ichunk / double(cchunk); // e.g., 5/8
177+
const double chunkR = (ichunk+1) / double(cchunk); // e.g., 6/8
178+
179+
printf("makeTextureMipmapChunk 0, vectorsize %d\n", vectorsize());
180+
cacheHTK.getbatchByte(bufByte,
181+
lerp(chunkL, tShowBound[0], tShowBound[1]),
182+
lerp(chunkR, tShowBound[0], tShowBound[1]),
183+
vectorsize(), width, m_iColormap);
184+
185+
arGuard _(lockQueue);
186+
queueChunk.push_back( QueueElement(bufByte, ichunk, width, mipmaplevel) );
187+
}
188+
189+
#include <GL/glx.h>
190+
#include <X11/Xlib.h>
191+
192+
// Multithreaded OpenGL is tricky, brittle, poorly documented.
193+
// So we call OpenGL not from the worker pool but only afterwards.
194+
void Feature::finishMipmap(const QueueElement& arg) {
195+
for (int j=0; j<vectorsize(); ++j) {
196+
printf("finishMipmap\n");
197+
//;;;;FAILS assert(glIsTexture(rgTex[arg.ichunk].tex[j]) == GL_TRUE);
198+
glBindTexture(GL_TEXTURE_1D, rgTex[arg.ichunk].tex[j]);
199+
glTexImage1D(GL_TEXTURE_1D, arg.mipmaplevel, GL_INTENSITY8, arg.width, 0, GL_RED, GL_UNSIGNED_BYTE, arg.bufByte + arg.width*j);
200+
}
201+
//;;;;?last one FAILS delete [] arg.bufByte;
202+
}
203+
204+
extern WorkerPool* pool;
205+
const void Feature::makeTextureMipmap(const CHello& cacheHTK, const int mipmaplevel, int width) const {
206+
assert(vectorsize() <= vecLim);
207+
assert(width % cchunk == 0);
208+
width /= cchunk;
209+
210+
//if (mipmaplevel<3) printf(" Computing %d mipmaps of width %d.\n", cchunk, width);
211+
//printf("vectorsize %d\n", vectorsize());
212+
unsigned char* bufByte = new unsigned char[vectorsize()*width];
213+
for (int ichunk=0; ichunk<cchunk; ++ichunk) {
214+
#if 1
215+
pool->task(new WorkerArgs(*this, cacheHTK, mipmaplevel, width, ichunk));
216+
#else
217+
const double chunkL = ichunk / double(cchunk); // e.g., 5/8
218+
const double chunkR = (ichunk+1) / double(cchunk); // e.g., 6/8
219+
printf("makeTextureMipmapChunk 0, vectorsize %d\n", vectorsize());
220+
cacheHTK.getbatchByte(bufByte,
221+
lerp(chunkL, tShowBound[0], tShowBound[1]),
222+
lerp(chunkR, tShowBound[0], tShowBound[1]),
223+
vectorsize(), width, m_iColormap);
224+
for (int j=0; j<vectorsize(); ++j) {
225+
assert(glIsTexture(rgTex[ichunk].tex[j]) == GL_TRUE);
226+
glBindTexture(GL_TEXTURE_1D, rgTex[ichunk].tex[j]);
227+
glTexImage1D(GL_TEXTURE_1D, mipmaplevel, GL_INTENSITY8, width, 0, GL_RED, GL_UNSIGNED_BYTE, bufByte + width*j);
228+
}
229+
#endif
230+
}
231+
delete [] bufByte;
232+
}

timeliner_feature.h

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#pragma once
2+
#include "timeliner_cache.h"
3+
#include <string>
4+
5+
#include <GL/glew.h> // before gl.h
6+
#include <GL/glut.h> // only for GLuint
7+
8+
class QueueElement {
9+
public:
10+
unsigned char* bufByte;
11+
int ichunk;
12+
int width;
13+
int mipmaplevel;
14+
QueueElement( unsigned char* a, int b, int c, int d) :
15+
bufByte(a), ichunk(b), width(c), mipmaplevel(d) {}
16+
};
17+
18+
class Feature {
19+
20+
enum { vecLim = CQuartet_widthMax+1 }; // from timeliner_cache.h
21+
static int mb;
22+
enum { mbUnknown, mbZero, mbPositive };
23+
24+
class Slartibartfast {
25+
public:
26+
GLuint tex[vecLim];
27+
};
28+
29+
public:
30+
int cchunk;
31+
std::vector<Slartibartfast> rgTex;
32+
33+
Feature(int /*iColormap*/, const std::string& filename, const std::string& dirname);
34+
35+
void makeMipmaps();
36+
const void makeTextureMipmap (const CHello& cacheHTK, int mipmaplevel, int width) const;
37+
const void makeTextureMipmapChunk(const CHello& cacheHTK, int mipmaplevel, int width, int ichunk) const;
38+
void finishMipmap(const QueueElement&);
39+
40+
bool hasGraphicsRAM() const { return mb == mbPositive; }
41+
42+
void binaryload(const char* pch, long cch);
43+
44+
bool fValid() const { return m_fValid; }
45+
int vectorsize() const { return m_vectorsize; }
46+
int samples() const { return m_cz / m_vectorsize; }
47+
const char* name() const { return m_name; }
48+
49+
private:
50+
bool m_fValid;
51+
int m_iColormap;
52+
float m_period; // seconds per sample
53+
int m_vectorsize; // e.g., how many frequency bins in a spectrogram
54+
const float* m_pz; // m_pz[0..m_cz] is the (vectors of) raw data
55+
long m_cz;
56+
char m_name[1000];
57+
};

timeliner_pre.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -847,4 +847,3 @@ int main(int argc, char** argv)
847847
return mainCore(argc, argv);
848848
}
849849

850-

0 commit comments

Comments
 (0)