Skip to content

Commit

Permalink
add audio functions
Browse files Browse the repository at this point in the history
New functions for audio capture and make LEDs react to music
  • Loading branch information
Tom committed Jul 19, 2021
1 parent 83ecf02 commit c440bb8
Show file tree
Hide file tree
Showing 14 changed files with 999 additions and 31 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Version 6.1
* Added functions to make LEDs react to audio input

# Version 6.0
* Added support for 2D graphics
* Added virtual channels
Expand Down
101 changes: 95 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ read the change log to see what is new:
You can use this with the WS2811, WS2812, SK6812, SK9822 chips.

# Installation
On the raspberry you open a terminal window and type following commands:
Standard installation with support for all features use the following commands:
```
sudo apt-get update
sudo apt-get install gcc make git libjpeg-dev libpng-dev pkg-config
sudo apt-get install gcc make git libjpeg-dev libpng-dev pkg-config libasound2-dev libcairo2-dev libx11-dev libxcb1-dev libfreetype6-dev
git clone https://github.com/tom-2015/rpi-ws2812-server.git
cd rpi-ws2812-server
make
make ENABLE_2D=1 ENABLE_AUDIO=1
sudo chmod +x ws2812svr
```

Expand All @@ -33,6 +33,12 @@ sudo apt-get install pkg-config libcairo2-dev libx11-dev libxcb1-dev libfreetype
make ENABLE_2D=1
```

If you want to use audio commands to make your LEDs react to music input (through USB sound card). You must
install libasound2-dev and compile with ENABLE_AUDIO=1
```
make ENABLE_AUDIO=1
```

On newer Raspbian (>=Jessie) operating system the audio output is activated by default, you need to disable this:
You can do this by blacklisting the sound module:
```
Expand Down Expand Up @@ -325,13 +331,13 @@ color_change <channel>,<start_color>,<stop_color>,<duration>,<start>,<len>

* `chaser` makes a chaser light
```
chaser <channel>,<duration>,<color>,<direction>,<count>,<delay>,<start>,<len>,<brightness>,<loops>
chaser <channel>,<duration>,<color>,<count>,<direction>,<delay>,<start>,<len>,<brightness>,<loops>
# <channel> Channel number to use
# <duration> Max number of seconds the event may take in seconds (default 10) use 0 to make chaser run forever
# <color> Color 000000-FFFFFF to use for chasing leds
# <direction> Direction 1 or 0 to indicate forward/backwards direction of movement
# <count> Number of LEDs in the chaser that will light up
# <direction> Direction 1 or 0 to indicate forward/backwards direction of movement
# <delay> Delay between moving one pixel (milliseconds) default is 10ms
# <start> Start effect at this led position
# <len> Number of leds the chaser will move before starting back start led
Expand Down Expand Up @@ -897,4 +903,87 @@ pixel_color <channel>,<x>,<y>,<z>,<color>
# <y> Y location to fill
# <z> Z location to fill, a.t.m. Z is not supported
# <color> Color to set in the pixel
```
```

# Audio input
Since version 6.1 it's possible to make LEDs react to audio input. This audio will be captured from an ALSA sound device (USB sound card,...).
This requires to compile with the parameter ENABLE_AUDIO=1.
Before you can use any of the audio commands you first need to start recording, you can do so with the record_audio command.
NOTE: if you want to use multiple threads each thread needs to run the record_audio command before you can use the audio effect commands.

```
* `record_audio` records audio
```
record_audio <channel>,<sample_rate>,<sample_count>,<channels>

# <channel> Channel number
# <sample_rate> Here you can change the number of samples per second, default is 24000. Higher will require more CPU but better results with high frequency.
# <sample_count> Number of samples to store in the internal buffer before forwarding to DSP. Default is 1024 and you better leave it like that.
# <channels> Number of audio channels, default is 2 (stereo)
```
```

* `light_organ` Generates a light organ, alle LEDs blink to the rhythm of the music.

```
light_organ <channel>,<color_mode>,<color(s)>,<color_change_delay>,<duration>,<delay>,<start>,<len>
# <channel> Channel number
# <color_mode> Sets how changing of color should behave (default 2).
# <color(s)> Give color or multiple colors like FF0000 (red) or FF000000FF000000FF (red,green,blue)
# <color_change_delay> Number of seconds to wait before changing the color
# <duration> Number of seconds before automatic exit of the command, 0 to run forever.
# <delay> Delay between rendering of the strip. default is 25ms increase to make flashing slower
# <start> Start at this LED number
# <len> Effect for this number of LEDs starting at start
```
Color modes:
0 = 1 fixed color
1 = color sequence provided in the color(s) argument
2 = random colors provided in the color(s) argument, color(s) argument can be left empty for some default colors.
3 = keep existing color in the strip, this must be set first

```
* `pulses` Generates wave pattern on the LED strip (like a music driven chaser)
```
pulses <channel>,<threshold>,<color_mode>,<color(s)>,<delay>,<color_change_delay>,<direction>,<duration>,<min_brightness>,<start>,<len>

# <channel> Channel number
# <threshold> Minimum level of audio sample to react, float value range 0-1 default is 0.1, audio sample values <0.1 will be ignored
# <color_mode> Sets how changing of color should behave, see light_organ for possible values (default 2).
# <color(s)> Give color or multiple colors like FF0000 (red) or FF000000FF000000FF (red,green,blue)
# <delay> Delay between rendering of the strip, default 25ms. Decrease to make wave go faster but increase CPU load
# <color_change_delay> Number of seconds to wait before changing the color
# <direction> Chaser go forward or backwards
# <duration> Number of seconds before automatic exit of the command, 0 to run forever.
# <min_brightness> Minimum level of brightness or LEDs will be turned off (default is 10 max 255).
# <start> Start at this LED number
# <len> Effect for this number of LEDs starting at start
```
* `vu_meter`
```
vu_meter <channel>,<color_mode>,<color(s)>,<color_change_delay>,<duration>,<delay>,<brightness>,<start>,<len>

# <channel> Channel number
# <color_mode> Sets how changing of color should behave (default 4).
# <color(s)> Give color or multiple colors like FF0000 (red) or FF000000FF000000FF (red,green,blue)
# <color_change_delay> Number of seconds to wait before changing the color
# <duration> Number of seconds before automatic exit of the command, 0 to run forever.
# <delay> Delay between rendering of the strip, default 25ms. Decrease to make wave go faster but increase CPU load
# <brightness> Brightness level of turned on LEDs, (0-255) default is 255
# <start> Start at this LED number
# <len> Effect for this number of LEDs starting at start
```
Color modes:
0 = 1 fixed color
1 = color sequence provided in the color(s) argument
2 = random colors provided in the color(s) argument, color(s) argument can be left empty for some default colors.
3 = keep existing color in the strip, this must be set first
4 = Classic VU meter colors (green - yellow - orange - red)
132 changes: 132 additions & 0 deletions audio/light_organ.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//light_organ <channel>,<color_mode>,<color(s)>,<color_change_delay>,<duration>,<delay>,<start>,<len>
void light_organ(thread_context* context, char* args) {
const unsigned int MAX_COLORS = 255;
int channel = 0;
int duration = 0;
int delay = 25;
int len = 0, start = 0;
unsigned int next_color = 255;
unsigned int colors[MAX_COLORS];
int color_count = 0;
int color_mode = 2; //default some random colors
int color_change_delay = 60; //60 sec change color
float gain = 4.0;
int i;

if (!context->audio_capture.capturing) {
fprintf(stderr, "Error audio capture is not running on this thread. Use the audio_capture command to start capturing first!\n");
return;
}

args = read_channel(args, &channel);

if (is_valid_channel_number(channel)) {
//args = read_float(args, &threshold);
args = read_int(args, &color_mode);

switch (color_mode) {
case 0: //fixed color
args = read_color_arg(args, &next_color, get_color_size(channel));
colors[0] = next_color;
break;
case 1: //color sequence
if (*args == ',') args++;
while (color_count < MAX_COLORS && *args != ',' && *args != 0) {
args = read_color(args, &colors[color_count], get_color_size(channel));
color_count++;
}
next_color = colors[0];
break;
case 2: //random colors
if (*args == ',') args++;
while (color_count < MAX_COLORS && *args != ',' && *args != 0) {
args = read_color(args, &colors[color_count], get_color_size(channel));
color_count++;
}
if (color_count == 0) {
colors[0] = color(255, 0, 0);
colors[1] = color(255, 255, 0);
colors[2] = color(0, 255, 255);
colors[3] = color(0, 255, 0);
colors[4] = color(255, 128, 0);
colors[5] = color(255, 0, 128);
colors[6] = color(0, 0, 255);
color_count = 7;
}
next_color = colors[0];
break;
case 3: //don't care about color

break;
default:
fprintf(stderr, "Error invalid color mode %d\n", color_mode);
return;
break;
}

args = read_int(args, &color_change_delay);
args = read_int(args, &duration);
args = read_int(args, &delay);
args = read_int(args, &start);
args = read_int(args, &len);

if (debug) printf("light_organ %d,%d,%d,%d,%d,%d,%d,%d\n", channel, color_mode, color_count, color_change_delay, duration, delay, start, len);

if (start >= get_led_count(channel)) start = 0;
if ((start + len) > get_led_count(channel)) len = get_led_count(channel) - start;
if (len == 0) len = get_led_count(channel);

ws2811_led_t* leds = get_led_string(channel);
unsigned int start_time = time(0);
unsigned int color_time = time(0);
unsigned int next_color_time = color_time;
unsigned int color_index = 0;
unsigned int current_color = next_color;

volatile float low_pass_buffer=0; //here the record function will store the number of times threshold was reached
context->audio_capture.capture_dst_buffer = (void*)&low_pass_buffer;
context->audio_capture.dsp_mode = DSP_MODE_LOW_PASS;

while ((((time(0) - start_time) < duration) || duration == 0) && context->end_current_command == 0) {
float low_pass_value;

pthread_mutex_lock(&context->audio_capture.buffer_mutex);
low_pass_value = low_pass_buffer;
pthread_mutex_unlock(&context->audio_capture.buffer_mutex);

unsigned int intensity = 255.0 * low_pass_value * gain;

if (color_mode!=3 && next_color_time <= time(0)) {
switch (color_mode) {
case 0: //fixed color
break;
case 1: //color sequence
next_color = colors[color_index];
color_index++;
if (color_index == color_count) color_index = 0;
break;
case 2: //random color from color list
color_index = (float)color_count * ((float)rand() / (float)RAND_MAX);
next_color = colors[color_index];
break;
}
next_color_time = time(0) + color_change_delay;
for (i = 0;i < len;i++) {
leds[start + i].color = next_color;
}
}

for (i = 0;i < len;i++) {
leds[start + i].brightness = intensity;
}

render_channel(channel);
usleep(delay * 1000);

}
context->audio_capture.dsp_mode = DSP_MODE_NONE;
}
else {
fprintf(stderr, ERROR_INVALID_CHANNEL);
}
}
Loading

0 comments on commit c440bb8

Please sign in to comment.