Timing function for "Wheel of Fortune" style ticker - objective-c

I'm trying to create a function that changes a label's text. I want it in a "Wheel of Fortune" style where the text changes really quickly at first but slows down over time, then stops at the final text. I have the text as strings in an array.
I'm guessing I would need an exponential function to do this but maths is not my strong point. Currently I'm trying:
- (void)timer {
float time = 0.5;
float increase = 0.05;
for(int x = 0; x < 100; x++) {
sleep(time);
time = time + increase;
NSLog(#"%f", time); //Log to show time of each iteration
}
}
I don't need help linking it up with labels etc., I just need help to get the timing right.

Related

How to overlap a buffer in Objective-C

I'm trying to do FFT on the iPhone, and I realised that I had not overlapped my input prior to windowing. I was wondering if anyone could give me some insight on to how to properly overlap my input buffer.
I am wanting to overlap bufferSamples by a factor of 4, and I understand that it is to be done using memove functions, but I can't figure out how to get it to work in regard to overlapping.
enum
{
frameSize = 2048,
overlap = 4,
range = 8192,
step = frameSize/overlap,
};
static COMPLEX_SPLIT A;
// For each sample in buffer...
for (int j = 0; j < audioBufferList.mNumberBuffers; j++)
{
// Declaring samples from audio buffer list
SInt16 *bufferSamples = (SInt16*)audioBufferList.mBuffers[j].mData;
// Overlapping here?
////////////////////////
//// vDSP FUNCTIONS ////
////////////////////////
// Creating Hann window function
for (int i = 0; i < frameSize; i++)
{
double window = 0.5 * (1.0 - cos((2.0 * M_PI * i) / (frameSize - 1)));
// Applying window to each sample
A.realp[i] = window * bufferSamples[i];
A.imagp[i] = 0;
}
// Further DSP...
To get an overlap factor of 4, you need to save the last 75% of the data that, before windowing, was input to the previous FFT. Then use that saved data as the first 75% of the current FFT, with only the last 25% from current or not yet used data. memmove can be used to copy data to and from the temporary save data buffers. Repeat as necessary to use up the data available.

Something weird in for loop speed

here is a part of my program code:
int test;
for(uint i = 0; i < 1700; i++) {
test++;
}
the whole program takes 0.5 seconds to finish, but when I change it to:
int test[1];
for(uint i = 0; i < 1700; i++) {
test[0]++;
}
it will takes 3.5 seconds! and when I change the int to double, it will gets very worse:
double test;
for(uint i = 0; i < 1700; i++) {
test++;
}
it will takes about 18 seconds to finish !!!
I have to increase an int array element and a double variable in my real for loop, and it will takes about 30 seconds!
What's happening here?! Why should it takes that much time for just an increment?!
I know a floating point data type like double has different structure from a fixed point data type like int, but is it the only cause for such a big different time? and what about the second example which is also an int array element?!
Thanks
You have answered your question yourself.
float (double) operations are different from integer ones. Even if you just add 1.0f.
Your second example takes longer than the first one just because you added some pointer refernces. An array in C is -bottom down- not much different from a pointer to the first element. Accessing any element, even the first one, would cause the machine code to load the starting address of the array multiply the index (0 in this case) with the length of each member (4 or whatever bytes int has) and add that (0) to the pointer. Then it has to dereference the pointer, meaning to acutally load the value at that very address. Add one and write back the result.
A smart modern compiler should optimize this a bit. When you want to avoid this optimization, then modify the code a bit and don`t use a constant for the index.
I never tried that with a modern objective-c compiler. But I guess that this code would take much loger than 3.5s to run:
int test[2];
int index = 0;
for(uint i = 0; i < 1700; i++) {
test[index]++;
}
If that does not make much of a change then try this:
-(void)foo:(int)index {
int test[2];
for(uint i = 0; i < 1700; i++) {
test[index]++;
}
}
and then call foo:0;
Give it a try and let us know :)

NSString constrainedToSize method?

Not to get confused with the NSString sizeWithFont method that returns a CGSize, what I'm looking for is a method that returns an NSString constrained to a certain CGSize. The reason I want to do this is so that when drawing text with Core Text, I can get append an ellipses (...) to the end of the string. I know NSString's drawInRect method does this for me, but I'm using Core Text, and kCTLineBreakByTruncatingTail truncates the end of each line rather than the end of the string.
There's this method that I found that truncates a string to a certain width, and it's not that hard to change it to make it work for a CGSize, but the algorithm is unbelievably slow for long strings, and is practically unusable. (It took over 10 seconds to truncate a long string). There has to be a more "computer science"/mathematical algorithm way to do this faster. Anyone daring enough to try to come up with a faster implementation?
Edit: I've managed to make this in to a binary algorithm:
-(NSString*)getStringByTruncatingToSize:(CGSize)size string:(NSString*)string withFont:(UIFont*)font
{
int min = 0, max = string.length, mid;
while (min < max) {
mid = (min+max)/2;
NSString *currentString = [string substringWithRange:NSMakeRange(min, mid - min)];
CGSize currentSize = [currentString sizeWithFont:font constrainedToSize:CGSizeMake(size.width, MAXFLOAT)];
if (currentSize.height < size.height){
min = mid + 1;
} else if (currentSize.height > size.height) {
max = mid - 1;
} else {
break;
}
}
NSMutableString *finalString = [[string substringWithRange:NSMakeRange(0, min)] mutableCopy];
if(finalString.length < self.length)
[finalString replaceCharactersInRange:NSMakeRange(finalString.length - 3, 3) withString:#"..."];
return finalString;
}
The problem is that this sometimes cuts the string too short when it has room to spare. I think this is where that last condition comes in to play. How do I make sure it doesn't cut off too much?
Good news! There is a "computer science/mathematical way" to do this faster.
The example you link to does a linear search: it just chops one character at a time from the end of the string until it's short enough. So, the amount of time it takes will scale linearly with the length of the string, and with long strings it will be really slow, as you've discovered.
However, you can easily apply a binary search technique to the string. Instead of starting at the end and dropping off one character at a time, you start in the middle:
THIS IS THE STRING THAT YOU WANT TO TRUNCATE
^
You compute the width of "THIS IS THE STRING THAT". If it is too wide, you move your test point to the midpoint of the space on the left. Like this:
THIS IS THE STRING THAT YOU WANT TO TRUNCATE
^ |
On the other hand, if it isn't wide enough, you move the test point to the midpoint of the other half:
THIS IS THE STRING THAT YOU WANT TO TRUNCATE
| ^
You repeat this until you find the point that is just under your width limit. Because you're dividing your search area in half each time, you'll never need to compute the width more than log2 N times (where N is the length of the string) which doesn't grow very fast, even for very long strings.
To put it another way, if you double the length of your input string, that's only one additional width computation.
Starting with Wikipedia's binary search sample, here's an example. Note that since we're not looking for an exact match (you want largest that will fit) the logic is slightly different.
int binary_search(NSString *A, float max_width, int imin, int imax)
{
// continue searching while [imin,imax] is not empty
while (imax >= imin)
{
/* calculate the midpoint for roughly equal partition */
int imid = (imin + imax) / 2;
// determine which subarray to search
float width = ComputeWidthOfString([A substringToIndex:imid]);
if (width < max_width)
// change min index to search upper subarray
imin = imid + 1;
else if (width > max_width )
// change max index to search lower subarray
imax = imid - 1;
else
// exact match found at index imid
return imid;
}
// Normally, this is the "not found" case, but we're just looking for
// the best fit, so we return something here.
return imin;
}
You need to do some math or testing to figure out what's the right index at the bottom, but it's definitely imin or imax, plus or minus one.

wxGrid shows large empty border on right

By default, wxGrid shows a small ( 10 pixels? ) blank border on the right hand side, after the last column. Calling SetMargins() has no effect on it.
It is irritating, but I can live with it.
However, if I set the the row label width to zero then the blank border grows much larger. If I have just one column, the effect is horrible. It looks like wxGrid is leaving room for the non-existent label.
myPatGrid = new wxGrid(panel,IDC_PatGrid,wxPoint(10,10),wxSize(150,300) );
myPatGrid->SetRowLabelSize(0);
myPatGrid->CreateGrid(200,1);
myPatGrid->SetColLabelValue(0,L"Patient IDs");
Is there a way to remove this border?
Note that if I set the size of the wxgrid window to narrower in the wxGrid constructor, hoping to hide the border, I now get a horizontal scroll bar which is horrible too.
myPatGrid = new wxGrid(panel,IDC_PatGrid,wxPoint(10,10),wxSize(100,300) );
myPatGrid->SetRowLabelSize(0);
myPatGrid->CreateGrid(200,1);
myPatGrid->SetColLabelValue(0,L"Patient IDs");
Gives me
I just upgraded to wxWidgets v2.8.12 - problem still exists.
I didn't find an "autosize" function to fit columns in the grid space.
As a workaround, if you have only one column set its width to
myPatGrid->SetColMinimalWidth(0, grid_width - wxSYS_VSCROLL_X - 10)
otherwise, sum other column's width and adapt the last one to fit the remaining space (minus scrollbar width, minus 10).
EDIT: I have a working example, which produces this:
int gridSize = 150;
int minSize = gridSize - wxSYS_VSCROLL_X - 2; // scrollbar appear if higher
grid->SetRowLabelSize(0);
grid->SetColMinimalWidth(0, minSize);
grid->SetColSize(0, minSize); // needed, otherwise column will not resize
grid->ForceRefresh();
grid->SetColLabelValue(0, "COORD");
EDIT2: I succeded to remove the remaining margin with this:
int gridSize = 150;
int minSize = gridSize - 16; // trial & error
grid->SetMargins(0 - wxSYS_VSCROLL_X, 0);
Solving something similar yesterday I would like to contribute with following what does the job for me. Perhaps this is going to help someone else:
void RecalculateGridSize(wxGrid *grid, int cols) {
if (grid == NULL)
return;
grid->AutoSizeColumns();
float cumulative = 0, param = 0;
for (int i = 0; i < cols; ++i)
cumulative += grid->GetColSize(i);
//not stretching when client size lower then calculated
if(grid->GetClientSize().x < cumulative)
return;
param = (float) grid->GetClientSize().x / cumulative;
for (int i = 0; i < cols; ++i) {
if (i != cols - 1)
grid->SetColSize(i, int(grid->GetColSize(i)*param) - 2); //-2 for each line per column
else
grid->SetColSize(i, int(grid->GetColSize(i)*param)); //leaving last column full to fill properly
}
}
Note: This is doing particularly well when linked with OnSize() event.

Looping moving background objective-C

I am testing a background-loop animation where there will be to images both 1024x768 pixels in dimension, move leftwards, go offscreen, then jump back to the other side, and repeat.
I was able to do this by creating a constant speed for both background image to move (successful), and then I tried the following code to make it jump, but there was a problem:
if((background.center.x) < -511){
background.center = CGPointMake(1536, background.center.y);
}
if((background2.center.x) < -511){
background2.center = CGPointMake(1536, background2.center.y);
}
Somehow this is not working the way I expected. It leaves a few pixels of gap every time, and I am confused why. Does anyone know what's causing this to happen and how to fix it? Thanks!
It seems like you have forgotten to take into account the distance moved. The greater than expression might have been triggered because you moved to far. I guess your movement is larger than 1 pixel/frame.
I am not sure what kind of values that are feeding your movement but I think to take into account the movement you should do something like...
if ((background.center.x) < -511){
CGFloat dist = background.center.x + 512;
background.center = CGPointMake(1536+dist, background.center.y);
}
if ((background2.center.x) < -511){
CGFloat dist = background2.center.x + 512;
background2.center = CGPointMake(1536+dist, background2.center.y);
}
Rather than have the two images move (sort of) independently, I would keep track of a single backgroundPosition variable and then constantly update the position of both images relative to that one position. This should keep everything nice and tidy:
CGFloat const backgroundWidth = 1024;
CGFloat const backgroundSpeed = 2;
- (void)animateBackground {
backgroundPosition -= backgroundSpeed;
if (backgroundPosition < 0) {
backgroundPosition += backgroundWidth;
}
background1.center.x = backgroundPosition - backgroundWidth/2;
background2.center.x = backgroundPosition + backgroundWidth/2;
}