Merge branch 'master' into patch-1

This commit is contained in:
Joey Babcock 2016-12-30 23:04:34 -08:00 committed by GitHub
commit daf08d45bb
12 changed files with 774 additions and 24 deletions

View File

@ -63,10 +63,10 @@ If `pip` is not found try using `python -m pip install` instead.
## Arduino dependencies ## Arduino dependencies
ESP8266 firmare is uploaded using the Arduino IDE. See [this tutorial](https://learn.sparkfun.com/tutorials/esp8266-thing-hookup-guide/installing-the-esp8266-arduino-addon) to setup the Arduino IDE for ESP8266. ESP8266 firmare is uploaded using the Arduino IDE. See [this tutorial](https://learn.sparkfun.com/tutorials/esp8266-thing-hookup-guide/installing-the-esp8266-arduino-addon) to setup the Arduino IDE for ESP8266.
This [ws2812b i2s library](https://github.com/JoDaNl/esp8266_ws2812_i2s) must be downloaded and installed in the Arduino libraries folder. <!-- This [ws2812b i2s library](https://github.com/JoDaNl/esp8266_ws2812_i2s) must be downloaded and installed in the Arduino libraries folder.
-->
# Hardware Connections # Hardware Connections
The ESP8266 has hardware support for [I²S](https://en.wikipedia.org/wiki/I%C2%B2S) and this peripheral is used by the [ws2812b i2s library](https://github.com/JoDaNl/esp8266_ws2812_i2s) to control the ws2812b LED strip. This signficantly improves performance compared to bit-banging the IO pin. Unfortunately, this means that the LED strip **must** be connected to the RX1 pin, which is not accessible in some ESP8266 modules (such as the ESP-01). The ESP8266 has hardware support for [I²S](https://en.wikipedia.org/wiki/I%C2%B2S) and this peripheral is used <!-- by the [ws2812b i2s library](https://github.com/JoDaNl/esp8266_ws2812_i2s) -->to control the ws2812b LED strip. This signficantly improves performance compared to bit-banging the IO pin. Unfortunately, this means that the LED strip **must** be connected to the RX1 pin, which is not accessible in some ESP8266 modules (such as the ESP-01).
The RX1 pin on the ESP8266 module should be connected to the data input pin of the ws2812b LED strip (often labelled DIN or D0). The RX1 pin on the ESP8266 module should be connected to the data input pin of the ws2812b LED strip (often labelled DIN or D0).
@ -99,5 +99,8 @@ A PyQtGraph GUI will open to display the output of the visualization on the comp
If you encounter any issues or have questions about this project, feel free to open a new issue. If you encounter any issues or have questions about this project, feel free to open a new issue.
# Limitations
The visualization code currently supports up to 256 LEDs. Support for additional LEDs will be added in the near future.
# License # License
All code in this project is released under the MIT License. All code in this project is released under the MIT License.

View File

@ -0,0 +1,26 @@
// ws2812.h
#ifndef __WS2812_H__
#define __WS2812_H__
// Gamma Correction
// Uses a nonlinear lookup table to correct for human perception of light.
// When gamma correction is used, a brightness value of 2X should appear twice
// as bright as a value of X.
// 1 = Enable gamma correction
// 0 = Disable gamma correction
// Note: There seems to be a bug and you can't actually disable this
#define WS2812_GAMMA_CORRECTION (0)
// Temporal Dithering
// Dithering preserves color and light when brightness is low.
// Sometimes this can cause undesirable flickering.
// 1 = Disable temporal dithering
// 2, 6, 8 = Enable temporal dithering (larger values = more dithering)
#define WS2812_DITHER_NUM (4)
#define WS2812_USE_INTERRUPT (0) // not supported yet
#endif
// end of file

View File

@ -3,10 +3,12 @@
#include <WebSocketsServer.h> #include <WebSocketsServer.h>
#include <Hash.h> #include <Hash.h>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include <ws2812_i2s.h> #include "ws2812_i2s.h"
// Set this to the number of LEDs in your LED strip // Set this to the number of LEDs in your LED strip
#define NUM_LEDS 50
#define NUM_LEDS 60
// Maximum number of packets to hold in the buffer. Don't change this.
#define BUFFER_LEN 1024 #define BUFFER_LEN 1024
#define PRINT_FPS 1 #define PRINT_FPS 1

View File

@ -0,0 +1,172 @@
// w2812_defs.h
//
// contains material from Charles Lohr, but changed by me.
#ifndef __WS2812_I2S_DEFS_H__
#define __WS2812_I2S_DEFS_H__
#include <stdint.h>
// include file from project folder
#include "ws2812.h"
// -----------------------------------------------------
// Helper macro's
#define NUM_RGB_BYTES (num_leds * 3)
#define NUM_I2S_PIXEL_BYTES (num_leds * 3 * 4) // (#leds) * (RGB) * (4 i2s bits)
#define NUM_I2S_PIXEL_WORDS (num_leds * 3)
// -----------------------------------------------------
// I2S timing parameters
// Creates an I2S SR of 93,750 Hz, or 3 MHz Bitclock (.333us/sample)
// Measured on scope : 4 bitclock-ticks every 1200ns --> 300ns bitclock ???
// 12000000L/(div*bestbck*2)
#define WS_I2S_BCK (17)
#define WS_I2S_DIV (4)
#define NUM_I2S_ZERO_BYTES (28) // WS2812 LED Treset = 67us (must be >50us)
#define NUM_I2S_ZERO_WORDS (NUM_I2S_ZERO_BYTES / 4)
// -----------------------------------------------------
#ifndef i2c_bbpll
#define i2c_bbpll 0x67
#define i2c_bbpll_en_audio_clock_out 4
#define i2c_bbpll_en_audio_clock_out_msb 7
#define i2c_bbpll_en_audio_clock_out_lsb 7
#define i2c_bbpll_hostid 4
#endif
// -----------------------------------------------------
#define i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) rom_i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata)
#define i2c_readReg_Mask(block, host_id, reg_add, Msb, Lsb) rom_i2c_readReg_Mask(block, host_id, reg_add, Msb, Lsb)
#define i2c_writeReg_Mask_def(block, reg_add, indata) i2c_writeReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, reg_add##_lsb, indata)
#define i2c_readReg_Mask_def(block, reg_add) i2c_readReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, reg_add##_lsb)
// -----------------------------------------------------
#ifndef ETS_SLC_INUM
#define ETS_SLC_INUM 1
#endif
// -----------------------------------------------------
// from i2s_reg.h
#define DR_REG_I2S_BASE (0x60000e00)
#define I2STXFIFO (DR_REG_I2S_BASE + 0x0000)
#define I2SRXFIFO (DR_REG_I2S_BASE + 0x0004)
#define I2SCONF (DR_REG_I2S_BASE + 0x0008)
#define I2S_BCK_DIV_NUM 0x0000003F
#define I2S_BCK_DIV_NUM_S 22
#define I2S_CLKM_DIV_NUM 0x0000003F
#define I2S_CLKM_DIV_NUM_S 16
#define I2S_BITS_MOD 0x0000000F
#define I2S_BITS_MOD_S 12
#define I2S_RECE_MSB_SHIFT (BIT(11))
#define I2S_TRANS_MSB_SHIFT (BIT(10))
#define I2S_I2S_RX_START (BIT(9))
#define I2S_I2S_TX_START (BIT(8))
#define I2S_MSB_RIGHT (BIT(7))
#define I2S_RIGHT_FIRST (BIT(6))
#define I2S_RECE_SLAVE_MOD (BIT(5))
#define I2S_TRANS_SLAVE_MOD (BIT(4))
#define I2S_I2S_RX_FIFO_RESET (BIT(3))
#define I2S_I2S_TX_FIFO_RESET (BIT(2))
#define I2S_I2S_RX_RESET (BIT(1))
#define I2S_I2S_TX_RESET (BIT(0))
#define I2S_I2S_RESET_MASK 0xf
#define I2SINT_RAW (DR_REG_I2S_BASE + 0x000c)
#define I2S_I2S_TX_REMPTY_INT_RAW (BIT(5))
#define I2S_I2S_TX_WFULL_INT_RAW (BIT(4))
#define I2S_I2S_RX_REMPTY_INT_RAW (BIT(3))
#define I2S_I2S_RX_WFULL_INT_RAW (BIT(2))
#define I2S_I2S_TX_PUT_DATA_INT_RAW (BIT(1))
#define I2S_I2S_RX_TAKE_DATA_INT_RAW (BIT(0))
#define I2SINT_ST (DR_REG_I2S_BASE + 0x0010)
#define I2S_I2S_TX_REMPTY_INT_ST (BIT(5))
#define I2S_I2S_TX_WFULL_INT_ST (BIT(4))
#define I2S_I2S_RX_REMPTY_INT_ST (BIT(3))
#define I2S_I2S_RX_WFULL_INT_ST (BIT(2))
#define I2S_I2S_TX_PUT_DATA_INT_ST (BIT(1))
#define I2S_I2S_RX_TAKE_DATA_INT_ST (BIT(0))
#define I2SINT_ENA (DR_REG_I2S_BASE + 0x0014)
#define I2S_I2S_TX_REMPTY_INT_ENA (BIT(5))
#define I2S_I2S_TX_WFULL_INT_ENA (BIT(4))
#define I2S_I2S_RX_REMPTY_INT_ENA (BIT(3))
#define I2S_I2S_RX_WFULL_INT_ENA (BIT(2))
#define I2S_I2S_TX_PUT_DATA_INT_ENA (BIT(1))
#define I2S_I2S_RX_TAKE_DATA_INT_ENA (BIT(0))
#define I2SINT_CLR (DR_REG_I2S_BASE + 0x0018)
#define I2S_I2S_TX_REMPTY_INT_CLR (BIT(5))
#define I2S_I2S_TX_WFULL_INT_CLR (BIT(4))
#define I2S_I2S_RX_REMPTY_INT_CLR (BIT(3))
#define I2S_I2S_RX_WFULL_INT_CLR (BIT(2))
#define I2S_I2S_PUT_DATA_INT_CLR (BIT(1))
#define I2S_I2S_TAKE_DATA_INT_CLR (BIT(0))
#define I2STIMING (DR_REG_I2S_BASE + 0x001c)
#define I2S_TRANS_BCK_IN_INV (BIT(22))
#define I2S_RECE_DSYNC_SW (BIT(21))
#define I2S_TRANS_DSYNC_SW (BIT(20))
#define I2S_RECE_BCK_OUT_DELAY 0x00000003
#define I2S_RECE_BCK_OUT_DELAY_S 18
#define I2S_RECE_WS_OUT_DELAY 0x00000003
#define I2S_RECE_WS_OUT_DELAY_S 16
#define I2S_TRANS_SD_OUT_DELAY 0x00000003
#define I2S_TRANS_SD_OUT_DELAY_S 14
#define I2S_TRANS_WS_OUT_DELAY 0x00000003
#define I2S_TRANS_WS_OUT_DELAY_S 12
#define I2S_TRANS_BCK_OUT_DELAY 0x00000003
#define I2S_TRANS_BCK_OUT_DELAY_S 10
#define I2S_RECE_SD_IN_DELAY 0x00000003
#define I2S_RECE_SD_IN_DELAY_S 8
#define I2S_RECE_WS_IN_DELAY 0x00000003
#define I2S_RECE_WS_IN_DELAY_S 6
#define I2S_RECE_BCK_IN_DELAY 0x00000003
#define I2S_RECE_BCK_IN_DELAY_S 4
#define I2S_TRANS_WS_IN_DELAY 0x00000003
#define I2S_TRANS_WS_IN_DELAY_S 2
#define I2S_TRANS_BCK_IN_DELAY 0x00000003
#define I2S_TRANS_BCK_IN_DELAY_S 0
#define I2S_FIFO_CONF (DR_REG_I2S_BASE + 0x0020)
#define I2S_I2S_RX_FIFO_MOD 0x00000007
#define I2S_I2S_RX_FIFO_MOD_S 16
#define I2S_I2S_TX_FIFO_MOD 0x00000007
#define I2S_I2S_TX_FIFO_MOD_S 13
#define I2S_I2S_DSCR_EN (BIT(12))
#define I2S_I2S_TX_DATA_NUM 0x0000003F
#define I2S_I2S_TX_DATA_NUM_S 6
#define I2S_I2S_RX_DATA_NUM 0x0000003F
#define I2S_I2S_RX_DATA_NUM_S 0
#define I2SRXEOF_NUM (DR_REG_I2S_BASE + 0x0024)
#define I2S_I2S_RX_EOF_NUM 0xFFFFFFFF
#define I2S_I2S_RX_EOF_NUM_S 0
#define I2SCONF_SIGLE_DATA (DR_REG_I2S_BASE + 0x0028)
#define I2S_I2S_SIGLE_DATA 0xFFFFFFFF
#define I2S_I2S_SIGLE_DATA_S 0
#define I2SCONF_CHAN (DR_REG_I2S_BASE + 0x002c)
#define I2S_RX_CHAN_MOD 0x00000003
#define I2S_RX_CHAN_MOD_S 3
#define I2S_TX_CHAN_MOD 0x00000007
#define I2S_TX_CHAN_MOD_S 0
// -----------------------------------------------------
#endif
// end of file

View File

@ -0,0 +1,102 @@
// ws2812_init.c
// C-based helper function for initilalizing
// the I2S system
#include <string.h>
#include "slc_register.h"
#include "user_interface.h"
#include "ws2812_defs.h"
#include "ws2812_dma.h"
#if WS2812_USE_INTERRUPT == 1
// for debugging purposes
static volatile uint32_t interrupt_count = 0;
static void ws2812_isr(void)
{
//clear all intr flags
WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff);//slc_intr_status);
interrupt_count++;
}
#endif
void ws2812_dma(sdio_queue_t *i2s_pixels_queue)
{
// Reset DMA
SET_PERI_REG_MASK(SLC_CONF0, SLC_RXLINK_RST); //|SLC_TXLINK_RST);
CLEAR_PERI_REG_MASK(SLC_CONF0, SLC_RXLINK_RST); //|SLC_TXLINK_RST);
// Clear DMA int flags
SET_PERI_REG_MASK(SLC_INT_CLR, 0xffffffff);
CLEAR_PERI_REG_MASK(SLC_INT_CLR, 0xffffffff);
// Enable and configure DMA
CLEAR_PERI_REG_MASK(SLC_CONF0,(SLC_MODE<<SLC_MODE_S));
SET_PERI_REG_MASK(SLC_CONF0,(1<<SLC_MODE_S));
SET_PERI_REG_MASK(SLC_RX_DSCR_CONF,SLC_INFOR_NO_REPLACE|SLC_TOKEN_NO_REPLACE);
CLEAR_PERI_REG_MASK(SLC_RX_DSCR_CONF, SLC_RX_FILL_EN|SLC_RX_EOF_MODE | SLC_RX_FILL_MODE);
// configure DMA descriptor
CLEAR_PERI_REG_MASK(SLC_RX_LINK,SLC_RXLINK_DESCADDR_MASK);
SET_PERI_REG_MASK(SLC_RX_LINK, ((uint32)i2s_pixels_queue) & SLC_RXLINK_DESCADDR_MASK);
#if WS2812_USE_INTERRUPT == 1
// Attach the DMA interrupt
ets_isr_attach(ETS_SLC_INUM, (int_handler_t)ws2812_isr , (void *)0);
//Enable DMA operation intr
// WRITE_PERI_REG(SLC_INT_ENA, SLC_RX_EOF_INT_ENA);
//clear any interrupt flags that are set
WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff);
///enable DMA intr in cpu
ets_isr_unmask(1<<ETS_SLC_INUM);
#endif
//Start transmission
SET_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_START);
//Init pins to i2s functions
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_I2SO_DATA);
//Enable clock to i2s subsystem
i2c_writeReg_Mask_def(i2c_bbpll, i2c_bbpll_en_audio_clock_out, 1);
//Reset I2S subsystem
CLEAR_PERI_REG_MASK(I2SCONF,I2S_I2S_RESET_MASK);
SET_PERI_REG_MASK(I2SCONF,I2S_I2S_RESET_MASK);
CLEAR_PERI_REG_MASK(I2SCONF,I2S_I2S_RESET_MASK);
//Select 16bits per channel (FIFO_MOD=0), no DMA access (FIFO only)
CLEAR_PERI_REG_MASK(I2S_FIFO_CONF, I2S_I2S_DSCR_EN|(I2S_I2S_RX_FIFO_MOD<<I2S_I2S_RX_FIFO_MOD_S)|(I2S_I2S_TX_FIFO_MOD<<I2S_I2S_TX_FIFO_MOD_S));
//Enable DMA in i2s subsystem
SET_PERI_REG_MASK(I2S_FIFO_CONF, I2S_I2S_DSCR_EN);
//trans master&rece slave,MSB shift,right_first,msb right
CLEAR_PERI_REG_MASK(I2SCONF, I2S_TRANS_SLAVE_MOD|
(I2S_BITS_MOD<<I2S_BITS_MOD_S)|
(I2S_BCK_DIV_NUM <<I2S_BCK_DIV_NUM_S)|
(I2S_CLKM_DIV_NUM<<I2S_CLKM_DIV_NUM_S));
SET_PERI_REG_MASK(I2SCONF, I2S_RIGHT_FIRST|I2S_MSB_RIGHT|I2S_RECE_SLAVE_MOD|
I2S_RECE_MSB_SHIFT|I2S_TRANS_MSB_SHIFT|
(((WS_I2S_BCK-1)&I2S_BCK_DIV_NUM )<<I2S_BCK_DIV_NUM_S)|
(((WS_I2S_DIV-1)&I2S_CLKM_DIV_NUM)<<I2S_CLKM_DIV_NUM_S));
#if WS2812_USE_INTERRUPT == 1
//clear int
SET_PERI_REG_MASK(I2SINT_CLR, I2S_I2S_RX_WFULL_INT_CLR|I2S_I2S_PUT_DATA_INT_CLR|I2S_I2S_TAKE_DATA_INT_CLR);
CLEAR_PERI_REG_MASK(I2SINT_CLR, I2S_I2S_RX_WFULL_INT_CLR|I2S_I2S_PUT_DATA_INT_CLR|I2S_I2S_TAKE_DATA_INT_CLR);
//enable int
SET_PERI_REG_MASK(I2SINT_ENA, I2S_I2S_RX_REMPTY_INT_ENA|I2S_I2S_RX_TAKE_DATA_INT_ENA);
#endif
//Start transmission
SET_PERI_REG_MASK(I2SCONF,I2S_I2S_TX_START);
}
// end of file

View File

@ -0,0 +1,25 @@
// ws2812_dma.h
#ifndef __WS2812_DMA_H__
#define __WS2812_DMA_H__
// type definition taken from : sdio_slv.h
typedef struct
{
uint32_t blocksize:12;
uint32_t datalen:12;
uint32_t unused:5;
uint32_t sub_sof:1;
uint32_t eof:1;
uint32_t owner:1;
uint32_t buf_ptr;
uint32_t next_link_ptr;
} sdio_queue_t;
// -----------------------------------------------------
extern void ws2812_dma(sdio_queue_t *);
#endif

View File

@ -0,0 +1,165 @@
// ws2812_gamma.cpp
//
// dithering tables with gamma correction
#include <stdint.h>
#include "ws2812_gamma.h"
static const uint8_t gamma0[] = {
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9,
10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25,
26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 34, 35, 36,
37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49,
50, 51, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 80, 81,
82, 83, 84, 85, 86, 88, 89, 90, 91, 92, 94, 95, 96, 97, 98, 100,
101, 102, 103, 105, 106, 107, 109, 110, 111, 113, 114, 115, 117, 118, 119, 121,
122, 123, 125, 126, 128, 129, 130, 132, 133, 135, 136, 138, 139, 141, 142, 144,
145, 147, 148, 150, 151, 153, 154, 156, 157, 159, 161, 162, 164, 165, 167, 169,
170, 172, 173, 175, 177, 178, 180, 182, 183, 185, 187, 189, 190, 192, 194, 196,
197, 199, 201, 203, 204, 206, 208, 210, 212, 213, 215, 217, 219, 221, 223, 225,
226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 255 };
static const uint8_t gamma1[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9,
9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16,
16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24,
25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35,
36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 84, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, 97, 98, 99,
100, 102, 103, 104, 105, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120,
121, 123, 124, 126, 127, 128, 130, 131, 133, 134, 136, 137, 139, 140, 142, 143,
145, 146, 148, 149, 151, 152, 154, 155, 157, 158, 160, 162, 163, 165, 166, 168,
170, 171, 173, 175, 176, 178, 180, 181, 183, 185, 186, 188, 190, 192, 193, 195,
197, 199, 200, 202, 204, 206, 207, 209, 211, 213, 215, 217, 218, 220, 222, 224,
226, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma2[] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4,
4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9,
9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16,
16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 35, 36,
36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, 49,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
82, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 96, 97, 98, 99,
101, 102, 103, 104, 106, 107, 108, 110, 111, 112, 114, 115, 116, 118, 119, 120,
122, 123, 125, 126, 127, 129, 130, 132, 133, 134, 136, 137, 139, 140, 142, 143,
145, 146, 148, 149, 151, 152, 154, 156, 157, 159, 160, 162, 163, 165, 167, 168,
170, 172, 173, 175, 177, 178, 180, 182, 183, 185, 187, 188, 190, 192, 194, 195,
197, 199, 201, 202, 204, 206, 208, 210, 211, 213, 215, 217, 219, 221, 222, 224,
226, 228, 230, 232, 234, 236, 238, 240, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma3[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4,
4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8,
9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15,
16, 16, 17, 17, 18, 18, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24,
25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 34, 35,
36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 48,
49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 79, 80,
81, 82, 83, 84, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 98, 99,
100, 101, 103, 104, 105, 107, 108, 109, 110, 112, 113, 114, 116, 117, 118, 120,
121, 123, 124, 125, 127, 128, 130, 131, 133, 134, 135, 137, 138, 140, 141, 143,
144, 146, 147, 149, 150, 152, 153, 155, 157, 158, 160, 161, 163, 165, 166, 168,
169, 171, 173, 174, 176, 178, 179, 181, 183, 184, 186, 188, 190, 191, 193, 195,
197, 198, 200, 202, 204, 205, 207, 209, 211, 213, 214, 216, 218, 220, 222, 224,
226, 228, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma4[] = {
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4,
4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9,
9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 14, 15, 15, 16,
16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36,
37, 37, 38, 39, 40, 40, 41, 42, 43, 44, 44, 45, 46, 47, 48, 49,
50, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 81,
82, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 95, 96, 97, 98, 100,
101, 102, 103, 105, 106, 107, 108, 110, 111, 112, 114, 115, 116, 118, 119, 120,
122, 123, 125, 126, 127, 129, 130, 132, 133, 135, 136, 138, 139, 140, 142, 143,
145, 146, 148, 149, 151, 153, 154, 156, 157, 159, 160, 162, 164, 165, 167, 168,
170, 172, 173, 175, 177, 178, 180, 182, 183, 185, 187, 188, 190, 192, 194, 195,
197, 199, 201, 202, 204, 206, 208, 210, 211, 213, 215, 217, 219, 221, 223, 224,
226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma5[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4,
4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 9,
9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 14, 15, 15,
16, 16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24,
25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35,
36, 37, 38, 38, 39, 40, 41, 41, 42, 43, 44, 45, 46, 46, 47, 48,
49, 50, 51, 52, 53, 54, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 79, 80,
81, 82, 83, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 97, 98, 99,
100, 102, 103, 104, 105, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120,
121, 123, 124, 126, 127, 128, 130, 131, 133, 134, 136, 137, 138, 140, 141, 143,
144, 146, 147, 149, 151, 152, 154, 155, 157, 158, 160, 161, 163, 165, 166, 168,
170, 171, 173, 174, 176, 178, 179, 181, 183, 185, 186, 188, 190, 191, 193, 195,
197, 198, 200, 202, 204, 206, 207, 209, 211, 213, 215, 216, 218, 220, 222, 224,
226, 228, 230, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma6[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4,
4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9,
9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 16,
16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25,
25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36,
36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 96, 97, 98, 99,
101, 102, 103, 104, 106, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120,
122, 123, 124, 126, 127, 129, 130, 131, 133, 134, 136, 137, 139, 140, 142, 143,
145, 146, 148, 149, 151, 152, 154, 155, 157, 159, 160, 162, 163, 165, 167, 168,
170, 171, 173, 175, 176, 178, 180, 181, 183, 185, 186, 188, 190, 192, 193, 195,
197, 199, 200, 202, 204, 206, 208, 209, 211, 213, 215, 217, 219, 220, 222, 224,
226, 228, 230, 232, 234, 236, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma7[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8,
9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15,
16, 16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 23, 24,
25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35,
36, 37, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, 45, 46, 47, 48,
49, 50, 51, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 78, 79, 80,
81, 82, 83, 84, 86, 87, 88, 89, 90, 91, 93, 94, 95, 96, 98, 99,
100, 101, 103, 104, 105, 106, 108, 109, 110, 112, 113, 114, 116, 117, 118, 120,
121, 122, 124, 125, 127, 128, 130, 131, 132, 134, 135, 137, 138, 140, 141, 143,
144, 146, 147, 149, 150, 152, 153, 155, 156, 158, 160, 161, 163, 164, 166, 168,
169, 171, 173, 174, 176, 178, 179, 181, 183, 184, 186, 188, 189, 191, 193, 195,
196, 198, 200, 202, 203, 205, 207, 209, 211, 213, 214, 216, 218, 220, 222, 224,
226, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
#if WS2812_DITHER_NUM == 1
const uint8_t *gamma_dither[WS2812_DITHER_NUM] = {gamma0};
#elif WS2812_DITHER_NUM == 2
const uint8_t *gamma_dither[WS2812_DITHER_NUM] = {gamma0,gamma1};
#elif WS2812_DITHER_NUM == 4
const uint8_t *gamma_dither[WS2812_DITHER_NUM] = {gamma0,gamma1,gamma2,gamma3};
#elif WS2812_DITHER_NUM == 8
const uint8_t *gamma_dither[WS2812_DITHER_NUM] = {gamma0,gamma1,gamma2,gamma3,gamma4,gamma5,gamma6,gamma7};
#else
#error Invalid WS2812_DITHER_NUM value. Allowed values are 1, 2, 4, 8
#endif
// end of file

View File

@ -0,0 +1,12 @@
// ws2812_gamma.h
#ifndef __WS2812_GAMMA_H__
#define __WS2812_GAMMA_H__
#include <stdint.h>
#include "ws2812.h"
extern const uint8_t *gamma_dither[WS2812_DITHER_NUM];
#endif

View File

@ -0,0 +1,169 @@
// ws2812_lib.cpp
//
// main library file / contains class implementation
//
// Need to give credits to Charles Lohr (https://github.com/cnlohr due
// to his work on his software for driving ws2812 led-strips using
// the i2s interface of the ESP8266.
//
// This inspired me to create an ESP8266 library for the Arduino IDE
// I've added temporal dithering & gamma-correction
// for a 'more natural' light intensity profile.
//
// No pin-definitions/mapings are required/possible as the library used the I2S
// output-pin. This pin in it's turn is shared with GPIO3 & RXD0
//
#include <Arduino.h>
#include <stdint.h>
#include "ws2812_i2s.h"
#include "ws2812_defs.h"
#include "ws2812_gamma.h"
// include C-style header
extern "C"
{
#include "ws2812_dma.h"
};
// class constructor
WS2812::WS2812(void)
{
// empty for now
}
// class de-constructor
WS2812::~WS2812(void)
{
// empty for now
// TODO : should implement switching of DMA
}
// Init led-string / memory buffers etc.
void WS2812::init(uint16_t _num_leds)
{
uint8_t i;
uint16_t j;
num_leds = _num_leds;
// clear zero buffer
for(j=0; j<NUM_I2S_ZERO_WORDS; j++)
{
i2s_zeros_buffer[j] = 0;
}
// do memory allocation for i2s buffer(s)
for(i=0; i<WS2812_DITHER_NUM; i++)
{
i2s_pixels_buffer[i] = new uint32_t[NUM_I2S_PIXEL_WORDS];
// TODO : handle memory allocation error better
if (i2s_pixels_buffer[i] == 0)
{
Serial.println("WS2812_I2S : ERROR ALLOCATING MEMORY");
return;
}
for(j=0; j<NUM_I2S_PIXEL_WORDS; j++)
{
i2s_pixels_buffer[i][j] = 0;
}
}
// set-up DMA descriptors / 1 pair per dither-factor
for(i=0; i<WS2812_DITHER_NUM; i++)
{
i2s_pixels_queue[i].owner = 1;
i2s_pixels_queue[i].eof = 1;
i2s_pixels_queue[i].sub_sof = 0;
i2s_pixels_queue[i].datalen = NUM_I2S_PIXEL_BYTES; // Size in bytes
i2s_pixels_queue[i].blocksize = NUM_I2S_PIXEL_BYTES; // Size in bytes
i2s_pixels_queue[i].buf_ptr = (uint32_t)i2s_pixels_buffer[i];
i2s_pixels_queue[i].unused = 0;
i2s_pixels_queue[i].next_link_ptr = (uint32_t)&i2s_zeros_queue[i]; // always link to zeros-buffer
i2s_zeros_queue[i].owner = 1;
i2s_zeros_queue[i].eof = 1;
i2s_zeros_queue[i].sub_sof = 0;
i2s_zeros_queue[i].datalen = NUM_I2S_ZERO_BYTES; // Size in bytes)
i2s_zeros_queue[i].blocksize = NUM_I2S_ZERO_BYTES; // Size in bytes
i2s_zeros_queue[i].buf_ptr = (uint32_t)i2s_zeros_buffer;
i2s_zeros_queue[i].unused = 0;
if (i == (WS2812_DITHER_NUM-1)) // last DMA descriptor in linked list ?
{
// yes : link to first DMA descriptor
i2s_zeros_queue[i].next_link_ptr = (uint32_t)&i2s_pixels_queue[0];
}
else
{
// no : link to next DMA descriptor
i2s_zeros_queue[i].next_link_ptr = (uint32_t)&i2s_pixels_queue[i+1];
}
}
// call C based helper function
// I did not really succeed in putting the code from the helper
// funtion directly into this constructor...has something to do
// with C vs. C++
// the i2c_writeReg_Mask macro (+ others) failed.. see ws2812_defs.h
// may be I solve this later
// parameter = first entry in DMA descriptor list, i.e the descriptor list
ws2812_dma(i2s_pixels_queue);
}
// left this comment in tact....credit to Charles
// All functions below this line are Public Domain 2015 Charles Lohr.
// this code may be used by anyone in any way without restriction or limitation.
// Send out WS2812 bits with coded pulses, one nibble, then the other.
static const uint16_t bitpatterns[16] =
{
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110,
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
};
// display the pixels
void WS2812::show(Pixel_t *pixels)
{
uint8_t *buffer;
uint8_t pixelbyte;
uint8_t gammabyte;
uint16_t i,b;
uint16_t *i2s_ptr[WS2812_DITHER_NUM];
buffer = (uint8_t *)pixels;
// set-up pointers into the i2s-pixel-buffers
for(i=0; i<WS2812_DITHER_NUM; i++)
{
i2s_ptr[i] = (uint16_t *)i2s_pixels_buffer[i];
}
// for every pixel in the input-array
// - get the pixel value (either R,G, or B)
// - for every dithered buffer
// get the gamma-corrected output value
// - and transform into i2s nibble
for(b=0; b<NUM_RGB_BYTES; b++)
{
pixelbyte = *buffer++;
for(i=0; i<WS2812_DITHER_NUM; i++)
{
gammabyte = gamma_dither[i][pixelbyte];
*(i2s_ptr[i]++) = bitpatterns[ (gammabyte & 0x0f) ];
*(i2s_ptr[i]++) = bitpatterns[ (gammabyte>>4) & 0x0f ];
}
}
}
// end of file

View File

@ -0,0 +1,41 @@
// ws2812_lib.h
#ifndef __WS2812_I2S_H__
#define __WS2812_I2S_H__
#include <stdint.h>
#include "ws2812_defs.h"
// include C-style header
extern "C"
{
#include "ws2812_dma.h"
};
typedef struct
{
uint8_t G; // G,R,B order is determined by WS2812B
uint8_t R;
uint8_t B;
} Pixel_t;
class WS2812
{
public:
WS2812(void);
~WS2812(void);
void init(uint16_t num_leds);
void show(Pixel_t *);
private:
uint16_t num_leds;
uint32_t *i2s_pixels_buffer[WS2812_DITHER_NUM];
uint32_t i2s_zeros_buffer[NUM_I2S_ZERO_WORDS];
sdio_queue_t i2s_zeros_queue[WS2812_DITHER_NUM];
sdio_queue_t i2s_pixels_queue[WS2812_DITHER_NUM];
};
#endif
// end of file

View File

@ -15,7 +15,7 @@ pixels = np.tile(1, (3, config.N_PIXELS))
def update(): def update():
global pixels, _prev_pixels global pixels, _prev_pixels
pixels = np.clip(pixels, 0, 255) pixels = np.clip(pixels, 0, 255).astype(int)
m = '' m = ''
p = _gamma[pixels] if config.GAMMA_CORRECTION else np.copy(pixels) p = _gamma[pixels] if config.GAMMA_CORRECTION else np.copy(pixels)
for i in range(config.N_PIXELS): for i in range(config.N_PIXELS):
@ -27,6 +27,19 @@ def update():
_sock.sendto(m.encode(), (config.UDP_IP, config.UDP_PORT)) _sock.sendto(m.encode(), (config.UDP_IP, config.UDP_PORT))
# Execute this file to run a LED strand test
# If everything is working, you should see a red, green, and blue pixel scroll
# across the LED strip continously
if __name__ == '__main__': if __name__ == '__main__':
import time
# Turn all pixels off
pixels *= 0 pixels *= 0
update() pixels[0, 0] = 255 # Set 1st pixel red
pixels[1, 1] = 255 # Set 2nd pixel green
pixels[2, 2] = 255 # Set 3rd pixel blue
print('Starting LED strand test')
while True:
pixels = np.roll(pixels, 1, axis=1)
update()
time.sleep(0.2)

View File

@ -75,13 +75,13 @@ b_filt = dsp.ExpFilter(np.tile(0.01, config.N_PIXELS // 2),
alpha_decay=0.25, alpha_rise=0.99) alpha_decay=0.25, alpha_rise=0.99)
p_filt = dsp.ExpFilter(np.tile(1, (3, config.N_PIXELS // 2)), p_filt = dsp.ExpFilter(np.tile(1, (3, config.N_PIXELS // 2)),
alpha_decay=0.05, alpha_rise=0.8) alpha_decay=0.05, alpha_rise=0.8)
p = np.tile(1, (3, config.N_PIXELS // 2)) p = np.tile(1.0, (3, config.N_PIXELS // 2))
gain = dsp.ExpFilter(np.tile(0.01, config.N_FFT_BINS), gain = dsp.ExpFilter(np.tile(0.01, config.N_FFT_BINS),
alpha_decay=0.001, alpha_rise=0.99) alpha_decay=0.001, alpha_rise=0.99)
def largest_indices(ary, n): def largest_indices(ary, n):
"""Returns the n largest indices from a numpy array.""" """Returns indices of the n largest values in the given a numpy array"""
flat = ary.flatten() flat = ary.flatten()
indices = np.argpartition(flat, -n)[-n:] indices = np.argpartition(flat, -n)[-n:]
indices = indices[np.argsort(-flat[indices])] indices = indices[np.argsort(-flat[indices])]
@ -89,11 +89,12 @@ def largest_indices(ary, n):
def visualize_max(y): def visualize_max(y):
"""Experimental sandbox effect. Not recommended for use"""
y = np.copy(interpolate(y, config.N_PIXELS // 2)) * 255.0 y = np.copy(interpolate(y, config.N_PIXELS // 2)) * 255.0
ind = largest_indices(y, 15) ind = largest_indices(y, 15)
y[ind] *= -1 y[ind] *= -1.0
y[y > 0] = 0 y[y > 0] = 0.0
y[ind] *= -1 y[ind] *= -1.0
# Blur the color channels with different strengths # Blur the color channels with different strengths
r = gaussian_filter1d(y, sigma=0.25) r = gaussian_filter1d(y, sigma=0.25)
g = gaussian_filter1d(y, sigma=0.10) g = gaussian_filter1d(y, sigma=0.10)
@ -111,14 +112,15 @@ def visualize_max(y):
led.pixels[0, :] = pixel_r led.pixels[0, :] = pixel_r
led.pixels[1, :] = pixel_g led.pixels[1, :] = pixel_g
led.pixels[2, :] = pixel_b led.pixels[2, :] = pixel_b
led.update()
# Update the GUI plots # Update the GUI plots
GUI.curve[0][0].setData(y=pixel_r) GUI.curve[0][0].setData(y=pixel_r)
GUI.curve[0][1].setData(y=pixel_g) GUI.curve[0][1].setData(y=pixel_g)
GUI.curve[0][2].setData(y=pixel_b) GUI.curve[0][2].setData(y=pixel_b)
led.update()
def visualize_scroll(y): def visualize_scroll(y):
"""Effect that originates in the center and scrolls outwards"""
global p global p
y = gaussian_filter1d(y, sigma=1.0)**3.0 y = gaussian_filter1d(y, sigma=1.0)**3.0
y = np.copy(y) y = np.copy(y)
@ -134,11 +136,17 @@ def visualize_scroll(y):
p[0, 0] = r p[0, 0] = r
p[1, 0] = g p[1, 0] = g
p[2, 0] = b p[2, 0] = b
# Update the LED strip
led.pixels = np.concatenate((p[:, ::-1], p), axis=1) led.pixels = np.concatenate((p[:, ::-1], p), axis=1)
led.update() led.update()
# Update the GUI plots
GUI.curve[0][0].setData(y=np.concatenate((p[0, :][::-1], p[0, :])))
GUI.curve[0][1].setData(y=np.concatenate((p[1, :][::-1], p[1, :])))
GUI.curve[0][2].setData(y=np.concatenate((p[2, :][::-1], p[2, :])))
def visualize_energy(y): def visualize_energy(y):
"""Effect that expands from the center with increasing sound energy"""
global p global p
y = gaussian_filter1d(y, sigma=1.0)**3.0 y = gaussian_filter1d(y, sigma=1.0)**3.0
gain.update(y) gain.update(y)
@ -147,22 +155,28 @@ def visualize_energy(y):
r = int(np.mean(y[:len(y) // 3])) r = int(np.mean(y[:len(y) // 3]))
g = int(np.mean(y[len(y) // 3: 2 * len(y) // 3])) g = int(np.mean(y[len(y) // 3: 2 * len(y) // 3]))
b = int(np.mean(y[2 * len(y) // 3:])) b = int(np.mean(y[2 * len(y) // 3:]))
p[0, :r] = 255 p[0, :r] = 255.0
p[0, r:] = 0 p[0, r:] = 0.0
p[1, :g] = 255 p[1, :g] = 255.0
p[1, g:] = 0 p[1, g:] = 0.0
p[2, :b] = 255 p[2, :b] = 255.0
p[2, b:] = 0 p[2, b:] = 0.0
p_filt.update(p) p_filt.update(p)
p = p_filt.value.astype(int) p = p_filt.value.astype(int)
p[0, :] = gaussian_filter1d(p[0, :], sigma=4.0) p[0, :] = gaussian_filter1d(p[0, :], sigma=4.0)
p[1, :] = gaussian_filter1d(p[1, :], sigma=4.0) p[1, :] = gaussian_filter1d(p[1, :], sigma=4.0)
p[2, :] = gaussian_filter1d(p[2, :], sigma=4.0) p[2, :] = gaussian_filter1d(p[2, :], sigma=4.0)
# Update LED pixel arrays
led.pixels = np.concatenate((p[:, ::-1], p), axis=1) led.pixels = np.concatenate((p[:, ::-1], p), axis=1)
led.update() led.update()
# Update the GUI plots
GUI.curve[0][0].setData(y=np.concatenate((p[0, :][::-1], p[0, :])))
GUI.curve[0][1].setData(y=np.concatenate((p[1, :][::-1], p[1, :])))
GUI.curve[0][2].setData(y=np.concatenate((p[2, :][::-1], p[2, :])))
def visualize_spectrum(y): def visualize_spectrum(y):
"""Effect that maps the Mel filterbank frequencies onto the LED strip"""
y = np.copy(interpolate(y, config.N_PIXELS // 2)) * 255.0 y = np.copy(interpolate(y, config.N_PIXELS // 2)) * 255.0
# Blur the color channels with different strengths # Blur the color channels with different strengths
r = gaussian_filter1d(y, sigma=0.25) r = gaussian_filter1d(y, sigma=0.25)
@ -179,11 +193,11 @@ def visualize_spectrum(y):
led.pixels[0, :] = pixel_r led.pixels[0, :] = pixel_r
led.pixels[1, :] = pixel_g led.pixels[1, :] = pixel_g
led.pixels[2, :] = pixel_b led.pixels[2, :] = pixel_b
led.update()
# Update the GUI plots # Update the GUI plots
GUI.curve[0][0].setData(y=pixel_r) GUI.curve[0][0].setData(y=pixel_r)
GUI.curve[0][1].setData(y=pixel_g) GUI.curve[0][1].setData(y=pixel_g)
GUI.curve[0][2].setData(y=pixel_b) GUI.curve[0][2].setData(y=pixel_b)
led.update()
mel_gain = dsp.ExpFilter(np.tile(1e-1, config.N_FFT_BINS), mel_gain = dsp.ExpFilter(np.tile(1e-1, config.N_FFT_BINS),
@ -194,7 +208,7 @@ volume = dsp.ExpFilter(config.MIN_VOLUME_THRESHOLD,
def microphone_update(stream): def microphone_update(stream):
global y_roll, prev_rms, prev_exp global y_roll, prev_rms, prev_exp
# Normalize new audio samples # Retrieve and normalize the new audio samples
y = np.fromstring(stream.read(samples_per_frame, y = np.fromstring(stream.read(samples_per_frame,
exception_on_overflow=False), dtype=np.int16) exception_on_overflow=False), dtype=np.int16)
y = y / 2.0**15 y = y / 2.0**15
@ -209,19 +223,25 @@ def microphone_update(stream):
led.pixels = np.tile(0, (3, config.N_PIXELS)) led.pixels = np.tile(0, (3, config.N_PIXELS))
led.update() led.update()
else: else:
# Transform audio input into the frequency domain
XS, YS = dsp.fft(y_data, window=np.hamming) XS, YS = dsp.fft(y_data, window=np.hamming)
# Remove half of the FFT data because of symmetry
YS = YS[:len(YS) // 2] YS = YS[:len(YS) // 2]
XS = XS[:len(XS) // 2] XS = XS[:len(XS) // 2]
# Construct a Mel filterbank from the FFT data
YS = np.atleast_2d(np.abs(YS)).T * dsp.mel_y.T YS = np.atleast_2d(np.abs(YS)).T * dsp.mel_y.T
# Scale data to values more suitable for visualization
YS = np.sum(YS, axis=0)**2.0 YS = np.sum(YS, axis=0)**2.0
mel = YS**0.5 mel = YS**0.5
mel = gaussian_filter1d(mel, sigma=1.0) mel = gaussian_filter1d(mel, sigma=1.0)
# Normalize the Mel filterbank to make it volume independent
mel_gain.update(np.max(mel)) mel_gain.update(np.max(mel))
mel = mel / mel_gain.value mel = mel / mel_gain.value
visualize_spectrum(mel) # Visualize the filterbank output
# visualize_spectrum(mel)
# visualize_max(mel) # visualize_max(mel)
# visualize_scroll(mel) # visualize_scroll(mel)
# visualize_energy(mel) visualize_energy(mel)
GUI.app.processEvents() GUI.app.processEvents()
print('FPS {:.0f} / {:.0f}'.format(frames_per_second(), config.FPS)) print('FPS {:.0f} / {:.0f}'.format(frames_per_second(), config.FPS))
@ -235,8 +255,8 @@ y_roll = np.random.rand(config.N_ROLLING_HISTORY, samples_per_frame) / 1e16
if __name__ == '__main__': if __name__ == '__main__':
import pyqtgraph as pg import pyqtgraph as pg
# Create GUI plot for visualizing LED strip output
GUI = gui.GUI(width=800, height=400, title='Audio Visualization') GUI = gui.GUI(width=800, height=400, title='Audio Visualization')
# Audio plot
GUI.add_plot('Color Channels') GUI.add_plot('Color Channels')
r_pen = pg.mkPen((255, 30, 30, 200), width=6) r_pen = pg.mkPen((255, 30, 30, 200), width=6)
g_pen = pg.mkPen((30, 255, 30, 200), width=6) g_pen = pg.mkPen((30, 255, 30, 200), width=6)