├── spectrogram.png ├── Makefile ├── .github └── workflows │ └── c.yml ├── Makefile.bsd-wrapper ├── README.md ├── LICENSE ├── fft.h ├── sio.h ├── cms.h ├── aux.h ├── widget.h ├── fft.c ├── alsa.c ├── sio.c ├── aux.c ├── cms.c ├── spectrogram.c └── widget.c /spectrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dim13/spectrogram/HEAD/spectrogram.png -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | 3 | LDLIBS = -lfftw3 -lX11 -lasound -lm 4 | 5 | spectrogram: spectrogram.o fft.o cms.o aux.o widget.o alsa.o 6 | 7 | clean: 8 | rm -f spectrogram *.o 9 | -------------------------------------------------------------------------------- /.github/workflows/c.yml: -------------------------------------------------------------------------------- 1 | name: C CI 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | branches: master 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - run: sudo apt install libfftw3-dev libasound2-dev libx11-dev 16 | - run: make 17 | -------------------------------------------------------------------------------- /Makefile.bsd-wrapper: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | 3 | VERSION=3.0 4 | PROG= spectrogram 5 | 6 | SRCS= spectrogram.c fft.c cms.c aux.c widget.c sio.c 7 | LIBS= fftw3 x11 8 | BINDIR= /usr/local/bin 9 | 10 | PCCF!= pkg-config --cflags ${LIBS} 11 | CFLAGS+=${PCCF} 12 | 13 | PCLA!= pkg-config --libs ${LIBS} 14 | LDADD+= ${PCLA} -lm -lsndio 15 | 16 | DEBUG+= -Wall 17 | NOMAN= 18 | DIR= ${PROG}-${VERSION} 19 | 20 | package: 21 | @mkdir ${DIR} 22 | @cp Makefile ${SRCS} ${HEADERS} ${DIR} 23 | @tar zcf ${DIR}.tar.gz ${DIR} 24 | @rm -rf ${DIR} 25 | 26 | .include 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spectrogram 2 | 3 | Visualisation hack for OpenBSD (sndio) and Linux (alsa) playback. 4 | 5 | ![spectrogram](spectrogram.png) 6 | 7 | ## OpenBSD 8 | 9 | Install required packages: 10 | 11 | ``` 12 | pkg_add fftw3 13 | ``` 14 | 15 | Build: 16 | 17 | ``` 18 | make -f Makefile.bsd-wrapper 19 | ``` 20 | 21 | `sndiod` must be started in monitoring mode (see the [Multimedia: Recording a Monitor Mix of All Audio Playback](https://www.openbsd.org/faq/faq13.html#recordmon) section of the FAQ:) 22 | 23 | ``` 24 | rcctl set sndiod flags -s default -m play,mon -s mon 25 | rcctl restart sndiod 26 | ``` 27 | 28 | ## Linux 29 | 30 | Build packages: `sudo apt install libfftw3-dev libasound2-dev libx11-dev` 31 | 32 | Audo loopback device: `sudo modprobe snd-aloop` 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Dimitri Sokolyuk 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /fft.h: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2010 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifndef __FFT_H 19 | #define __FFT_H 20 | 21 | __BEGIN_DECLS 22 | int init_fft(size_t, size_t); 23 | int exec_fft(double *); 24 | void free_fft(void); 25 | __END_DECLS 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /sio.h: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2013 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifndef __SIO_H 19 | #define __SIO_H 20 | 21 | __BEGIN_DECLS 22 | int init_sio(void); 23 | void read_sio(double *, double *, size_t); 24 | unsigned int max_samples_sio(void); 25 | void free_sio(void); 26 | __END_DECLS 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /cms.h: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2014 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifndef __CMS_H 19 | #define __CMS_H 20 | 21 | struct hsl { 22 | double h, s, l; 23 | }; 24 | 25 | struct palette { 26 | struct hsl from, to; 27 | }; 28 | 29 | __BEGIN_DECLS 30 | unsigned long *init_palette(Display *, struct palette, int); 31 | unsigned long hslcolor(Display *, double h, double s, double l); 32 | __END_DECLS 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /aux.h: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2010 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifndef _AUX_H 19 | #define _AUX_H 20 | 21 | __BEGIN_DECLS 22 | void fullscreen(Display *, Window); 23 | void hide_ptr(Display *, Window); 24 | void move(Display *, Window, Window); 25 | void restrictsize(Display *, Window, int, int, int, int); 26 | void blit(Display *, Drawable, GC, XRectangle); 27 | void clear(Display *, Drawable, GC, XRectangle); 28 | void copy(Display *, Drawable, Drawable, GC, XRectangle, Drawable); 29 | __END_DECLS 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /widget.h: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2010 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifndef _WIDGET_H 19 | #define _WIDGET_H 20 | 21 | #define HGAP 4 22 | #define VGAP 1 23 | 24 | enum mirror { LTR, RTL }; 25 | struct panel; 26 | 27 | __BEGIN_DECLS 28 | struct panel *init_panel(Display *, Window, XRectangle, enum mirror); 29 | void draw_panel(struct panel *); 30 | void flip_panel(struct panel *); 31 | void free_panel(struct panel *); 32 | void toggle_mirror(struct panel *); 33 | double *dataptr(struct panel *); 34 | __END_DECLS 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /fft.c: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2010 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include /* must be prior fftw3 */ 24 | #include 25 | 26 | #include "fft.h" 27 | 28 | static fftw_plan plan; 29 | static fftw_complex *out; 30 | static double *in; 31 | static size_t sz; 32 | static double *window; 33 | static double *sq; 34 | 35 | static double * 36 | hamming(size_t n) 37 | { 38 | double alpha = 0.53836; 39 | double beta = 1.0 - alpha; 40 | double *p; 41 | int i; 42 | 43 | p = calloc(n, sizeof(double)); 44 | assert(p); 45 | 46 | for (i = 0; i < n; i++) { 47 | p[i] = alpha - beta * cos(2 * M_PI * i / (n - 1)); 48 | p[i] /= INT16_MAX; 49 | } 50 | 51 | return p; 52 | } 53 | 54 | static double * 55 | squares(size_t n) 56 | { 57 | double *p; 58 | int i; 59 | 60 | p = calloc(n, sizeof(double)); 61 | assert(p); 62 | 63 | for (i = 0; i < n; i++) 64 | p[i] = sqrt(i + 1); 65 | 66 | return p; 67 | } 68 | 69 | int 70 | init_fft(size_t maxn, size_t n) 71 | { 72 | in = fftw_malloc(maxn * sizeof(double)); 73 | out = fftw_malloc(maxn * sizeof(fftw_complex) / 2); 74 | assert(in && out); 75 | 76 | plan = fftw_plan_dft_r2c_1d(n, in, out, FFTW_MEASURE); 77 | window = hamming(n); 78 | sq = squares(n / 2); 79 | sz = n; 80 | 81 | return 0; 82 | } 83 | 84 | int 85 | exec_fft(double *io) 86 | { 87 | int i; 88 | 89 | for (i = 0; i < sz; i++) 90 | in[i] = window[i] * io[i]; 91 | 92 | fftw_execute(plan); 93 | 94 | for (i = 0; i < sz / 2; i++) 95 | io[i] = sq[i] * cabs(out[i]); 96 | 97 | return 0; 98 | } 99 | 100 | void 101 | free_fft(void) 102 | { 103 | fftw_free(in); 104 | fftw_free(out); 105 | free(window); 106 | free(sq); 107 | } 108 | -------------------------------------------------------------------------------- /alsa.c: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2013 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "sio.h" 24 | 25 | #define STEREO 2 26 | #define RATE 48000 27 | #define FPS 25 28 | 29 | static snd_pcm_t *hdl; 30 | static snd_pcm_hw_params_t *par; 31 | static int16_t *buffer; 32 | static unsigned int samples; 33 | 34 | struct data { 35 | int16_t left; 36 | int16_t right; 37 | }; 38 | 39 | int 40 | init_sio(void) 41 | { 42 | snd_pcm_uframes_t round; 43 | unsigned int rate; 44 | int rc; 45 | 46 | rc = snd_pcm_open(&hdl, "default", SND_PCM_STREAM_CAPTURE, 0); 47 | if (rc < 0) 48 | errx(1, "unable to open pcm device: %s", snd_strerror(rc)); 49 | 50 | snd_pcm_hw_params_malloc(&par); 51 | snd_pcm_hw_params_any(hdl, par); 52 | snd_pcm_hw_params_set_access(hdl, par, 53 | SND_PCM_ACCESS_RW_INTERLEAVED); 54 | snd_pcm_hw_params_set_format(hdl, par, 55 | SND_PCM_FORMAT_S16_LE); 56 | snd_pcm_hw_params_set_channels(hdl, par, STEREO); 57 | snd_pcm_hw_params_set_rate(hdl, par, RATE, 0); 58 | 59 | rc = snd_pcm_hw_params(hdl, par); 60 | if (rc < 0) 61 | errx(1, "unable to set hw parameters: %s", snd_strerror(rc)); 62 | 63 | snd_pcm_hw_params_get_period_size(par, &round, NULL); 64 | snd_pcm_hw_params_get_rate(par, &rate, 0); 65 | snd_pcm_hw_params_free(par); 66 | snd_pcm_prepare(hdl); 67 | 68 | samples = rate / FPS; 69 | samples -= samples % round - round; 70 | buffer = calloc(samples * STEREO, sizeof(int16_t)); 71 | assert(buffer); 72 | 73 | return 0; 74 | } 75 | 76 | unsigned int 77 | max_samples_sio(void) 78 | { 79 | return samples; 80 | } 81 | 82 | void 83 | read_sio(double *left, double *right, size_t n) 84 | { 85 | snd_pcm_sframes_t rc; 86 | struct data *data; 87 | int i; 88 | 89 | if (n > samples) 90 | n = samples; 91 | 92 | rc = snd_pcm_readi(hdl, buffer, samples); 93 | if (rc != samples) { 94 | warnx("audio read error: %s", snd_strerror(rc)); 95 | if (rc == -EPIPE) 96 | snd_pcm_prepare(hdl); 97 | } 98 | 99 | data = (struct data *)&buffer[samples - n]; 100 | 101 | for (i = 0; i < n; i++) { 102 | left[i] = data[i].left; 103 | right[i] = data[i].right; 104 | } 105 | } 106 | 107 | void 108 | free_sio(void) 109 | { 110 | snd_pcm_drain(hdl); 111 | snd_pcm_close(hdl); 112 | free(buffer); 113 | } 114 | -------------------------------------------------------------------------------- /sio.c: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2013 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "sio.h" 24 | 25 | #define STEREO 2 26 | #define BITS 16 27 | #define SIGNED 1 28 | #define FPS 25 29 | 30 | static struct sio_hdl *hdl; 31 | static struct sio_par par; 32 | static int16_t *buffer; 33 | static unsigned int samples; 34 | 35 | struct data { 36 | int16_t left; 37 | int16_t right; 38 | }; 39 | 40 | int 41 | init_sio(void) 42 | { 43 | hdl = sio_open(SIO_DEVANY, SIO_REC, 0); 44 | if (!hdl) 45 | errx(1, "cannot connect to sound server, is it running?"); 46 | 47 | sio_initpar(&par); 48 | 49 | par.rchan = STEREO; 50 | par.bits = BITS; 51 | par.le = SIO_LE_NATIVE; 52 | par.sig = SIGNED; 53 | 54 | if (!sio_setpar(hdl, &par)) 55 | errx(1, "SIO set params failed"); 56 | if (!sio_getpar(hdl, &par)) 57 | errx(1, "SIO get params failed"); 58 | 59 | if (par.rchan != STEREO || 60 | par.bits != BITS || 61 | par.le != SIO_LE_NATIVE || 62 | par.sig != SIGNED) 63 | errx(1, "unsupported audio params"); 64 | 65 | samples = par.rate / FPS; 66 | samples -= samples % par.round - par.round; 67 | buffer = calloc(samples * par.rchan, sizeof(int16_t)); 68 | assert(buffer); 69 | 70 | return sio_start(hdl); 71 | } 72 | 73 | unsigned int 74 | max_samples_sio(void) 75 | { 76 | /* 77 | * maximal number of samples we're willing to provide 78 | * with 1920 at 25 fps and 48000 Hz or 79 | * with 1764 at 25 fps and 44100 Hz it shall fit on most screens 80 | */ 81 | return samples; 82 | } 83 | 84 | void 85 | read_sio(double *left, double *right, size_t n) 86 | { 87 | int done, i; 88 | char *p = (char *)buffer; 89 | size_t bufsz = samples * par.rchan * sizeof(int16_t); 90 | size_t rndsz = n * par.rchan * sizeof(int16_t); 91 | struct data *data; 92 | 93 | if (rndsz > bufsz) 94 | rndsz = bufsz; 95 | 96 | for (done = 0; bufsz > 0; p += done, bufsz -= done) { 97 | done = sio_read(hdl, p, bufsz); 98 | if (sio_eof(hdl)) 99 | errx(1, "SIO EOF"); 100 | } 101 | 102 | /* 103 | * return a pointer to the latest ROUND samples (the most recent 104 | * ones) to minimize latency between picture and sound 105 | */ 106 | data = (struct data *)(p - rndsz); 107 | 108 | for (i = 0; i < n; i++) { 109 | left[i] = data[i].left; 110 | right[i] = data[i].right; 111 | } 112 | } 113 | 114 | void 115 | free_sio(void) 116 | { 117 | sio_stop(hdl); 118 | sio_close(hdl); 119 | free(buffer); 120 | } 121 | -------------------------------------------------------------------------------- /aux.c: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2010 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | void 24 | fullscreen(Display *d, Window win) 25 | { 26 | XClientMessageEvent cm; 27 | 28 | bzero(&cm, sizeof(cm)); 29 | cm.type = ClientMessage; 30 | cm.send_event = True; 31 | cm.message_type = XInternAtom(d, "_NET_WM_STATE", False); 32 | cm.window = win; 33 | cm.format = 32; 34 | cm.data.l[0] = 1; /* _NET_WM_STATE_ADD */ 35 | cm.data.l[1] = XInternAtom(d, "_NET_WM_STATE_FULLSCREEN", False); 36 | cm.data.l[2] = 0; /* no secondary property */ 37 | cm.data.l[3] = 1; /* normal application */ 38 | 39 | XSendEvent(d, DefaultRootWindow(d), False, NoEventMask, (XEvent *)&cm); 40 | XMoveWindow(d, win, 0, 0); 41 | } 42 | 43 | void 44 | hide_ptr(Display *d, Window win) 45 | { 46 | Pixmap bm; 47 | Cursor ptr; 48 | Colormap cmap; 49 | XColor black, dummy; 50 | static char empty[] = {0, 0, 0, 0, 0, 0, 0, 0}; 51 | 52 | cmap = DefaultColormap(d, DefaultScreen(d)); 53 | XAllocNamedColor(d, cmap, "black", &black, &dummy); 54 | bm = XCreateBitmapFromData(d, win, empty, 8, 8); 55 | ptr = XCreatePixmapCursor(d, bm, bm, &black, &black, 0, 0); 56 | 57 | XDefineCursor(d, win, ptr); 58 | XFreeCursor(d, ptr); 59 | if (bm != None) 60 | XFreePixmap(d, bm); 61 | XFreeColors(d, cmap, &black.pixel, 1, 0); 62 | } 63 | 64 | void 65 | move(Display *d, Window win, Window container) 66 | { 67 | XWindowAttributes wa, wac; 68 | int dx, dy; 69 | 70 | XGetWindowAttributes(d, win, &wa); 71 | XGetWindowAttributes(d, container, &wac); 72 | 73 | dx = (wa.width - wac.width) / 2; 74 | dy = (wa.height - wac.height) / 2; 75 | if (dy < 0) 76 | dy = 0; 77 | 78 | XMoveWindow(d, container, dx, dy); 79 | } 80 | 81 | void 82 | restrictsize(Display *d, Window win, int minw, int minh, int maxw, int maxh) 83 | { 84 | Atom nhints; 85 | XSizeHints *hints; 86 | 87 | nhints = XInternAtom(d, "WM_NORMAL_HINTS", 0); 88 | hints = XAllocSizeHints(); 89 | hints->flags = PMinSize|PMaxSize; 90 | hints->min_width = minw; 91 | hints->min_height = minh; 92 | hints->max_width = maxw; 93 | hints->max_height = maxh; 94 | XSetWMSizeHints(d, win, hints, nhints); 95 | XFree(hints); 96 | } 97 | 98 | void 99 | blit(Display *d, Drawable p, GC gc, XRectangle r) 100 | { 101 | XCopyArea(d, p, p, gc, 0, 0, r.width, r.height - 1, 0, 1); 102 | } 103 | 104 | void 105 | clear(Display *d, Drawable p, GC gc, XRectangle r) 106 | { 107 | XSetForeground(d, gc, BlackPixel(d, DefaultScreen(d))); 108 | XFillRectangle(d, p, gc, 0, 0, r.width, r.height); 109 | } 110 | 111 | void 112 | copy(Display *d, Drawable from, Drawable to, GC gc, XRectangle r, Drawable mask) 113 | { 114 | XSetClipMask(d, gc, mask); 115 | XCopyArea(d, from, to, gc, 0, 0, r.width, r.height, 0, 0); 116 | XSetClipMask(d, gc, None); 117 | } 118 | -------------------------------------------------------------------------------- /cms.c: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2014 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | /* 19 | * input: H 0..360, S 0..100, V 0..100 20 | * output: R, G, B 0..65535 21 | */ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "cms.h" 30 | 31 | #if 0 32 | static void 33 | hsv2rgb(unsigned short *r, unsigned short *g, unsigned short *b, 34 | double h, double s, double v) 35 | { 36 | double F, M, N, K; 37 | int i; 38 | 39 | /* normalize */ 40 | h /= 360.0; 41 | s /= 100.0; 42 | v /= 100.0; 43 | 44 | if (s > 0.0) { 45 | if (h >= 1.0) 46 | h -= 1.0; 47 | 48 | h *= 6.0; 49 | i = (int)h; 50 | F = h - i; 51 | M = v * (1 - s); 52 | N = v * (1 - s * F); 53 | K = v * (1 - s * (1 - F)); 54 | 55 | /* scale up */ 56 | v *= UINT16_MAX; 57 | M *= UINT16_MAX; 58 | K *= UINT16_MAX; 59 | N *= UINT16_MAX; 60 | 61 | switch (i) { 62 | case 0: *r = v; *g = K; *b = M; break; 63 | case 1: *r = N; *g = v; *b = M; break; 64 | case 2: *r = M; *g = v; *b = K; break; 65 | case 3: *r = M; *g = N; *b = v; break; 66 | case 4: *r = K; *g = M; *b = v; break; 67 | case 5: *r = v; *g = M; *b = N; break; 68 | } 69 | } else { 70 | *r = *g = *b = UINT16_MAX * v; 71 | } 72 | } 73 | #endif 74 | 75 | /* 76 | * input: H 0..360, S 0..100, L 0..100 77 | * output: R, G, B 0..65535 78 | */ 79 | 80 | static void 81 | hsl2rgb(unsigned short *r, unsigned short *g, unsigned short *b, 82 | double h, double s, double l) 83 | { 84 | double v, F, M, K, N; 85 | int i; 86 | 87 | /* normalize */ 88 | h /= 360.0; 89 | s /= 100.0; 90 | l /= 100.0; 91 | 92 | v = (l <= 0.5) ? (l + l * s) : (l - l * s + s); 93 | 94 | if (v > 0.0) { 95 | if (h >= 1.0) 96 | h -= 1.0; 97 | 98 | h *= 6.0; 99 | i = (int)h; 100 | F = h - i; 101 | M = l + l - v; 102 | K = M + F * (v - M); 103 | N = v - F * (v - M); 104 | 105 | /* scale up */ 106 | v *= UINT16_MAX; 107 | M *= UINT16_MAX; 108 | K *= UINT16_MAX; 109 | N *= UINT16_MAX; 110 | 111 | switch (i) { 112 | case 0: *r = v; *g = K; *b = M; break; 113 | case 1: *r = N; *g = v; *b = M; break; 114 | case 2: *r = M; *g = v; *b = K; break; 115 | case 3: *r = M; *g = N; *b = v; break; 116 | case 4: *r = K; *g = M; *b = v; break; 117 | case 5: *r = v; *g = M; *b = N; break; 118 | } 119 | } else { 120 | *r = *g = *b = UINT16_MAX * l; 121 | } 122 | } 123 | 124 | unsigned long 125 | hslcolor(Display *d, double h, double s, double l) 126 | { 127 | int scr = DefaultScreen(d); 128 | Colormap cmap = DefaultColormap(d, scr); 129 | XColor c; 130 | 131 | hsl2rgb(&c.red, &c.green, &c.blue, h, s, l); 132 | c.flags = DoRed|DoGreen|DoBlue; 133 | 134 | XAllocColor(d, cmap, &c); 135 | 136 | return c.pixel; 137 | } 138 | 139 | unsigned long * 140 | init_palette(Display *d, struct palette pal, int n) 141 | { 142 | unsigned long *p; 143 | float hstep, sstep, lstep; 144 | int i; 145 | 146 | p = calloc(n, sizeof(unsigned long)); 147 | assert(p); 148 | 149 | hstep = (pal.to.h - pal.from.h) / (n - 1); 150 | sstep = (pal.to.s - pal.from.s) / (n - 1); 151 | lstep = (pal.to.l - pal.from.l) / (n - 1); 152 | 153 | for (i = 0; i < n; i++) { 154 | p[i] = hslcolor(d, pal.from.h, pal.from.s, pal.from.l); 155 | pal.from.h += hstep; 156 | pal.from.s += sstep; 157 | pal.from.l += lstep; 158 | } 159 | 160 | return p; 161 | } 162 | -------------------------------------------------------------------------------- /spectrogram.c: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2010 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "aux.h" 28 | #include "fft.h" 29 | #include "sio.h" 30 | #include "widget.h" 31 | 32 | extern char *__progname; 33 | int die = 0; 34 | 35 | void 36 | catch(int notused) 37 | { 38 | die = 1; 39 | } 40 | 41 | void 42 | usage(void) 43 | { 44 | #define USGFMT "\t%-12s%s\n" 45 | fprintf(stderr, "Usage: %s [-dfph] [-r ]\n", __progname); 46 | fprintf(stderr, USGFMT, "-d", "daemonize"); 47 | fprintf(stderr, USGFMT, "-f", "fullscreen mode"); 48 | fprintf(stderr, USGFMT, "-p", "don't hide pointer in fullscreen mode"); 49 | fprintf(stderr, USGFMT, "-r ", "sio round count"); 50 | fprintf(stderr, USGFMT, "-h", "this help"); 51 | 52 | exit(0); 53 | } 54 | 55 | int 56 | main(int argc, char **argv) 57 | { 58 | Display *dsp; 59 | Window win, container; 60 | Atom delwin; 61 | XClassHint *class; 62 | XWindowAttributes wa; 63 | XRectangle geo; 64 | int scr; 65 | 66 | struct panel *left, *right; 67 | double *ldata, *rdata; 68 | 69 | int fflag = 0; /* fullscreen */ 70 | int pflag = 1; /* hide ptr */ 71 | 72 | int ch; 73 | int width, height; 74 | unsigned int maxwidth, maxheight; 75 | unsigned long black, white; 76 | float factor = 0.75; 77 | int round = 1024; /* FFT is fastest with powers of two */ 78 | 79 | while ((ch = getopt(argc, argv, "dfpr:h")) != -1) 80 | switch (ch) { 81 | case 'f': 82 | fflag = 1; 83 | break; 84 | case 'p': 85 | pflag = 0; 86 | break; 87 | case 'r': 88 | round = atoi(optarg); 89 | break; 90 | case 'h': 91 | default: 92 | usage(); 93 | /* NOTREACHED */ 94 | } 95 | argc -= optind; 96 | argv += optind; 97 | 98 | signal(SIGINT, catch); 99 | 100 | init_sio(); 101 | maxwidth = max_samples_sio(); 102 | maxheight = wa.height; 103 | 104 | dsp = XOpenDisplay(NULL); 105 | if (!dsp) 106 | errx(1, "Cannot connect to X11 server"); 107 | 108 | if (round > maxwidth) 109 | round = maxwidth; 110 | 111 | XGetWindowAttributes(dsp, DefaultRootWindow(dsp), &wa); 112 | width = round + HGAP; 113 | if (fflag || width > wa.width) { 114 | round = wa.width - HGAP; 115 | width = wa.width; 116 | } 117 | 118 | height = factor * width; 119 | if (height > wa.height) 120 | height = wa.height; 121 | 122 | scr = DefaultScreen(dsp); 123 | white = WhitePixel(dsp, scr); 124 | black = BlackPixel(dsp, scr); 125 | 126 | win = XCreateSimpleWindow(dsp, RootWindow(dsp, scr), 127 | 0, 0, width, height, 0, white, black); 128 | 129 | XStoreName(dsp, win, __progname); 130 | class = XAllocClassHint(); 131 | if (class) { 132 | class->res_name = __progname; 133 | class->res_class = __progname; 134 | XSetClassHint(dsp, win, class); 135 | XFree(class); 136 | } 137 | XSelectInput(dsp, win, KeyPressMask|StructureNotifyMask); 138 | 139 | /* catch delete window */ 140 | delwin = XInternAtom(dsp, "WM_DELETE_WINDOW", 0); 141 | XSetWMProtocols(dsp, win, &delwin, 1); 142 | 143 | /* set minimal size */ 144 | restrictsize(dsp, win, width, height, maxwidth, maxheight); 145 | 146 | container = XCreateSimpleWindow(dsp, win, 147 | 0, 0, width, height, 0, white, black); 148 | XMapWindow(dsp, container); 149 | 150 | init_fft(maxwidth, round); 151 | 152 | geo.x = 0; 153 | geo.y = 0; 154 | geo.width = round / 2; 155 | geo.height = height; 156 | left = init_panel(dsp, container, geo, RTL); 157 | ldata = dataptr(left); 158 | 159 | geo.x = round / 2 + HGAP; 160 | geo.y = 0; 161 | geo.width = round / 2; 162 | geo.height = height; 163 | right = init_panel(dsp, container, geo, LTR); 164 | rdata = dataptr(right); 165 | 166 | XClearWindow(dsp, win); 167 | XMapRaised(dsp, win); /* XMapWindow */ 168 | 169 | if (fflag) { 170 | fullscreen(dsp, win); 171 | if (pflag) 172 | hide_ptr(dsp, win); 173 | } 174 | 175 | while (!die) { 176 | while (XPending(dsp)) { 177 | XEvent ev; 178 | 179 | XNextEvent(dsp, &ev); 180 | 181 | switch (ev.type) { 182 | case KeyPress: 183 | switch (XLookupKeysym(&ev.xkey, 0)) { 184 | case XK_q: 185 | die = 1; 186 | break; 187 | case XK_1: 188 | toggle_mirror(left); 189 | break; 190 | case XK_2: 191 | toggle_mirror(right); 192 | break; 193 | case XK_3: 194 | toggle_mirror(left); 195 | toggle_mirror(right); 196 | break; 197 | default: 198 | break; 199 | } 200 | break; 201 | case ClientMessage: 202 | die = *ev.xclient.data.l == delwin; 203 | break; 204 | case ConfigureNotify: 205 | move(dsp, win, container); 206 | break; 207 | default: 208 | break; 209 | } 210 | } 211 | 212 | read_sio(ldata, rdata, round); 213 | 214 | exec_fft(ldata); 215 | exec_fft(rdata); 216 | 217 | draw_panel(left); 218 | draw_panel(right); 219 | 220 | flip_panel(left); 221 | flip_panel(right); 222 | 223 | if (fflag) 224 | XResetScreenSaver(dsp); 225 | } 226 | 227 | free_sio(); 228 | free_fft(); 229 | free_panel(left); 230 | free_panel(right); 231 | 232 | XDestroyWindow(dsp, win); 233 | XCloseDisplay(dsp); 234 | 235 | return 0; 236 | } 237 | -------------------------------------------------------------------------------- /widget.c: -------------------------------------------------------------------------------- 1 | /* $Id$ */ 2 | /* 3 | * Copyright (c) 2010 Dimitri Sokolyuk 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "aux.h" 24 | #include "cms.h" 25 | #include "widget.h" 26 | 27 | struct background { 28 | Pixmap pix; 29 | Pixmap mask; 30 | GC gc; 31 | XRectangle geo; 32 | }; 33 | 34 | struct subwin { 35 | Window win; 36 | Pixmap pix; /* buffer */ 37 | GC gc; 38 | XRectangle geo; 39 | }; 40 | 41 | struct panel { 42 | Display *dsp; 43 | Window win; /* container */ 44 | struct subwin *wf; 45 | struct subwin *sp; 46 | struct background *bg; 47 | struct background *shadow; 48 | int mirror; 49 | int maxval; 50 | double *data; 51 | unsigned long *palette; 52 | }; 53 | 54 | struct palette p_spectr = {{ 120.0, 100.0, 75.0 }, { 0.0, 100.0, 25.0 }}; 55 | struct palette p_shadow = {{ 120.0, 100.0, 10.0 }, { 0.0, 100.0, 10.0 }}; 56 | struct palette p_waterfall = {{ 210.0, 75.0, 0.0 }, { 180.0, 100.0, 100.0 }}; 57 | 58 | void 59 | draw_panel(struct panel *p) 60 | { 61 | int i, v, x; 62 | 63 | /* blit waterfall */ 64 | blit(p->dsp, p->wf->pix, p->wf->gc, p->wf->geo); 65 | /* blit shadow mask */ 66 | blit(p->dsp, p->shadow->mask, p->shadow->gc, p->shadow->geo); 67 | 68 | /* clear spectrogram */ 69 | clear(p->dsp, p->sp->pix, p->sp->gc, p->sp->geo); 70 | /* clear mask */ 71 | clear(p->dsp, p->bg->mask, p->bg->gc, p->bg->geo); 72 | 73 | for (i = 0; i < p->sp->geo.width; i++) { 74 | /* limit maxval */ 75 | v = p->data[i] >= p->maxval ? p->maxval - 1 : p->data[i]; 76 | x = p->mirror ? p->sp->geo.width - i - 1 : i; 77 | 78 | /* draw waterfall */ 79 | XSetForeground(p->dsp, p->wf->gc, p->palette[v]); 80 | XDrawPoint(p->dsp, p->wf->pix, p->wf->gc, x, 0); 81 | 82 | /* draw spectrogram */ 83 | XSetForeground(p->dsp, p->bg->gc, 1); 84 | XDrawLine(p->dsp, p->bg->mask, p->bg->gc, 85 | x, p->bg->geo.height - v, 86 | x, p->bg->geo.height); 87 | } 88 | 89 | /* copy mask to shadow mask */ 90 | copy(p->dsp, p->bg->mask, p->shadow->mask, p->shadow->gc, 91 | p->shadow->geo, p->bg->mask); 92 | /* shadow to buffer */ 93 | copy(p->dsp, p->shadow->pix, p->sp->pix, p->sp->gc, p->sp->geo, 94 | p->shadow->mask); 95 | /* spectrogram to buffer */ 96 | copy(p->dsp, p->bg->pix, p->sp->pix, p->sp->gc, p->sp->geo, 97 | p->bg->mask); 98 | } 99 | 100 | void 101 | flip_panel(struct panel *p) 102 | { 103 | /* flip spectrogram */ 104 | copy(p->dsp, p->sp->pix, p->sp->win, p->sp->gc, p->sp->geo, None); 105 | /* flip waterfall */ 106 | copy(p->dsp, p->wf->pix, p->wf->win, p->wf->gc, p->wf->geo, None); 107 | } 108 | 109 | static void 110 | draw_background(Display *d, Pixmap pix, GC gc, XRectangle r, unsigned long *pal) 111 | { 112 | int i, x, y; 113 | 114 | x = r.width - 1; 115 | for (i = 0; i < r.height; i++) { 116 | y = r.height - i - 1; 117 | XSetForeground(d, gc, pal[i]); 118 | XDrawLine(d, pix, gc, 0, y, x, y); 119 | } 120 | } 121 | 122 | static struct background * 123 | init_background(Display *d, Drawable parent, XRectangle r) 124 | { 125 | struct background *p; 126 | int scr = DefaultScreen(d); 127 | int planes = DisplayPlanes(d, scr); 128 | 129 | p = malloc(sizeof(struct subwin)); 130 | assert(p); 131 | 132 | p->pix = XCreatePixmap(d, parent, r.width, r.height, planes); 133 | p->mask = XCreatePixmap(d, parent, r.width, r.height, 1); 134 | p->gc = XCreateGC(d, p->mask, 0, NULL); 135 | p->geo = r; 136 | 137 | clear(d, p->mask, p->gc, p->geo); 138 | 139 | return p; 140 | } 141 | 142 | static struct subwin * 143 | init_subwin(Display *d, Drawable parent, XRectangle r) 144 | { 145 | struct subwin *p; 146 | int scr = DefaultScreen(d); 147 | int white = WhitePixel(d, scr); 148 | int black = BlackPixel(d, scr); 149 | int planes = DisplayPlanes(d, scr); 150 | 151 | p = malloc(sizeof(struct subwin)); 152 | assert(p); 153 | 154 | p->win = XCreateSimpleWindow(d, parent, r.x, r.y, 155 | r.width, r.height, 0, white, black); 156 | p->pix = XCreatePixmap(d, p->win, r.width, r.height, planes); 157 | p->gc = XCreateGC(d, p->pix, 0, NULL); 158 | p->geo = r; 159 | 160 | clear(d, p->pix, p->gc, p->geo); 161 | 162 | XMapWindow(d, p->win); 163 | 164 | return p; 165 | } 166 | 167 | struct panel * 168 | init_panel(Display *d, Window win, XRectangle r, enum mirror m) 169 | { 170 | struct panel *p; 171 | int scr = DefaultScreen(d); 172 | unsigned long white = WhitePixel(d, scr); 173 | unsigned long gray = hslcolor(d, 0.0, 0.0, 20.0); 174 | 175 | unsigned long *palette; 176 | unsigned int maxval = r.height / 4; 177 | XRectangle geo; 178 | 179 | p = malloc(sizeof(struct panel)); 180 | assert(p); 181 | 182 | p->dsp = d; 183 | p->data = calloc(2 * r.width, sizeof(double)); 184 | assert(p->data); 185 | 186 | /* main panel window */ 187 | p->win = XCreateSimpleWindow(d, win, 188 | r.x, r.y, r.width, r.height, 189 | 0, white, gray); 190 | 191 | /* sperctrogram window and its bitmasks */ 192 | geo.x = 0; 193 | geo.y = 0; 194 | geo.width = r.width; 195 | geo.height = maxval; 196 | p->sp = init_subwin(d, p->win, geo); 197 | 198 | p->bg = init_background(d, p->sp->win, geo); 199 | palette = init_palette(d, p_spectr, maxval); 200 | draw_background(d, p->bg->pix, p->sp->gc, p->bg->geo, palette); 201 | free(palette); 202 | 203 | p->shadow = init_background(d, p->sp->win, geo); 204 | palette = init_palette(d, p_shadow, maxval); 205 | draw_background(d, p->shadow->pix, p->sp->gc, p->shadow->geo, palette); 206 | free(palette); 207 | 208 | p->palette = init_palette(d, p_waterfall, maxval); 209 | p->maxval = maxval; 210 | p->mirror = m; 211 | 212 | /* waterfall window and double buffer */ 213 | geo.x = 0; 214 | geo.y = p->sp->geo.height + VGAP; 215 | geo.width = r.width; 216 | geo.height = r.height - maxval; 217 | p->wf = init_subwin(d, p->win, geo); 218 | 219 | XMapWindow(d, p->win); 220 | 221 | return p; 222 | } 223 | 224 | static void 225 | free_background(Display *d, struct background *p) 226 | { 227 | XFreePixmap(d, p->pix); 228 | XFreePixmap(d, p->mask); 229 | XFreeGC(d, p->gc); 230 | } 231 | 232 | static void 233 | free_subwin(Display *d, struct subwin *p) 234 | { 235 | XFreePixmap(d, p->pix); 236 | XFreeGC(d, p->gc); 237 | XUnmapWindow(d, p->win); 238 | XDestroyWindow(d, p->win); 239 | } 240 | 241 | void 242 | free_panel(struct panel *p) 243 | { 244 | free_background(p->dsp, p->bg); 245 | free_background(p->dsp, p->shadow); 246 | free_subwin(p->dsp, p->sp); 247 | free_subwin(p->dsp, p->wf); 248 | XUnmapWindow(p->dsp, p->win); 249 | XDestroyWindow(p->dsp, p->win); 250 | free(p->palette); 251 | free(p->data); 252 | free(p); 253 | } 254 | 255 | void 256 | toggle_mirror(struct panel *p) 257 | { 258 | p->mirror ^= 1; 259 | } 260 | 261 | double * 262 | dataptr(struct panel *p) 263 | { 264 | return p->data; 265 | } 266 | --------------------------------------------------------------------------------