The USB Start of Frame packet is sent 1000 times a second from the host to a 
USB device. This packet is used to synchronize audio data being sent to a USB 
sound card. On my system the Start of Frame packet is only sent about 830 times 
a second. This makes audio that is played thru the USB sound card sound kind of 
slow. 

Research into the problem indicates the rate QEMU is suppose to send the packet 
isn't actually being done. How it is set up now the emulated sound hardware 
just assumes the rate it is suppose to send data is what actually happens. This 
isn't true. More variables need to be accounted for. 

What I propose is code be made that actively monitors how many Start of Frame 
packets are being sent in a given second. If the value goes down, then QEMU 
should compensate and try to send more Start of Frame packets. If the value 
goes too high, QEMU should throttle back on Start of Frame packet transmission. 

This code is basically what I think should be put into place to help maintain a 
consistent Start of Frame packet transmission rate. This code is easy to build 
yourself and try out. What it does is makes two threads. One is a consumer and 
the other is a producer thread. The consumer thread monitors the production of 
the producer thread. The consumer thread can actively adjust the rate of the 
producer thread to help it maintain a steady pace. You can test this program 
out by having it run and seeing how its values change when you do CPU intensive 
stuff. What I do is open a YouTube video and scroll in the window really fast 
to see how the program adapts. 

I would like to know if this looks like something that could do a good job at 
maintaining a steady Start of Frame packet transmission rate. I'm hoping for 
more suggestions and improvements I could make to this program before I try to 
implement it in the hcd-ohci.c file. 

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <memory.h>
#include <sched.h>

int counter, producer_sleep_value;
int goalPerSecond = 1000;  // the number of cycles we want the producer 
function to run at in a second
int range = 0;
int delta_sleep_value = 1;
#define MAX_NUM_VALUES 5
int sampleArray[MAX_NUM_VALUES];
int totalValue;
int totalNumberOfValues;
int checkInterval = 1000*30; // in microseconds sets how often we check on the 
producer
float secondsPerCheckInterval;
int goalPerCheckInterval;

// Set some of the global variables
void init()
{
        secondsPerCheckInterval = (float)checkInterval/(1000*1000);
        goalPerCheckInterval = goalPerSecond*secondsPerCheckInterval;   
        producer_sleep_value = 1000;
}

// Obtain a sample count value and place it into the sampleArray
void inputSample(int new_value)
{
        static int index;
        
        totalNumberOfValues++;
        totalValue += new_value;
        sampleArray[index++] = new_value;
        if (index >= MAX_NUM_VALUES) {
                index = 0;
        }
}

// Return the average value of all stored samples
float getAverageValue()
{
        float total = 0;
        for (int i = 0; i < MAX_NUM_VALUES; i++) {
                total += sampleArray[i];
        }
        return total/MAX_NUM_VALUES;
}

// Return the average sample size over the last 30 seconds
float getTotalAverage()
{
        int valuesPerTimeFrame;
        float returnValue;
        
/*      1 value/checkInterval microseconds x 1,000,000 microseconds/second x 30 
seconds = 1 * 1,000,000 * 30 / (checkInterval*1) = 
        30,000,000/checkInterval
*/
        
        valuesPerTimeFrame = 30000000/checkInterval;
        
        // only last 30 seconds
        if (totalNumberOfValues >= valuesPerTimeFrame) {
                totalNumberOfValues = 0;
                totalValue = 0;
                
                printf("30 SECOND RESET ************\n");
        }
        
        if (totalNumberOfValues == 0) {
                returnValue = 0;
        } else {
                returnValue = (float)totalValue/totalNumberOfValues;
        }
        return returnValue;
}


// Figure out how close we are to the goal
float getGoalPercent()
{
        return getTotalAverage() * ((float)1/goalPerCheckInterval) * 100;
}

// Tries to maintain a consistent amount of output from the producer
void *consumerFunc(void *dummy)
{
        while(1) {
                inputSample(counter);
                printf("current = %3d, current average: %3.2f, percent goal: 
%3.2f%c\n", counter, getAverageValue(), getGoalPercent(), '%');
                
        // If the produced amount is too low
                if (counter+range < goalPerCheckInterval) {
                        producer_sleep_value -= delta_sleep_value;
                        printf("Reducing sleep value: %d\n", 
producer_sleep_value);
                        
        // If the produced amount is too high
                } else if (counter-range > goalPerCheckInterval) {
                        producer_sleep_value += delta_sleep_value;
                        printf("Increasing sleep value: %d\n", 
producer_sleep_value);
                }               
                counter = 0;
                usleep(checkInterval);
        }
}

// Produces the output we need
void *producerFunc(void *dummy)
{
        while(1) {
                counter++;
                if (usleep(producer_sleep_value) != 0) 
                        printf("Problem detected with usleep!\n");
        }
}

int main (int argc, const char * argv[]) {

        init();
        
        pthread_t consumerThread, producerThread;
        
        pthread_create(&producerThread, NULL, producerFunc, NULL);
        
        pthread_create(&consumerThread, NULL, consumerFunc, NULL);
        
        pthread_join(producerThread, NULL);
        pthread_join(consumerThread, NULL);
        
        
    return 0;
}

Reply via email to