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; }