{"id":7077,"date":"2026-08-06T12:51:10","date_gmt":"2026-08-06T10:51:10","guid":{"rendered":"https:\/\/www.pschatzmann.ch\/home\/?p=7077"},"modified":"2026-08-06T13:06:19","modified_gmt":"2026-08-06T11:06:19","slug":"receiving-audio-via-usb-a-deep-dive","status":"publish","type":"post","link":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/","title":{"rendered":"Receiving Audio via USB: a Deep Dive"},"content":{"rendered":"<p>In one of my <a href=\"https:\/\/www.pschatzmann.ch\/home\/2026\/06\/22\/usb-audio-class-2-0-for-arduino\/\">last blogs<\/a>, I gave an overview of the USB Class 2 Audio functionality provided by the <a href=\"https:\/\/github.com\/pschatzmann\/arduino-audio-tools\">AudioTools<\/a> library<\/p>\n<p>Many users were struggeling to get the process of receiving data via USB and writing to I2S stable and working properly. In this blog I am providing a deep dive of the releated <strong>challenges and the solution approaches<\/strong>:<\/p>\n<h3 id=\"standard-processing-logic\">Standard Processing Logic<\/h3>\n<p>Here is a simple basic sketch that demonstrates the standard processing logic:<\/p>\n<pre><code class=\"language-C++\">#include \"AudioTools.h\"\r\n#include \"AudioTools\/Communication\/USB\/USBAudioStream.h\"\r\n\r\nAudioInfo info(48000, 2, 16);\r\nUSBAudioStream in;\r\nMeasuringStream out(500, &amp;Serial);\r\nStreamCopy copier(out, in, 80);\r\n\r\nvoid setup() {\r\n  \/\/ Manual begin() is required on core without built-in support e.g. mbed\r\n  \/\/ rp2040\r\n  if (!TinyUSBDevice.isInitialized()) {\r\n    TinyUSBDevice.begin(0);\r\n  }\r\n\r\n  \/\/ Start MeasuringStream so it knows the audio format.\r\n  out.begin(info);\r\n\r\n  \/\/ Register USB audio in RX mode (host \u2192 device, i.e. USB speaker).\r\n  \/\/ begin_usb defaults to false so USB.begin() below controls the start.\r\n  auto config = in.defaultConfig(RX_MODE);\r\n  config.copyFrom(info);\r\n  config.fifo_packets = 32;\r\n  in.begin(config);\r\n\r\n  \/\/ If already enumerated, additional class driver begin() e.g msc, hid, midi\r\n  \/\/ won't take effect until re-enumeration: on ESP32 you can alternatively call USB.begin()\r\n  if (TinyUSBDevice.mounted()) {\r\n    TinyUSBDevice.detach();\r\n    delay(10);\r\n    TinyUSBDevice.attach();\r\n  }\r\n}\r\n\r\nvoid loop() {\r\n  copier.copy();  \/\/ read ep_out_ff \u2192 MeasuringStream\r\n}\r\n<\/code><\/pre>\n<p>The audio data is first copied into an internal USB buffer: it&#8217;s size is defined by the fifo_packets configuration setting. By default 32 ms of data is defined which should usually be big enough.<\/p>\n<p>We use a StreamCopy to copy the data from the USB buffer to the required destionation: in the example above we copy to a MeasuringStream which just reports the data thruput. There is an automatic feedback in place that prevents the USB from over and underflowing which is active when config.enable_feedback_ep is true, which it is by default.<\/p>\n<p>This mechanism works perfectly, when draining of the USB buffer to the destination is quick:\u00a0e.g when writing the data to a SD drive, sending it via UDP or just measuring the content for the volume, thruput frequency using FFT etc.<\/p>\n<h3 id=\"extended-processing-logic-i2s\">Extended Processing Logic: I2S<\/h3>\n<p>The feedback logic described above falls apart when writing the data to I2S which is never faster then what we receive, so as long as the I2S buffer is not full, everything works fine, but as soon as it is filled up, the USB buffer is filling up too quickly for the feedback to be able to react. What we need, is to provide the feedback from the I2S buffer instead. Furtunately we can do this with the help of the <strong>setFeedbackPercent(int percent)<\/strong> method:<\/p>\n<pre><code class=\"language-C++\">#include \"AudioTools.h\"\r\n#include \"AudioTools\/Communication\/USB\/USBAudioStream.h\"\r\n\r\nAudioInfo info(48000, 2, 16);\r\nUSBAudioStream in;\r\nI2SStream out;\r\nStreamCopy copier(out, in);\r\n\r\n\/\/ diagnostics: total capacity of the I2S output buffer, so we can print\r\n\/\/ out.availableForWrite() as a percentage (see setup()\/loop())\r\nsize_t i2s_buffer_capacity = 0;\r\n\r\n\r\nvoid setup() {\r\n  \/\/ Manual begin() is required on core without built-in support e.g. mbed\r\n  \/\/ rp2040\r\n  if (!TinyUSBDevice.isInitialized()) {\r\n    TinyUSBDevice.begin(0);\r\n  }\r\n\r\n  \/\/ Start I2S so it knows the audio format.\r\n  auto cfg = out.defaultConfig(TX_MODE);\r\n  cfg.copyFrom(info);\r\n  cfg.buffer_size = 512;\r\n  cfg.buffer_count = 20;\r\n  out.begin(cfg);\r\n  \/\/ Capture the true capacity directly from the driver right after begin()\r\n  \/\/ (buffer is empty here, so availableForWrite() == full capacity) --\r\n  \/\/ avoids assuming buffer_size\/buffer_count map 1:1 to availableForWrite()'s\r\n  \/\/ units, which they don't on RP2040 (confirmed by the initial &gt;100% readings).\r\n  i2s_buffer_capacity = (size_t)out.availableForWrite();\r\n\r\n  \/\/ Register USB audio in RX mode (host \u2192 device, i.e. USB speaker).\r\n  \/\/ begin_usb defaults to false so USB.begin() below controls the start.\r\n  auto config = in.defaultConfig(RX_MODE);\r\n  config.copyFrom(info);\r\n  \/\/config.fifo_packets = 10;\r\n  in.begin(config);\r\n\r\n  \/\/ If already enumerated, additional class driver begin() e.g msc, hid, midi\r\n  \/\/ won't take effect until re-enumeration: on ESP32 you can alternatively call USB.begin()\r\n  if (TinyUSBDevice.mounted()) {\r\n    TinyUSBDevice.detach();\r\n    delay(10);\r\n    TinyUSBDevice.attach();\r\n  }\r\n}\r\n\r\nvoid loop() {\r\n  \/\/read USB RX buffer \u2192 I2S \r\n  copier.copy();  \/\/ read USB RX buffer \u2192 I2S\r\n  \r\n  \/\/ Update feedback percent\r\n  int i2s_free_pct = 100 * i2s_stream.availableForWrite() \/ (int)i2s_buffer_capacity;\r\n  usb_stream.setFeedbackPercent(i2s_free_pct);\r\n}\r\n<\/code><\/pre>\n<p>This is using the same data flow like the one described in the initial Standard Processing Logic chapter with the only difference that the fill level of the I2S buffer is used for the USB data feedback synchronization. This is preventing the I2S buffer under and overflows.<\/p>\n<h3 id=\"optimized-processing-logic-i2s\">Optimized Processing Logic: I2S<\/h3>\n<p>The copy logic in the loop above adds quite some overhead because we need to make sure that both the I2S and the USB buffers are big enough so that we are not running out of buffers between the different copy calls. We can optimize this by just forwarding the data to the I2S buffer, whenever we received any data. This is exactily what the <strong>setRxDoneCallback()<\/strong> method is desiged for.<\/p>\n<p>Here is the improved sketch:<\/p>\n<pre><code class=\"language-cpp\">#include \"AudioTools.h\"\r\n#include \"AudioTools\/Communication\/USB\/USBAudioStream.h\"\r\n\r\nAudioInfo info(48000, 2, 16);\r\nUSBAudioStream in;\r\nI2SStream out;\r\n\r\n\/\/ diagnostics: total capacity of the I2S output buffer, so we can print\r\n\/\/ out.availableForWrite() as a percentage (see setup()\/loop())\r\nsize_t i2s_buffer_capacity = 0;\r\n\r\n\/\/ Callback to push data to I2S\r\nbool rxDone(USBAudioDeviceBase* p_usb, uint8_t, USBAudioDeviceBase::audiod_function_t*, uint16_t) {\r\n    \/\/ read Audio data\r\n    int len = in.bufferRx().available();\r\n    uint8_t data[len];\r\n    in.bufferRx().readArray(data, len);\r\n\r\n    \/\/ write to I2S\r\n    out.write(data, len);\r\n\r\n    \/\/ update feedback\r\n    int i2s_free_pct = 100 * out.availableForWrite() \/ (int)i2s_buffer_capacity;\r\n    in.setFeedbackPercent(i2s_free_pct);\r\n    return true;\r\n}\r\n\r\nvoid setup() {\r\n  \/\/ Manual begin() is required on core without built-in support e.g. mbed\r\n  \/\/ rp2040\r\n  if (!TinyUSBDevice.isInitialized()) {\r\n    TinyUSBDevice.begin(0);\r\n  }\r\n\r\n  \/\/ Start I2S so it knows the audio format.\r\n  auto cfg = out.defaultConfig(TX_MODE);\r\n  cfg.copyFrom(info);\r\n  cfg.buffer_size = 256;\r\n  cfg.buffer_count = 10;\r\n  out.begin(cfg);\r\n\r\n  \/\/ Capture the true capacity directly from the driver right after begin()\r\n  \/\/ (buffer is empty here, so availableForWrite() == full capacity) --\r\n  \/\/ avoids assuming buffer_size\/buffer_count map 1:1 to availableForWrite()'s\r\n  \/\/ units, which they don't on RP2040 (confirmed by the initial &gt;100% readings).\r\n  i2s_buffer_capacity = (size_t)out.availableForWrite();\r\n\r\n  \/\/ We can avoid a copy in the loop by triggering a write to i2s when we receive a packet\r\n  in.setRxDoneCallback(rxDone);\r\n\r\n  \/\/ Register USB audio in RX mode (host \u2192 device, i.e. USB speaker).\r\n  \/\/ begin_usb defaults to false so USB.begin() below controls the start.\r\n  auto config = in.defaultConfig(RX_MODE);\r\n  config.copyFrom(info);\r\n  config.fifo_packets = 1;\r\n  in.begin(config);\r\n\r\n\r\n  \/\/ If already enumerated, additional class driver begin() e.g msc, hid, midi\r\n  \/\/ won't take effect until re-enumeration: on ESP32 you can alternatively call USB.begin()\r\n  if (TinyUSBDevice.mounted()) {\r\n    TinyUSBDevice.detach();\r\n    delay(10);\r\n    TinyUSBDevice.attach();\r\n  }\r\n}\r\n\r\nvoid loop() {}\r\n<\/code><\/pre>\n<p>The StreamCopy is gone and the corresponding logic is executed in the rxDone callback method: This approch lets us decrease the buffers down to a minimum!<\/p>\n<h3 id=\"i2s-wo-feedback-synchronization\">I2S w\/o Feedback Synchronization<\/h3>\n<p>Last but not least I want do show a scenario where we do not rely on the USB feedback synchronization mechanism, but we use an additional buffer and a resampler to regulate the playback speed automatically: When the buffer is getting full we are playing too slow, if it getting empty the playback is too fast: A Kalman Filter is used to regulate the resampling correction rate:<\/p>\n<pre><code class=\"language-cpp\">#include \"AudioTools.h\"\r\n#include \"AudioTools\/Communication\/USB\/USBAudioStream.h\"\r\n#include \"AudioTools\/Communication\/AdaptiveResamplingStream.h\"\r\n\r\nAudioInfo info(48000, 2, 16);\r\nUSBAudioStream in;\r\nRingBufferSPSC&lt;uint8_t&gt; buffer(1024 * 5);\r\nAdaptiveResamplingStream resampler(buffer, 2.0f);\r\nI2SStream out;\r\nStreamCopy copier(out, resampler);\r\n\r\nbool rxDone(USBAudioDeviceBase* p_usb, uint8_t, USBAudioDeviceBase::audiod_function_t*, uint16_t) {\r\n    int len = in.bufferRx().available();\r\n    uint8_t data[len];\r\n    in.bufferRx().readArray(data, len);\r\n    \/\/ fill resampler\r\n    resampler.write(data, len);\r\n    return true;\r\n}\r\n\r\nvoid setup() {\r\n  \/\/ Manual begin() is required on core without built-in support e.g. mbed\r\n  \/\/ rp2040\r\n  if (!TinyUSBDevice.isInitialized()) {\r\n    TinyUSBDevice.begin(0);\r\n  }\r\n\r\n  \/\/ Start I2S so it knows the audio format.\r\n  auto cfg = out.defaultConfig(TX_MODE);\r\n  cfg.copyFrom(info);\r\n  cfg.buffer_size = 256;\r\n  cfg.buffer_count = 5;\r\n  out.begin(cfg);\r\n\r\n  \/\/ Start the resampler\r\n  resampler.begin(info);\r\n\r\n  \/\/ We can avoid a copy in the loop by triggering a write to i2s when we receive a packet\r\n  in.setRxDoneCallback(rxDone);\r\n\r\n  \/\/ Register USB audio in RX mode (host \u2192 device, i.e. USB speaker).\r\n  \/\/ begin_usb defaults to false so USB.begin() below controls the start.\r\n  auto config = in.defaultConfig(RX_MODE);\r\n  config.copyFrom(info);\r\n  config.fifo_packets = 1;\r\n  config.enable_feedback_ep = false;\r\n  in.begin(config);\r\n\r\n  \/\/ If already enumerated, additional class driver begin() e.g msc, hid, midi\r\n  \/\/ won't take effect until re-enumeration: on ESP32 you can alternatively call USB.begin()\r\n  if (TinyUSBDevice.mounted()) {\r\n    TinyUSBDevice.detach();\r\n    delay(10);\r\n    TinyUSBDevice.attach();\r\n  }\r\n\r\n  MySerial.println(\"USB audio RX + CDC started\");\r\n}\r\n\r\nvoid loop() {\r\n  copier.copy();\r\n}\r\n<\/code><\/pre>\n<p>The <strong>AdaptiveResamplingStream<\/strong> is doing the heavy lifting here: We just need to provide it with a buffer: The single producer and single consumer RingBufferSPSC is used in our case and a max resampling percentage: above we limit it to +- 2%!<\/p>\n<p>Like in the previos example we use the rxDone() callback just to forward the data to the AdaptiveResamplingStream\/RingBufferSPSC!<\/p>\n<h2><span>Conclusion<\/span><\/h2>\n<p class=\"isSelectedEnd\"><span>Receiving audio over USB turned out to be much more than simply reading samples from an endpoint. Behind the seemingly straightforward USB Audio Class lies a surprisingly sophisticated protocol with descriptors, alternate interface settings, clock synchronization, feedback mechanisms, and isochronous transfers that all have to work together before the first audio sample reaches your application.<\/span><\/p>\n<p class=\"isSelectedEnd\"><span>The good news is that once these concepts are understood, the implementation becomes much less mysterious. By leveraging TinyUSB and integrating the USB transport into the AudioTools framework, it is possible to expose USB audio as just another audio source, making it easy to combine with existing codecs, DSP pipelines, recorders, network streams, or file writers without introducing USB-specific code into the application layer.<\/span><\/p>\n<p><span>This project also demonstrates that modern microcontrollers such as the ESP32-S3 and the RP2040 have enough processing power to act as capable USB audio devices while still leaving room for additional audio processing. Whether you want to build a USB microphone, capture audio from a computer, implement a measurement device, or bridge USB audio to another interface, the necessary building blocks are now available.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In one of my last blogs, I gave an overview of the USB Class 2 Audio functionality provided by the AudioTools library Many users were struggeling to get the process of receiving data via USB and writing to I2S stable and working properly. In this blog I am providing a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2487,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","_exactmetrics_skip_tracking":false,"footnotes":""},"categories":[20,22],"tags":[48,65],"class_list":["post-7077","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-arduino","category-machine-sound","tag-tinyusb","tag-usb"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Receiving Audio via USB: a Deep Dive - Phil Schatzmann<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Receiving Audio via USB: a Deep Dive - Phil Schatzmann\" \/>\n<meta property=\"og:description\" content=\"In one of my last blogs, I gave an overview of the USB Class 2 Audio functionality provided by the AudioTools library Many users were struggeling to get the process of receiving data via USB and writing to I2S stable and working properly. In this blog I am providing a [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/\" \/>\n<meta property=\"og:site_name\" content=\"Phil Schatzmann\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-06T10:51:10+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-06T11:06:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2021\/02\/USB.png\" \/>\n\t<meta property=\"og:image:width\" content=\"324\" \/>\n\t<meta property=\"og:image:height\" content=\"155\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"pschatzmann\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"pschatzmann\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/\"},\"author\":{\"name\":\"pschatzmann\",\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/#\\\/schema\\\/person\\\/73a53638a4e34e8373405fd737dac9b1\"},\"headline\":\"Receiving Audio via USB: a Deep Dive\",\"datePublished\":\"2026-08-06T10:51:10+00:00\",\"dateModified\":\"2026-08-06T11:06:19+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/\"},\"wordCount\":787,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/#\\\/schema\\\/person\\\/73a53638a4e34e8373405fd737dac9b1\"},\"image\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.pschatzmann.ch\\\/wp-content\\\/uploads\\\/2021\\\/02\\\/USB.png\",\"keywords\":[\"TinyUSB\",\"USB\"],\"articleSection\":[\"Arduino\",\"Machine Sound\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/\",\"url\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/\",\"name\":\"Receiving Audio via USB: a Deep Dive - Phil Schatzmann\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.pschatzmann.ch\\\/wp-content\\\/uploads\\\/2021\\\/02\\\/USB.png\",\"datePublished\":\"2026-08-06T10:51:10+00:00\",\"dateModified\":\"2026-08-06T11:06:19+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.pschatzmann.ch\\\/wp-content\\\/uploads\\\/2021\\\/02\\\/USB.png\",\"contentUrl\":\"https:\\\/\\\/www.pschatzmann.ch\\\/wp-content\\\/uploads\\\/2021\\\/02\\\/USB.png\",\"width\":324,\"height\":155},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/2026\\\/08\\\/06\\\/receiving-audio-via-usb-a-deep-dive\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Receiving Audio via USB: a Deep Dive\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/#website\",\"url\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/\",\"name\":\"Phil Schatzmann Consulting\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/#\\\/schema\\\/person\\\/73a53638a4e34e8373405fd737dac9b1\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/home\\\/#\\\/schema\\\/person\\\/73a53638a4e34e8373405fd737dac9b1\",\"name\":\"pschatzmann\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/wp-content\\\/uploads\\\/2022\\\/08\\\/pschatzmann.png\",\"url\":\"https:\\\/\\\/www.pschatzmann.ch\\\/wp-content\\\/uploads\\\/2022\\\/08\\\/pschatzmann.png\",\"contentUrl\":\"https:\\\/\\\/www.pschatzmann.ch\\\/wp-content\\\/uploads\\\/2022\\\/08\\\/pschatzmann.png\",\"width\":305,\"height\":305,\"caption\":\"pschatzmann\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.pschatzmann.ch\\\/wp-content\\\/uploads\\\/2022\\\/08\\\/pschatzmann.png\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Receiving Audio via USB: a Deep Dive - Phil Schatzmann","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/","og_locale":"en_US","og_type":"article","og_title":"Receiving Audio via USB: a Deep Dive - Phil Schatzmann","og_description":"In one of my last blogs, I gave an overview of the USB Class 2 Audio functionality provided by the AudioTools library Many users were struggeling to get the process of receiving data via USB and writing to I2S stable and working properly. In this blog I am providing a [&hellip;]","og_url":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/","og_site_name":"Phil Schatzmann","article_published_time":"2026-08-06T10:51:10+00:00","article_modified_time":"2026-08-06T11:06:19+00:00","og_image":[{"width":324,"height":155,"url":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2021\/02\/USB.png","type":"image\/png"}],"author":"pschatzmann","twitter_card":"summary_large_image","twitter_misc":{"Written by":"pschatzmann","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/#article","isPartOf":{"@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/"},"author":{"name":"pschatzmann","@id":"https:\/\/www.pschatzmann.ch\/home\/#\/schema\/person\/73a53638a4e34e8373405fd737dac9b1"},"headline":"Receiving Audio via USB: a Deep Dive","datePublished":"2026-08-06T10:51:10+00:00","dateModified":"2026-08-06T11:06:19+00:00","mainEntityOfPage":{"@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/"},"wordCount":787,"commentCount":0,"publisher":{"@id":"https:\/\/www.pschatzmann.ch\/home\/#\/schema\/person\/73a53638a4e34e8373405fd737dac9b1"},"image":{"@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/#primaryimage"},"thumbnailUrl":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2021\/02\/USB.png","keywords":["TinyUSB","USB"],"articleSection":["Arduino","Machine Sound"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/","url":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/","name":"Receiving Audio via USB: a Deep Dive - Phil Schatzmann","isPartOf":{"@id":"https:\/\/www.pschatzmann.ch\/home\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/#primaryimage"},"image":{"@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/#primaryimage"},"thumbnailUrl":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2021\/02\/USB.png","datePublished":"2026-08-06T10:51:10+00:00","dateModified":"2026-08-06T11:06:19+00:00","breadcrumb":{"@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/#primaryimage","url":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2021\/02\/USB.png","contentUrl":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2021\/02\/USB.png","width":324,"height":155},{"@type":"BreadcrumbList","@id":"https:\/\/www.pschatzmann.ch\/home\/2026\/08\/06\/receiving-audio-via-usb-a-deep-dive\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.pschatzmann.ch\/home\/"},{"@type":"ListItem","position":2,"name":"Receiving Audio via USB: a Deep Dive"}]},{"@type":"WebSite","@id":"https:\/\/www.pschatzmann.ch\/home\/#website","url":"https:\/\/www.pschatzmann.ch\/home\/","name":"Phil Schatzmann Consulting","description":"","publisher":{"@id":"https:\/\/www.pschatzmann.ch\/home\/#\/schema\/person\/73a53638a4e34e8373405fd737dac9b1"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.pschatzmann.ch\/home\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.pschatzmann.ch\/home\/#\/schema\/person\/73a53638a4e34e8373405fd737dac9b1","name":"pschatzmann","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2022\/08\/pschatzmann.png","url":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2022\/08\/pschatzmann.png","contentUrl":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2022\/08\/pschatzmann.png","width":305,"height":305,"caption":"pschatzmann"},"logo":{"@id":"https:\/\/www.pschatzmann.ch\/wp-content\/uploads\/2022\/08\/pschatzmann.png"}}]}},"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/posts\/7077","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/comments?post=7077"}],"version-history":[{"count":8,"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/posts\/7077\/revisions"}],"predecessor-version":[{"id":7085,"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/posts\/7077\/revisions\/7085"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/media\/2487"}],"wp:attachment":[{"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/media?parent=7077"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/categories?post=7077"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pschatzmann.ch\/home\/wp-json\/wp\/v2\/tags?post=7077"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}