I have been given a new requirement that I make the option available to play media files in reverse, and the option to be able to increase/decrease playback speed.
The program with which I am working relies on VideoLan.Net to handle all our media playback needs, so the question I have is this:
Using VideoLan.Net, is it possible to play media backwards, and is it possible to increase/decrease the playback speed?
vlc can not play media in reverse.
With LibVLC it certainly is possible to increase/decrease the playback speed: see libvlc_media_player_set_rate(float rate).
Use e.g 0.5f for half speed, 1.0f to return to normal speed, 2.0f for double speed and so on.
You could use position, remembering that it implies a little play (used by VLC in order to re-render):
float deltaStep = (float)60000 / vlcControl.Length;
float backwardStep = deltaStep * 0.0000015f;
while (vlcControl.Position > backwardStep)
{
vlcControl.Position -= backwardStep * vlcControl.Rate;
}
For increasing/decreasing/normalizing playback speed:
vlcControl.Rate *= 2; // Faster
...
vlcControl.Rate /= 2; // Slower
...
vlcControl.Rate = 1.0f; // Normal
Related
I've been working on a GUI toolkit for my future programming needs. It's basically reinventing the wheel and implementing many controls found in Windows' common controls, QT and other frameworks. It's going to be used by me mainly.
It's main design guidelines are:
implemented in plain C (not C++) and Win32 (GDI + Direct2D) (no other external dependencies)
easy to look at (even for a long time)
customization similar to QT's css-based stylesheets
easy to render (not much complex geometry)
really good performance (no performance issues, even in large GUI projects)
It's been going quite well for now and I have managed to implement quite a few important and trivial controls. Right now, I am building my slider control that can be a rotary slider (like QDial), or a horizontal or vertical bar slider.
While there are no obvious bugs that I have noticed during my testing, I am questioning the way I am rendering the control (using Direct2D and GDI).
Below you can find the commented draw code and the result it produces. I know it's not perfect by any means but it works flawlessly for me. Please do not judge my coding style for this question is really not on that.
static int __Slider_Internal_DCDBufferDraw(Slider *sSlider) {
if (!sSlider->_Base.sDraw)
return ERROR_OK;
/* start timer */
LARGE_INTEGER t1, t2;
QueryPerformanceCounter(&t1);
/* appearance depends on enabled state of the control */
_Bool blIsEnabled = IsWindowEnabled(sSlider->_Base.hwWindow /* control's HWND instance */);
D2D1_ELLIPSE sInnerCircle = {
.point = { __SlR_C /* center of circle, essentially width / 2 */, __SlR_C + __ClH(sSlider) / 6.0f /* center + some offset */ },
.radiusX = __SlR_IR + 0.5f, /* IR = inner radius */
.radiusY = ___SlR_IR + 0.5f
};
D2D1_ELLIPSE sOuterCircle = {
.point = { __SlR_C, __SlR_C },
.radiusX = __SlR_OR + 0.5f, /* OR = outer radius */
.radiusY = __SlR_OR + 0.5f
};
D2D1_BEGIN:
/*
Global struct "gl_sD2D1Renderer" contains a ID2D1Factory, a ID2D1DCRenderTarget (.sDCTarget), and a ID2D1SolidColorBrush (.sDCSCBrush).
Every control uses this DC to draw Direct2D content. Before anything is drawn, the DC is bound. Right now, I draw everything to an control instance-specific
HDC "sSlider->_Base.sDraw->hMemDC" in this function. In my actual WM_PAINT handler, I just BitBlt the memory bitmap. This
(1) removes flickering,
(2) improves draw speed for normal WM_PAINT commands, for example, when the client area of the window is uncovered/moved/etc.
In these cases, I just use the most recent representation without redrawing everything because the control
only changes its appearance in reaction to user input.
The brush is used to basically draw all the color information. It just gets its color changed every time it's needed.
The reason I am using a DC render target is because
(1) of its reusability (can be used for drawing all controls, without having to create separate render targets for each control instance)
(2) GDI compatibility (see "__Slider_Internal_DrawNumbersAndText"'s comment below to learn why I need it)
*/
ID2D1DCRenderTarget_BindDC(gl_sD2D1Renderer.sDCTarget, sSlider->_Base.sDraw->hMemDC, &sSlider->_Base.sClientRect);
ID2D1DCRenderTarget_BeginDraw(gl_sD2D1Renderer.sDCTarget);
ID2D1DCRenderTarget_Clear(gl_sD2D1Renderer.sDCTarget, &colBkgnd);
/* rotate the smaller circle by the current slider position (min ... max) */
D2D1_MATRIX_3X2_F sMatrix;
D2D1MakeRotateMatrix(sSlider->flPos, (D2D1_POINT_2F){ __SlR_C, __SlR_C}, &sMatrix);
ID2D1DCRenderTarget_SetTransform(gl_sD2D1Renderer.sDCTarget, &sMatrix);
/* draw the outer circle */
ID2D1SolidColorBrush_SetColor(gl_sD2D1Renderer.sDCSCBrush, blIsEnabled ? &colBtnSurf : &colBtnDisSurf);
ID2D1DCRenderTarget_FillEllipse(gl_sD2D1Renderer.sDCTarget, &sOuterCircle, (ID2D1Brush *)gl_sD2D1Renderer.sDCSCBrush);
ID2D1SolidColorBrush_SetColor(gl_sD2D1Renderer.sDCSCBrush, &colOutline);
ID2D1DCRenderTarget_DrawEllipse(gl_sD2D1Renderer.sDCTarget, &sOuterCircle, (ID2D1Brush *)gl_sD2D1Renderer.sDCSCBrush, 1.0f, NULL);
/* draw the inner circle */
ID2D1SolidColorBrush_SetColor(gl_sD2D1Renderer.sDCSCBrush, blIsEnabled ? (sSlider->_Base.wState & STATE_CAPTURE || sSlider->_Base.wState & STATE_MINSIDE ? &colBtnSelSurf : &colMark) : &colMarkDis);
ID2D1DCRenderTarget_FillEllipse(gl_sD2D1Renderer.sDCTarget, &sInnerCircle, (ID2D1Brush *)gl_sD2D1Renderer.sDCSCBrush);
ID2D1SolidColorBrush_SetColor(gl_sD2D1Renderer.sDCSCBrush, &colOutline);
ID2D1DCRenderTarget_DrawEllipse(gl_sD2D1Renderer.sDCTarget, &sInnerCircle, (ID2D1Brush *)gl_sD2D1Renderer.sDCSCBrush, 1.0f, NULL);
/* reset the transform */
ID2D1DCRenderTarget_SetTransform(gl_sD2D1Renderer.sDCTarget, &gl_sD2D1Renderer.sIdentityMatrix);
/* draw ticks using Direct2D */
__Slider_Internal_DrawTicks(sSlider, 0); /* draw small ticks */
__Slider_Internal_DrawTicks(sSlider, 1); /* draw big ticks */
/* Call EndDraw, check for render target errors, drop the render target if necessary, recreate it and "goto D2D1_BEGIN;" */
ID2D1DCRenderTarget_SafeEndDraw(gl_sD2D1Renderer.sDCTarget, NULL, NULL);
/*
Draw text using plain GDI (no DirectWrite because there is no functioning C-API.)
I have to do this here because I need to finish rendering the D2D content first. If I render GDI content in between Direct2D calls,
it would just be overdrawn because drawing is actually done in "EndDraw", rather than in "DrawEllipse", "Clear", etc. These calls just
build a batch while "Ellipse" or "TextExtOut" do immediately draw.
*/
__Slider_Internal_DrawNumbersAndText(sSlider);
/* end timer */
QueryPerformanceCounter(&t2);
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
double elapsed = (double)(t2.QuadPart - t1.QuadPart) / (freq.QuadPart / 1000.0);
printf("Draw call of \"%s\" took: %g ms\n", sSlider->_Base.strID, elapsed);
return ERROR_OK; /* 0 */
}
static int __Slider_Internal_DrawTicks(Slider *sSlider, int dwType) {
/* BTC = big tick count */
/* STC = small tick count */
/* check if ticks can be drawn, return if, for instance, not all data is present or tick drawing is disabled */
if (!(dwType ? sSlider->wBTC : sSlider->wSTC) || !(sSlider->wType & (dwType ? SLO_BIGTICKS : SLO_SMALLTICKS)))
return __ERROR_OK;
/* tick color */
ID2D1SolidColorBrush_SetColor(gl_sD2D1Renderer.sDCSCBrush, &colOutline); /* RGB(0, 0, 0) */
float flCurrPos = sSlider->sPosRange.flMin; /* start at the minimum possible angle for this slider */
/* calculate the step, i.e. angle to advance based on requested tick count and valid position (angle) range */
float flStep = (sSlider->sPosRange.flMax - sSlider->sPosRange.flMin) / (dwType ? sSlider->wBTC : sSlider->wSTC);
D2D1_MATRIX_3X2_F sMatrix; /* rotation matrix */
D2D1_POINT_2F sP1, sP2; /* start and end point of the line representing a tick */
D2D1_POINT_2F sCenter = {
__SlR_C + 0.5f,
__SlR_C + 0.5f
};
/* calculate tick dimensions given the type (= small or large) */
__Slider_getTickDimensions(sSlider, &sP1, &sP2, dwType);
int dwCount = 0;
do {
/* prevent drawing over big ticks */
if (!dwType && !(sSlider->wSTC % sSlider->wBTC))
if (!(dwCount % (sSlider->wSTC / sSlider->wBTC)))
goto ADD_STEP; /* just advance, do not draw */
if (sSlider->wType & SLT_RADIAL) { /* only do this if our slider is a rotary knob */
/* use the rotation matrix to draw the ticks in the same manner the inner circle of the slider is drawn */
D2D1MakeRotateMatrix(flCurrPos, sCenter, &sMatrix);
ID2D1DCRenderTarget_SetTransform(gl_sD2D1Renderer.sDCTarget, &sMatrix);
}
ID2D1DCRenderTarget_DrawLine(gl_sD2D1Renderer.sDCTarget, sP1, sP2, (ID2D1Brush *)gl_sD2D1Renderer.sDCSCBrush, 1.0f, NULL);
ADD_STEP:
flCurrPos += flStep; /* advance current position by previously */
} while (dwCount++ < (dwType ? sSlider->wBTC : sSlider->wSTC));
return ERROR_OK;
}
static int __Slider_Internal_DrawNumbersAndText(Slider *sSlider) {
/* only draw numbers if the option is specified */
if (sSlider->wType & SLO_NUMBERS) {
float flPosX, flPosY;
CHAR strString[8] = { 0 }; /* number string buffer */
SIZE sExtends = { 0 };
/* the same as in "DrawTicks" */
float flAngle = sSlider->sPosRange.flMin;
int dwNumber = sSlider->sNRange.dwMin; /* first number in the number range */
float flAStep = (sSlider->sPosRange.flMax - sSlider->sPosRange.flMin) / sSlider->wBTC; /* angle step */
int dwNStep = (sSlider->sNRange.dwMax - sSlider->sNRange.dwMin) / sSlider->wBTC; /* number step */
do {
/* this should be clear what it does */
sprintf_s(strString, 7, "%i", dwNumber);
GetTextExtentPoint32A(sSlider->_Base.sDraw->hMemDC, strString, (int)strlen(strString), &sExtends);
/* calculate text position around the outer circle */
/* gl_flBTL = big tick length, gl_flTDP = pitch between outer circle edge and tick start */
flPosX = cosf(__toRad(flAngle - 90.0f)) /* deg to rad */ * (__SlR_OR + gl_flTDP + gl_flBTL + 10.0f);
flPosY = sinf(__toRad(flAngle - 90.0f)) * (__SlR_OR + gl_flTDP + gl_flBTL + 10.0f);
TextOutA(
sSlider->_Base.sDraw->hMemDC,
(int)(__SlR_C - flPosX),
(int)(__SlR_C - flPosY - sExtends.cy / 2.0f),
strString,
(int)strlen(strString)
);
flAngle += flAStep;
dwNumber += dwNStep;
/* prevent overdrawing first number when 360 degrees range */
if (dwNumber == sSlider->sNRange.dwMax && sSlider->sPosRange.flMin == 0.0f && sSlider->sPosRange.flMax == 360.0f)
break;
} while (dwNumber <= sSlider->sNRange.dwMax);
}
/* draw the main slider text in the middle at the bottom edge of the control */
/* __Cl* = extends of the client area of the window (X = left, Y = top, W = right, H = bottom) */
TextOut(
sSlider->_Base.sDraw->hMemDC,
(__ClW(sSlider) - __ClX(sSlider)) / 2,
(__ClH(sSlider) - __ClY(sSlider)) / 2 + (int)__SlR_OR + 10,
sSlider->_Text.strText,
sSlider->_Text.dwLengthInChars
);
return TEGTK_ERROR_OK; /* 0 */
}
With certain exemplary values given, it produces this result:
While I find the result visually pleasing and its rendering procedure relatively simple, I think it's slow. I have not noticed any performance issues yet; therefore, I have measured the time it takes to complete an entire draw call. Note that this is done every time the slider's appearance changes due to user input.
I have also found that when I move the mouse slowly, the draw calls are way slower than when I move the mouse quickly.
Slow mouse movement:
Fast mouse movement:
The issue is now that I create a separate memory DC for every control instance, which I later draw to using the code above. I have heard that I can only use 10k GDI objects per process. I already use at least 2 per control (a DC and a bitmap). What if I have a really large GUI project with a lot going on? I really do not ever want to run into the limits.
That's why I was thinking of moving the paint code entirely into WM_PAINT and using the DC I get from "BeginPaint()" (so no extra memory DC and bitmap needed). Basically forcing an entire repaint when it gets called. That's where the speed issue comes into play as WM_PAINT can be sent really frequently.
I know I can smartly repaint only what's needed, but the atomic primitive draw calls do not do a lot when it comes to performance. What takes a lot of time is binding the DC and EndDraw.
I now have a dilemma because I want to be both fast but also not using more GDI objects than I absolutely have to. So not using a separate memory buffer is an option if the draw described above is in-fact not slow objectively.
These are my questions:
Is my drawing code actually slow or is it okay if redrawing the control takes like 1-5 ms on average?
What can I do to improve its performance if it's actually slow? (I have tried to buffer as much computational data as I can -- while it essentially doubles the control's memory footprint, it does not really do anything for performance.)
How is the actual redrawing done in commercially available frameworks such as QT and wxWidgets?
I really hope it's clear what I want. If there are any questions, feel free to ask. It's not only about good code, but also about good design. I want to make sure I do not implement major design flaws that early in the project.
We use the L3GD20 gyro sensor and the LSM303DLHC accelerometer sensor in combination with the complementary filter to measure the angles of the drone.
If we simulate the angles of the drone with our hands, for example, if we tilt the drone forward, our x angle is positive. And if we tilt it backwards, our x angle is negative.
But if we activate our motors, the drone will always go to a negative x-angle. On top of that, the x-angle that used to be positive is now negative. Because the drone tries to compensate this angle, but the angles are inverted, the drone will never go back to its original state.
#define RAD_TO_DEG 57.29578
#define G_GAIN 0.070
#define AA 0.98
#define LOOP_TIME 0.02
void calculate_complementaryfilter(float* complementary_angles)
{
complementary_angles[X] = AA * (complementary_angles[X] + gyro_rates[X] * LOOP_TIME) + (1 - AA) * acc_angles[X];
complementary_angles[Y] = AA * (complementary_angles[Y] + gyro_rates[Y] * LOOP_TIME) + (1 - AA) * acc_angles[Y];
}
void convert_accelerometer_data_to_deg()
{
acc_angles[X] = (float) atan2(acc_raw[X], (sqrt(acc_raw[Y] * acc_raw[Y] + acc_raw[Z] * acc_raw[Z]))) * RAD_TO_DEG;
acc_angles[Y] = (float) atan2(acc_raw[Y], (sqrt(acc_raw[X] * acc_raw[X] + acc_raw[Z] * acc_raw[Z]))) * RAD_TO_DEG;
}
void convert_gyro_data_to_dps()
{
gyro_rates[X] = (float)gyr_raw[X] * G_GAIN;
gyro_rates[Y] = (float)gyr_raw[Y] * G_GAIN;
gyro_rates[Z] = (float)gyr_raw[Z] * G_GAIN;
}
The problem isn't the shaking of the drone. If we put the motors on max speed and simulate the angles by hand, we get the right angles. Thus also the right compensation by the motors.
If we need to add more code, just ask.
Thankyou in advance.
Standard exhaustive methodology for this kind of problems
You can go top-bottom or bottom-top. In this case, I'm more inclined to think in a hardware related problem, but it is up to you:
Power related problem
When you take the drone with your hand and run motors at full throttle, do they have propellers installed?
Motors at full speed without propellers drawn only a fraction of their full load. When lifting drone weight, a voltage drop can cause electronic malfunction.
Alternative cause: shortcircuits/derivations?
Mechanical problem (a.k.a vibrations spoil sensor readings)
In the past, I've seen MEMS sensors suffer a lot under heavy vibrations (amplitude +-4g). And with "suffer a lot" I mean accelerometer not even registering gravity and gyros returning meaningless data.
If vibrations are significative, you need either a better frame for the drone or a better vibration isolation for the sensors.
SW issues (data/algorithm/implementation)
If it is definitely unrelated with power and mechanical aspects, you can log raw sensor data and process it offline to see if it makes sense.
For this, you need an implementation of the same algorithm embedded in the drone.
Here you will be able to discern between:
Buggy/wrong embedded implementation.
Algorithm fails under working conditions.
Other: data looks wrong (problem reading), SW not reaching time cycle constraints.
I'm developing a plugin in C that detects audio peaks using gstreamer-1.0. I don't really have any knowledge about audio programming and so far, my plugin can only detect sound impulsion (if there is no audio, nothing happens, if there is sound, I print the energy).
Here is the sample code of my (really simple) algorithm.
gfloat energy_of_sample(guint8 array[], int num_elements, gfloat *p)
{
gfloat energy=0.f;
for(int i=0 ; i<num_elements ; i++)
{
energy += array[i]*array[i]/4096;
if (*p < (array[i]*array[i]/4096)) *p = array[i]*array[i]/4096;
}
return energy/num_elements;
}
static void
audio_process(GstBPMdetect *filter, GstBuffer *music)
{
GstMapInfo info;
gint threshold = 6;
// gets the information of the buffer and put it in "info"
gst_buffer_map (music, &info, GST_MAP_READ);
// calculate the average of the buffer data
gfloat energy = 0;
gfloat peak = 0;
energy = energy_of_sample(info.data, info.size, &peak);
if (energy >= threshold )g_print("energy : %f , peak : %f \n", energy,peak);
}
If the audio source is, for exemple, a simple hand clap or kick drum only, my plugin detects the audio peak just fine. But when the audio source is a song, my plugin is constantly detecting sound impulsion (always over the threshold).
My solution for that issue was to add a low-pass filter so only bass sound would be detected. By doing that, I'm cutting every part of the song containing high frequencies only and this is not what I want (will not work for high frequency beats).
So my question is : Does anyone have an idea on how to detect beats (audio impulsion) without cutting the high frequencies ? Tanks to everyone and hope that my question is clear !
You should measure energy not peak. There is a great method to calculate energy. Use variance formula from statistics. You need count square of sum and sum of squares for all points within an interval of 20 - 50 milliseconds. Using the variance formula you get the energy. The formula is here
http://staff.icdi.wvu.edu/djhstats/variance1.JPG
As an alternative you may use the existing plugin level in the set of good plugins.
We all know how to draw a line in Processing.
But when we draw a line, the line is shown immediately.
What if i want to witness the drawing process, namely, to see the line moving forward, gradually completes a whole line.
Here's what i want to realize: to DRAW several lines and curves which finally turn into some pattern.
So how to make that happen? Using array?
Many thanks.
In processing all of the drawing happens in a loop. An easy way to create animated sequences like you describe is to use frameCount to drive it and using the modulus function % is a good way to create a loop. For example, to animate along the x axis:
void draw() {
float x = 50;
float y = 50;
float lineLength = 50;
int framesToAnimate = 60;
line(x,y,x+float(frameCount % framesToAnimate)/framesToAnimate*lineLength, y);
}
Note: strange things will happen if you don't cast / convert to a float
I use this pretty often to animate other features such as the color.
fill(color(127 + sin(float(frameCount)/90)*127, 0, 0, 127));
If you want to get more advanced, setting vectors and coordinates with PVector. There is a pretty good tutorial on Daniel Shiffman's site.
If you want to set your animation independent of frame rate, you can use mills() instead. That will return current time since the sketch started so you can set something to happen in a given time in seconds.
like for example:
long initialTime;
void setup(){
size(400,200);
initialTime = millis();
}
void draw() {
float x = 50;
float y = 50; //set the multiplier to adjust speed
line(x,y,x+(millis()-initialTime)*0.01, y); //10 px/sec
line(x,y+50,x+(millis()-initialTime)*0.05, y+50); //50 px/sec
line(x,y+100,x+(millis()-initialTime)*0.001, y+100); // 1 px/sec
}
There is also some animation libraries, i've seen some impressive results with some, but i never used them. Here a list.
I'm in the process of writing a little game to teach myself OpenGL rendering as it's one of the things I haven't tackled yet. I used SDL before and this same function, while still performing badly, didn't go as over the top as it does now.
Basically, there is not much going on in my game yet, just some basic movement and background drawing. When I switched to OpenGL, it appears as if it's way too fast. My frames per second exceed 2000 and this function uses up most of the processing power.
What is interesting is that the program in it's SDL version used 100% CPU but ran smoothly, while the OpenGL version uses only about 40% - 60% CPU but seems to tax my graphics card in such a way that my whole desktop becomes unresponsive. Bad.
It's not a too complex function, it renders a 1024x1024 background tile according to the player's X and Y coordinates to give the impression of movement while the player graphic itself stays locked in the center. Because it's a small tile for a bigger screen, I have to render it multiple times to stitch the tiles together for a full background. The two for loops in the code below iterate 12 times, combined, so I can see why this is ineffective when called 2000 times per second.
So to get to the point, this is the evil-doer:
void render_background(game_t *game)
{
int bgw;
int bgh;
int x, y;
glBindTexture(GL_TEXTURE_2D, game->art_background);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &bgw);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &bgh);
glBegin(GL_QUADS);
/*
* Start one background tile too early and end one too late
* so the player can not outrun the background
*/
for (x = -bgw; x < root->w + bgw; x += bgw)
{
for (y = -bgh; y < root->h + bgh; y += bgh)
{
/* Offsets */
int ox = x + (int)game->player->x % bgw;
int oy = y + (int)game->player->y % bgh;
/* Top Left */
glTexCoord2f(0, 0);
glVertex3f(ox, oy, 0);
/* Top Right */
glTexCoord2f(1, 0);
glVertex3f(ox + bgw, oy, 0);
/* Bottom Right */
glTexCoord2f(1, 1);
glVertex3f(ox + bgw, oy + bgh, 0);
/* Bottom Left */
glTexCoord2f(0, 1);
glVertex3f(ox, oy + bgh, 0);
}
}
glEnd();
}
If I artificially limit the speed by called SDL_Delay(1) in the game loop, I cut the FPS down to ~660 ± 20, I get no "performance overkill". But I doubt that is the correct way to go on about this.
For the sake of completion, these are my general rendering and game loop functions:
void game_main()
{
long current_ticks = 0;
long elapsed_ticks;
long last_ticks = SDL_GetTicks();
game_t game;
object_t player;
if (init_game(&game) != 0)
return;
init_player(&player);
game.player = &player;
/* game_init() */
while (!game.quit)
{
/* Update number of ticks since last loop */
current_ticks = SDL_GetTicks();
elapsed_ticks = current_ticks - last_ticks;
last_ticks = current_ticks;
game_handle_inputs(elapsed_ticks, &game);
game_update(elapsed_ticks, &game);
game_render(elapsed_ticks, &game);
/* Lagging stops if I enable this */
/* SDL_Delay(1); */
}
cleanup_game(&game);
return;
}
void game_render(long elapsed_ticks, game_t *game)
{
game->tick_counter += elapsed_ticks;
if (game->tick_counter >= 1000)
{
game->fps = game->frame_counter;
game->tick_counter = 0;
game->frame_counter = 0;
printf("FPS: %d\n", game->fps);
}
render_background(game);
render_objects(game);
SDL_GL_SwapBuffers();
game->frame_counter++;
return;
}
According to gprof profiling, even when I limit the execution with SDL_Delay(), it still spends about 50% of the time rendering my background.
Turn on VSYNC. That way you'll calculate graphics data exactly as fast as the display can present it to the user, and you won't waste CPU or GPU cycles calculating extra frames inbetween that will just be discarded because the monitor is still busy displaying a previous frame.
First of all, you don't need to render the tile x*y times - you can render it once for the entire area it should cover and use GL_REPEAT to have OpenGL cover the entire area with it. All you need to do is to compute the proper texture coordinates once, so that the tile doesn't get distorted (stretched). To make it appear to be moving, increase the texture coordinates by a small margin every frame.
Now down to limiting the speed. What you want to do is not to just plug a sleep() call in there, but measure the time it takes to render one complete frame:
function FrameCap (time_t desiredFrameTime, time_t actualFrameTime)
{
time_t delay = 1000 / desiredFrameTime;
if (desiredFrameTime > actualFrameTime)
sleep (desiredFrameTime - actualFrameTime); // there is a small imprecision here
}
time_t startTime = (time_t) SDL_GetTicks ();
// render frame
FrameCap ((time_t) SDL_GetTicks () - startTime);
There are ways to make this more precise (e.g. by using the performance counter functions on Windows 7, or using microsecond resolution on Linux), but I think you get the general idea. This approach also has the advantage of being driver independent and - unlike coupling to V-Sync - allowing an arbitrary frame rate.
At 2000 FPS it only takes 0.5 ms to render the entire frame. If you want to get 60 FPS then each frame should take about 16 ms. To do this, first render your frame (about 0.5 ms), then use SDL_Delay() to use up the rest of the 16 ms.
Also, if you are interested in profiling your code (which isn't needed if you are getting 2000 FPS!) then you may want to use High Resolution Timers. That way you could tell exactly how long any block of code takes, not just how much time your program spends in it.