28浏览
查看: 28|回复: 2

[项目] 【Arduino 动手做】Fast Audio 快速音频频谱分析仪

[复制链接]
这个设计如果不是惊人的话,也没什么!想象一下 3500 个彩色 LED 以非常高的刷新率随着音乐跳舞。所有这些都由一个 2 美元的微控制器控制,该微控制器使用蓝牙连接馈送音乐。

最重要的是,它是开源的,因此任何人都可以构建它!

蓝牙音频连接
使用 14 个串行连接同时驱动 3584 个 PixelLED
I2S 音频输出
120 帧/秒
背景动画
展示的卡通人物
我有没有提到它真的很快?每秒 120 帧...这意味着微控制器将在 8.3 毫秒内驱动 3584 个 LED。每一帧都会显示输入信号的新实际频率数据。

作为奖励,它还将显示动画背景,同时一些卡通人物出现在屏幕上。
吹牛够了......让我们开始构建吧!

用品
1 个 EPS32 开发套件
14 个 LED 面板 16 x 16 个 LED
1 个电源 I 使用的 MeanWell S-25-5 输出 5V / 5A
1 个 PCB (稍后会详细介绍)
一些插座、接头和电线
14 个保险丝 1AT
1 个 I2S 音频板 CJMCU-5102
如果您想听到音频,您还必须连接一组(放大的)扬声器

作这款最令人印象深刻的音频分析仪。

连接一组(放大的)扬声器或模拟扬声器来收听音频。
我首先使用了一组放大的电脑扬声器。
通过蓝牙建立连接
只需找到并连接到名为 Lightnsound 的分析器
开始在设备上播放音频文件
使用按钮打开和关闭背景,使用 OF 更改颜色
使用电位计设置面板的亮度
如果调暗,某些字符可能会消失。

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图2


【Arduino 动手做】Fast Audio  快速音频频谱分析仪图1

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图3

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图4

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图5

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图6

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图7

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图8

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图9

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图10

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图11

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图13

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图12

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图14

驴友花雕  中级技神
 楼主|

发表于 昨天 16:42

【Arduino 动手做】Fast Audio 快速音频频谱分析仪

项目代码

  1. /*
  2. * Bluetooth spectrum analyzer V1.0
  3. TODO
  4. DONE 4096 (is it even possible?) IT IS POSSIBLE
  5. DONE 128 bands might be possible too at 45 fps. It's actually possible at 115 fps.
  6. - Change backgound dynamically
  7. - Animated backgound
  8.   - Perhaps GIFs
  9. DONE Get samples based on the time the last loop took + 1 %
  10. - Retain both channels so I can analyze them both separately if I wawnt to.
  11.   I'll do the conversion to 16 bits on core 1 instead of core 0.
  12.   This might cost me some microseconds but probably not much since they are just bitwise operations.
  13.   - Print the channels mirrored and not mirrored and reversed.
  14. - Hard code several different images. I can also flip them to get variety.
  15. - Flip analyzer upside down. Maybe I can come up with an elegant solution to all these flips.
  16.   Perhaps I can flip images in place or have four different funtions to flip the images dynamically while I do the conversion from 2D to snakewise panel.
  17. - Slow the falling down
  18. - Add peaks that fall down. Maybe they start slowly falling down right away or wait a while.
  19. */
  20. /*
  21.     Bluetooth Speaker Spectrum Analyzer
  22.     Copyright (C) 2022 Antti Yliniemi
  23.    
  24.     This program is free software: you can redistribute it and/or modify
  25.     it under the terms of the GNU Affero General Public License as
  26.     published by the Free Software Foundation, either version 3 of the
  27.     License, or (at your option) any later version.
  28.     This program is distributed in the hope that it will be useful,
  29.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  30.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  31.     GNU Affero General Public License for more details.
  32.     You should have received a copy of the GNU Affero General Public License
  33.     along with this program.  If not, see <https://www.gnu.org/licenses/>.
  34. */
  35. /*
  36. I use these libraries:
  37. https://github.com/hpwit/I2SClocklessLedDriver                // I use a heavily modified version of this library. Will be putting a pull request on his github repo after I've done more testing.
  38. https://github.com/yliniemi/I2SClocklessLedDriver/tree/dev    // This is my current heavily modified version. Perhaps the changes end up in the hpwit's version some day
  39. https://github.com/fakufaku/esp32-fft                         // This is the fastest FFT library I could find. Analyzing 4096 samples takes 5 milliseconds.
  40. https://github.com/pschatzmann/ESP32-A2DP
  41. */
  42. /*
  43. FQBN: esp32:esp32:esp32doit-devkit-v1:PartitionScheme=no_ota
  44. Using board 'esp32doit-devkit-v1' from platform in folder: C:\Users\chord\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6
  45. Using core 'esp32' from platform in folder: C:\Users\chord\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6
  46. Using library ESP32-A2DP at version 1.7.1 in folder: C:\Users\chord\Documents\Arduino\libraries\ESP32-A2DP-main
  47. Using library I2SClocklessLedDriver at version 1.0.0 in folder: C:\Users\chord\Documents\Arduino\libraries\I2SClocklessLedDriver-main
  48. */
  49. #include "BluetoothA2DPSink.h"
  50. extern "C"
  51. {
  52.    #include "fft.h"
  53. }
  54. struct CRGB {
  55.   union {
  56.     struct {
  57.             union {
  58.                 uint8_t r;
  59.                 uint8_t red;
  60.             };
  61.             union {
  62.                 uint8_t g;
  63.                 uint8_t green;
  64.             };
  65.             union {
  66.                 uint8_t b;
  67.                 uint8_t blue;
  68.             };
  69.         };
  70.     uint8_t raw[3];
  71.   };
  72. };
  73. struct FloatOffset
  74. {
  75.   float x;
  76.   float y;
  77. };
  78. #include "constants.h"
  79. // #define STRESS_RAM
  80. enum MapMode
  81. {
  82.   RAINBOW,
  83.   TEXTURES
  84. };
  85. MapMode mapMode = RAINBOW;
  86. //MapMode mapMode = TEXTURES;
  87. #define TILE_WIDTH 32
  88. #define TILE_HEIGHT 32
  89. #define ENABLE_LEDMAP
  90. #define PANEL_WIDTH 16
  91. #define PANEL_HEIGHT 16
  92. #define NUM_PANELS_PER_ROW 7
  93. #define NUM_PANELS_PER_COLUMN 2
  94. #define SCREEN_WIDTH PANEL_WIDTH * NUM_PANELS_PER_ROW
  95. #define SCREEN_HEIGHT PANEL_HEIGHT * NUM_PANELS_PER_COLUMN
  96. #define ATX_POWER_ON 13
  97. #define DEFAULT_SAMPLE_RATE 44100
  98. #define HIGHEST_FREQUENCY 20000
  99. #define START_SPECTRUM_AT_THIS_BIN 3
  100. #define ADD_TO_DELTA 0.5
  101. int isrCounter=0;
  102. // by mark
  103. const uint8_t InterruptPin = 35;
  104. bool Request;
  105. void IRAM_ATTR isr() {
  106. Request = true;
  107. }
  108. int BRIGHTNESSMARK = 0;
  109. #define BRIGHTNESSPOT 34
  110. #define BRIGHTNESSMAX 100  // read brightness potmeter
  111. // TaskHandle_t task1, task2, task3;
  112. // has to be between 512 - 2048
  113. // more than 2048 and we run out of ram. less than 512 and we run out of time to draw it on the led panel
  114. // 512 => 86 fps, 1024 => 43 fps, 2048 => 21 fps
  115. #define SAMPLES 4096 //4096
  116. // #define WAIT_UNTIL_DRAWING_DONE
  117. #define SECONDS_BETWEEN_DEBUG 60
  118. #define BANDS SCREEN_WIDTH
  119. const float dynamicRange = 5.0;    // in bels, not decibels. bel is ten decibels. it's metric. bel is the base unit. long live the metric.
  120. // #define PRINT_PLOT
  121. #define DEBUG false
  122. // #define DEBUG true
  123. // #define PRINT_BANDS
  124. // #define PRINT_CEILING
  125. #define USE_SERIAL
  126. // #define PRINT_OUTPUT
  127. // #define PRINT_PEAKS
  128. //#define PRINT_FFT_TIME
  129. //#define PRINT_ALL_TIME
  130. //#define PRINT_FASTLED_TIME
  131. //#define PRINT_MAPLEDS_TIME
  132. //#define PRINT_RAM                   // This uses some resources that block the isr and take a long time
  133. #ifdef STRESS_RAM
  134. //#define PRINT_RAM
  135. #endif
  136. // #define PRINT_INDEXES
  137. // #define TEST_FULL_BUFFER
  138. //#define PRINT_SAMPLE_RATE
  139. //#define PRINT_LESSER_SAMPLES
  140. //#define PRINT_BUFFER_FULL
  141. // Kaiser windowing has the best reduction of side lobes and somewhat narrow main lobe. The other windows don't even hold a candle.
  142. int maxCurrent = 15000;
  143. // int maxBrightness = 32;
  144. #define BUFFER_LENGTH 3072   // 2048 is also just fine. maybe 4096 has too much extra space 3078
  145. IRAM_ATTR int32_t bufferRing[BUFFER_LENGTH] = {};
  146. volatile int writeIndex = 0;   // writeIndex will stay 4 behind readIndex. it can't go past
  147. volatile int readIndex = 0;    // readIndex can be equal to writeIndex but cannot advance
  148. volatile int bufferFull = 0;  
  149. volatile int lesserSamples = 0;
  150. IRAM_ATTR int32_t realRing[SAMPLES] = {};
  151. int realRingIndex = 0;
  152. const int numLeds = NUM_PANELS_PER_ROW * NUM_PANELS_PER_COLUMN * PANEL_WIDTH * PANEL_HEIGHT;
  153. // uint8_t *leds = NULL;
  154. CRGB *leds;
  155. uint32_t loopMicros = 0;
  156. uint32_t loopCycles = 0;
  157. float deltaRatio = 0;
  158. static uint16_t ledMappingFunction(uint16_t hardwareLed);
  159. #define __SOFTWARE_MAP
  160. #include "I2SClocklessLedDriver.h"
  161. I2SClocklessLedDriver driver;
  162. char BTname[] = "Lightnsound";
  163. BluetoothA2DPSink a2dp_sink;
  164. OffsetDisplay offd;
  165. FloatOffset groundOffset;
  166. FloatOffset skyOffset;
  167. enum {bluetooth, microphone, artnet} programMode;
  168. __attribute__((always_inline)) IRAM_ATTR static uint16_t ledMappingFunction(uint16_t hardwareLed)
  169. {
  170.   int x, y, moduloRow;
  171.   x = (hardwareLed / PANEL_HEIGHT) % (PANEL_WIDTH * NUM_PANELS_PER_ROW);
  172.   moduloRow = x % 2;
  173.   #ifdef SIGNAL_STARTS_FROM_THE_BOTTOM
  174.   y = (moduloRow * hardwareLed + (1 - moduloRow) * (PANEL_HEIGHT * 12345 - 1 - hardwareLed)) % PANEL_HEIGHT + (hardwareLed / (PANEL_WIDTH * PANEL_HEIGHT * NUM_PANELS_PER_ROW)) * PANEL_HEIGHT;
  175.   #else
  176.   y = ((1 - moduloRow) * hardwareLed + moduloRow * (PANEL_HEIGHT * 12345 - 1 - hardwareLed)) % PANEL_HEIGHT + (hardwareLed / (PANEL_WIDTH * PANEL_HEIGHT * NUM_PANELS_PER_ROW)) * PANEL_HEIGHT;
  177.   #endif
  178.   
  179.   x = (x + driver._offsetDisplay.offsetx) % driver._offsetDisplay.panel_width;
  180.   return x + y * driver._offsetDisplay.panel_width;
  181. }
  182. float sqrtApprox(float number)
  183. {
  184.   union { float f; uint32_t u; } y = {number};
  185.   y.u = 0x5F1FFFF9ul - (y.u >> 1);
  186.   return number * 0.703952253f * y.f * (2.38924456f - number * y.f * y.f);
  187. }
  188. CRGB redToBlue(float hue)
  189. {
  190.   CRGB color;
  191.   color.r = std::max((float)255 * (1 - 2 * hue), (float)0) * 0.094 * 2.5;
  192.   color.g = std::max(min(hue * 2 * 255, (1 - hue) * 2 * 255), (float)0) * 0.113 * 2.5;
  193.   color.b = std::max((float)255 * (hue * 2 - 1), (float)0) * 0.080 * 2.5;
  194.   return color;
  195. }
  196. /*
  197. void mapLeds()
  198. {
  199.   for (int i = 0; i < BANDS; i++)
  200.   {
  201.     for (int j = 0; j < 16; j++)
  202.     {
  203.       if ((bands[i] * 16 - j) >= 0) leds[ledMap[i * 16 + j]] = templateLeds[i * 16 + j];
  204.       else if ((bands[i] * 16 - j) > -1)
  205.       {
  206.         leds[ledMap[i * 16 + j]].r = ((float)templateLeds[i * 16 + j].r + 1.0) * (bands[i] * 16.0 - j + 1.0);   // I add one to the color so that if it's small it doesn't disappear first. now if the color is one, it will disappear in the middle.
  207.         leds[ledMap[i * 16 + j]].g = ((float)templateLeds[i * 16 + j].g + 1.0) * (bands[i] * 16.0 - j + 1.0);
  208.         leds[ledMap[i * 16 + j]].b = ((float)templateLeds[i * 16 + j].b + 1.0) * (bands[i] * 16.0 - j + 1.0);
  209.       }
  210.       else leds[ledMap[i * 16 + j]] = {0, 0, 0};
  211.     }
  212.   }
  213. }
  214. */
  215. /*
  216. void mapLeds()
  217. {
  218.   for (int x = 0; x < SCREEN_WIDTH; x++)
  219.   {
  220.     for (int y = 0; y < SCREEN_HEIGHT; y++)
  221.     {
  222.         leds[x + y * SCREEN_WIDTH].r = 4;
  223.     }
  224.   }
  225. }
  226. */
  227. void mapLeds_rainbow(float* bands)
  228. {
  229.   for (int x = 0; x < BANDS; x++)
  230.   {
  231.     CRGB color = redToBlue(((float)x / (BANDS - 1)) * sqrtApprox((float)x / (BANDS - 1)));
  232.     for (int y = 0; y < SCREEN_HEIGHT; y++)
  233.     {
  234.       if ((bands[x] * SCREEN_HEIGHT - (SCREEN_HEIGHT - 1 - y)) >= 0) leds[x + SCREEN_WIDTH * y] = color;
  235.       else if ((bands[x] * SCREEN_HEIGHT - (SCREEN_HEIGHT - 1 - y)) > -1)
  236.       {
  237.         leds[x + SCREEN_WIDTH * y].r = ((float)color.r + 0.999) * (bands[x] * SCREEN_HEIGHT - (SCREEN_HEIGHT - 1 - y) + 0.999);
  238.         leds[x + SCREEN_WIDTH * y].g = ((float)color.g + 0.999) * (bands[x] * SCREEN_HEIGHT - (SCREEN_HEIGHT - 1 - y) + 0.999);
  239.         leds[x + SCREEN_WIDTH * y].b = ((float)color.b + 0.999) * (bands[x] * SCREEN_HEIGHT - (SCREEN_HEIGHT - 1 - y) + 0.999);
  240.       }
  241.       else leds[x + SCREEN_WIDTH * y] = {0, 0, 0};
  242.     }
  243.   }
  244. }
  245. __attribute__((always_inline)) void mapLeds_textures(int x, FloatOffset groundOffset, FloatOffset skyOffset, float* bands)
  246. {
  247.     for (int y = 0; y < SCREEN_HEIGHT; y++)
  248.     {
  249.       CRGB groundColor = ((CRGB*)yellow_sand)[((y + (int)groundOffset.y) % TILE_HEIGHT) * TILE_WIDTH + (x + (int)groundOffset.x) % TILE_WIDTH];
  250.       groundColor.r = groundColor.r / 4;
  251.       groundColor.g = groundColor.g / 64;
  252.       groundColor.b = groundColor.b / 64;
  253.       CRGB skyColor = ((CRGB*)dark_blue_water)[((y + (int)skyOffset.y) % TILE_HEIGHT) * TILE_WIDTH + (x + (int)skyOffset.x) % TILE_WIDTH];
  254.       skyColor.r = skyColor.r / 64;
  255.       skyColor.g = skyColor.g / 64;
  256.       skyColor.b = skyColor.b / 64;
  257.       if ((bands[x] * SCREEN_HEIGHT - (SCREEN_HEIGHT - 1 - y)) >= 0)
  258.       {
  259.         leds[x + SCREEN_WIDTH * y] = groundColor;
  260.       }
  261.       else if ((bands[x] * SCREEN_HEIGHT - (SCREEN_HEIGHT - 1 - y)) > -1)
  262.       {
  263.         float skyPortion = SCREEN_HEIGHT - 1 - y - bands[x] * SCREEN_HEIGHT;
  264.         float groundPortion = 1 - skyPortion;
  265.         leds[x + SCREEN_WIDTH * y].r = (float)groundColor.r * groundPortion + (float)skyColor.r * skyPortion;
  266.         leds[x + SCREEN_WIDTH * y].g = (float)groundColor.g * groundPortion + (float)skyColor.g * skyPortion;
  267.         leds[x + SCREEN_WIDTH * y].b = (float)groundColor.b * groundPortion + (float)skyColor.b * skyPortion;
  268.       }
  269.       else
  270.       {
  271.         leds[x + SCREEN_WIDTH * y] = skyColor;
  272.       }
  273.     }
  274. }
  275. void drawCharacter(const CRGB* character, float startingColumn, float offSet, float columnsPerFrame, int numberOfFrames, int frameWidth, int frameHeight)
  276. {
  277.   int frame = (int)((startingColumn + offSet) / columnsPerFrame) % numberOfFrames;
  278.   for (int x = (int)(startingColumn + 100) - 100; x < (int)startingColumn + frameWidth; x++)
  279.   {
  280.     if (x >= 0 && x < SCREEN_WIDTH)
  281.     {
  282.       for (int y = max(SCREEN_HEIGHT - frameHeight, 0); y < SCREEN_HEIGHT; y++)
  283.       {
  284.         int characterPixel = x - (int)(startingColumn + 100) + 100 + frame * frameWidth + (y - SCREEN_HEIGHT + frameHeight) * (numberOfFrames * frameWidth);
  285.         // if (character[characterPixel].r != 0xFE && character[characterPixel].g != 0xFD && character[characterPixel].b != 0xFC)
  286.         if ((*(uint32_t*)&(character[characterPixel]) & 0x00FFFFFF) != 0x00FCFDFE)    // I don't know why it's not 0xFEFDFC. It's because esp32 is little endian.
  287.         {
  288.           leds[x + y * SCREEN_WIDTH].r = character[characterPixel].r / 16;
  289.           leds[x + y * SCREEN_WIDTH].g = character[characterPixel].g / 16;
  290.           leds[x + y * SCREEN_WIDTH].b = character[characterPixel].b / 16;
  291.         }
  292.       }
  293.     }
  294.   }
  295.   
  296. }
  297. /*
  298. void mapLeds()     // upside down
  299. {
  300.   for (int x = 0; x < BANDS; x++)
  301.   {
  302.     CRGB color = redToBlue(((float)x / (BANDS - 1)) * sqrtApprox((float)x / (BANDS - 1)));
  303.     for (int y = 0; y < SCREEN_HEIGHT; y++)
  304.     {
  305.       if ((bands[x] * SCREEN_HEIGHT - y) >= 0) leds[x + SCREEN_WIDTH * y] = color;
  306.       else if ((bands[x] * 32 - y) > -1)
  307.       {
  308.         leds[x + SCREEN_WIDTH * y].r = ((float)color.r + 0.999) * (bands[x] * SCREEN_HEIGHT - y + 0.999);
  309.         leds[x + SCREEN_WIDTH * y].g = ((float)color.g + 0.999) * (bands[x] * SCREEN_HEIGHT - y + 0.999);
  310.         leds[x + SCREEN_WIDTH * y].b = ((float)color.b + 0.999) * (bands[x] * SCREEN_HEIGHT - y + 0.999);
  311.       }
  312.       else leds[x + SCREEN_WIDTH * y] = {0, 0, 0};
  313.     }
  314.   }
  315. }
  316. */
  317. void substractAverage(float* inputReal)
  318. {
  319.   float average = 0;
  320.   for (int i = 0; i < SAMPLES; i++)
  321.   {
  322.     average += inputReal[i];
  323.   }
  324.   average /= SAMPLES;
  325.   for (int i = 0; i < SAMPLES; i++)
  326.   {
  327.     inputReal[i] = inputReal[i] - average;
  328.   }
  329. }
  330. void doWindowing(float* inputReal)
  331. {
  332.   for (int i = 0; i < SAMPLES; i++)
  333.   {
  334.     inputReal[i] = inputReal[i] * windowingArray[i];
  335.   }
  336. }
  337. void audio_data_callback(const uint8_t *data, uint32_t byteLen)
  338. {
  339.   int actualLen = byteLen / 4;
  340.   if ((readIndex - writeIndex + BUFFER_LENGTH - 1) % BUFFER_LENGTH <= actualLen)
  341.   {
  342.     bufferFull++;
  343.   }
  344.   else
  345.   {
  346.     for (int i = 0; i < actualLen; i++)
  347.     {
  348.       // bufferRing[(writeIndex + i) % BUFFER_LENGTH] = *(int16_t*)&(data[i * 4]) / 2 + *(int16_t*)&(data[i * 4 + 2]) / 2;
  349.       bufferRing[(writeIndex + i) % BUFFER_LENGTH] = ((int)*(int16_t*)&(data[i * 4]) + (int)*(int16_t*)&(data[i * 4 + 2]));
  350.     }
  351.   }
  352.   writeIndex = (writeIndex + actualLen) % BUFFER_LENGTH;
  353.   
  354.   #ifdef PRINT_INDEXES
  355.   Serial.print("Got ");
  356.   Serial.print(actualLen);
  357.   Serial.print(" data, writeIndex = ");
  358.   Serial.println(writeIndex);
  359.   #endif
  360. }
  361. void startAudio()
  362. {
  363.   i2s_config_t i2s_config = {
  364.     .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
  365.     .sample_rate = DEFAULT_SAMPLE_RATE, // updated automatically by A2DP
  366.     .bits_per_sample = (i2s_bits_per_sample_t)16,
  367.     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
  368.     .communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_STAND_I2S),
  369.     .intr_alloc_flags = 0, // default interrupt priority
  370.     .dma_buf_count = 8,
  371.     .dma_buf_len = 64,
  372.     .use_apll = true,
  373.     .tx_desc_auto_clear = true // avoiding noise in case of data unavailability
  374.   };
  375.   a2dp_sink.set_i2s_config(i2s_config);
  376.   a2dp_sink.set_i2s_port(I2S_NUM_1);
  377.   a2dp_sink.set_stream_reader(audio_data_callback);
  378.   
  379.   i2s_pin_config_t my_pin_config = {
  380.       .bck_io_num = 4,
  381.       .ws_io_num = 15,
  382.       .data_out_num = 16,
  383.       .data_in_num = I2S_PIN_NO_CHANGE };
  384.   a2dp_sink.set_pin_config(my_pin_config);
  385.   // a2dp_sink.set_auto_reconnect(false);    // maybe this helps with my compatibility problem
  386.   
  387.   a2dp_sink.start(BTname);
  388.   Serial.println(String("Started Bluetooth audio receiver with the name ") + BTname);
  389. }
  390. bool readBuffer(float* inputReal)
  391. {
  392.   int newSamples = min(min(SAMPLES, a2dp_sink.sample_rate() * (int)loopMicros / 990000), BUFFER_LENGTH);
  393.   if ((writeIndex - readIndex + BUFFER_LENGTH) % BUFFER_LENGTH < newSamples / 2)   // i only ask for half as many samples to be present
  394.   {
  395.     // delay(1);
  396.     return false;
  397.   }
  398.   if ((writeIndex - readIndex + BUFFER_LENGTH) % BUFFER_LENGTH < newSamples)
  399.   {
  400.     // delay(1);
  401.     newSamples = (writeIndex - readIndex + BUFFER_LENGTH) % BUFFER_LENGTH;
  402.     lesserSamples = newSamples;
  403.   }
  404.   
  405.   {
  406.     for (int i = 0; i < newSamples; i++)
  407.     {
  408.       realRing[realRingIndex] = (bufferRing[readIndex]);
  409.       realRingIndex = (realRingIndex + 1) % SAMPLES;
  410.       readIndex = (readIndex + 1) % BUFFER_LENGTH;
  411.     }
  412.     #ifdef PRINT_INDEXES
  413.     Serial.println(String("readIndex = ") + readIndex + ", realRingIndex = " + realRingIndex);
  414.     #endif
  415.   }
  416.   // DC-offset removal goes here. I don't yet know how. I need a few variables of ram.
  417.   // But since I do overlapping fft, I need to do the DC removal again but the variables are continous.
  418.   // I'm not going to save a history of them. Perhaps I need to calculate them again
  419.   // by first calculating them to the point where I delete the circular buffer and save them for use the next loop.
  420.   // only after that I'll read the buffer. I'll need to do the calculations like 7 times more than I should if I had an extra 16 kB.
  421.   // I know the most probable amount of new samples I will use.
  422.   // Only when I reach the head of the ring buffer I need to get variable amount of samples.
  423.   // This only saves 15 % of calculating the DC-offset. Perhaps it's not worth it.
  424.   // I could separate the channels at this point too. I can get them for free now since they are in IRAM.
  425.   for (int i = 0; i < SAMPLES; i++)
  426.   {
  427.     inputReal[i] = (float)realRing[(realRingIndex + i) % SAMPLES];
  428.   }
  429.   return true;
  430. }
  431. void powerOfTwo(float* output)
  432. {
  433.   for (int index = 0; index < SAMPLES / 2; index++)
  434.   {
  435.     output[index] = output[index * 2] * output[index * 2] + output[index * 2 + 1] * output[index * 2 + 1];
  436.   }
  437. }
  438. void zeroSmallBins(float* output)
  439. {
  440.   float biggest = 0;
  441.   for (int i = 0; i < SAMPLES / 2; i++)
  442.   {
  443.     if (biggest < output[i]) biggest = output[i];
  444.   }
  445.   for (int i = 0; i < SAMPLES / 2; i++)
  446.   {
  447.     // we get rid of the unwanted frequency side lobes this way. kaiser 2 is quite eficient and we shouldn't see more than -60 dB on the side lobes
  448.     // at the same time we make sure that we don't take logarithm out of zero. that would be -infinite
  449.     output[i] = std::max(output[i] - biggest * 0.0000003, 0.0000000001);
  450.     }
  451. }
  452. /*
  453. void powTwoBands(float* output, float* bands)
  454. {
  455.   for (int i = 0; i < BANDS; i++)
  456.   {
  457.     bands[i] = 0;
  458.     #if SAMPLES == 4096
  459.     #if BANDS == 112
  460.     for (int j = bins_4096_112[i]; j < bins_4096_112[i + 1]; j++)
  461.     #endif
  462.     #if BANDS == 64
  463.     for (int j = bins_4096_64[i]; j < bins_4096_64[i + 1]; j++)
  464.     #endif
  465.     #if BANDS == 7
  466.     for (int j = bins_4096_7[i]; j < bins_4096_7[i + 1]; j++)
  467.     #endif
  468.     #endif
  469.     #if SAMPLES == 2048
  470.     for (int j = bins_2048_64[i]; j < bins_2048_64[i + 1]; j++)
  471.     #endif
  472.    
  473.     {
  474.       bands[i] += output[j];
  475.     }
  476.   }
  477. }
  478. */
  479. void logBands(float* bands, float* peakBands)
  480. {
  481.   for (int i = 0; i < BANDS; i++)
  482.   {
  483.     bands[i] = log10f(bands[i]);
  484.     #ifdef PRINT_PEAKS
  485.     if (peakBands[i] < bands[i]) peakBands[i] = bands[i];
  486.     #endif
  487.     #if BANDS == 7
  488.     bands[i] = bands[i] - substract_7[i] + 6;
  489.     #endif
  490.     #if BANDS == 16
  491.     bands[i] = bands[i] - substract_16[i] + 6;
  492.     #endif
  493.     #if BANDS == 32
  494.     bands[i] = bands[i] - substract_32[i] + 6;
  495.     #endif
  496.     #if BANDS == 48
  497.     bands[i] = bands[i] - substract_48[i] + 6;
  498.     #endif
  499.     #if BANDS == 64
  500.     bands[i] = bands[i] - substract_64[i] + 6;
  501.     #endif
  502.     #if BANDS == 112
  503.     bands[i] = bands[i] - substract_112[i] + 6;
  504.     #endif
  505.   }
  506. }
  507. void normalizeBands(float* bands)
  508. {
  509.   static float bandCeiling = 0.1;
  510.   #ifdef PRINT_CEILING
  511.   Serial.println(String("bandCeiling = ") + bandCeiling);
  512.   #endif
  513.   bandCeiling -= 0.00005;          // now it takes 200 seconds to come 10 dB down
  514.   for (int i = 0; i < BANDS; i++)
  515.   {
  516.     if (bands[i] > bandCeiling) bandCeiling = bands[i];
  517.   }
  518.   for (int i = 0; i < BANDS; i++)
  519.   {
  520.     bands[i] = (bands[i] - bandCeiling + dynamicRange) / dynamicRange;
  521.   }
  522. }
  523.   /* These will be combined to one function
  524.   powTwoBands(output, bands);
  525.   logBands(bands, peakBands);
  526.   normalizeBands(bands);
  527.   */
  528. void powerBinsToBands(float* output, float* bands)
  529. {
  530.   int32_t startingPoint = START_SPECTRUM_AT_THIS_BIN;
  531.   for (int32_t i = 0; i < BANDS; i++)
  532.   {
  533.     bands[i] = 0;
  534.     int32_t delta = (startingPoint * deltaRatio) + ADD_TO_DELTA;
  535.     if (delta <= 0) delta = 1;
  536.     for (int32_t j = 0; j < delta; j++)
  537.     {
  538.       bands[i] += output[startingPoint + j];
  539.     }
  540.     startingPoint += delta;
  541.     bands[i] = log10f(bands[i]);
  542.     #ifdef PRINT_PEAKS
  543.     if (peakBands[i] < bands[i]) peakBands[i] = bands[i];
  544.     #endif
  545.     // if (delta < 1) delta = 1;
  546.     if (delta > 6) delta = 6;
  547.     bands[i] -= substract_universal[delta - 1];
  548.   }
  549.   static float bandCeiling = -1000000;
  550.   #ifdef PRINT_CEILING
  551.   Serial.println(String("bandCeiling = ") + bandCeiling);
  552.   #endif
  553.   bandCeiling -= 0.00002;          // now it takes 300 seconds to come 10 dB down
  554.   for (int i = 0; i < BANDS; i++)
  555.   {
  556.     if (bands[i] > bandCeiling) bandCeiling = bands[i];
  557.   }
  558.   for (int i = 0; i < BANDS; i++)
  559.   {
  560.     bands[i] = (bands[i] - bandCeiling + dynamicRange) / dynamicRange;
  561.   }
  562. }
  563. float __attribute__ ((noinline)) checkDeltaRatio(float ratio, float startingPoint, int32_t rounds)
  564. {
  565.     for (int32_t i = 0; i < rounds; i++)
  566.     {
  567.         float delta = floorf((startingPoint * ratio) + ADD_TO_DELTA);
  568.         if (delta <= 0) delta = 1;
  569.         startingPoint += delta;
  570.     }
  571.     return startingPoint;
  572. }
  573. float __attribute__ ((noinline)) findDeltaRatio(int32_t maxDepth, float low, float high, float lowestValid, float startingPoint, int32_t endGoal, int32_t rounds)
  574. {
  575.     float middle = (low + high) * 0.5;
  576.     float result = checkDeltaRatio(middle, startingPoint, rounds);
  577.     // Serial.printf("%.17f : %f\n", middle, result);
  578.     if (maxDepth <= 0) return lowestValid;   
  579.     if (result > endGoal)
  580.     {
  581.         return findDeltaRatio(maxDepth - 1, low, middle, lowestValid, startingPoint, endGoal, rounds);
  582.     }
  583.     else
  584.     {
  585.         return findDeltaRatio(maxDepth - 1, middle, high, middle, startingPoint, endGoal, rounds);
  586.     }
  587. }
  588. void setup()
  589. {
  590.   
  591.   #ifdef USE_SERIAL
  592.   Serial.begin(115200);
  593.   #endif
  594.   programMode = bluetooth;
  595.   Serial.printf("programMode = %d\n\r", programMode);
  596.   Serial.println("Just booted up");
  597.   Serial.print("ESP.getFreeHeap() = ");
  598.   Serial.println(ESP.getFreeHeap());
  599.   Serial.print("heap_caps_get_largest_free_block(MALLOC_CAP_8BIT) = ");
  600.   Serial.println(heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
  601.   Serial.print("heap_caps_get_largest_free_block(MALLOC_CAP_32BIT) = ");
  602.   Serial.println(heap_caps_get_largest_free_block(MALLOC_CAP_32BIT));
  603.   delay(100);
  604.   int32_t lastBin = min(SAMPLES / 2 - 1, HIGHEST_FREQUENCY * SAMPLES / DEFAULT_SAMPLE_RATE);
  605.   deltaRatio = findDeltaRatio(25, 0, 0.2, 0, START_SPECTRUM_AT_THIS_BIN, lastBin, BANDS);
  606.   Serial.printf("deltaRatio = %f\n", deltaRatio);
  607.   Serial.printf("numLeds = %d\n", numLeds);
  608.   Serial.printf("SCREEN_WIDTH = %d\n", SCREEN_WIDTH);
  609.   Serial.printf("SCREEN_HEIGHT = %d\n", SCREEN_HEIGHT);
  610.   
  611.   int pins[] = {32, 33, 25, 26, 27, 14, 12, 23, 22, 21, 19, 18, 5, 17};              // esp32 dev kit v1
  612.   
  613.   #ifndef WAIT_UNTIL_DRAWING_DONE
  614.   driver.__displayMode = NO_WAIT;
  615.   #endif
  616.   
  617.   #ifdef STRESS_RAM
  618.   leds = (CRGB*)calloc(numLeds * 2, sizeof(CRGB));      // testing if I have enough ram for a 112 x 64 screen
  619.   driver.initled((uint8_t*)leds, pins, NUM_PANELS_PER_ROW * NUM_PANELS_PER_COLUMN, PANEL_WIDTH * PANEL_HEIGHT * 2, ORDER_GRB);    // simulating screen twice as big and half as fast
  620.   #else
  621.   leds = (CRGB*)calloc(numLeds, sizeof(CRGB));
  622.   driver.initled((uint8_t*)leds, pins, NUM_PANELS_PER_ROW * NUM_PANELS_PER_COLUMN, PANEL_WIDTH * PANEL_HEIGHT, ORDER_GRB);
  623.   #endif
  624.   // These two lines have to be after driver.initled()
  625.   driver._offsetDisplay.panel_width=SCREEN_WIDTH;
  626.   driver._offsetDisplay.panel_height=SCREEN_HEIGHT;
  627.   
  628.   offd = driver.getDefaultOffset();
  629.   offd.panel_width=SCREEN_WIDTH;
  630.   offd.panel_height=SCREEN_HEIGHT;
  631.   Serial.printf("driver._offsetDisplay.panel_width = %d\n", driver._offsetDisplay.panel_width);
  632.   Serial.printf("driver._offsetDisplay.panel_height = %d\n", driver._offsetDisplay.panel_height);
  633.   
  634.   groundOffset.x = 0;
  635.   groundOffset.y = 0;
  636.   skyOffset.x = 0;
  637.   skyOffset.y = 0;
  638.   
  639.   // driver.setMapLed(mapLedsOnPanel);
  640.   
  641.   startAudio();
  642.   Serial.print("ESP.getFreeHeap() = ");
  643.   Serial.println(ESP.getFreeHeap());
  644.   Serial.print("heap_caps_get_largest_free_block(MALLOC_CAP_8BIT) = ");
  645.   Serial.println(heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
  646.   Serial.print("heap_caps_get_largest_free_block(MALLOC_CAP_32BIT) = ");
  647.   Serial.println(heap_caps_get_largest_free_block(MALLOC_CAP_32BIT));
  648.   delay(100);
  649.   
  650.   pinMode(ATX_POWER_ON, OUTPUT);
  651.   digitalWrite(ATX_POWER_ON, LOW);
  652.   // gpio_set_direction((gpio_num_t)13, GPIO_MODE_OUTPUT);
  653.   // gpio_set_level((gpio_num_t)13, 0);
  654.   
  655.   Serial.printf("ledMappingFunction(0) = %d\n\r", ledMappingFunction(0));
  656.   Serial.printf("ledMappingFunction(1790) = %d\n\r", ledMappingFunction(1790));
  657. BRIGHTNESSMARK = map(analogRead(BRIGHTNESSPOT), 0, 4093, 1,BRIGHTNESSMAX);  // read brightness potmeter
  658.   driver.setBrightness(BRIGHTNESSMARK);
  659. pinMode(InterruptPin, INPUT);
  660. attachInterrupt(InterruptPin, isr, RISING);
  661.   
  662. }
  663. void loopBluetooth()
  664. {
  665.   
  666.   static float* inputReal = (float*)calloc(SAMPLES, sizeof(float));
  667.   static float* output = (float*)calloc(SAMPLES, sizeof(float));
  668.   static fft_config_t* fftReal = fft_init(SAMPLES, FFT_REAL, FFT_FORWARD, inputReal, output);
  669.   static float* bands = (float*)calloc(BANDS, sizeof(float));
  670.   #ifdef PRINT_PEAKS
  671.   static float* peakBands = (float*)calloc(BANDS, sizeof(float));
  672.   #else
  673.   static float* peakBands;
  674.   #endif
  675.   
  676.   bool charactersOn = true;
  677.   static unsigned int beebBoob = 178;
  678.   static uint32_t previousMicros = 0;
  679.   static uint32_t previousCycles = 0;
  680.   uint32_t newMicros = micros();
  681.   uint32_t newCycles = xthal_get_ccount();
  682.   static uint32_t previousDebugMillis = 0;
  683.   loopMicros = newMicros - previousMicros;
  684.   loopCycles = newCycles - previousCycles;
  685.   previousMicros = newMicros;
  686.   previousCycles = newCycles;
  687.   if (previousDebugMillis == 0)
  688.   {
  689.     previousDebugMillis = 1;
  690.     Serial.print("ESP.getFreeHeap() = ");
  691.     Serial.println(ESP.getFreeHeap());
  692.     Serial.print("heap_caps_get_largest_free_block(MALLOC_CAP_8BIT) = ");
  693.     Serial.println(heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
  694.     Serial.print("heap_caps_get_largest_free_block(MALLOC_CAP_32BIT) = ");
  695.     Serial.println(heap_caps_get_largest_free_block(MALLOC_CAP_32BIT));
  696.   }
  697.   
  698.   while (true)
  699.   {
  700.     if (readBuffer(inputReal)) break;
  701.     else
  702.     {
  703.       vTaskDelay(1);
  704.       previousMicros = micros();
  705.       previousCycles = xthal_get_ccount();
  706.     }
  707.   }
  708.   #ifdef PRINT_FFT_TIME
  709.   uint32_t fftMicros = micros();
  710.   uint32_t fftCycles = xthal_get_ccount();
  711.   #endif
  712.   
  713.   // substractAverage(inputReal);
  714.   doWindowing(inputReal);
  715.   fft_execute(fftReal);
  716.   powerOfTwo(output);   // if we end up doing log() of the output anyways there is no need to do costly sqrt() because it's the same as dividing log() by 2
  717.   // sqrtBins();     // we don't actually need this since we are dealing with the power of the signal and not the amplitude
  718.   zeroSmallBins(output);  // we do this because there are plenty of of reflections in the surrounding bins
  719.   /* These will be combined to one function
  720.   powTwoBands(output, bands);
  721.   logBands(bands, peakBands);
  722.   normalizeBands(bands);
  723.   */
  724.   powerBinsToBands(output, bands);
  725.   
  726.   groundOffset.x += 0.03;
  727.   groundOffset.y += 0.5;
  728.   skyOffset.x += 0.23;
  729.   skyOffset.y += 0.05;
  730.   if (groundOffset.x < 0) groundOffset.x += TILE_WIDTH;
  731.   if (groundOffset.y < 0) groundOffset.y += TILE_HEIGHT;
  732.   if (skyOffset.x < 0) skyOffset.x += TILE_WIDTH;
  733.   if (skyOffset.y < 0) skyOffset.y += TILE_HEIGHT;
  734.   
  735.   #ifdef PRINT_FFT_TIME
  736.   fftMicros = micros() - fftMicros;
  737.   fftCycles = xthal_get_ccount() - fftCycles;
  738.   #endif
  739.   
  740.   #ifndef WAIT_UNTIL_DRAWING_DONE
  741.   while (driver.isDisplaying == true) vTaskDelay(1);
  742.   #endif
  743.   
  744.   #ifdef PRINT_RAM
  745.   // I had to move these commands here because they bothered I2SCloclessLedDriver
  746.   // They do something that blocks the interruots or something
  747.   // I was getting pretty garbled results just because of this
  748.   // I had to wait until I2SCloclessLedDriver was done drawing the leds
  749.   static int heap_caps_get_largest_free_block_8bit_min = 1000000000;
  750.   static int heap_caps_get_largest_free_block_32bit_min = 1000000000;
  751.   int newValue = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
  752.   if (newValue < heap_caps_get_largest_free_block_8bit_min) heap_caps_get_largest_free_block_8bit_min = newValue;
  753.   newValue = heap_caps_get_largest_free_block(MALLOC_CAP_32BIT);
  754.   if (newValue < heap_caps_get_largest_free_block_32bit_min) heap_caps_get_largest_free_block_32bit_min = newValue;
  755.   #endif
  756.   
  757.   uint32_t mapLedsMicros = micros();
  758.   uint32_t mapLedsCycles = xthal_get_ccount();
  759.     BRIGHTNESSMARK = map(analogRead(BRIGHTNESSPOT), 0, 4093, 1,BRIGHTNESSMAX);  // read brightness potmeter
  760.   driver.setBrightness(BRIGHTNESSMARK);
  761.   // mapLeds();
  762.   // mapLeds_mario();
  763.   // driver.ledToDisplay will be needed next
  764. if (Request){
  765.    isrCounter++;
  766.    if(isrCounter>20){
  767.     if (mapMode==RAINBOW)mapMode=TEXTURES;
  768.     else mapMode=RAINBOW;
  769.     Request = false;
  770.     Serial.println(mapMode);
  771.     isrCounter=0;
  772.    }
  773. }
  774.   switch (mapMode)
  775.   {
  776.     case TEXTURES:
  777.     for (int x = 0; x < BANDS; x++)
  778.     {
  779.       mapLeds_textures(x, groundOffset, skyOffset, bands);
  780.     }
  781.     break;
  782.    
  783.     case RAINBOW:
  784.     mapLeds_rainbow(bands);
  785.     break;
  786.   }
  787.   
  788.   if (charactersOn == true)
  789.   {
  790.     // drawCharacter(CRGB* character, float startingColumn, float offset, int columnsPerFrame, int numberOfFrames, int frameWidth, int frameHeight)
  791.     static uint32_t characterNumber = 0;
  792.     static int frameWidth = 32;
  793.     static int frameHeight = 32;
  794.     static int numberOfFrames = 8;
  795.     static const CRGB* currentCharacter = (const CRGB*)tuxes_walking;
  796.     static float columnsPerFrame = 1.5;
  797.     static float offset = 100.4 + ((double)esp_random() / (double)4194304);
  798.     static float startingColumn = 0 - frameWidth;
  799.     // [frameHeight * 2 * (characterNumber % 11) * 3 * frameWidth * numberOfFrames]
  800.     drawCharacter(currentCharacter, startingColumn, offset, columnsPerFrame, numberOfFrames, frameHeight, frameWidth);
  801.     startingColumn += 0.2;
  802.     if (startingColumn > SCREEN_WIDTH + frameWidth + 4)
  803.     {
  804.       characterNumber = (characterNumber + 1) % 24;
  805.       {
  806.         currentCharacter = (const CRGB*)tuxes_walking + characterNumber * frameHeight * frameWidth * numberOfFrames;
  807.         frameWidth = 32;
  808.         frameHeight = 32;
  809.         numberOfFrames = 8;
  810.       }
  811.       // offset = 100.4 + (float)esp_random() / 1000000.4;
  812.       offset = 100.4 + ((double)esp_random() / (double)4194304);
  813.       if ((characterNumber % 2) == 0)
  814.       {
  815.         columnsPerFrame = 1.5;
  816.       }
  817.       else
  818.       {
  819.         columnsPerFrame = 2;
  820.       }
  821.       if (characterNumber == 22)
  822.       {
  823.         currentCharacter = (const CRGB*)guido_walking;
  824.         frameWidth = 32;
  825.         frameHeight = 32;
  826.         numberOfFrames = 8;
  827.         columnsPerFrame = 2;
  828.       }
  829.       if (characterNumber == 23)
  830.       {
  831.         currentCharacter = (const CRGB*)squirrel_walking;
  832.         frameWidth = 20;
  833.         frameHeight = 20;
  834.         numberOfFrames = 6;
  835.         columnsPerFrame = 2;
  836.       }
  837.       startingColumn = 0 - frameWidth - 4;
  838.       // Serial.printf("Color of the first pixel is %u\n\r", (*(uint32_t*)&(currentCharacter[0])));
  839.       // it should be 16711164;
  840.       // it's actually 4277992958 because the cpu is little endian
  841.       // that means that the last byte of the number is stored in the beginning
  842.       // all the bytes in a number are in reverse order
  843.       // back in the day when the bit width of the memory lane was less than the bit width of the cpu
  844.       // it allowed the cpu to start the calculations one clock cycle before it got the whole number
  845.     }
  846.   }
  847.   mapLedsMicros = micros() - mapLedsMicros;
  848.   mapLedsCycles = xthal_get_ccount() - mapLedsCycles;
  849.   
  850.   uint32_t showPixelsMicros = micros();
  851.   uint32_t showPixelsCycles = xthal_get_ccount();
  852.   
  853.   // driver.showPixels((uint8_t *)leds);
  854.   // driver.showPixels(offd);
  855.   // leds[8 * SCREEN_WIDTH + 51].r = 255;
  856.   // leds[8 * SCREEN_WIDTH + 51].g = 255;
  857.   // leds[8 * SCREEN_WIDTH + 51].b = 255;
  858.   driver.showPixels(NO_WAIT);
  859.   showPixelsMicros = micros() - showPixelsMicros;
  860.   showPixelsCycles = xthal_get_ccount() - showPixelsCycles;
  861.   
  862.   #ifdef PRINT_PLOT
  863.   plot();
  864.   #endif
  865.   #ifdef PRINT_BANDS
  866.   printBands();
  867.   #endif
  868.   
  869.   if (millis() - previousDebugMillis > SECONDS_BETWEEN_DEBUG * 1000)
  870.   {
  871.     previousDebugMillis = millis();
  872.     Serial.printf("driver._offsetDisplay.panel_width = %d\n", driver._offsetDisplay.panel_width);
  873.     Serial.printf("driver._offsetDisplay.panel_height = %d\n", driver._offsetDisplay.panel_height);
  874.     #ifdef PRINT_FFT_TIME
  875.     //Serial.print("FFT took ");
  876.     //Serial.print(fftMicros);
  877.     //Serial.print(" ");
  878.     //Serial.print((fftCycles) / 240);
  879.     //Serial.println(" μs");
  880.     #endif
  881.     #ifdef PRINT_RAM
  882.     Serial.print("ESP.getFreeHeap() = ");
  883.     Serial.println(ESP.getFreeHeap());
  884.     Serial.print("heap_caps_get_largest_free_block(MALLOC_CAP_8BIT) = ");
  885.     Serial.println(heap_caps_get_largest_free_block_8bit_min);
  886.     Serial.print("heap_caps_get_largest_free_block(MALLOC_CAP_32BIT) = ");
  887.     Serial.println(heap_caps_get_largest_free_block_32bit_min);
  888.     heap_caps_get_largest_free_block_8bit_min = 1000000000;
  889.     heap_caps_get_largest_free_block_32bit_min = 1000000000;
  890.     #endif
  891.     #ifdef PRINT_FASTLED_TIME
  892.     //Serial.print("driver.showPixels() took ");
  893.     //Serial.print(showPixelsMicros);
  894.     //Serial.print(" ");
  895.     //Serial.print((showPixelsCycles) / 240);
  896.     //Serial.println(" μs ");
  897.     #endif
  898.     #ifdef PRINT_MAPLEDS_TIME
  899.     //Serial.print("MapLeds took ");
  900.     //Serial.print(mapLedsMicros);
  901.     //Serial.print(" ");
  902.     //Serial.print((mapLedsCycles) / 240);
  903.     //Serial.println(" μs ");
  904.     #endif
  905.     #ifdef PRINT_SAMPLE_RATE
  906.     //Serial.print("a2dp_sink.sample_rate() = ");
  907.     //Serial.println(a2dp_sink.sample_rate());
  908.     #endif
  909.     #ifdef PRINT_ALL_TIME
  910.     //Serial.print("everything took ");
  911.     //Serial.print(loopMicros);
  912.     //Serial.print(" ");
  913.     //Serial.print((loopCycles) / 240);
  914.     //Serial.println(" μs");
  915.     #endif
  916.     #ifdef PRINT_PEAKS
  917.     //Serial.print("Peak bands: ");
  918.    // for (int i = 0; i < BANDS; i++)
  919.    // {
  920.    //   Serial.printf("%f, ", peakBands[i] - 6.0);
  921.    // }
  922.    // Serial.println();
  923.     #endif
  924.     #ifdef PRINT_LESSER_SAMPLES
  925.    // if (lesserSamples > 0)
  926.    // {
  927.    //   Serial.print("lesserSamples = ");
  928.   //    Serial.println(lesserSamples);
  929.   //    lesserSamples = 0;
  930.   //  }
  931.     #endif
  932.     #ifdef PRINT_BUFFER_FULL
  933.    // if (bufferFull > 0)
  934.    // {
  935.     //  Serial.print("BUFFER FULL. DISCARDING DATA. BUFFER FULL. DISCARDING DATA. BUFFER FULL. bufferFull = ");
  936.      // Serial.println(bufferFull);
  937.      // bufferFull = 0;
  938.    // }
  939.     #endif
  940.   }
  941.   #ifdef TEST_FULL_BUFFER
  942.   //delay(150);
  943.   #endif
  944. }
  945. void loopMicrophone()
  946. {
  947.   
  948. }
  949. void loopArtnet()
  950. {
  951.   
  952. }
  953. void loop()
  954. {
  955.   
  956.   switch(programMode)
  957.   {
  958.     case bluetooth:
  959.       while(true) loopBluetooth();
  960.       break;
  961.     case microphone:
  962.       while(true) loopMicrophone();
  963.       break;
  964.     case artnet:
  965.       while(true) loopArtnet();
  966.       break;
  967.   }
  968. }
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 昨天 16:45

【Arduino 动手做】Fast Audio 快速音频频谱分析仪

【Arduino 动手做】Fast Audio  快速音频频谱分析仪
项目链接:https://www.instructables.com/Fast-Audio-Spectrum-Analyzer/
项目作者:emdee401

项目视频:https://www.youtube.com/watch?v=C-ics1zhJkE
项目代码:https://github.com/donnersm/BluetoothAnalyzer

Antti Yliniemi 编写了原始代码并允许我使用和调整它。
https://github.com/yliniemi/bluetooth-spectrum-analyzer
https://www.youtube.com/@acidangel162

Antti 使用了 Yves Bazin 编写的 Led 驱动器
https://github.com/hpwit/I2SClocklessLedDriver
https://www.youtube.com/@TekWit

将 ESP32 DOIT devkit 1 连接到 USB 端口,并使用 Chome 或 Edge 访问:
donnersm.github.io/BluetoothAnalyzer/flash.html

【Arduino 动手做】Fast Audio  快速音频频谱分析仪图1

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail