├── maker-templates ├── rpi_ws281x │ ├── segments │ │ ├── gbd.h │ │ └── main.c │ ├── simple │ │ ├── gbd.h │ │ └── main.c │ └── README.md ├── README.md ├── gbd.h ├── gbd-text-display.c └── gbd-gl.c ├── armv6l ├── gbd.so └── gbdserver ├── armv71 ├── gbd.so └── gbdserver ├── usr └── local │ └── share │ └── rtbeats │ └── rtbeats-keys ├── x86-64-ubuntu-18.04 ├── gbd.so └── gbdserver ├── docs └── img │ ├── gbd-with-web-ui.png │ ├── gbd-iot-framework.jpg │ ├── gbd-with-bt-audio.png │ ├── gbd-simple-mpv-webui.png │ ├── generalized-gbd-arch.jpg │ ├── smplayer-gbd-config.png │ ├── smplayer-volume-control.png │ ├── alsamixer-volume-control.png │ ├── gbd-simple-mpv-webui-play.png │ ├── gbd-framework-arch-economy.jpg │ ├── gbd-simple-mpv-webui-playlist.png │ ├── unity-sound-system-volume-control.png │ └── gbd-framework-arch-rpi-standalone-hw-addon.jpg ├── gbdclient ├── Makefile-x86_64 ├── Makefile-arm-rpi └── gbdclient.c ├── LICENSE.txt └── README.md /maker-templates/rpi_ws281x/segments/gbd.h: -------------------------------------------------------------------------------- 1 | ../../gbd.h -------------------------------------------------------------------------------- /maker-templates/rpi_ws281x/simple/gbd.h: -------------------------------------------------------------------------------- 1 | ../../gbd.h -------------------------------------------------------------------------------- /armv6l/gbd.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/armv6l/gbd.so -------------------------------------------------------------------------------- /armv71/gbd.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/armv71/gbd.so -------------------------------------------------------------------------------- /usr/local/share/rtbeats/rtbeats-keys: -------------------------------------------------------------------------------- 1 | 8866131180607834214 2 | 1511949662453254381 3 | -------------------------------------------------------------------------------- /armv6l/gbdserver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/armv6l/gbdserver -------------------------------------------------------------------------------- /armv71/gbdserver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/armv71/gbdserver -------------------------------------------------------------------------------- /x86-64-ubuntu-18.04/gbd.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/x86-64-ubuntu-18.04/gbd.so -------------------------------------------------------------------------------- /docs/img/gbd-with-web-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/gbd-with-web-ui.png -------------------------------------------------------------------------------- /docs/img/gbd-iot-framework.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/gbd-iot-framework.jpg -------------------------------------------------------------------------------- /docs/img/gbd-with-bt-audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/gbd-with-bt-audio.png -------------------------------------------------------------------------------- /x86-64-ubuntu-18.04/gbdserver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/x86-64-ubuntu-18.04/gbdserver -------------------------------------------------------------------------------- /docs/img/gbd-simple-mpv-webui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/gbd-simple-mpv-webui.png -------------------------------------------------------------------------------- /docs/img/generalized-gbd-arch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/generalized-gbd-arch.jpg -------------------------------------------------------------------------------- /docs/img/smplayer-gbd-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/smplayer-gbd-config.png -------------------------------------------------------------------------------- /docs/img/smplayer-volume-control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/smplayer-volume-control.png -------------------------------------------------------------------------------- /docs/img/alsamixer-volume-control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/alsamixer-volume-control.png -------------------------------------------------------------------------------- /docs/img/gbd-simple-mpv-webui-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/gbd-simple-mpv-webui-play.png -------------------------------------------------------------------------------- /docs/img/gbd-framework-arch-economy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/gbd-framework-arch-economy.jpg -------------------------------------------------------------------------------- /docs/img/gbd-simple-mpv-webui-playlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/gbd-simple-mpv-webui-playlist.png -------------------------------------------------------------------------------- /docs/img/unity-sound-system-volume-control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/unity-sound-system-volume-control.png -------------------------------------------------------------------------------- /docs/img/gbd-framework-arch-rpi-standalone-hw-addon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generic-beat-detector/GBD/HEAD/docs/img/gbd-framework-arch-rpi-standalone-hw-addon.jpg -------------------------------------------------------------------------------- /maker-templates/README.md: -------------------------------------------------------------------------------- 1 | ### Hello, 2 | 3 | See [*GBD Quick Start*](https://github.com/generic-beat-detector/GBD/wiki/Quick-Start) for basic usage. Check the `README.md` files in the respective directories for detailed usage. 4 | -------------------------------------------------------------------------------- /maker-templates/gbd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * file: gbd.h 3 | * desc: GBD Linux POSIX SHM interface 4 | */ 5 | 6 | #ifndef __GBD_H__ 7 | #define __GBD_H__ 8 | 9 | /* GBD beat count data is stored in a simple "C int" 10 | * array in POSIX SHM */ 11 | 12 | /* Name of GBD beat data file in POSIX SHM */ 13 | #define GBD_BEAT_COUNT_FILE "gbd" 14 | 15 | /* Array offsets */ 16 | #define KICKDRUM 0 17 | #define SNARE 1 18 | #define CYMBALS 2 19 | #define AVG_ENERGY_L_CHANNEL 3 20 | #define AVG_ENERGY_R_CHANNEL 4 21 | #define RESERVED0 5 22 | #define RESERVED1 6 23 | #define RESERVED2 7 24 | #define RESERVED3 8 25 | #define BASSLINE 9 26 | 27 | /* Number of array elements */ 28 | #define GBD_BEAT_COUNT_BUF_SIZE 10 29 | 30 | #endif /* __GBD_H__ */ 31 | -------------------------------------------------------------------------------- /gbdclient/Makefile-x86_64: -------------------------------------------------------------------------------- 1 | 2 | CC := gcc 3 | override CFLAGS += -I. -O2 -Wall -funroll-loops -ffast-math -fPIC -DPIC 4 | LD := gcc 5 | override LDFLAGS += -O2 -Wall -shared -lasound 6 | 7 | SND_PCM_OBJECTS = gbdclient.o 8 | SND_PCM_LIBS = 9 | SND_PCM_BIN = libasound_module_pcm_gbdclient.so 10 | 11 | .PHONY: all clean install uninstall 12 | 13 | all: $(SND_PCM_BIN) 14 | 15 | $(SND_PCM_BIN): $(SND_PCM_OBJECTS) 16 | @echo Building $@ ... 17 | $(LD) $(LDFLAGS) $(SND_PCM_LIBS) $(SND_PCM_OBJECTS) -o $(SND_PCM_BIN) 18 | @echo Stripping $@ ... 19 | strip $(SND_PCM_BIN) 20 | 21 | %.o: %.c 22 | @echo GCC $< 23 | $(CC) -c $(CFLAGS) $< 24 | 25 | clean: 26 | @echo Cleaning... 27 | $(Q)rm -vf *.o *.so *~ 28 | 29 | install: all 30 | @echo Installing... 31 | install -m 644 $(SND_PCM_BIN) ${DESTDIR}/usr/lib/x86_64-linux-gnu/alsa-lib/ 32 | 33 | uninstall: 34 | @echo Un-installing... 35 | rm -f ${DESTDIR}/usr/lib/x86_64-linux-gnu/alsa-lib/$(SND_PCM_BIN) 36 | 37 | -------------------------------------------------------------------------------- /gbdclient/Makefile-arm-rpi: -------------------------------------------------------------------------------- 1 | 2 | CC := gcc 3 | override CFLAGS += -I. -O2 -Wall -funroll-loops -ffast-math -fPIC -DPIC 4 | LD := gcc 5 | override LDFLAGS += -O2 -Wall -shared -lasound 6 | 7 | SND_PCM_OBJECTS = gbdclient.o 8 | SND_PCM_LIBS = 9 | SND_PCM_BIN = libasound_module_pcm_gbdclient.so 10 | 11 | .PHONY: all clean install uninstall 12 | 13 | all: $(SND_PCM_BIN) 14 | 15 | $(SND_PCM_BIN): $(SND_PCM_OBJECTS) 16 | @echo Building $@ ... 17 | $(LD) $(LDFLAGS) $(SND_PCM_LIBS) $(SND_PCM_OBJECTS) -o $(SND_PCM_BIN) 18 | @echo Stripping $@ ... 19 | strip $(SND_PCM_BIN) 20 | 21 | %.o: %.c 22 | @echo GCC $< 23 | $(CC) -c $(CFLAGS) $< 24 | 25 | clean: 26 | @echo Cleaning... 27 | $(Q)rm -vf *.o *.so *~ 28 | 29 | install: all 30 | @echo Installing... 31 | install -m 644 $(SND_PCM_BIN) ${DESTDIR}/usr/lib/arm-linux-gnueabihf/alsa-lib/ 32 | 33 | uninstall: 34 | @echo Un-installing... 35 | rm -f ${DESTDIR}/usr/lib/arm-linux-gnueabihf/alsa-lib/$(SND_PCM_BIN) 36 | 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018] [generic-beat-detector] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /maker-templates/gbd-text-display.c: -------------------------------------------------------------------------------- 1 | /* file : gbd-text-display.c 2 | * desc : illustrates GBD Linux POSIX SHM IPC for beat counts 3 | * 4 | * build: gcc -Wall -O2 gbd-text-display.c -o gbd-text-display -lrt 5 | * 6 | * This program is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "gbd.h" 20 | 21 | static void *shm_init(const char *filename) 22 | { 23 | int fd, ret = -1; 24 | void *lmap = NULL; 25 | size_t shm_filesize; 26 | 27 | fd = shm_open(filename, O_RDWR | O_CREAT, (mode_t) 0666); 28 | if(fd < 0){ 29 | fprintf(stderr, "shm_open(3): %s\n", strerror(errno)); 30 | goto exit; 31 | } 32 | 33 | shm_filesize = sysconf(_SC_PAGE_SIZE); 34 | ret = ftruncate(fd, shm_filesize); 35 | if (ret < 0) { 36 | fprintf(stderr, "ftruncate(2): %sn", strerror(errno)); 37 | goto exit; 38 | } 39 | 40 | lmap = mmap(0, shm_filesize, PROT_READ | PROT_WRITE, 41 | MAP_SHARED, fd, 0); 42 | if(lmap == MAP_FAILED) { 43 | fprintf(stderr, "mmap(2): %s\n", strerror(errno)); 44 | goto exit; 45 | } 46 | 47 | return lmap; 48 | exit: 49 | close(fd); 50 | return NULL; 51 | } 52 | 53 | 54 | int main(void) 55 | { 56 | int *beat_cnt_map; 57 | beat_cnt_map = (int *)shm_init(GBD_BEAT_COUNT_FILE); 58 | if (!beat_cnt_map) { 59 | fprintf(stderr, "Could not open GBD IPC file!\n"); 60 | return -1; 61 | } 62 | 63 | for (;;) { 64 | 65 | static int bcnt, scnt, ccnt, __attribute__((unused)) blcnt; 66 | static int prevcnt[GBD_BEAT_COUNT_BUF_SIZE]; 67 | 68 | usleep(10000); /* 10ms or 100Hz */ 69 | 70 | if (beat_cnt_map[KICKDRUM] != prevcnt[KICKDRUM]) { 71 | prevcnt[KICKDRUM] = beat_cnt_map[KICKDRUM]; 72 | printf("BassBeat (%i)\n", bcnt++); 73 | } 74 | 75 | if (beat_cnt_map[SNARE] != prevcnt[SNARE]) { 76 | prevcnt[SNARE] = beat_cnt_map[SNARE]; 77 | printf("\tSnareHit (%i)\n", scnt++); 78 | } 79 | 80 | if (beat_cnt_map[CYMBALS] != prevcnt[CYMBALS]) { 81 | prevcnt[CYMBALS] = beat_cnt_map[CYMBALS]; 82 | printf("\t\tTweeters (%i)\n", ccnt++); 83 | } 84 | 85 | if (beat_cnt_map[BASSLINE] != prevcnt[BASSLINE]) { 86 | prevcnt[BASSLINE] = beat_cnt_map[BASSLINE]; 87 | // printf("\t\t\tBassline action %i\n", blcnt++); 88 | } 89 | } 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generic Beat Detector (GBD) 2 | 3 | The *Generic Beat Detector (GBD)* strives to deliver an industrial strength library and IoT framework for realtime beat 4 | detection (and soon other DSP analysis operations) for live music streams. 5 | 6 | ## Description 7 | 8 | GBD primarily consists of a library for music DSP analysis, and support plugins for 9 | audio signal routing between hosts. 10 |

11 | GBD IoT Framework 12 |

13 | 14 | This GBD release (v0.21) offers low-latency and realtime beat detection for 15 | live/playing music streams: 16 | 17 | * Bass beat (e.g. kick drum) detection (below 200Hz) 18 | * Detection of snare-like hits (between 3KHz and 10KHz) 19 | * Detection of cymbals-like sounds (above 15KHz) 20 | 21 | ## Targeted Users 22 | 23 | The GBD framework is primarily designed for the __IoT maker__ industry and community. The 24 | target is to supply __robust__, industrial-strength __realtime__ music 25 | beat detection (and soon other music DSP analysis) engines for makers 26 | working with __commodity hardware__ and developing setups or products similar to, 27 | for example, the Philips Hue Disco framework. 28 | 29 | ## Operation 30 | 31 | The Raspberry Pi computer is used as the platform of choice for the GBD core given the board's popularity with the IoT maker community. The *GBD core* (PCM routing server and DSP library) is GNU/Linux based. The GBD client and/or audio applications 32 | may execute on a remote host which need not be a GNU/Linux system. This is discussed at greater depth in [*GBD Architecture*](https://github.com/generic-beat-detector/GBD/wiki/The-GBD-IoT-Framework#gbd-architecture). 33 | 34 |

35 | Generalized GBD Arch

36 | 37 | With realtime beat detection, as an audio stream is routed through the GBD framework, the 38 | GBD music DSP analysis library delivers beat counts to Linux POSIX SHM. 39 | The IoT maker can then use this information to synchronize the control of light bulbs/leds to the 40 | music's beats. Check this OpenGL LED emulation on [*Youtube*](https://youtu.be/1wmrO51TZqA). This beat 41 | detection mode is referred to as *GBD Standard*. 42 | 43 | Bear in mind that while GBD's realtime beat detection is sturdy, it still requires some "compliance" on the part of the user 44 | if it is to function in the most generic manner possible, i.e. across the variety of music genres. For the best results, the quality of the music should be that of professional studio CD recordings: preferably WAV uncompressed PCM or, at least, an encoding by an industry-standard audio format converter. For example, pirated `.mp3` downloads from the Internet (i.e. poorly encoded or transcoded formats) or amateur recordings are likely to produce unsatisfactory results. These issues are discussed more fully 45 | [*here*](https://github.com/generic-beat-detector/GBD/wiki#features). 46 | 47 | ## Licenses 48 | 49 | ### GBD 50 | 51 | Released under the MIT license, GBD is partially open sourced. See [*GBD Releases and Directions*](https://github.com/generic-beat-detector/GBD/wiki/GBD-Releases-and-Directions). 52 | 53 | ### Attribution/Infrastructure 54 | 55 | The GBD DSP library employs [*KISS (Keep It Simple, Stupid) FFT*](https://github.com/mborgerding/kissfft) which, by default, is released under a revised BSD license. 56 | 57 | ## Documentation and Tutorials 58 | 59 | Check the [*GBD wiki*](https://github.com/generic-beat-detector/GBD/wiki) for resources on the GBD IoT framework, GBD HowTos, GBD maker guides, etc. 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /maker-templates/rpi_ws281x/README.md: -------------------------------------------------------------------------------- 1 | ## Background 2 | 3 | These template programs demonstrate use of GBD with Jeremy Garff's [*rpi_ws281x*](https://github.com/jgarff/rpi_ws281x) project. They build upon `../gbd-text-display.c` and assume local `gbdserver` shared memory IPC. If everything goes well, then: 4 | * `simple` will toggle 120 LEDs (of a WS2812B) between RGB colors upon `gbdserver` kickdrum beat detection 5 | * `segments` will mirror the [*GBD Youtube*](https://youtu.be/1wmrO51TZqA) OpenGL emulation -- but only for kickdrum beats and "bassline" sounds 6 | 7 | The main advantage of controlling an LED strip directly from the RPi running `gbdserver` -- as opposed to, say, over WiFi via ESP8266/ESP32 -- is that jitter is practically eliminated. 8 | 9 | ## Setup 10 | 11 | ### PWM/DMA 12 | 13 | By default, the template programs use PWM0 and DMA channel 10. According to [*RPi PWM Limitation*](https://github.com/jgarff/rpi_ws281x#pwm), use of [*rpi_ws281x*](https://github.com/jgarff/rpi_ws281x)'s PWM conflicts with the usage of the RPi's onboard audio. But this restriction doesn't really matter for GBD setups since RPi's onboard audio should not be used for GBD operation anyway. See [*GBD Setups for RPi*](https://github.com/generic-beat-detector/GBD/wiki/The-GBD-IoT-Framework#gbd-setups-for-the-raspberry-pi). 14 | 15 | ### LED Strip Hookup 16 | 17 | These template programs have been tested on a Raspberry Pi 3B with a WS2812B strip. The data pin for the WS2812B was connected to the default RPi GPIO18. 18 | 19 | ## Download and Build 20 | 21 | * Clone [*rpi_ws281x*](https://github.com/jgarff/rpi_ws281x) 22 | 23 | $ git clone https://github.com/jgarff/rpi_ws281x/ 24 | 25 | * Install Scons -- according to [*build instructions*](https://github.com/jgarff/rpi_ws281x#build), i.e: 26 | 27 | $ apt-get install scons 28 | 29 | * Copy `main.c` and `gbd.h` from the respective directory (i.e. `simple`, `segments`, etc) to `rpi_ws281x/` -- replacing the original `rpi_ws281x/main.c`. Then build: 30 | 31 | $ cd rpi_ws281x 32 | $ scons 33 | 34 | This will result in an executable file named `rpi_ws281x/test`. 35 | 36 | ## Runtime 37 | 38 | ### Execution 39 | 40 | Simply execute `rpi_ws281x/test` with: 41 | 42 | $ sudo ./test 43 | BassBeat (0) 44 | BassBeat (1) 45 | BassBeat (2) 46 | BassBeat (3) 47 | BassBeat (4) 48 | 49 | ... where the `BassBeat (N)`s will appear as `gbdserver` detects bass beats in the audio stream. Type `CTRL+C` to quit. 50 | 51 | You may also play around with arbitrary (but sensible) values of `-x|--width` and `-y|--height`, e.g.: 52 | 53 | $ sudo ./test -x 15 -y 8 54 | $ sudo ./test --width 20 --height 6 55 | 56 | For the `segments` demo, the value of `--height` implies the number of segments (or "light units") while `--width` translates to the number of LEDs per segment. For `simple`, the product of `-x` and `-y` simply becomes the number of LEDs on the strip that will get lit up. 57 | 58 | * __IMPORTANT NOTE:__ 59 | 60 | Since the `test` program has to be executed with `sudo` priviledges, it is important to note that if `/dev/shm/gbd` does not exist, then `test` will create it with `root:root` ownership. This will require `gbdserver` to either: 61 | * be executed with `sudo`, or 62 | * to have already created `/dev/shm/gbd` 63 | 64 | ... since `gbdserver` creates the SHM file with default ownership e.g. `pi:pi` 65 | 66 | In short, if `gbdserver` reports 67 | 68 | init:1213:: Permission denied 69 | 70 | or if the GBD ALSA PCM plugin complains with: 71 | 72 | gbdclient.c:gbdclient_transfer:165:: WARNING!! GBD PCM cmd write failed! 73 | 74 | then it is likely that `gbdserver` does not have sufficient priviledges to write to `/dev/shm/gbd` 75 | 76 | ### System Metrics 77 | 78 | CPU and memory usage can be inspected via: 79 | 80 | pi@raspberrypi:~ $ PIDLIST=$(for i in `pidof gbdserver` ; do echo -n "$i, "; done ; echo `pidof test`) 81 | pi@raspberrypi:~ $ top -d .4 -p $PIDLIST 82 | 83 | On RPi 3B, CPU usage by `test` avaraged at around 6% while the signal analysis by `gbdserver` consumes about 5%. 84 | 85 | -------------------------------------------------------------------------------- /maker-templates/gbd-gl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * prog : gbd-gl.c 3 | * desc : demo opengl (glut) program for the gbd framework 4 | * 5 | * NOTE: This is borrowed code. Sub-optimal for RPi. Use 6 | * OpenGLES instead. 7 | * 8 | * Compile with: 9 | * 10 | * "gcc -Wall -O2 gbd-gl.c -o gbd-gl -lglut -lGLU -lrt -lm" 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "gbd.h" 28 | static int *beat_cnt_map; 29 | 30 | #define BARWIDTH 30 31 | #define BARSPACING 7 32 | #define X_BAROFFSET (BARSPACING + BARWIDTH) 33 | #define Y_BAROFFSET 50 34 | 35 | #define WINWIDTH BARSPACING+100+100+100+100 36 | #define WINHEIGHT 150 37 | static int win_x = WINWIDTH; 38 | static int win_y = WINHEIGHT; 39 | static int win_id; 40 | 41 | #define BOX_PER_FBAND 6 42 | static float g_rgb[][3] = { 43 | {1.0f, 0.01f, 0.01f}, 44 | {0.01f, 0.01f, 1.0f}, 45 | {0.01f, 1.0f, 0.01f}, 46 | {0.0f, 0.0f, 0.0f} 47 | }; 48 | 49 | #define LFE KICKDRUM 50 | #define MID SNARE 51 | #define TWT CYMBALS 52 | static void render_pattern(void); 53 | static void render_cymbals(void); 54 | static void render_snare(void); 55 | 56 | static int prevcnt[GBD_BEAT_COUNT_BUF_SIZE]; 57 | static int step_cnt = 1, prev_step_cnt = 0; 58 | static int c_step_cnt = 1, prev_c_step_cnt = 0; 59 | 60 | #define W_INTVL 20 61 | static void draw_bands(void) 62 | { 63 | static int i; 64 | static float r = 1.0f, g, b; 65 | static float w_val = 1.0f; 66 | #define C_CNT 12 67 | #define W_CNT 12 68 | static int w_intvl = W_INTVL, snare_hit_on; 69 | 70 | if (!snare_hit_on) { 71 | if (beat_cnt_map[MID] != prevcnt[MID]) { 72 | prevcnt[MID] = beat_cnt_map[MID]; 73 | snare_hit_on = 1; 74 | w_val = 1.0f; 75 | } 76 | } 77 | 78 | if (snare_hit_on) { 79 | glColor3f(w_val, w_val, w_val); 80 | w_val -= 1.0f/W_INTVL; 81 | if (w_intvl-- <= 0) { 82 | w_intvl = W_INTVL; 83 | snare_hit_on = 0; 84 | } 85 | render_snare(); 86 | } 87 | 88 | if (beat_cnt_map[BASSLINE] != prevcnt[BASSLINE]) { 89 | prevcnt[BASSLINE] = beat_cnt_map[BASSLINE]; 90 | 91 | if (g_rgb[i][0] == 0.0f) i = 0; 92 | 93 | r = g_rgb[i][0]; g = g_rgb[i][1]; b = g_rgb[i][2]; 94 | i++; 95 | } 96 | 97 | glColor3f(r, g, b); 98 | if (beat_cnt_map[LFE] != prevcnt[LFE]) { 99 | prevcnt[LFE] = beat_cnt_map[LFE]; 100 | step_cnt++; 101 | } 102 | render_pattern(); 103 | 104 | if (beat_cnt_map[TWT] != prevcnt[TWT]) { 105 | prevcnt[TWT] = beat_cnt_map[TWT]; 106 | c_step_cnt++; 107 | } 108 | 109 | render_cymbals(); 110 | return; 111 | } 112 | 113 | #define SNARE_LED_HGHT 20 114 | #define CYMBALS_LED_HGHT 4 115 | #define CYMBALS_LED_WDTH 200 116 | #define BASE_LED_HGHT 100 117 | #define BASE_LED_WDTH 100 118 | static void __attribute__((unused)) render_snare(void) 119 | { 120 | 121 | #define Y_SBASE BARSPACING+CYMBALS_LED_HGHT+BARSPACING+BASE_LED_HGHT+BARSPACING 122 | glRectf(BARSPACING, Y_SBASE, 200, Y_SBASE+20); 123 | glRectf(BARSPACING + 200, Y_SBASE, 400, Y_SBASE+20); 124 | return; 125 | } 126 | 127 | static void __attribute__((unused)) render_cymbals(void) 128 | { 129 | int xidx = 0; 130 | #define CPATTERN_WDTH 9 131 | #define CPATTERN_LEN 17 132 | float cpattern[CPATTERN_WDTH][CPATTERN_LEN] = { 133 | {1.0f, .9f, .8f, .7f, .6f, .5f, .4f, .3f, .3f, .4f, .5f, .6f, .7f, .8f, .9f, 1.0f}, 134 | {0.0f, .9f, .8f, .7f, .6f, .5f, .4f, .3f, .3f, .4f, .5f, .6f, .7f, .8f, .9f, 0.0f}, 135 | {0.0f, .0f, .8f, .7f, .6f, .5f, .4f, .3f, .3f, .4f, .5f, .6f, .7f, .8f, .0f, 0.0f}, 136 | {0.0f, .0f, .0f, .7f, .6f, .5f, .4f, .3f, .3f, .4f, .5f, .6f, .7f, .0f, .0f, 0.0f}, 137 | {0.0f, .0f, .0f, .0f, .6f, .5f, .4f, .3f, .3f, .4f, .5f, .6f, .0f, .0f, .0f, 0.0f}, 138 | {0.0f, .0f, .0f, .0f, .0f, .5f, .4f, .3f, .3f, .4f, .5f, .0f, .0f, .0f, .0f, 0.0f}, 139 | {0.0f, .0f, .0f, .0f, .0f, .0f, .4f, .3f, .3f, .4f, .0f, .0f, .0f, .0f, .0f, 0.0f}, 140 | {0.0f, .0f, .0f, .0f, .0f, .0f, .0f, .3f, .3f, .0f, .0f, .0f, .0f, .0f, .0f, 0.0f}, 141 | {0.0f, .0f, .0f, .0f, .0f, .0f, .0f, .0f, .0f, .0f, .0f, .0f, .0f, .0f, .0f, 0.0f}, 142 | }; 143 | static float *pattern; 144 | static int i = 0; 145 | 146 | static int once = 0; 147 | 148 | if (!once) { 149 | pattern = cpattern[0]; 150 | once = 1; 151 | } 152 | 153 | if (c_step_cnt != prev_c_step_cnt) { /* beat detected? */ 154 | pattern = cpattern[0]; 155 | i = 1; 156 | } 157 | 158 | #define Y_CBASE BARSPACING 159 | for (xidx = 0; xidx < CPATTERN_LEN; xidx++) { 160 | if (pattern[xidx] != 0.0f) { 161 | glColor3f(pattern[xidx], pattern[xidx], pattern[xidx]); 162 | glRectf(xidx * 25 + BARSPACING, Y_CBASE, xidx * 25+25, Y_CBASE+4); 163 | } 164 | } 165 | 166 | if (i < 9) 167 | pattern = cpattern[i++]; 168 | 169 | prev_c_step_cnt = c_step_cnt; 170 | 171 | return; 172 | } 173 | static void __attribute__((unused)) render_pattern(void) 174 | { 175 | int xidx = 0; 176 | static int pattern = 0; 177 | 178 | if (step_cnt != prev_step_cnt) { /* beat detected? */ 179 | pattern++; 180 | } 181 | /* select display pattern */ 182 | pattern %= 2; 183 | 184 | #define Y_BBASE BARSPACING+CYMBALS_LED_HGHT+BARSPACING 185 | /* render display pattern */ 186 | if (pattern == 0) { 187 | xidx = 0; 188 | glRectf((xidx*(100))+BARSPACING, Y_BBASE, 189 | (xidx*(100))+ (100), Y_BBASE+100); 190 | xidx = 3; 191 | glRectf((xidx*(100))+BARSPACING, Y_BBASE, 192 | (xidx*(100))+ (100), Y_BBASE + 100); 193 | } 194 | if (pattern == 1) { 195 | xidx = 1; 196 | glRectf((xidx*(100))+BARSPACING, Y_BBASE, 197 | (xidx*(100))+ (100), Y_BBASE + 100); 198 | xidx = 2; 199 | glRectf((xidx*(100))+BARSPACING, Y_BBASE, 200 | (xidx*(100))+ (100), Y_BBASE + 100); 201 | } 202 | 203 | prev_step_cnt = step_cnt; 204 | } 205 | 206 | static void pre_display ( void ) 207 | { 208 | glViewport ( 0, 0, win_x, win_y ); 209 | glMatrixMode ( GL_PROJECTION ); 210 | glLoadIdentity (); 211 | gluOrtho2D ( 0.0, win_x, 0.0, win_y ); 212 | glClearColor ( 0.0f, 0.0f, 0.0f, 1.0f ); 213 | glClear(GL_COLOR_BUFFER_BIT); 214 | } 215 | 216 | static void post_display ( void ) 217 | { 218 | glutSwapBuffers (); 219 | } 220 | 221 | static void display_func ( void ) 222 | { 223 | pre_display (); 224 | draw_bands(); 225 | post_display (); 226 | } 227 | 228 | static void idle_func ( void ) 229 | { 230 | glutSetWindow ( win_id ); 231 | glutPostRedisplay (); 232 | } 233 | 234 | static void key_func ( unsigned char key, int x, int y ) 235 | { 236 | switch ( key ) 237 | { 238 | case 27 : /* ESC */ 239 | case 'q': 240 | case 'Q': 241 | exit (EXIT_SUCCESS); 242 | break; 243 | } 244 | } 245 | 246 | static void reshape_func ( int width, int height ) 247 | { 248 | glutSetWindow ( win_id ); 249 | glutReshapeWindow ( width, height ); 250 | 251 | win_x = width; 252 | win_y = height; 253 | } 254 | 255 | static void *shm_init(const char *filename) 256 | { 257 | int fd, ret = -1; 258 | void *lmap = NULL; 259 | size_t shm_filesize; 260 | 261 | fd = shm_open(filename, O_RDWR | O_CREAT, (mode_t) 0666); 262 | if(fd < 0){ 263 | fprintf(stderr, "shm_open(3): %s\n", strerror(errno)); 264 | goto exit; 265 | } 266 | 267 | shm_filesize = sysconf(_SC_PAGE_SIZE); 268 | ret = ftruncate(fd, shm_filesize); 269 | if (ret < 0) { 270 | fprintf(stderr, "ftruncate(2): %sn", strerror(errno)); 271 | abort(); 272 | } 273 | 274 | lmap = mmap(0, shm_filesize, PROT_READ | PROT_WRITE, 275 | MAP_SHARED, fd, 0); 276 | if(lmap == MAP_FAILED) { 277 | fprintf(stderr, "mmap(2): %s\n", strerror(errno)); 278 | goto exit; 279 | } 280 | 281 | return lmap; 282 | exit: 283 | close(fd); 284 | return NULL; 285 | } 286 | 287 | static void open_glut_window ( void ) 288 | { 289 | glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); 290 | glutInitWindowPosition ( 0, 0 ); 291 | glutInitWindowSize ( win_x, win_y ); 292 | win_id = glutCreateWindow ( "GBD OpenGL Demo" ); 293 | glClearColor ( 0.0f, 0.0f, 0.0f, 1.0f ); 294 | glClear ( GL_COLOR_BUFFER_BIT ); 295 | glutSwapBuffers (); 296 | glClear ( GL_COLOR_BUFFER_BIT ); 297 | glutSwapBuffers (); 298 | pre_display (); 299 | glutKeyboardFunc ( key_func ); 300 | glutReshapeFunc ( reshape_func ); 301 | glutIdleFunc ( idle_func ); 302 | glutDisplayFunc ( display_func ); 303 | } 304 | 305 | int main(int argc, char **argv) 306 | { 307 | void *lmap; 308 | glutInit(&argc, argv); 309 | const char *filename = "gbd"; 310 | 311 | if (argv[1]) 312 | filename = argv[1]; 313 | 314 | if(!(lmap = shm_init(filename))) 315 | exit(EXIT_FAILURE); 316 | beat_cnt_map = (int *)lmap; 317 | 318 | open_glut_window(); 319 | glutMainLoop(); 320 | exit(EXIT_SUCCESS); 321 | } 322 | 323 | -------------------------------------------------------------------------------- /gbdclient/gbdclient.c: -------------------------------------------------------------------------------- 1 | /* 2 | * file : gbdclient.c 3 | * desc : ALSA external PCM filter plugin for the gbd (Generic Beat Detector) 4 | * framework 5 | * 6 | * This file is a part of the GBD framework, 7 | * https://github.com/generic-beat-detector/GBD 8 | * 9 | * Copyright (c) GBD, 10 | * generic.beat.detector@gmail.com 11 | * 12 | * The program in this file is licensed under the terms of the MIT license. 13 | */ 14 | 15 | /* alsa */ 16 | #include 17 | #include 18 | #include 19 | 20 | /* external pcm filter plugin object */ 21 | typedef struct snd_pcm_gbdclient { 22 | snd_pcm_extplug_t ext; 23 | int fd; 24 | int channels; 25 | } snd_pcm_gbdclient_t; 26 | 27 | /* internet sockets */ 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | /* GBD command passing */ 34 | #define GBD_ERROR -1 35 | #define GBD_SUCCESS 0 36 | #define GBD_LADSPA_LIB_INIT 1 37 | #define GBD_CLIENT_CHANNELS 2 38 | #define GBD_AUDIO_SAMPLE_RATE 3 39 | #define GBD_PCM_PLUGIN_INIT 4 40 | #define GBD_BEAT_DETECTION_FUNC 5 41 | #define GBD_PCM_PLUGIN_CLOSE 6 42 | typedef struct __gbd_msg { 43 | /* note: declare strict types to avoid 44 | * problems when executing on 45 | * a 64-bit gbdclient host OS ... 46 | * recall that the gbdserver on 47 | * the raspberry pi is 32-bit */ 48 | int32_t cmd; 49 | int32_t data; 50 | } gbd_msg_t; 51 | 52 | static ssize_t gbd_read(int fd, void *buf, size_t n) 53 | { 54 | ssize_t ret; 55 | size_t count; 56 | char *pbuf; 57 | 58 | pbuf = buf; 59 | for (count = 0; count < n; ) { 60 | ret = read(fd, pbuf, n - count); 61 | if (ret == 0) /* eof */ 62 | return count; 63 | if (ret == -1) { 64 | if (errno == EINTR) 65 | continue; 66 | else 67 | return -1; 68 | } 69 | count += ret; 70 | pbuf += ret; 71 | } 72 | return count; 73 | } 74 | 75 | static ssize_t gbd_write(int fd, const void *buf, size_t n) 76 | { 77 | ssize_t ret; 78 | size_t count; 79 | const char *pbuf; 80 | 81 | pbuf = buf; 82 | for (count = 0; count < n; ) { 83 | ret = write(fd, pbuf, n - count); 84 | if (ret <= 0) { 85 | if (ret == -1 && errno == EINTR) 86 | continue; 87 | else 88 | return -1; 89 | } 90 | count += ret; 91 | pbuf += ret; 92 | } 93 | return count; 94 | } 95 | 96 | static int gbd_connect(const char *ipaddr, 97 | const char *port, int type) 98 | { 99 | struct addrinfo hints; 100 | struct addrinfo *result, *rp; 101 | int sfd, s; 102 | 103 | memset(&hints, 0, sizeof(struct addrinfo)); 104 | hints.ai_canonname = NULL; 105 | hints.ai_addr = NULL; 106 | hints.ai_next = NULL; 107 | hints.ai_family = AF_UNSPEC; /* v4/v6 */ 108 | hints.ai_socktype = type; 109 | 110 | s = getaddrinfo(ipaddr, port, &hints, &result); 111 | if (s != 0) { 112 | errno = ENOSYS; 113 | return -1; 114 | } 115 | 116 | for (rp = result; rp != NULL; rp = rp->ai_next) { 117 | sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 118 | if (sfd == -1) 119 | continue; 120 | if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) 121 | break; 122 | close(sfd); 123 | } 124 | freeaddrinfo(result); 125 | return (rp == NULL) ? -1 : sfd; 126 | } 127 | 128 | /* 129 | * func: gbdclient_transfer 130 | * desc: this function is invoked by the alsa-lib runtime and 131 | * receives the alsa period pcm signal in alsa float format; 132 | * it is responsible for sending the signal to the gbdserver 133 | * for analysis as well as for passing the signal to the 134 | * configured alsa pcm slave. 135 | * 136 | * plugin writers wishing to port this pcm plugin to a standalone 137 | * audio application platform or some other operating system's 138 | * sound framework should consult the documentation on the 139 | * GBD github wiki for more details. 140 | */ 141 | static snd_pcm_sframes_t gbdclient_transfer(snd_pcm_extplug_t * ext, 142 | const snd_pcm_channel_area_t * dst_areas, 143 | snd_pcm_uframes_t dst_offset, 144 | const snd_pcm_channel_area_t * src_areas, 145 | snd_pcm_uframes_t src_offset, 146 | snd_pcm_uframes_t size) 147 | { 148 | snd_pcm_gbdclient_t *gbd = (snd_pcm_gbdclient_t *) ext; 149 | float *src, *dst; 150 | gbd_msg_t msg; 151 | size_t nbytes = size * gbd->channels * sizeof(float); 152 | 153 | src = (float *)(src_areas->addr + 154 | (src_areas->first + src_areas->step * src_offset) / 8); 155 | 156 | dst = (float *)(dst_areas->addr + 157 | (dst_areas->first + dst_areas->step * dst_offset) / 8); 158 | 159 | /* notify gbdserver of incoming audio signal for analysis */ 160 | msg.cmd = GBD_BEAT_DETECTION_FUNC; 161 | msg.data = (int32_t)size; 162 | if (gbd_write(gbd->fd, &msg, sizeof(msg)) < 0) { 163 | fprintf(stderr, 164 | "%s:%s:%d:: WARNING!! GBD PCM cmd write failed!\n", 165 | __FILE__, __func__, __LINE__); 166 | } 167 | 168 | /* send audio signal to gbdserver for analysis */ 169 | if (gbd_write(gbd->fd, src, nbytes) < 0) { 170 | fprintf(stderr, 171 | "%s:%s:%d:: WARNING!! GBD PCM data write failed!\n", 172 | __FILE__, __func__, __LINE__); 173 | }; 174 | 175 | /* pass audio signal to alsa pcm slave */ 176 | memcpy(dst, src, nbytes); 177 | return size; 178 | } 179 | 180 | static int gbdclient_close(snd_pcm_extplug_t * ext) 181 | { 182 | snd_pcm_gbdclient_t *gbd = ext->private_data; 183 | gbd_msg_t msg; 184 | msg.cmd = GBD_PCM_PLUGIN_CLOSE; 185 | if (gbd_write(gbd->fd, &msg, sizeof(msg)) < 0) { 186 | SNDERR("WARNING: gbd server-side PCM plugin release failed!"); 187 | } 188 | close(gbd->fd); 189 | free(gbd); 190 | return 0; 191 | } 192 | 193 | static int gbdclient_init(snd_pcm_extplug_t * ext) 194 | { 195 | snd_pcm_gbdclient_t *gbd = (snd_pcm_gbdclient_t *) ext; 196 | int srate = ext->rate; 197 | int err; 198 | gbd_msg_t msg; 199 | 200 | /* prepare to initialize gbdserver-side pcm plugin */ 201 | msg.cmd = GBD_CLIENT_CHANNELS; 202 | msg.data = gbd->channels; 203 | err = gbd_write(gbd->fd, &msg, sizeof(msg)); 204 | if (err < 0) { 205 | SNDERR("gbd_write failed! (channels)"); 206 | return -EINVAL; 207 | } 208 | 209 | msg.cmd = GBD_AUDIO_SAMPLE_RATE; 210 | msg.data = srate; 211 | err = gbd_write(gbd->fd, &msg, sizeof(msg)); 212 | if (err < 0) { 213 | SNDERR("gbd_write failed! (sampel rate)"); 214 | return -EINVAL; 215 | } 216 | 217 | /* initialize gbd server-side pcm plugin */ 218 | msg.cmd = GBD_PCM_PLUGIN_INIT; 219 | err = gbd_write(gbd->fd, &msg, sizeof(msg)); 220 | if (err < 0) { 221 | SNDERR("gbd_write failed! (plugin init)"); 222 | return -EINVAL; 223 | } 224 | 225 | err = gbd_read(gbd->fd, &msg, sizeof(msg)); 226 | if (err < 0 || msg.cmd == GBD_ERROR) { 227 | SNDERR("Failed to initialize gbdserver-side PCM plugin module"); 228 | return -EINVAL; 229 | } 230 | 231 | return 0; 232 | } 233 | 234 | static snd_pcm_extplug_callback_t pcm_gbdclient_callback = { 235 | .transfer = gbdclient_transfer, 236 | .init = gbdclient_init, 237 | .close = gbdclient_close, 238 | }; 239 | 240 | SND_PCM_PLUGIN_DEFINE_FUNC(gbdclient) 241 | { 242 | snd_config_iterator_t i, next; 243 | snd_pcm_gbdclient_t *gbd; 244 | snd_config_t *sconf = NULL; 245 | const char *ipaddr; 246 | const char *port; 247 | long channels = 2; 248 | int err; 249 | gbd_msg_t msg; 250 | 251 | /* Parse config file (e.g. .asoundrc) options */ 252 | snd_config_for_each(i, next, conf) { 253 | snd_config_t *n = snd_config_iterator_entry(i); 254 | const char *id; 255 | if (snd_config_get_id(n, &id) < 0) 256 | continue; 257 | 258 | if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 259 | || strcmp(id, "hint") == 0) 260 | continue; 261 | 262 | if (strcmp(id, "slave") == 0) { 263 | sconf = n; 264 | continue; 265 | } 266 | 267 | if (strcmp(id, "ipaddr") == 0) { 268 | snd_config_get_string(n, &ipaddr); 269 | continue; 270 | } 271 | 272 | if (strcmp(id, "port") == 0) { 273 | snd_config_get_string(n, &port); 274 | continue; 275 | } 276 | 277 | if (strcmp(id, "channels") == 0) { 278 | snd_config_get_integer(n, &channels); 279 | if (channels != 2) { 280 | SNDERR("Only stereo streams supported"); 281 | return -EINVAL; 282 | } 283 | continue; 284 | } 285 | SNDERR("Unknown field %s", id); 286 | return -EINVAL; 287 | } 288 | 289 | /* Make sure an ip address was specified */ 290 | if (!ipaddr || !port) { 291 | SNDERR("Missing \"ipaddr\" or \"port\"\n"); 292 | return -EINVAL; 293 | } 294 | 295 | /* Make sure an ALSA slave PCM device was specified */ 296 | if (!sconf) { 297 | SNDERR("Must include slave configuration with gbdclient plugin"); 298 | return -EINVAL; 299 | } 300 | 301 | /* Alloc local GBD object */ 302 | gbd = calloc(1, sizeof(*gbd)); 303 | if (gbd == NULL) 304 | return -ENOMEM; 305 | 306 | /* Initialize local GDB object members */ 307 | gbd->ext.version = SND_PCM_EXTPLUG_VERSION; 308 | gbd->ext.name = "gbdclient"; 309 | gbd->ext.callback = &pcm_gbdclient_callback; 310 | gbd->ext.private_data = gbd; 311 | gbd->channels = channels; 312 | gbd->fd = gbd_connect(ipaddr, port, SOCK_STREAM); 313 | if (gbd->fd < 0) { 314 | SNDERR("Failed to connect with gbdserver"); 315 | return -EINVAL; 316 | } 317 | 318 | /* Load gbd server-side DSP LADSPA library module */ 319 | msg.cmd = GBD_LADSPA_LIB_INIT; 320 | err = gbd_write(gbd->fd, &msg, sizeof(msg)); 321 | if (err < 0) { 322 | SNDERR("Failed to initialize gbd LADSPA module"); 323 | return -EINVAL; 324 | } 325 | 326 | err = gbd_read(gbd->fd, &msg, sizeof(msg)); 327 | if (err < 0 || msg.cmd == GBD_ERROR) { 328 | SNDERR("Failed to initialize gbd LADSPA module"); 329 | return -EINVAL; 330 | } 331 | 332 | /* Create gbdclient external PCM filter plugin */ 333 | err = snd_pcm_extplug_create(&gbd->ext, name, root, sconf, stream, mode); 334 | if (err < 0) { 335 | close(gbd->fd); 336 | free(gbd); 337 | return err; 338 | } 339 | 340 | /* Set external PCM filter plugin constraints */ 341 | snd_pcm_extplug_set_param_minmax(&gbd->ext, 342 | SND_PCM_EXTPLUG_HW_CHANNELS, 343 | gbd->channels, 344 | gbd->channels); 345 | snd_pcm_extplug_set_slave_param(&gbd->ext, 346 | SND_PCM_EXTPLUG_HW_CHANNELS, 347 | gbd->channels); 348 | snd_pcm_extplug_set_param(&gbd->ext, 349 | SND_PCM_EXTPLUG_HW_FORMAT, 350 | SND_PCM_FORMAT_FLOAT); 351 | snd_pcm_extplug_set_slave_param(&gbd->ext, SND_PCM_EXTPLUG_HW_FORMAT, 352 | SND_PCM_FORMAT_FLOAT); 353 | 354 | *pcmp = gbd->ext.pcm; 355 | return 0; 356 | } 357 | SND_PCM_PLUGIN_SYMBOL(gbdclient); 358 | -------------------------------------------------------------------------------- /maker-templates/rpi_ws281x/simple/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * newtest.c 3 | * 4 | * Copyright (c) 2014 Jeremy Garff 5 | * 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without modification, are permitted 9 | * provided that the following conditions are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright notice, this list of 12 | * conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list 14 | * of conditions and the following disclaimer in the documentation and/or other materials 15 | * provided with the distribution. 16 | * 3. Neither the name of the owner nor the names of its contributors may be used to endorse 17 | * or promote products derived from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 | * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 24 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | */ 29 | 30 | static char VERSION[] = "XX.YY.ZZ"; 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "clk.h" 46 | #include "gpio.h" 47 | #include "dma.h" 48 | #include "pwm.h" 49 | #include "version.h" 50 | 51 | #include "ws2811.h" 52 | 53 | #define ARRAY_SIZE(stuff) (sizeof(stuff) / sizeof(stuff[0])) 54 | 55 | // defaults for cmdline options 56 | #define TARGET_FREQ WS2811_TARGET_FREQ 57 | #define GPIO_PIN 18 58 | #define DMA 10 59 | //#define STRIP_TYPE WS2811_STRIP_RGB // WS2812/SK6812RGB integrated chip+leds 60 | #define STRIP_TYPE WS2811_STRIP_GBR // WS2812/SK6812RGB integrated chip+leds 61 | //#define STRIP_TYPE SK6812_STRIP_RGBW // SK6812RGBW (NOT SK6812RGB) 62 | 63 | #define WIDTH 120 64 | #define HEIGHT 1 65 | #define LED_COUNT (WIDTH * HEIGHT) 66 | 67 | int width = WIDTH; 68 | int height = HEIGHT; 69 | int led_count = LED_COUNT; 70 | 71 | int clear_on_exit = 1; 72 | 73 | ws2811_t ledstring = { 74 | .freq = TARGET_FREQ, 75 | .dmanum = DMA, 76 | .channel = { 77 | [0] = { 78 | .gpionum = GPIO_PIN, 79 | .count = LED_COUNT, 80 | .invert = 0, 81 | .brightness = 255, 82 | .strip_type = STRIP_TYPE, 83 | }, 84 | [1] = { 85 | .gpionum = 0, 86 | .count = 0, 87 | .invert = 0, 88 | .brightness = 0, 89 | }, 90 | }, 91 | }; 92 | 93 | static uint8_t running = 1; 94 | 95 | void matrix_render(int color) 96 | { 97 | int x, y; 98 | 99 | for (x = 0; x < width; x++) { 100 | for (y = 0; y < height; y++) { 101 | ledstring.channel[0].leds[(y * width) + x] = color; 102 | } 103 | } 104 | } 105 | 106 | static void ctrl_c_handler(int signum) 107 | { 108 | (void)(signum); 109 | running = 0; 110 | } 111 | 112 | static void setup_handlers(void) 113 | { 114 | struct sigaction sa = { 115 | .sa_handler = ctrl_c_handler, 116 | }; 117 | 118 | sigaction(SIGINT, &sa, NULL); 119 | sigaction(SIGTERM, &sa, NULL); 120 | } 121 | 122 | void parseargs(int argc, char **argv, ws2811_t * ws2811) 123 | { 124 | int index; 125 | int c; 126 | 127 | static struct option longopts[] = { 128 | {"help", no_argument, 0, 'h'}, 129 | {"dma", required_argument, 0, 'd'}, 130 | {"gpio", required_argument, 0, 'g'}, 131 | {"invert", no_argument, 0, 'i'}, 132 | {"clear", no_argument, 0, 'c'}, 133 | {"strip", required_argument, 0, 's'}, 134 | {"height", required_argument, 0, 'y'}, 135 | {"width", required_argument, 0, 'x'}, 136 | {"version", no_argument, 0, 'v'}, 137 | {0, 0, 0, 0} 138 | }; 139 | 140 | while (1) { 141 | 142 | index = 0; 143 | c = getopt_long(argc, argv, "cd:g:his:vx:y:", longopts, &index); 144 | 145 | if (c == -1) 146 | break; 147 | 148 | switch (c) { 149 | case 0: 150 | /* handle flag options (array's 3rd field non-0) */ 151 | break; 152 | 153 | case 'h': 154 | fprintf(stderr, "%s version %s\n", argv[0], VERSION); 155 | fprintf(stderr, "Usage: %s \n" 156 | "-h (--help) - this information\n" 157 | "-s (--strip) - strip type - rgb, grb, gbr, rgbw\n" 158 | "-x (--width) - matrix width (default 8)\n" 159 | "-y (--height) - matrix height (default 8)\n" 160 | "-d (--dma) - dma channel to use (default 10)\n" 161 | "-g (--gpio) - GPIO to use\n" 162 | " If omitted, default is 18 (PWM0)\n" 163 | "-i (--invert) - invert pin output (pulse LOW)\n" 164 | "-c (--clear) - clear matrix on exit.\n" 165 | "-v (--version) - version information\n", 166 | argv[0]); 167 | exit(-1); 168 | 169 | case 'D': 170 | break; 171 | 172 | case 'g': 173 | if (optarg) { 174 | int gpio = atoi(optarg); 175 | /* 176 | PWM0, which can be set to use GPIOs 12, 18, 40, and 52. 177 | Only 12 (pin 32) and 18 (pin 12) are available on the B+/2B/3B 178 | PWM1 which can be set to use GPIOs 13, 19, 41, 45 and 53. 179 | Only 13 is available on the B+/2B/PiZero/3B, on pin 33 180 | PCM_DOUT, which can be set to use GPIOs 21 and 31. 181 | Only 21 is available on the B+/2B/PiZero/3B, on pin 40. 182 | SPI0-MOSI is available on GPIOs 10 and 38. 183 | Only GPIO 10 is available on all models. 184 | 185 | The library checks if the specified gpio is available 186 | on the specific model (from model B rev 1 till 3B) 187 | 188 | */ 189 | ws2811->channel[0].gpionum = gpio; 190 | } 191 | break; 192 | 193 | case 'i': 194 | ws2811->channel[0].invert = 1; 195 | break; 196 | 197 | case 'c': 198 | clear_on_exit = 1; 199 | break; 200 | 201 | case 'd': 202 | if (optarg) { 203 | int dma = atoi(optarg); 204 | if (dma < 14) { 205 | ws2811->dmanum = dma; 206 | } else { 207 | printf("invalid dma %d\n", dma); 208 | exit(-1); 209 | } 210 | } 211 | break; 212 | 213 | case 'y': 214 | if (optarg) { 215 | height = atoi(optarg); 216 | if (height > 0) { 217 | ws2811->channel[0].count = 218 | height * width; 219 | } else { 220 | printf("invalid height %d\n", height); 221 | exit(-1); 222 | } 223 | } 224 | break; 225 | 226 | case 'x': 227 | if (optarg) { 228 | width = atoi(optarg); 229 | if (width > 0) { 230 | ws2811->channel[0].count = 231 | height * width; 232 | } else { 233 | printf("invalid width %d\n", width); 234 | exit(-1); 235 | } 236 | } 237 | break; 238 | 239 | case 's': 240 | if (optarg) { 241 | if (!strncasecmp("rgb", optarg, 4)) { 242 | ws2811->channel[0].strip_type = 243 | WS2811_STRIP_RGB; 244 | } else if (!strncasecmp("rbg", optarg, 4)) { 245 | ws2811->channel[0].strip_type = 246 | WS2811_STRIP_RBG; 247 | } else if (!strncasecmp("grb", optarg, 4)) { 248 | ws2811->channel[0].strip_type = 249 | WS2811_STRIP_GRB; 250 | } else if (!strncasecmp("gbr", optarg, 4)) { 251 | ws2811->channel[0].strip_type = 252 | WS2811_STRIP_GBR; 253 | } else if (!strncasecmp("brg", optarg, 4)) { 254 | ws2811->channel[0].strip_type = 255 | WS2811_STRIP_BRG; 256 | } else if (!strncasecmp("bgr", optarg, 4)) { 257 | ws2811->channel[0].strip_type = 258 | WS2811_STRIP_BGR; 259 | } else if (!strncasecmp("rgbw", optarg, 4)) { 260 | ws2811->channel[0].strip_type = 261 | SK6812_STRIP_RGBW; 262 | } else if (!strncasecmp("grbw", optarg, 4)) { 263 | ws2811->channel[0].strip_type = 264 | SK6812_STRIP_GRBW; 265 | } else { 266 | printf("invalid strip %s\n", optarg); 267 | exit(-1); 268 | } 269 | } 270 | break; 271 | 272 | case 'v': 273 | fprintf(stderr, "%s version %s\n", argv[0], VERSION); 274 | exit(-1); 275 | 276 | case '?': 277 | /* getopt_long already reported error? */ 278 | exit(-1); 279 | 280 | default: 281 | exit(-1); 282 | } 283 | } 284 | } 285 | 286 | #include 287 | #include "gbd.h" 288 | 289 | static void *shm_init(const char *filename) 290 | { 291 | int fd, ret = -1; 292 | void *lmap = NULL; 293 | size_t shm_filesize; 294 | 295 | fd = shm_open(filename, O_RDWR | O_CREAT, (mode_t) 0666); 296 | if (fd < 0) { 297 | fprintf(stderr, "shm_open(3): %s\n", strerror(errno)); 298 | goto exit; 299 | } 300 | 301 | shm_filesize = sysconf(_SC_PAGE_SIZE); 302 | ret = ftruncate(fd, shm_filesize); 303 | if (ret < 0) { 304 | fprintf(stderr, "ftruncate(2): %sn", strerror(errno)); 305 | goto exit; 306 | } 307 | 308 | lmap = mmap(0, shm_filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 309 | if (lmap == MAP_FAILED) { 310 | fprintf(stderr, "mmap(2): %s\n", strerror(errno)); 311 | goto exit; 312 | } 313 | 314 | return lmap; 315 | exit: 316 | close(fd); 317 | return NULL; 318 | } 319 | 320 | int main(int argc, char *argv[]) 321 | { 322 | ws2811_return_t ret; 323 | 324 | sprintf(VERSION, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, 325 | VERSION_MICRO); 326 | 327 | parseargs(argc, argv, &ledstring); 328 | 329 | setup_handlers(); 330 | 331 | if ((ret = ws2811_init(&ledstring)) != WS2811_SUCCESS) { 332 | fprintf(stderr, "ws2811_init failed: %s\n", 333 | ws2811_get_return_t_str(ret)); 334 | return ret; 335 | } 336 | 337 | int *beat_cnt_map; 338 | beat_cnt_map = (int *)shm_init(GBD_BEAT_COUNT_FILE); 339 | if (!beat_cnt_map) { 340 | fprintf(stderr, "Could not open GBD IPC file!\n"); 341 | return -1; 342 | } 343 | 344 | while (running) { 345 | static int tmp_cnt, bcnt; 346 | static int prevcnt[GBD_BEAT_COUNT_BUF_SIZE]; 347 | 348 | usleep(1000000/30); /* 30 FPS */ 349 | 350 | if (beat_cnt_map[KICKDRUM] != prevcnt[KICKDRUM]) { 351 | prevcnt[KICKDRUM] = beat_cnt_map[KICKDRUM]; 352 | printf("BassBeat (%i)\n", bcnt++); 353 | 354 | if (tmp_cnt == 0) { 355 | matrix_render(0x00200000); 356 | } else if (tmp_cnt == 1) { 357 | matrix_render(0x00002000); 358 | } else { 359 | matrix_render(0x00000020); 360 | } 361 | tmp_cnt = ++tmp_cnt > 2 ? 0 : tmp_cnt; 362 | 363 | if ((ret = ws2811_render(&ledstring)) != WS2811_SUCCESS) { 364 | fprintf(stderr, "ws2811_render failed: %s\n", 365 | ws2811_get_return_t_str(ret)); 366 | break; 367 | } 368 | } /* if (beat_cnt_map[KICKDRUM]) */ 369 | } 370 | 371 | if (clear_on_exit) { 372 | matrix_render(0); 373 | ws2811_render(&ledstring); 374 | } 375 | 376 | ws2811_fini(&ledstring); 377 | 378 | printf("\n"); 379 | return ret; 380 | } 381 | -------------------------------------------------------------------------------- /maker-templates/rpi_ws281x/segments/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * newtest.c 3 | * 4 | * Copyright (c) 2014 Jeremy Garff 5 | * 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without modification, are permitted 9 | * provided that the following conditions are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright notice, this list of 12 | * conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list 14 | * of conditions and the following disclaimer in the documentation and/or other materials 15 | * provided with the distribution. 16 | * 3. Neither the name of the owner nor the names of its contributors may be used to endorse 17 | * or promote products derived from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 | * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 24 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | */ 29 | 30 | static char VERSION[] = "XX.YY.ZZ"; 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "clk.h" 46 | #include "gpio.h" 47 | #include "dma.h" 48 | #include "pwm.h" 49 | #include "version.h" 50 | 51 | #include "ws2811.h" 52 | 53 | #define ARRAY_SIZE(stuff) (sizeof(stuff) / sizeof(stuff[0])) 54 | 55 | // defaults for cmdline options 56 | #define TARGET_FREQ WS2811_TARGET_FREQ 57 | #define GPIO_PIN 18 58 | #define DMA 10 59 | //#define STRIP_TYPE WS2811_STRIP_RGB // WS2812/SK6812RGB integrated chip+leds 60 | #define STRIP_TYPE WS2811_STRIP_GBR // WS2812/SK6812RGB integrated chip+leds 61 | //#define STRIP_TYPE SK6812_STRIP_RGBW // SK6812RGBW (NOT SK6812RGB) 62 | 63 | #define WIDTH 30 64 | #define HEIGHT 4 65 | #define LED_COUNT (WIDTH * HEIGHT) 66 | 67 | int width = WIDTH; 68 | int height = HEIGHT; 69 | int led_count = LED_COUNT; 70 | 71 | int clear_on_exit = 1; 72 | 73 | ws2811_t ledstring = { 74 | .freq = TARGET_FREQ, 75 | .dmanum = DMA, 76 | .channel = { 77 | [0] = { 78 | .gpionum = GPIO_PIN, 79 | .count = LED_COUNT, 80 | .invert = 0, 81 | .brightness = 255, 82 | .strip_type = STRIP_TYPE, 83 | }, 84 | [1] = { 85 | .gpionum = 0, 86 | .count = 0, 87 | .invert = 0, 88 | .brightness = 0, 89 | }, 90 | }, 91 | }; 92 | 93 | static uint8_t running = 1; 94 | 95 | void matrix_render(int height, uint32_t color) 96 | { 97 | int x, y = height; 98 | 99 | /* RHS border */ 100 | x = 0; 101 | ledstring.channel[0].leds[(y * width) + x] = 0; 102 | 103 | /* light-unit */ 104 | for (x = 1; x < width - 1; x++) { 105 | ledstring.channel[0].leds[(y * width) + x] = color; 106 | } 107 | 108 | /* LHS border */ 109 | ledstring.channel[0].leds[(y * width) + x] = 0; 110 | } 111 | 112 | static void ctrl_c_handler(int signum) 113 | { 114 | (void)(signum); 115 | running = 0; 116 | } 117 | 118 | static void setup_handlers(void) 119 | { 120 | struct sigaction sa = { 121 | .sa_handler = ctrl_c_handler, 122 | }; 123 | 124 | sigaction(SIGINT, &sa, NULL); 125 | sigaction(SIGTERM, &sa, NULL); 126 | } 127 | 128 | void parseargs(int argc, char **argv, ws2811_t * ws2811) 129 | { 130 | int index; 131 | int c; 132 | 133 | static struct option longopts[] = { 134 | {"help", no_argument, 0, 'h'}, 135 | {"dma", required_argument, 0, 'd'}, 136 | {"gpio", required_argument, 0, 'g'}, 137 | {"invert", no_argument, 0, 'i'}, 138 | {"clear", no_argument, 0, 'c'}, 139 | {"strip", required_argument, 0, 's'}, 140 | {"height", required_argument, 0, 'y'}, 141 | {"width", required_argument, 0, 'x'}, 142 | {"version", no_argument, 0, 'v'}, 143 | {0, 0, 0, 0} 144 | }; 145 | 146 | while (1) { 147 | 148 | index = 0; 149 | c = getopt_long(argc, argv, "cd:g:his:vx:y:", longopts, &index); 150 | 151 | if (c == -1) 152 | break; 153 | 154 | switch (c) { 155 | case 0: 156 | /* handle flag options (array's 3rd field non-0) */ 157 | break; 158 | 159 | case 'h': 160 | fprintf(stderr, "%s version %s\n", argv[0], VERSION); 161 | fprintf(stderr, "Usage: %s \n" 162 | "-h (--help) - this information\n" 163 | "-s (--strip) - strip type - rgb, grb, gbr, rgbw\n" 164 | "-x (--width) - matrix width (default 8)\n" 165 | "-y (--height) - matrix height (default 8)\n" 166 | "-d (--dma) - dma channel to use (default 10)\n" 167 | "-g (--gpio) - GPIO to use\n" 168 | " If omitted, default is 18 (PWM0)\n" 169 | "-i (--invert) - invert pin output (pulse LOW)\n" 170 | "-c (--clear) - clear matrix on exit.\n" 171 | "-v (--version) - version information\n", 172 | argv[0]); 173 | exit(-1); 174 | 175 | case 'D': 176 | break; 177 | 178 | case 'g': 179 | if (optarg) { 180 | int gpio = atoi(optarg); 181 | /* 182 | PWM0, which can be set to use GPIOs 12, 18, 40, and 52. 183 | Only 12 (pin 32) and 18 (pin 12) are available on the B+/2B/3B 184 | PWM1 which can be set to use GPIOs 13, 19, 41, 45 and 53. 185 | Only 13 is available on the B+/2B/PiZero/3B, on pin 33 186 | PCM_DOUT, which can be set to use GPIOs 21 and 31. 187 | Only 21 is available on the B+/2B/PiZero/3B, on pin 40. 188 | SPI0-MOSI is available on GPIOs 10 and 38. 189 | Only GPIO 10 is available on all models. 190 | 191 | The library checks if the specified gpio is available 192 | on the specific model (from model B rev 1 till 3B) 193 | 194 | */ 195 | ws2811->channel[0].gpionum = gpio; 196 | } 197 | break; 198 | 199 | case 'i': 200 | ws2811->channel[0].invert = 1; 201 | break; 202 | 203 | case 'c': 204 | clear_on_exit = 1; 205 | break; 206 | 207 | case 'd': 208 | if (optarg) { 209 | int dma = atoi(optarg); 210 | if (dma < 14) { 211 | ws2811->dmanum = dma; 212 | } else { 213 | printf("invalid dma %d\n", dma); 214 | exit(-1); 215 | } 216 | } 217 | break; 218 | 219 | case 'y': 220 | if (optarg) { 221 | height = atoi(optarg); 222 | if (height > 0) { 223 | if (height & 0x1) { 224 | height++; 225 | fprintf(stderr, 226 | "WARNING: forcing \"--height %i\"\n", 227 | height); 228 | } 229 | ws2811->channel[0].count = 230 | height * width; 231 | } else { 232 | printf("invalid height %d\n", height); 233 | exit(-1); 234 | } 235 | } 236 | break; 237 | 238 | case 'x': 239 | if (optarg) { 240 | width = atoi(optarg); 241 | if (width > 0) { 242 | ws2811->channel[0].count = 243 | height * width; 244 | } else { 245 | printf("invalid width %d\n", width); 246 | exit(-1); 247 | } 248 | } 249 | break; 250 | 251 | case 's': 252 | if (optarg) { 253 | if (!strncasecmp("rgb", optarg, 4)) { 254 | ws2811->channel[0].strip_type = 255 | WS2811_STRIP_RGB; 256 | } else if (!strncasecmp("rbg", optarg, 4)) { 257 | ws2811->channel[0].strip_type = 258 | WS2811_STRIP_RBG; 259 | } else if (!strncasecmp("grb", optarg, 4)) { 260 | ws2811->channel[0].strip_type = 261 | WS2811_STRIP_GRB; 262 | } else if (!strncasecmp("gbr", optarg, 4)) { 263 | ws2811->channel[0].strip_type = 264 | WS2811_STRIP_GBR; 265 | } else if (!strncasecmp("brg", optarg, 4)) { 266 | ws2811->channel[0].strip_type = 267 | WS2811_STRIP_BRG; 268 | } else if (!strncasecmp("bgr", optarg, 4)) { 269 | ws2811->channel[0].strip_type = 270 | WS2811_STRIP_BGR; 271 | } else if (!strncasecmp("rgbw", optarg, 4)) { 272 | ws2811->channel[0].strip_type = 273 | SK6812_STRIP_RGBW; 274 | } else if (!strncasecmp("grbw", optarg, 4)) { 275 | ws2811->channel[0].strip_type = 276 | SK6812_STRIP_GRBW; 277 | } else { 278 | printf("invalid strip %s\n", optarg); 279 | exit(-1); 280 | } 281 | } 282 | break; 283 | 284 | case 'v': 285 | fprintf(stderr, "%s version %s\n", argv[0], VERSION); 286 | exit(-1); 287 | 288 | case '?': 289 | /* getopt_long already reported error? */ 290 | exit(-1); 291 | 292 | default: 293 | exit(-1); 294 | } 295 | } 296 | } 297 | 298 | #include 299 | #include "gbd.h" 300 | 301 | static void *shm_init(const char *filename) 302 | { 303 | int fd, ret = -1; 304 | void *lmap = NULL; 305 | size_t shm_filesize; 306 | 307 | fd = shm_open(filename, O_RDWR | O_CREAT, (mode_t) 0666); 308 | if (fd < 0) { 309 | fprintf(stderr, "shm_open(3): %s\n", strerror(errno)); 310 | goto exit; 311 | } 312 | 313 | shm_filesize = sysconf(_SC_PAGE_SIZE); 314 | ret = ftruncate(fd, shm_filesize); 315 | if (ret < 0) { 316 | fprintf(stderr, "ftruncate(2): %sn", strerror(errno)); 317 | goto exit; 318 | } 319 | 320 | lmap = mmap(0, shm_filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 321 | if (lmap == MAP_FAILED) { 322 | fprintf(stderr, "mmap(2): %s\n", strerror(errno)); 323 | goto exit; 324 | } 325 | 326 | return lmap; 327 | exit: 328 | close(fd); 329 | return NULL; 330 | } 331 | 332 | int main(int argc, char *argv[]) 333 | { 334 | ws2811_return_t ret; 335 | 336 | sprintf(VERSION, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, 337 | VERSION_MICRO); 338 | 339 | parseargs(argc, argv, &ledstring); 340 | 341 | setup_handlers(); 342 | 343 | if ((ret = ws2811_init(&ledstring)) != WS2811_SUCCESS) { 344 | fprintf(stderr, "ws2811_init failed: %s\n", 345 | ws2811_get_return_t_str(ret)); 346 | return ret; 347 | } 348 | 349 | int *beat_cnt_map; 350 | beat_cnt_map = (int *)shm_init(GBD_BEAT_COUNT_FILE); 351 | if (!beat_cnt_map) { 352 | fprintf(stderr, "Could not open GBD IPC file!\n"); 353 | return -1; 354 | } 355 | 356 | while (running) { 357 | static int bcnt, tmp_cnt; 358 | static uint32_t color = 0x00202000; 359 | static int prevcnt[GBD_BEAT_COUNT_BUF_SIZE]; 360 | 361 | usleep(1000000 / 30); /* 30 FPS */ 362 | 363 | if (beat_cnt_map[KICKDRUM] != prevcnt[KICKDRUM]) { 364 | prevcnt[KICKDRUM] = beat_cnt_map[KICKDRUM]; 365 | printf("BassBeat (%i)\n", bcnt++); 366 | } 367 | 368 | if (beat_cnt_map[BASSLINE] != prevcnt[BASSLINE]) { 369 | prevcnt[BASSLINE] = beat_cnt_map[BASSLINE]; 370 | if (tmp_cnt == 0) 371 | color = 0x00202000; 372 | else if (tmp_cnt == 1) 373 | color = 0x00002020; 374 | else 375 | color = 0x00200020; 376 | tmp_cnt = ++tmp_cnt > 2 ? 0 : tmp_cnt; 377 | } 378 | 379 | { 380 | static uint32_t last_color; 381 | 382 | if (bcnt & 0x1) { 383 | matrix_render(0, color); 384 | matrix_render(1, 0); 385 | last_color = 0; 386 | } else { 387 | matrix_render(0, 0); 388 | matrix_render(1, color); 389 | last_color = color; 390 | } 391 | 392 | for (int i = 2; i < height; i += 2) { 393 | if (last_color) { 394 | matrix_render(i, color); 395 | matrix_render(i + 1, 0); 396 | last_color = 0; 397 | } else { 398 | matrix_render(i, 0); 399 | matrix_render(i + 1, color); 400 | last_color = color; 401 | } 402 | } 403 | 404 | if ((ret = ws2811_render(&ledstring)) != WS2811_SUCCESS) { 405 | fprintf(stderr, "ws2811_render failed: %s\n", 406 | ws2811_get_return_t_str(ret)); 407 | break; 408 | } 409 | } 410 | } 411 | 412 | if (clear_on_exit) { 413 | for (int i = 0; i < height; i++) 414 | matrix_render(i, 0); 415 | ws2811_render(&ledstring); 416 | } 417 | 418 | ws2811_fini(&ledstring); 419 | 420 | printf("\n"); 421 | return ret; 422 | } 423 | --------------------------------------------------------------------------------