A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

usbaudio: convert to asynchronous operation

Add feedback not based on samples used, but on buffers filled - idea
being we can do "PID" (someone who has actually implemented Real PID
could probably rewrite the calculation) based on how many buffers
we have filled versus the ideal buffer filled level (16).

Feedback is based on a historical average of the last two feedback
intervals.

This feedback math is done as fixed-point math to keep floats out of core. Note that a couple division operations needed to be strategically staged to avoid overflow or truncation.

Floats are still used for debug screen printout.

Also fixed a typo in the definition of usb_audio_control_request()

Host:
Linux: works
MacOS: works
Windows: Feedback does not work! It appears that Windows may not
support asynchronous devices at all. Playback may "work",
but results will vary as the number of buffers filled will
drift over time.

Change-Id: I027feb16705e6e46c1144b1d08920b53de42cb26

+439 -65
+6 -1
apps/debug_menu.c
··· 2542 2542 simplelist_addline("out ep: 0x%X in ep: 0x%X", usb_audio_get_out_ep(), usb_audio_get_in_ep()); 2543 2543 simplelist_addline("Volume: %d", usb_audio_get_cur_volume()); 2544 2544 simplelist_addline("Playback Frequency: %lu", usb_audio_get_playback_sampling_frequency()); 2545 - simplelist_addline("Buffers filled: %d", usb_audio_get_prebuffering()); 2545 + simplelist_addline("Frames dropped: %d", usb_audio_get_frames_dropped()); 2546 + simplelist_addline("Buffers filled: %f", (double)usb_audio_get_prebuffering_avg()/(1<<16)); // convert from 16.16 fixed to float 2547 + simplelist_addline("Min: %d / Max: %d", usb_audio_get_prebuffering_maxmin(false), usb_audio_get_prebuffering_maxmin(true)); 2548 + simplelist_addline("Samples used per Frame: %f", (double)usb_audio_get_samplesperframe()/(1<<16)); // convert from 16.16 fixed to float 2549 + simplelist_addline("Samples received per frame: %f", (double)usb_audio_get_samples_rx_perframe()/(1<<16)); // convert from 16.16 fixed to float 2550 + simplelist_addline("Samples diff: %f", (double)(usb_audio_get_samplesperframe()-usb_audio_get_samples_rx_perframe())/(1<<16)); // convert from 16.16 fixed to float 2546 2551 simplelist_addline("%s", usb_audio_get_underflow()?"UNDERFLOW!":" "); 2547 2552 simplelist_addline("%s", usb_audio_get_overflow()?"OVERFLOW!":" "); 2548 2553 simplelist_addline("%s", usb_audio_get_alloc_failed()?"ALLOC FAILED!":" ");
+4
firmware/export/usb_ch9.h
··· 345 345 #define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2) 346 346 #define USB_ENDPOINT_SYNC_SYNC (3 << 2) 347 347 348 + #define USB_ENDPOINT_USAGE_DATA (0 << 4) /* in bmAttributes */ 349 + #define USB_ENDPOINT_USAGE_FEEDBACK (1 << 4) 350 + #define USB_ENDPOINT_USAGE_IMPLICITFEEDBACK (2 << 4) 351 + 348 352 #define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ 349 353 #define USB_ENDPOINT_XFER_CONTROL 0 350 354 #define USB_ENDPOINT_XFER_ISOC 1
+329 -57
firmware/usbstack/usb_audio.c
··· 16 16 * KIND, either express or implied. 17 17 * 18 18 ****************************************************************************/ 19 + 20 + /* NOTE 21 + * 22 + * This is USBAudio 1.0. USBAudio 2.0 is notably _not backwards compatible!_ 23 + * USBAudio 1.0 over _USB_ 2.0 is perfectly valid! 24 + * 25 + * Relevant specifications are USB 2.0 and USB Audio Class 1.0. 26 + */ 27 + 19 28 #include "string.h" 20 29 #include "system.h" 21 30 #include "usb_core.h" ··· 37 46 #define LOGF_ENABLE 38 47 #include "logf.h" 39 48 49 + // is there a "best practices" for converting between floats and fixed point? 50 + // NOTE: SIGNED 51 + #define TO_16DOT16_FIXEDPT(val) ((int32_t)(val) * (1<<16)) 52 + #define TO_DOUBLE(val) ((double)(val) / (1<<16)) 53 + 40 54 /* Audio Control Interface */ 41 55 static struct usb_interface_descriptor 42 56 ac_interface = ··· 58 72 .bLength = USB_AC_SIZEOF_HEADER(1), /* one interface */ 59 73 .bDescriptorType = USB_DT_CS_INTERFACE, 60 74 .bDescriptorSubType = USB_AC_HEADER, 61 - .bcdADC = 0x0100, 75 + .bcdADC = 0x0100, /* Identifies this as usb audio class 1.0 */ 62 76 .wTotalLength = 0, /* fill later */ 63 77 .bInCollection = 1, /* one interface */ 64 78 .baInterfaceNr = {0}, /* fill later */ ··· 140 154 .bDescriptorType = USB_DT_INTERFACE, 141 155 .bInterfaceNumber = 0, 142 156 .bAlternateSetting = 1, 143 - .bNumEndpoints = 1, 157 + .bNumEndpoints = 2, // iso audio, iso feedback 144 158 .bInterfaceClass = USB_CLASS_AUDIO, 145 159 .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, 146 160 .bInterfaceProtocol = 0, ··· 176 190 } 177 191 }; 178 192 179 - /* 180 - * TODO: 181 - * It appears that "Adaptive" sync mode means it it the device's duty 182 - * to adapt its consumption rate of data to whatever the host sends, which 183 - * has the possibility to cause trouble in underflows/overflows, etc. 184 - * In practice, this is probably not a large concern, as we have a fairly large 185 - * amount of buffering in the PCM system. 186 - * An improvement may be to use "Asynchronous", but this is more complicated 187 - * due to the need to inform the host about how fast the device will consume 188 - * the data. 189 - * So implementation of "Asynchronous" mode will be left for a later improvement. 190 - */ 191 - static struct usb_iso_audio_endpoint_descriptor 192 - out_iso_ep = 193 + static struct usb_as_iso_audio_endpoint 194 + as_iso_audio_out_ep = 193 195 { 194 - .bLength = sizeof(struct usb_iso_audio_endpoint_descriptor), 196 + .bLength = sizeof(struct usb_as_iso_audio_endpoint), 195 197 .bDescriptorType = USB_DT_ENDPOINT, 196 198 .bEndpointAddress = USB_DIR_OUT, /* filled later */ 197 - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE, 199 + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_USAGE_DATA, 198 200 .wMaxPacketSize = 0, /* filled later */ 199 - .bInterval = 0, /* filled later */ 201 + .bInterval = 0, /* filled later - 1 for full speed, 4 for high-speed */ 200 202 .bRefresh = 0, 201 - .bSynchAddress = 0 /* filled later */ 203 + .bSynchAddress = 0 /* filled later to the address of as_iso_synch_in_ep */ 204 + }; 205 + 206 + /* 207 + * Updaing the desired sample frequency: 208 + * 209 + * The iso OUT ep is inextricably linked to the feedback iso IN ep 210 + * when using Asynchronous mode. It periodically describes to the host 211 + * how fast to send the data. 212 + * 213 + * Some notes from the usbaudio 1.0 documentation: 214 + * - bSyncAddress of the iso OUT ep must be set to the address of the iso IN feedback ep 215 + * - bSyncAddress of the iso IN feedback ep must be zero 216 + * - F_f (desired sampling frequency) describes directly the number of samples the endpoint 217 + * wants to receive per frame to match the actual sampling frequency F_s 218 + * - There is a value, (2^(10-P)), which is how often (in 1mS frames) the F_f value will be sent 219 + * - P appears to be somewhat arbitrary, though the spec wants it to relate the real sample rate 220 + * F_s to the master clock rate F_m by the relationship (F_m = F_s * (2^(P-1))) 221 + * - The above description of P is somewhat moot because of how much buffering we have. I suspect it 222 + * was written for devices with essentially zero buffering. 223 + * - bRefresh of the feedback endpoint descriptor should be set to (10-P). This can range from 1 to 9. 224 + * A value of 1 would mean refreshing every 2^1 mS = 2 mS, a value of 9 would mean refreshing every 225 + * 2^9 mS = 512 mS. 226 + * - The F_f value should be encoded in "10.10" format, but justified to the leftmost 24 bits, 227 + * so it ends up looking like "10.14" format. This format is 3 bytes long. On USB 2.0, it seems that 228 + * the USB spec overrides the UAC 1.0 spec here, so high-speed bus operation needs "16.16" format, 229 + * in a 4 byte packet. 230 + */ 231 + #define FEEDBACK_UPDATE_RATE_P 5 232 + #define FEEDBACK_UPDATE_RATE_REFRESH (10-FEEDBACK_UPDATE_RATE_P) 233 + #define FEEDBACK_UPDATE_RATE_FRAMES (0x1<<FEEDBACK_UPDATE_RATE_REFRESH) 234 + static struct usb_as_iso_synch_endpoint 235 + as_iso_synch_in_ep = 236 + { 237 + .bLength = sizeof(struct usb_as_iso_synch_endpoint), 238 + .bDescriptorType = USB_DT_ENDPOINT, 239 + .bEndpointAddress = USB_DIR_IN, /* filled later */ 240 + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_NONE | USB_ENDPOINT_USAGE_FEEDBACK, 241 + .wMaxPacketSize = 4, 242 + .bInterval = 0, /* filled later - 1 or 4 depending on bus speed */ 243 + .bRefresh = FEEDBACK_UPDATE_RATE_REFRESH, /* This describes how often this ep will update F_f (see above) */ 244 + .bSynchAddress = 0 /* MUST be zero! */ 202 245 }; 203 246 204 - static struct usb_as_iso_endpoint 205 - as_out_iso_ep = 247 + static struct usb_as_iso_ctrldata_endpoint 248 + as_iso_ctrldata_samfreq = 206 249 { 207 - .bLength = sizeof(struct usb_as_iso_endpoint), 250 + .bLength = sizeof(struct usb_as_iso_ctrldata_endpoint), 208 251 .bDescriptorType = USB_DT_CS_ENDPOINT, 209 252 .bDescriptorSubType = USB_AS_EP_GENERAL, 210 253 .bmAttributes = USB_AS_EP_CS_SAMPLING_FREQ_CTL, ··· 216 259 { 217 260 (struct usb_descriptor_header *) &ac_header, 218 261 (struct usb_descriptor_header *) &ac_playback_input, 262 + (struct usb_descriptor_header *) &ac_playback_feature, 219 263 (struct usb_descriptor_header *) &ac_playback_output, 220 - (struct usb_descriptor_header *) &ac_playback_feature 221 264 }; 222 265 223 266 #define AC_CS_DESCRIPTORS_LIST_SIZE (sizeof(ac_cs_descriptors_list)/sizeof(ac_cs_descriptors_list[0])) 224 267 268 + // TODO: AudioControl Interrupt endpoint to inform the host that changes were made on-device! 269 + // The most immediately useful of this capability is volume and mute changes. 225 270 static const struct usb_descriptor_header* const usb_descriptors_list[] = 226 271 { 227 272 /* Audio Control */ ··· 237 282 (struct usb_descriptor_header *) &as_interface_alt_playback, 238 283 (struct usb_descriptor_header *) &as_playback_cs_interface, 239 284 (struct usb_descriptor_header *) &as_playback_format_type_i, 240 - (struct usb_descriptor_header *) &out_iso_ep, 241 - (struct usb_descriptor_header *) &as_out_iso_ep, 285 + /* NOTE: the order of these three is important for maximum compatibility. 286 + * Synch ep should follow iso out ep, with ctrldata descriptor coming first. */ 287 + (struct usb_descriptor_header *) &as_iso_ctrldata_samfreq, 288 + (struct usb_descriptor_header *) &as_iso_audio_out_ep, 289 + (struct usb_descriptor_header *) &as_iso_synch_in_ep, 242 290 }; 243 291 244 292 #define USB_DESCRIPTORS_LIST_SIZE (sizeof(usb_descriptors_list)/sizeof(usb_descriptors_list[0])) ··· 249 297 static int as_playback_freq_idx; /* audio playback streaming frequency index (in hw_freq_sampr) */ 250 298 251 299 static int out_iso_ep_adr; /* output isochronous endpoint */ 252 - static int in_iso_ep_adr; /* input isochronous endpoint */ 300 + static int in_iso_feedback_ep_adr; /* input feedback isochronous endpoint */ 253 301 254 302 /* small buffer used for control transfers */ 255 303 static unsigned char usb_buffer[128] USB_DEVBSS_ATTR; ··· 314 362 /* usb overflow ? */ 315 363 bool usb_rx_overflow; 316 364 365 + /* feedback variables */ 366 + #define USB_FRAME_MAX 0x7FF 367 + #define NR_SAMPLES_HISTORY 32 368 + int32_t samples_fb; 369 + int32_t buffers_filled_old; 370 + long buffers_filled_accumulator; 371 + long buffers_filled_accumulator_old; 372 + int buffers_filled_avgcount; 373 + int buffers_filled_avgcount_old; 374 + static uint8_t sendFf[4] USB_DEVBSS_ATTR; 375 + static bool sent_fb_this_frame = false; 376 + int fb_startframe = 0; 377 + bool send_fb = false; 378 + 379 + /* debug screen sample count display variables */ 380 + static unsigned long samples_received; 381 + static unsigned long samples_received_last; 382 + int32_t samples_received_report; 383 + int buffers_filled_min; 384 + int buffers_filled_min_last; 385 + int buffers_filled_max; 386 + int buffers_filled_max_last; 387 + 388 + /* frame drop recording variables */ 389 + static int last_frame = 0; 390 + static int frames_dropped = 0; 391 + 317 392 /* Schematic view of the RX situation: 318 393 * (in case NR_BUFFERS = 4) 319 394 * ··· 352 427 return arr[0] | (arr[1] << 8) | (arr[2] << 16); 353 428 } 354 429 430 + // size is samples per frame! 431 + static void encodeFBfixedpt(uint8_t arr[4], int32_t value, bool portspeed) 432 + { 433 + uint32_t fixedpt; 434 + // high-speed 435 + if (portspeed) 436 + { 437 + // Q16.16 438 + fixedpt = value; 439 + 440 + arr[0] = (fixedpt & 0xFF); 441 + arr[1] = (fixedpt>>8) & 0xFF; 442 + arr[2] = (fixedpt>>16) & 0xFF; 443 + arr[3] = (fixedpt>>24) & 0xFF; 444 + } 445 + else // full-speed 446 + { 447 + // Q16.16 --> Q10.10 --> Q10.14 448 + fixedpt = value / (1<<2); // convert from Q16.16 to Q10.14 449 + 450 + // then aligned so it's more like Q10.14 451 + // NOTE: this line left for posterity 452 + // fixedpt = fixedpt << (4); 453 + 454 + arr[0] = (fixedpt & 0xFF); 455 + arr[1] = (fixedpt>>8) & 0xFF; 456 + arr[2] = (fixedpt>>16) & 0xFF; 457 + } 458 + 459 + } 460 + 355 461 static void set_playback_sampling_frequency(unsigned long f) 356 462 { 357 463 // only values 44.1k and higher (array is in descending order) ··· 373 479 374 480 unsigned long usb_audio_get_playback_sampling_frequency(void) 375 481 { 376 - logf("usbaudio: get playback sampl freq %lu Hz", 377 - hw_freq_sampr[as_playback_freq_idx]); 482 + // logf("usbaudio: get playback sampl freq %lu Hz", hw_freq_sampr[as_playback_freq_idx]); 378 483 return hw_freq_sampr[as_playback_freq_idx]; 379 484 } 380 485 ··· 382 487 { 383 488 unsigned int i; 384 489 /* initialized tSamFreq array */ 385 - logf("usbaudio: supported frequencies"); 490 + logf("usbaudio: (init) supported frequencies"); 386 491 // only values 44.1k and higher (array is in descending order) 387 492 for(i = 0; i <= HW_FREQ_44; i++) 388 493 { ··· 393 498 394 499 int usb_audio_request_buf(void) 395 500 { 396 - 397 501 // stop playback first thing 398 502 audio_stop(); 399 503 ··· 439 543 return -1; 440 544 } 441 545 442 - in_iso_ep_adr = usb_core_request_endpoint(USB_ENDPOINT_XFER_ISOC, USB_DIR_IN, drv); 443 - if(in_iso_ep_adr < 0) 546 + in_iso_feedback_ep_adr = usb_core_request_endpoint(USB_ENDPOINT_XFER_ISOC, USB_DIR_IN, drv); 547 + if(in_iso_feedback_ep_adr < 0) 444 548 { 445 549 usb_core_release_endpoint(out_iso_ep_adr); 446 - logf("usbaudio: cannot get an out iso endpoint"); 550 + logf("usbaudio: cannot get an in iso endpoint"); 447 551 return -1; 448 552 } 449 553 450 - logf("usbaudio: iso out ep is 0x%x, in ep is 0x%x", out_iso_ep_adr, in_iso_ep_adr); 554 + logf("usbaudio: iso out ep is 0x%x, in ep is 0x%x", out_iso_ep_adr, in_iso_feedback_ep_adr); 451 555 452 - out_iso_ep.bEndpointAddress = out_iso_ep_adr; 453 - out_iso_ep.bSynchAddress = 0; 556 + as_iso_audio_out_ep.bEndpointAddress = out_iso_ep_adr; 557 + as_iso_audio_out_ep.bSynchAddress = in_iso_feedback_ep_adr; 558 + 559 + as_iso_synch_in_ep.bEndpointAddress = in_iso_feedback_ep_adr; 560 + as_iso_synch_in_ep.bSynchAddress = 0; 454 561 455 562 return 0; 456 563 } ··· 462 569 463 570 unsigned int usb_audio_get_in_ep(void) 464 571 { 465 - return in_iso_ep_adr; 572 + return in_iso_feedback_ep_adr; 466 573 } 467 574 468 575 int usb_audio_set_first_interface(int interface) ··· 478 585 unsigned int i; 479 586 unsigned char *orig_dest = dest; 480 587 481 - // logf("get config descriptors"); 588 + logf("get config descriptors"); 482 589 483 590 /** Configuration */ 484 591 ··· 498 605 as_interface_alt_playback.bInterfaceNumber = usb_interface + 1; 499 606 500 607 /* endpoints */ 501 - out_iso_ep.wMaxPacketSize = 1023; /* one micro-frame per transaction */ 608 + as_iso_audio_out_ep.wMaxPacketSize = 1023; 609 + 502 610 /** Endpoint Interval calculation: 503 611 * typically sampling frequency is 44100 Hz and top is 192000 Hz, which 504 612 * account for typical 44100*2(stereo)*2(16-bit) ~= 180 kB/s 505 - * and top 770 kB/s. Since there are 1000 frames per seconds and maximum 613 + * and top 770 kB/s. Since there are ~1000 frames per seconds and maximum 506 614 * packet size is set to 1023, one transaction per frame is good enough 507 - * for over 1 MB/s. At high-speed, add 3 to this value because there are 508 - * 8 = 2^3 micro-frames per frame. 615 + * for over 1 MB/s. 509 616 * Recall that actual is 2^(bInterval - 1) */ 510 - out_iso_ep.bInterval = usb_drv_port_speed() ? 4 : 1; 617 + 618 + /* In simpler language, This is intended to emulate full-speed's 619 + * one-packet-per-frame rate on high-speed, where we have multiple microframes per millisecond. 620 + */ 621 + as_iso_audio_out_ep.bInterval = usb_drv_port_speed() ? 4 : 1; 622 + as_iso_synch_in_ep.bInterval = usb_drv_port_speed() ? 4 : 1; // per spec 623 + 624 + logf("usbaudio: port_speed=%s", usb_drv_port_speed()?"hs":"fs"); 511 625 512 626 /** Packing */ 513 627 for(i = 0; i < USB_DESCRIPTORS_LIST_SIZE; i++) ··· 535 649 *start = rx_buffer + (rx_play_idx * REAL_BUF_SIZE); 536 650 *size = rx_buf_size[rx_play_idx]; 537 651 rx_play_idx = (rx_play_idx + 1) % NR_BUFFERS; 652 + 538 653 /* if usb RX buffers had overflowed, we can start to receive again 539 654 * guard against IRQ to avoid race with completion usb completion (although 540 655 * this function is probably running in IRQ context anyway) */ ··· 556 671 rx_play_idx = 0; 557 672 rx_usb_idx = 0; 558 673 674 + // feedback initialization 675 + fb_startframe = usb_drv_get_frame_number(); 676 + samples_fb = 0; 677 + samples_received_report = 0; 678 + 679 + // debug screen info - frame drop counter 680 + frames_dropped = 0; 681 + last_frame = -1; 682 + buffers_filled_min = -1; 683 + buffers_filled_min_last = -1; 684 + buffers_filled_max = -1; 685 + buffers_filled_max_last = -1; 686 + 687 + // debug screen info - sample counters 688 + samples_received = 0; 689 + samples_received_last = 0; 690 + 559 691 // TODO: implement recording from the USB stream 560 692 #if (INPUT_SRC_CAPS != 0) 561 693 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); ··· 570 702 571 703 static void usb_audio_stop_playback(void) 572 704 { 573 - // logf("usbaudio: stop playback"); 705 + logf("usbaudio: stop playback"); 574 706 if(usb_audio_playing) 575 707 { 576 708 mixer_channel_stop(PCM_MIXER_CHAN_USBAUDIO); 577 709 usb_audio_playing = false; 578 710 } 711 + send_fb = false; 579 712 } 580 713 581 714 int usb_audio_set_interface(int intf, int alt) ··· 642 775 { 643 776 return usb_as_playback_intf_alt; 644 777 } 778 + 779 + int32_t usb_audio_get_samplesperframe(void) 780 + { 781 + return samples_fb; 782 + } 645 783 646 - static bool usb_audio_as_playback_endpoint_request(struct usb_ctrlrequest* req, void *reqdata) 784 + int32_t usb_audio_get_samples_rx_perframe(void) 785 + { 786 + return samples_received_report; 787 + } 788 + 789 + static bool usb_audio_as_ctrldata_endpoint_request(struct usb_ctrlrequest* req, void *reqdata) 647 790 { 648 791 /* only support sampling frequency */ 649 792 if(req->wValue != (USB_AS_EP_CS_SAMPLING_FREQ_CTL << 8)) ··· 707 850 int ep = req->wIndex & 0xff; 708 851 709 852 if(ep == out_iso_ep_adr) 710 - return usb_audio_as_playback_endpoint_request(req, reqdata); 853 + return usb_audio_as_ctrldata_endpoint_request(req, reqdata); 711 854 else 712 855 { 713 856 logf("usbaudio: unhandled ep req (ep=%d)", ep); ··· 727 870 { 728 871 logf("usbaudio: mute !"); 729 872 tmp_saved_vol = sound_current(SOUND_VOLUME); 730 - // sound_set_volume(sound_min(SOUND_VOLUME)); 873 + 731 874 // setvol does range checking for us! 732 875 global_status.volume = sound_min(SOUND_VOLUME); 733 876 setvol(); ··· 736 879 else if(value == 0) 737 880 { 738 881 logf("usbaudio: not muted !"); 739 - // sound_set_volume(tmp_saved_vol); 882 + 740 883 // setvol does range checking for us! 741 884 global_status.volume = tmp_saved_vol; 742 885 setvol(); ··· 808 951 809 952 logf("usbaudio: set volume=%d dB", usb_audio_volume_to_db(value, sound_numdecimals(SOUND_VOLUME))); 810 953 811 - // sound_set_volume(usb_audio_volume_to_db(value, sound_numdecimals(SOUND_VOLUME))); 812 954 // setvol does range checking for us! 813 955 // we cannot guarantee the host will send us a volume within our range 814 956 global_status.volume = usb_audio_volume_to_db(value, sound_numdecimals(SOUND_VOLUME)); ··· 829 971 return false; 830 972 } 831 973 832 - logf("usbaudio: get %s volume=%d dB", usb_audio_ac_ctl_req_str(cmd), usb_audio_volume_to_db(*value, sound_numdecimals(SOUND_VOLUME))); 974 + // logf("usbaudio: get %s volume=%d dB", usb_audio_ac_ctl_req_str(cmd), usb_audio_volume_to_db(*value, sound_numdecimals(SOUND_VOLUME))); 833 975 return true; 834 976 } 835 977 ··· 856 998 return false; 857 999 } 858 1000 /* selectors */ 859 - /* all send/received values are integers so already read data if necessary and store in it in an integer */ 1001 + /* all send/received values are integers already - read data if necessary and store in it in an integer */ 860 1002 if(req->bRequest & USB_AC_GET_REQ) 861 1003 { 862 1004 /* get */ ··· 987 1129 } 988 1130 } 989 1131 990 - bool usb_audio_control_request(struct usb_ctrlrequest* req, void *reqdata) 1132 + bool usb_audio_control_request(struct usb_ctrlrequest* req, void *reqdata, unsigned char* dest) 991 1133 { 992 1134 (void) reqdata; 1135 + (void) dest; 993 1136 994 1137 switch(req->bRequestType & USB_RECIP_MASK) 995 1138 { ··· 1034 1177 /* determine if enough prebuffering has been done to restart audio */ 1035 1178 bool prebuffering_done(void) 1036 1179 { 1037 - /* restart audio if at least two buffers are filled */ 1180 + /* restart audio if at least MINIMUM_BUFFERS_QUEUED buffers are filled */ 1038 1181 int diff = (rx_usb_idx - rx_play_idx + NR_BUFFERS) % NR_BUFFERS; 1039 1182 return diff >= MINIMUM_BUFFERS_QUEUED; 1040 1183 } ··· 1044 1187 return (rx_usb_idx - rx_play_idx + NR_BUFFERS) % NR_BUFFERS; 1045 1188 } 1046 1189 1190 + int32_t usb_audio_get_prebuffering_avg(void) 1191 + { 1192 + if (buffers_filled_avgcount == 0) 1193 + { 1194 + return TO_16DOT16_FIXEDPT(usb_audio_get_prebuffering()); 1195 + } else { 1196 + return (TO_16DOT16_FIXEDPT(buffers_filled_accumulator)/buffers_filled_avgcount) + TO_16DOT16_FIXEDPT(MINIMUM_BUFFERS_QUEUED); 1197 + } 1198 + } 1199 + 1200 + int usb_audio_get_prebuffering_maxmin(bool max) 1201 + { 1202 + if (max) 1203 + { 1204 + return buffers_filled_max == -1 ? buffers_filled_max_last : buffers_filled_max; 1205 + } 1206 + else 1207 + { 1208 + return buffers_filled_min == -1 ? buffers_filled_min_last : buffers_filled_min; 1209 + } 1210 + } 1211 + 1047 1212 bool usb_audio_get_underflow(void) 1048 1213 { 1049 1214 return playback_audio_underflow; ··· 1052 1217 bool usb_audio_get_overflow(void) 1053 1218 { 1054 1219 return usb_rx_overflow; 1220 + } 1221 + 1222 + int usb_audio_get_frames_dropped(void) 1223 + { 1224 + return frames_dropped; 1055 1225 } 1056 1226 1057 1227 void usb_audio_transfer_complete(int ep, int dir, int status, int length) ··· 1067 1237 bool usb_audio_fast_transfer_complete(int ep, int dir, int status, int length) 1068 1238 { 1069 1239 (void) dir; 1240 + bool retval = false; 1070 1241 1071 1242 if(ep == out_iso_ep_adr && usb_as_playback_intf_alt == 1) 1072 1243 { 1073 - // logf("usbaudio: frame: %d", usb_drv_get_frame_number()); 1244 + // check for dropped frames 1245 + if (last_frame != usb_drv_get_frame_number()) 1246 + { 1247 + if ((((last_frame + 1) % (USB_FRAME_MAX + 1)) != usb_drv_get_frame_number()) && (last_frame != -1)) 1248 + { 1249 + frames_dropped++; 1250 + } 1251 + last_frame = usb_drv_get_frame_number(); 1252 + } 1253 + 1254 + // If audio and feedback EPs happen to have the same base number (with opposite directions, of course), 1255 + // we will get replies to the feedback here, don't want that to be interpreted as data. 1256 + if (length <= 4) 1257 + { 1258 + return true; 1259 + } 1260 + 1261 + logf("usbaudio: frame: %d bytes: %d", usb_drv_get_frame_number(), length); 1074 1262 if(status != 0) 1075 1263 return true; /* FIXME how to handle error here ? */ 1076 1264 /* store length, queue buffer */ 1077 1265 rx_buf_size[rx_usb_idx] = length; 1078 1266 rx_usb_idx = (rx_usb_idx + 1) % NR_BUFFERS; 1267 + 1268 + // debug screen counter 1269 + samples_received = samples_received + length; 1270 + 1079 1271 /* guard against IRQ to avoid race with completion audio completion */ 1080 1272 int oldlevel = disable_irq_save(); 1081 1273 /* setup a new transaction except if we ran out of buffers */ ··· 1098 1290 mixer_channel_play_data(PCM_MIXER_CHAN_USBAUDIO, playback_audio_get_more, NULL, 0); 1099 1291 } 1100 1292 restore_irq(oldlevel); 1101 - return true; 1293 + retval = true; 1102 1294 } 1103 1295 else 1104 - return false; 1296 + { 1297 + retval = false; 1298 + } 1299 + 1300 + // send feedback value every N frames! 1301 + // NOTE: important that we need to queue this up _the frame before_ it's needed - on MacOS especially! 1302 + if ((usb_drv_get_frame_number()+1) % FEEDBACK_UPDATE_RATE_FRAMES == 0 && send_fb) 1303 + { 1304 + if (!sent_fb_this_frame) 1305 + { 1306 + /* NOTE: the division of frequency must be staged to avoid overflow of 16-bit signed int 1307 + * as well as truncating the result to ones place! 1308 + * Must avoid values > 32,768 (2^15) 1309 + * Largest value: 192,000 --> /10: 19,200 --> /100: 192 1310 + * Smallest value: 44,100 --> /10: 4,410 --> /100: 44.1 1311 + */ 1312 + int32_t samples_base = TO_16DOT16_FIXEDPT(hw_freq_sampr[as_playback_freq_idx]/10)/100; 1313 + int32_t buffers_filled = 0; 1314 + 1315 + if (buffers_filled_avgcount != 0) 1316 + { 1317 + buffers_filled = TO_16DOT16_FIXEDPT((int32_t)buffers_filled_accumulator) / buffers_filled_avgcount; 1318 + } 1319 + buffers_filled_accumulator = buffers_filled_accumulator - buffers_filled_accumulator_old; 1320 + buffers_filled_avgcount = buffers_filled_avgcount - buffers_filled_avgcount_old; 1321 + buffers_filled_accumulator_old = buffers_filled_accumulator; 1322 + buffers_filled_avgcount_old = buffers_filled_avgcount; 1323 + 1324 + // someone who has implemented actual PID before might be able to do this correctly, 1325 + // but this seems to work good enough? 1326 + // Coefficients were 1, 0.25, 0.025 in float math --> 1, /4, /40 in fixed-point math 1327 + samples_fb = samples_base - (buffers_filled/4) + ((buffers_filled_old - buffers_filled)/40); 1328 + buffers_filled_old = buffers_filled; 1329 + 1330 + // must limit to +/- 1 sample from nominal 1331 + samples_fb = samples_fb > (samples_base + TO_16DOT16_FIXEDPT(1)) ? samples_base + TO_16DOT16_FIXEDPT(1) : samples_fb; 1332 + samples_fb = samples_fb < (samples_base - TO_16DOT16_FIXEDPT(1)) ? samples_base - TO_16DOT16_FIXEDPT(1) : samples_fb; 1333 + 1334 + encodeFBfixedpt(sendFf, samples_fb, usb_drv_port_speed()); 1335 + logf("usbaudio: frame %d fbval 0x%02X%02X%02X%02X", usb_drv_get_frame_number(), sendFf[3], sendFf[2], sendFf[1], sendFf[0]); 1336 + usb_drv_send_nonblocking(in_iso_feedback_ep_adr, sendFf, usb_drv_port_speed()?4:3); 1337 + 1338 + // debug screen counters 1339 + // 1340 + // samples_received NOTE: need some "division staging" to not overflow signed 16-bit value 1341 + // samples / (feedback frames * 2) --> samples/2 1342 + // samples_report / (2ch * 2bytes per sample) --> samples/4 1343 + // total: samples/8 1344 + samples_received_report = TO_16DOT16_FIXEDPT(samples_received/8) / FEEDBACK_UPDATE_RATE_FRAMES; 1345 + samples_received = samples_received - samples_received_last; 1346 + samples_received_last = samples_received; 1347 + buffers_filled_max_last = buffers_filled_max; 1348 + buffers_filled_max = -1; 1349 + buffers_filled_min_last = buffers_filled_min; 1350 + buffers_filled_min = -1; 1351 + } 1352 + sent_fb_this_frame = true; 1353 + } 1354 + else 1355 + { 1356 + sent_fb_this_frame = false; 1357 + if (!send_fb) 1358 + { 1359 + // arbitrary wait during startup 1360 + if (usb_drv_get_frame_number() == (fb_startframe + (FEEDBACK_UPDATE_RATE_FRAMES*2))%(USB_FRAME_MAX+1)) 1361 + { 1362 + send_fb = true; 1363 + } 1364 + } 1365 + buffers_filled_accumulator = buffers_filled_accumulator + (usb_audio_get_prebuffering() - MINIMUM_BUFFERS_QUEUED); 1366 + buffers_filled_avgcount++; 1367 + if (usb_audio_get_prebuffering() < buffers_filled_min || buffers_filled_min == -1) 1368 + { 1369 + buffers_filled_min = usb_audio_get_prebuffering(); 1370 + } else if (usb_audio_get_prebuffering() > buffers_filled_max) 1371 + { 1372 + buffers_filled_max = usb_audio_get_prebuffering(); 1373 + } 1374 + } 1375 + 1376 + return retval; 1105 1377 }
+56
firmware/usbstack/usb_audio.h
··· 21 21 22 22 #include "usb_ch9.h" 23 23 24 + /* NOTE 25 + * 26 + * This is USBAudio 1.0. USBAudio 2.0 is notably _not backwards compatible!_ 27 + * USBAudio 1.0 over _USB_ 2.0 is perfectly valid! 28 + * 29 + * Relevant specifications are USB 2.0 and USB Audio Class 1.0. 30 + */ 31 + 24 32 /* 25 33 * usb_audio_request_endpoints(): 26 34 * ··· 179 187 int usb_audio_get_alt_intf(void); 180 188 181 189 /* 190 + * usb_audio_get_samplesperframe(): 191 + * 192 + * Return the samples per frame over the last two feedback cycles 193 + * This is the samples sent to the mixer. 194 + * 195 + * This is returned in floating point 16.16 type. To convert to float, 196 + * do ((double)result / (1<<16)) 197 + */ 198 + int32_t usb_audio_get_samplesperframe(void); 199 + 200 + /* 201 + * usb_audio_get_samplesperframe(): 202 + * 203 + * Return the samples per frame over the last two feedback cycles 204 + * This is the samples received from USB. 205 + * 206 + * This is returned in floating point 16.16 type. To convert to float, 207 + * do ((double)result / (1<<16)) 208 + */ 209 + int32_t usb_audio_get_samples_rx_perframe(void); 210 + 211 + /* 182 212 * usb_audio_get_out_ep(): 183 213 * 184 214 * Return the out (to device) endpoint ··· 200 230 int usb_audio_get_prebuffering(void); 201 231 202 232 /* 233 + * usb_audio_get_prebuffering_avg(): 234 + * 235 + * Return the average number of buffers filled ahead of playback 236 + * over the last two feedback cycles 237 + * 238 + * This is returned in floating point 16.16 type. To convert to float, 239 + * do ((double)result / (1<<16)) 240 + */ 241 + int32_t usb_audio_get_prebuffering_avg(void); 242 + 243 + /* 244 + * usb_audio_get_prebuffering_maxmin(): 245 + * 246 + * Return the max or min number of buffers filled ahead of playback 247 + * over the last feedback cycle 248 + */ 249 + int usb_audio_get_prebuffering_maxmin(bool max); 250 + 251 + /* 203 252 * usb_audio_get_underflow(): 204 253 * 205 254 * Return whether playback is in "underflow" state ··· 212 261 * Return whether usb is in "overflow" state 213 262 */ 214 263 bool usb_audio_get_overflow(void); 264 + 265 + /* 266 + * usb_audio_get_frames_dropped(): 267 + * 268 + * Return the number of frames which have been dropped during playback 269 + */ 270 + int usb_audio_get_frames_dropped(void); 215 271 216 272 /* 217 273 * usb_audio_get_cur_volume():
+44 -7
firmware/usbstack/usb_audio_def.h
··· 18 18 ****************************************************************************/ 19 19 /* Parts of this file are based on Frank Gevaerts work and on audio.h from linux */ 20 20 21 + /* NOTE 22 + * 23 + * This is USBAudio 1.0. USBAudio 2.0 is notably _not backwards compatible!_ 24 + * USBAudio 1.0 over _USB_ 2.0 is perfectly valid! 25 + * 26 + * Relevant specifications are USB 2.0 and USB Audio Class 1.0. 27 + */ 28 + 21 29 #ifndef USB_AUDIO_DEF_H 22 30 #define USB_AUDIO_DEF_H 23 31 ··· 188 196 #define USB_AS_EP_CS_SAMPLING_FREQ_CTL 0x01 189 197 #define USB_AS_EP_CS_PITCH_CTL 0x02 190 198 191 - struct usb_iso_audio_endpoint_descriptor { 199 + // Standard* AudioStreaming Isochronous Audio Data Endpoint Descriptor 200 + // 201 + // *Note, per usbaudio 1.0 this is explititly identical 202 + // to a standard endpoint descriptor but with extra information 203 + struct usb_as_iso_audio_endpoint { 192 204 uint8_t bLength; 193 205 uint8_t bDescriptorType; 194 206 195 207 uint8_t bEndpointAddress; 196 208 uint8_t bmAttributes; 197 209 uint16_t wMaxPacketSize; 198 - uint8_t bInterval; 199 - uint8_t bRefresh; 200 - uint8_t bSynchAddress; 210 + uint8_t bInterval; /* MUST be set to 1 (full speed), +3 for high-speed? */ 211 + uint8_t bRefresh; /* USB Audio Class 1.0 specific: set to 0 */ 212 + uint8_t bSynchAddress; /* USB Audio Class 1.0 specific: used to identify synch ep */ 201 213 } __attribute__ ((packed)); 202 214 203 - struct usb_as_iso_endpoint { 215 + // Class-Specific AudioStreaming Isochronous Audio* (Control) Data Endpoint Descriptor 216 + // 217 + // *The name is a bit misleading, this is for _control_ of audio, 218 + // such as sampling frequency or pitch. 219 + struct usb_as_iso_ctrldata_endpoint { 204 220 uint8_t bLength; 205 221 uint8_t bDescriptorType; /* USB_DT_CS_ENDPOINT */ 206 222 uint8_t bDescriptorSubType; /* USB_AS_EP_GENERAL */ 207 223 uint8_t bmAttributes; 208 - uint8_t bLockDelayUnits; 209 - uint16_t wLockDelay; 224 + uint8_t bLockDelayUnits; /* USB Audio Class 1.0 specific */ 225 + uint16_t wLockDelay; /* USB Audio Class 1.0 specific */ 226 + } __attribute__ ((packed)); 227 + 228 + // Standard* AudioStreaming Isochronous Synch (Feedback) Endpoint Descriptor 229 + // 230 + // *Note, per usbaudio 1.0 this is explititly identical 231 + // to a standard endpoint descriptor but with extra information 232 + struct usb_as_iso_synch_endpoint { 233 + uint8_t bLength; 234 + uint8_t bDescriptorType; /* USB_DT_CS_ENDPOINT */ 235 + 236 + uint8_t bEndpointAddress; /* D7: direction (0 = source, 1 = sink) 237 + * D6..4: Zeros 238 + * D3..0: EP Number */ 239 + uint8_t bmAttributes; /* USBAudio 1.0: 240 + * D3..2: Synch type (00 = None) 241 + * D1..0: Transfer type (01 = Isochronous) 242 + */ 243 + uint16_t wMaxPacketSize; 244 + uint8_t bInterval; /* MUST be set to 1 (full speed), +3 for high-speed? */ 245 + uint8_t bRefresh; /* USB Audio Class 1.0 specific: Range of 1 to 9 (full-speed). high-speed, probably range of 1 to 16? */ 246 + uint8_t bSynchAddress; /* USB Audio Class 1.0 specific: MUST be set to 0 */ 210 247 } __attribute__ ((packed)); 211 248 212 249 #define USB_AS_FORMAT_TYPE_I_UNDEFINED 0x0