static int _draw_rect(unsigned int addr, int l, int t, int r, int b, unsigned int linepitch, unsigned int color) { int ret = 0; ret += _draw_line(addr, l, t, r, t, linepitch, color); ret += _draw_line(addr, l, t, l, b, linepitch, color); ret += _draw_line(addr, r, t, r, b, linepitch, color); ret += _draw_line(addr, l, b, r, b, linepitch, color); return ret; }
bool draw_wave_buffer_v_hi(Renderer* renderer, WaveformActor* actor, int block, bool is_first, bool is_last, double x_block0) { //for use at resolution 1, operates on audio data, NOT peak data. // @b_region - sample range within the current block. b_region.start is relative to the Waveform, not the block. // @rect - the canvas area corresponding exactly to the WfSampleRegion. XXX changed. // variable names: variables prefixed with x_ relate to screen coordinates (pixels), variables prefixed with s_ related to sample frames. const Waveform* w = actor->waveform; const WaveformContext* wfc = actor->canvas; const WfActorPriv* _a = actor->priv; const RenderInfo* ri = &_a->render_info; VHiRenderer* vhr = (VHiRenderer*)renderer; const WfRectangle* rect = &ri->rect; WfAudioData* audio = &w->priv->audio; if(!audio->n_blocks || w->offline) return false; WfBuf16* buf = audio->buf16[block]; if(!buf) return false; if(is_last) vhr->block_region_v_hi.len = (ri->region.start + ri->region.len) % WF_SAMPLES_PER_TEXTURE; //alternative calculation of block_region_v_hi - does it give same results? NO uint64_t st = MAX((uint64_t)(ri->region.start), (uint64_t)((block) * ri->samples_per_texture)); uint64_t e = MIN((uint64_t)(ri->region.start + ri->region.len), (uint64_t)((block + 1) * ri->samples_per_texture)); WfSampleRegion block_region_v_hi2 = {st, e - st}; //dbg(0, "block_region_v_hi=%Lu(%Lu)-->%Lu len=%Lu (buf->size=%Lu r->region=%Lu-->%Lu)", st, (uint64_t)block_region_v_hi.start, e, (uint64_t)block_region_v_hi2.len, ((uint64_t)buf->size), ((uint64_t)region.start), ((uint64_t)region.start) + ((uint64_t)region.len)); WfSampleRegion b_region = block_region_v_hi2; g_return_val_if_fail(b_region.len <= buf->size, false); if(rect->left + rect->len < ri->viewport.left){ gerr("rect is outside viewport"); } _v_hi_set_gl_state(actor); //#define BIG_NUMBER 4096 #define BIG_NUMBER 8192 // temporarily increased pending cropping to viewport-left Range sr = {{0,0},{0,0}, 0}; //TODO check we are consistent in that these values are all *within the current block* Range xr = {{0,0},{0,0}, 0}; const double zoom = rect->len / (double)ri->region.len; const float _block_wid = WF_SAMPLES_PER_TEXTURE * zoom; float block_rect_start = is_first ? fmodf(rect->left, _block_wid) : x_block0; // TODO simplify. why 2 separate cases needed? ** try just using the first case WfRectangle b_rect = {block_rect_start, rect->top, b_region.len * zoom, rect->height}; if(!(ri->viewport.right > b_rect.left)) gwarn("outside viewport: vp.r=%.2f b_rect.l=%.2f", ri->viewport.right, b_rect.left); g_return_val_if_fail(ri->viewport.right > b_rect.left, false); if(!(b_rect.left + b_rect.len > ri->viewport.left)) gwarn("outside viewport: vp.l=%.1f b_rect.l=%.1f b_rect.len=%.1f", ri->viewport.left, b_rect.left, b_rect.len); g_return_val_if_fail(b_rect.left + b_rect.len > ri->viewport.left, false); #ifdef MULTILINE_SHADER #elif defined(VERTEX_ARRAYS) glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); int n_lines = MIN(BIG_NUMBER, ri->viewport.right - b_rect.left); // TODO the arrays possibly can be smaller - dont use b_rect.left, use x0 Quad quads[n_lines]; Vertex texture_coords[n_lines * 4]; int j; for(j=0;j<n_lines;j++){ texture_coords[j * 4 ] = (Vertex){0.0, 0.0}; texture_coords[j * 4 + 1] = (Vertex){1.0, 0.0}; texture_coords[j * 4 + 2] = (Vertex){1.0, 1.0}; texture_coords[j * 4 + 3] = (Vertex){0.0, 1.0}; } glVertexPointer (TWO_COORDS_PER_VERTEX, GL_FLOAT, 0, quads); glTexCoordPointer (TWO_COORDS_PER_VERTEX, GL_FLOAT, 0, texture_coords); #endif #ifndef MULTILINE_SHADER uint32_t rgba = actor->fg_colour; const float r = ((float)((rgba >> 24) ))/0x100; const float g = ((float)((rgba >> 16) & 0xff))/0x100; const float b = ((float)((rgba >> 8) & 0xff))/0x100; const float alpha = ((float)((rgba ) & 0xff))/0x100; #endif #ifdef MULTILINE_SHADER xr.border = TEX_BORDER_HI; #else xr.border = 0; #endif sr.border = xr.border / zoom; sr.inner.l = b_region.start % WF_SAMPLES_PER_TEXTURE; //const int x0 = MAX(0, floor(viewport->left - rect->left) - 3); const int x0 = b_rect.left; //const int x0 = MIN(b_rect.left, viewport->left - xr.border); // no, x and s should not be calculated independently. // sr.inner.l = (x0 - x_block0) / zoom; //TODO crop to viewport-left const int s0 = sr.outer.l = sr.inner.l - sr.border; xr.inner.l = x0; xr.outer.l = x0 - xr.border; const int x_bregion_end = b_rect.left + (b_region.start + b_region.len) * zoom; xr.inner.r = MIN(MIN(MIN( x0 + BIG_NUMBER, (int)(b_rect.left + b_rect.len)), ri->viewport.right), x_bregion_end); const int x_stop = xr.inner.r + xr.border; /* if(x_stop < b_rect.left + b_rect.len){ dbg(0, "stopping early. x_bregion_end=%i x0+B=%i", x_bregion_end, x0 + BIG_NUMBER); } */ //dbg(0, "rect=%.2f-->%.2f b_region=%Lu-->%Lu viewport=%.1f-->%.1f zoom=%.3f", b_rect.left, b_rect.left + b_rect.len, b_region.start, b_region.start + ((uint64_t)b_region.len), viewport->left, viewport->right, zoom); #ifdef MULTILINE_SHADER int mls_tex_w = x_stop - x0 + 2 * TEX_BORDER_HI; int mls_tex_h = 2; // TODO if we really are not going to add the indirection for x (for zoom > 1), use a 1d texture. int t_width = agl_power_of_two(mls_tex_w); guchar* _pbuf = g_new0(guchar, t_width * mls_tex_h); guchar* pbuf[] = {_pbuf, _pbuf + t_width}; #endif sr.inner.r = s0 + (xr.inner.r - xr.inner.l) / zoom; sr.outer.r = s0 /* TODO should include border? */+ ((double)x_stop - x0) / zoom; //dbg(0, "x0=%i x_stop=%i s=%i,%i,%i,%i xre=%i", x0, x_stop, sr.outer.l, sr.inner.l, sr.inner.r, sr.outer.r, x_bregion_end); // because we access adjacent samples, s_max is the absolute maximum index into the sample buffer (must be less than). int s_max = /*s0 + */sr.outer.r + 4 + sr.border; if(s_max > buf->size){ int over = s_max - buf->size; if(over < 4 + sr.border) dbg(0, "TODO block overlap - need to access next block"); else gerr("error at block changeover. s_max=%i over=%i", s_max, over); }else{ // its fairly normal to be limited by region, as the region can be set deliberately to match the viewport. // (the region can either correspond to a defined Section/Part of the waveform, or dynamically created with the viewport // -if it is a defined Section, we must not go beyond it. As we do not know which case we have, we must honour the region limit) //uint64_t b_region_end1 = (region.start + region.len) % buf->size; //TODO this should be the same as b_region_end2 ? but appears to be too short uint64_t b_region_end2 = (!((b_region.start + b_region.len) % WF_SAMPLES_PER_TEXTURE)) ? WF_SAMPLES_PER_TEXTURE : (b_region.start + b_region.len) % WF_SAMPLES_PER_TEXTURE;//buf->size; //if(s_max > b_region_end2) gwarn("limited by region length. region_end=%Lu %Lu", (uint64_t)((region.start + region.len) % buf->size), b_region_end2); //s_max = MIN(s_max, (region.start + region.len) % buf->size); s_max = MIN(s_max, b_region_end2); } s_max = MIN(s_max, buf->size); //note that there is never any need to be separately limited by b_region - that should be taken care of by buffer limitation? int c; for(c=0;c<w->n_channels;c++){ if(!buf->buf[c]){ gwarn("audio buf not set. c=%i", c); continue; } if(!buf->buf[c]) continue; #ifdef MULTILINE_SHADER int val0 = ((2*c + 1) * 128) / w->n_channels; if(mls_tex_w > TEX_BORDER_HI + 7) //TODO improve this test - make sure index is not negative --- may not be needed (texture is bigger now (includes borders)) memset((void*)((uintptr_t)pbuf[c] + (uintptr_t)(mls_tex_w - TEX_BORDER_HI)) - 7, val0, TEX_BORDER_HI + 7); // zero the rhs border in case it is not filled with content. memset((void*)((uintptr_t)pbuf[c]), val0, t_width); #endif #ifndef MULTILINE_SHADER int oldx = x0 - 1; int oldy = 0; #endif int s = 0; int i = 0; //int x; for(x = x0; x < x0 + BIG_NUMBER && /*rect->left +*/ x < viewport->right + border_right; x++, i++){ // note that when using texture borders, at the viewport left edge x is NOT zero, it is TEX_BORDER_HI. int x; for(x = xr.inner.l; x < x_stop; x++, i++){ double s_ = ((double)x - xr.inner.l) / zoom; double dist = s_ - s; // dist = distance in samples from one pixel to the next. //if(i < 5) dbg(0, "x=%i s_=%.3f dist=%.2f", x, s_, dist); if (dist > 2.0) { // if(dist > 5.0) gwarn("dist %.2f", dist); int ds = dist - 1; dist -= ds; s += ds; } //if(c == 0 && i < 5) dbg(0, " ss=%i", s0 + s); #ifdef MULTILINE_SHADER if (s0 + s < 0){ // left border and no valid data. pbuf[c][i] = val0; continue; } #endif if (s0 + s >= (int)buf->size ) { gwarn("end of block reached: b_region.start=%i b_region.end=%"PRIi64" %i", s0, b_region.start + ((uint64_t)b_region.len), buf->size); break; } /* if (s + 3 >= b_region.len) { gwarn("end of b_region reached: b_region.len=%i x=%i s0=%i s=%i", b_region.len, x, s0, s); break; } */ short* d = buf->buf[c]; double y1 = (s0 + s < s_max) ? d[s0 + s ] : 0; //TODO have a separately loop for the last 4 values. double y2 = (s0 + s+1 < s_max) ? d[s0 + s+1] : 0; double y3 = (s0 + s+2 < s_max) ? d[s0 + s+2] : 0; double y4 = (s0 + s+3 < s_max) ? d[s0 + s+3] : 0; double d0 = dist; double d1 = dist - 1.0; double d2 = dist - 2.0; double d3 = dist - 3.0; //TODO for MULTILINE_SHADER we probably dont want b_rect.height to affect y. int y = (int)( ( - (d1 * d2 * d3 * y1) / 6 + (d0 * d2 * d3 * y2) / 2 - (d0 * d1 * d3 * y3) / 2 + (d0 * d1 * d2 * y4) / 6 ) * wfc->v_gain * (b_rect.height / (2.0 * w->n_channels )) / (1 << 15) ); //if(i < 10) printf(" x=%i s=%i %i y=%i dist=%.2f s=%i %.2f\n", x, s0 + s, d[s0 + s], y, dist, s, floor((x / zoom) - 1)); #if defined (MULTILINE_SHADER) if(i >= mls_tex_w){ gwarn("tex index out of range %i %i", i, mls_tex_w); break; } { //int val = ((2*c + 1) * 128 + y) / w->n_channels; //if(val < 0 || val > 256) gwarn("val out of range: %i", val); -- will be out of range when vgain is high. pbuf[c][i] = val0 + MIN(y, 63); // the 63 is to stop it wrapping - would be nice to remove this. //if(i >= 0 && i < 10) dbg(0, " s=%i y2=%.4f y=%i val=%i", s + s0, y2, y, ((2*c + 1) * 128 + y) / w->n_channels); } #elif defined(VERTEX_ARRAYS) { //float ys = y * rect->height / 256.0; float x0 = oldx; float y0 = b_rect.top - oldy + b_rect.height / 2; float x1 = x; float y1 = b_rect.top - y + b_rect.height / 2; float len = sqrtf(powf((y1 - y0), 2) + powf((x1 - x0), 2)); float xoff = (y1 - y0) * 5.0 / len; float yoff = (x1 - x0) * 5.0 / len; quads[i] = (Quad){ (Vertex){x0 - xoff/2, y0 + yoff/2}, (Vertex){x1 - xoff/2, y1 + yoff/2}, (Vertex){x1 + xoff/2, y1 - yoff/2}, (Vertex){x0 + xoff/2, y0 - yoff/2}, }; //if(i > 40 && i < 50) dbg(0, "len=%.2f xoff=%.2f yoff=%.2f (%.2f, %.2f) (%.2f, %.2f)", len, xoff, yoff, quads[i].v0.x, quads[i].v0.y, quads[i].v1.x, quads[i].v1.y); } #else // draw straight line from old pos to new pos _draw_line( oldx, b_rect.top - oldy + b_rect.height / 2, x, b_rect.top - y + b_rect.height / 2, r, g, b, alpha); #endif #ifndef MULTILINE_SHADER oldx = x; oldy = y; #endif } dbg(2, "n_lines=%i x=%i-->%i", i, x0, x); #if defined (MULTILINE_SHADER) #elif defined(VERTEX_ARRAYS) glColor4f(r, g, b, alpha); glPushMatrix(); glTranslatef(0.0, (w->n_channels == 2 ? -b_rect.height/4 : 0.0) + c * b_rect.height/2, 0.0); GLsizei count = (i - 0) * 4; glDrawArrays(GL_QUADS, 0, count); glPopMatrix(); #endif } // end channel #if defined (MULTILINE_SHADER) #define TEXELS_PER_PIXEL 1.0 guint texture =_wf_create_lines_texture(pbuf[0], t_width, mls_tex_h); float len = MIN(viewport->right - viewport->left, b_rect.len); //the texture contents are cropped by viewport so we should do the same when setting the rect width. AGlQuad t = { ((float)TEX_BORDER_HI)/((float)t_width), 0.0, ((float)((x_stop - x0) * TEXELS_PER_PIXEL)) / (float)t_width, 1.0 }; wfc->priv->shaders.lines->uniform.texture_width = t_width; agl_use_program((AGlShader*)wfc->priv->shaders.lines); // to set the uniform glPushMatrix(); glTranslatef(b_rect.left, 0.0, 0.0); agl_textured_rect_real(texture, 0.0, b_rect.top, len, b_rect.height, &t); glPopMatrix(); g_free(_pbuf); #elif defined(VERTEX_ARRAYS) glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); #endif // increment for next block vhr->block_region_v_hi.start = (vhr->block_region_v_hi.start / WF_PEAK_BLOCK_SIZE + 1) * WF_PEAK_BLOCK_SIZE; vhr->block_region_v_hi.len = WF_PEAK_BLOCK_SIZE - vhr->block_region_v_hi.start % WF_PEAK_BLOCK_SIZE; return true; }