├── .gitignore ├── processor.h ├── ring_buffer.h ├── README.md ├── sound.h ├── Makefile ├── processor.cpp ├── ring_buffer.cpp ├── main.cpp └── sound.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | !./build/.gitkeep 3 | *.o 4 | .vscode -------------------------------------------------------------------------------- /processor.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCESSOR_H 2 | #define PROCESSOR_H 3 | 4 | #define MAX_DELAY_TIME_IN_SEC 2 5 | 6 | #include "sound.h" 7 | 8 | void setup_processor(int sample_rate, float _delay_time, float _dry_wet, float _feedback); 9 | void process_buffer(audio_sample *original_buffer, audio_sample *processed_buffer, int data_amount); 10 | 11 | #endif -------------------------------------------------------------------------------- /ring_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef RING_BUFFER_H 2 | #define RING_BUFFER_H 3 | 4 | #include "sound.h" 5 | 6 | typedef struct 7 | { 8 | int head_index; 9 | int tail_index; 10 | audio_sample * buffer; 11 | int size; 12 | bool empty; 13 | char * id; 14 | } ring_buffer_t; 15 | 16 | void ring_buffer_init(ring_buffer_t *ring_buffer, int size, char *id); 17 | void ring_buffer_destroy(ring_buffer_t *ring_buffer); 18 | void ring_buffer_get(ring_buffer_t *ring_buffer, audio_sample * output_buffer, int requested_amount); 19 | void ring_buffer_put(ring_buffer_t *ring_buffer, audio_sample *data, int data_amount); 20 | int ring_buffer_get_avail(ring_buffer_t * ring_buffer); 21 | int ring_buffer_put_avail(ring_buffer_t * ring_buffer); 22 | 23 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delay processor 2 | This program was developed to run on a Huawei HG532s ADSL router. The firmware was modified in order to support ALSA and run this program. 3 | 4 | ## Build prerequisites 5 | To build the program for the router you must save the toolchain under `/opt/trendchip/mips-linux-uclibc/` and also save the libraries and includes for alsa-lib (compiled for the router architecture) under `/opt/router/alsa-lib/`. 6 | 7 | ## Building 8 | To compile the program for the router just run: 9 | ``` 10 | PROFILE=router make 11 | ``` 12 | If you want to build the program for run it on your PC: 13 | ``` 14 | make 15 | ``` 16 | 17 | ## Running 18 | Usage: 19 | ``` 20 | ./build/delay_processor -p -c -t -f -w 21 | ``` 22 | 23 | For example, using the `plughw:0,0` interfaces, with a delay time of 100 milliseconds (0.1 seconds), 50% of feedback and 50% of dry wet: 24 | 25 | ``` 26 | ./build/delay_processor -p plughw:0,0 -c plughw:0,0 -t 0.1 -f 0.5 -w 0.5 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /sound.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_H 2 | #define SOUND_H 3 | 4 | #include 5 | 6 | #ifndef CAPTURE_CHANNELS 7 | #define CAPTURE_CHANNELS 1 8 | #endif 9 | 10 | #ifndef PLAYBACK_CHANNELS 11 | #define PLAYBACK_CHANNELS 1 12 | #endif 13 | 14 | #ifndef SAMPLE_RATE 15 | #define SAMPLE_RATE 44100 16 | #endif 17 | 18 | #ifndef FRAMES_PER_BUFFER 19 | #define FRAMES_PER_BUFFER 2046 20 | #endif 21 | 22 | #ifndef RING_BUFFER_SIZE 23 | #define RING_BUFFER_SIZE FRAMES_PER_BUFFER * 16 24 | #endif 25 | 26 | #ifndef AVAILABLE_MIN 27 | #define AVAILABLE_MIN 1024 28 | #endif 29 | 30 | #ifndef PERIOD_SIZE_IN_FRAMES 31 | #define PERIOD_SIZE_IN_FRAMES 1 32 | #endif 33 | 34 | #ifndef AUDIO_FORMAT 35 | // Received from -D PCM_FORMAT_BE argument when gcc is called 36 | #ifdef PCM_FORMAT_BE 37 | #define AUDIO_FORMAT SND_PCM_FORMAT_S16_BE 38 | #else 39 | #define AUDIO_FORMAT SND_PCM_FORMAT_S16_LE 40 | #endif 41 | #endif 42 | 43 | typedef struct 44 | { 45 | float delay_time; 46 | float dry_wet; 47 | float feedback; 48 | } stream_params; 49 | 50 | typedef short audio_sample; 51 | #define SAMPLE_SILENCE 0 52 | 53 | int setup_alsa_handle(snd_pcm_t *handle, snd_pcm_stream_t stream); 54 | 55 | void start_stream(snd_pcm_t *_playback_handle, 56 | snd_pcm_t *_capture_handle, 57 | stream_params *params); 58 | 59 | #endif -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJS = main.o ring_buffer.o sound.o processor.o 2 | OUTPUT = delay_processor 3 | 4 | ifeq ($(strip $(PROFILE)),router) 5 | 6 | # For router building run: 7 | # $ PROFILE=router make 8 | 9 | PATH=/opt/trendchip/mips-linux-uclibc/usr/bin:$(PATH) 10 | HOST=mips-linux 11 | CROSS=mips-linux-uclibc- 12 | CROSS_COMPILE=$(CROSS) 13 | CC=mips-linux-uclibc-gcc 14 | CPP=mips-linux-uclibc-g++ 15 | STRIP=mips-linux-uclibc-strip 16 | SYSROOT=/opt/trendchip/mips-linux-uclibc 17 | CFG_CFLAGS = -mips32r2 -muclibc 18 | OBJ_CFLAGS = -g -c -Wall -D PCM_FORMAT_BE 19 | CPPFLAGS = -I/opt/router/alsa-lib/include 20 | LDFLAGS = -L/opt/router/alsa-lib/lib 21 | LDLIBS = -lasound 22 | 23 | else 24 | 25 | # For PC building run: 26 | # $ PROFILE=pc make 27 | # or just 28 | # $ make 29 | 30 | CC=gcc 31 | CPP=g++ 32 | CFG_CFLAGS = -pthread 33 | OBJ_CFLAGS = -g -c -Wall 34 | CPPFLAGS = 35 | LDFLAGS = 36 | LDLIBS = -lasound 37 | 38 | endif 39 | 40 | all: $(OBJS) 41 | $(CPP) $(CFG_CFLAGS) $(LDFLAGS) -o build/$(OUTPUT) $(OBJS) $(CPPFLAGS) $(LDLIBS) 42 | 43 | main.o: main.cpp 44 | $(CPP) $(OBJ_CFLAGS) $(LDFLAGS) main.cpp $(CPPFLAGS) $(LDLIBS) 45 | 46 | ring_buffer.o: ring_buffer.cpp 47 | $(CPP) $(OBJ_CFLAGS) $(LDFLAGS) ring_buffer.cpp $(CPPFLAGS) $(LDLIBS) 48 | 49 | sound.o: sound.cpp sound.h 50 | $(CPP) $(OBJ_CFLAGS) $(LDFLAGS) sound.cpp $(CPPFLAGS) $(LDLIBS) 51 | 52 | processor.o: processor.cpp processor.h 53 | $(CPP) $(OBJ_CFLAGS) $(LDFLAGS) processor.cpp $(CPPFLAGS) $(LDLIBS) 54 | 55 | clean: 56 | rm $(OBJS) build/$(OUTPUT) -------------------------------------------------------------------------------- /processor.cpp: -------------------------------------------------------------------------------- 1 | #include "processor.h" 2 | #include "ring_buffer.h" 3 | 4 | ring_buffer_t delay_ring_buffer = {}; 5 | 6 | // Seconds of delay 7 | float delay_time; 8 | 9 | // Ratio of dry wet effect. 1 = full wet. 0 = full dry. 10 | float dry_wet; 11 | 12 | // Amount of signal to be fed back. 1 = all the out signal will be part of the new delayed samples. 0 = none of the out signal will be fed back 13 | float feedback; 14 | 15 | audio_sample getted_delayed_samples[1]; 16 | audio_sample delayed_samples_to_be_put[1]; 17 | 18 | void setup_processor(int sample_rate, float _delay_time, float _dry_wet, float _feedback) 19 | { 20 | delay_time = _delay_time; 21 | dry_wet = _dry_wet; 22 | feedback = _feedback; 23 | 24 | // multiply by 2 to have enough space inside ring buffer to use the max delay time. if we reach max time limit we will have xruns. 25 | int ring_buffer_size = sample_rate * MAX_DELAY_TIME_IN_SEC * 2; 26 | ring_buffer_init(&delay_ring_buffer, ring_buffer_size, "delay"); 27 | delay_ring_buffer.empty = false; // ring buffer already has silence, so for this case is not empty 28 | 29 | if (delay_time > MAX_DELAY_TIME_IN_SEC) 30 | { 31 | fprintf(stderr, "ERROR: max delay time in seconds is (%d)\n", MAX_DELAY_TIME_IN_SEC); 32 | } 33 | 34 | // forward head index 35 | delay_ring_buffer.head_index = delay_time * sample_rate; 36 | } 37 | 38 | void process_buffer(audio_sample *original_buffer, audio_sample *processed_buffer, int data_amount) 39 | { 40 | for (int i = 0; i < data_amount; i++) 41 | { 42 | ring_buffer_get(&delay_ring_buffer, getted_delayed_samples, 1); 43 | processed_buffer[i] = ((float)original_buffer[i] * (1.0 - dry_wet) + (float)getted_delayed_samples[0] * dry_wet); 44 | 45 | delayed_samples_to_be_put[0] = ((float)original_buffer[i] + (float)getted_delayed_samples[0] * feedback); 46 | ring_buffer_put(&delay_ring_buffer, delayed_samples_to_be_put, 1); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ring_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "sound.h" 2 | #include "ring_buffer.h" 3 | 4 | /** 5 | * Init ring buffer 6 | */ 7 | void ring_buffer_init(ring_buffer_t *ring_buffer, int size, char *id) 8 | { 9 | ring_buffer->id = id; 10 | ring_buffer->head_index = ring_buffer->tail_index = 0; 11 | ring_buffer->size = size; 12 | ring_buffer->empty = true; 13 | 14 | int size_in_bytes = size * sizeof(audio_sample); 15 | ring_buffer->buffer = (audio_sample *)malloc(size_in_bytes); 16 | memset(ring_buffer->buffer, SAMPLE_SILENCE, size_in_bytes); 17 | } 18 | 19 | /** 20 | * Destroy ring buffer 21 | */ 22 | void ring_buffer_destroy(ring_buffer_t *ring_buffer) 23 | { 24 | free(ring_buffer->buffer); 25 | } 26 | 27 | /** 28 | * Get data from tail index, wrapping overflow 29 | */ 30 | void ring_buffer_get(ring_buffer_t *ring_buffer, audio_sample * output_buffer, int requested_amount) 31 | { 32 | int available_data = ring_buffer_get_avail(ring_buffer); 33 | if(available_data < requested_amount){ 34 | fprintf(stderr, "ringbuffer (%s) underrun\n", ring_buffer->id); 35 | } 36 | 37 | for (int i = 0; i < requested_amount; i++) 38 | { 39 | if (available_data < requested_amount && i >= available_data) 40 | { 41 | // Underrun: fill the rest of the buffer with silence 42 | output_buffer[i] = SAMPLE_SILENCE; 43 | } 44 | else 45 | { 46 | output_buffer[i] = ring_buffer->buffer[ring_buffer->tail_index++]; 47 | ring_buffer->tail_index %= ring_buffer->size; 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * Push data in head index, wrapping overflow 54 | */ 55 | void ring_buffer_put(ring_buffer_t *ring_buffer, audio_sample *data, int data_amount) 56 | { 57 | int available_space = ring_buffer_put_avail(ring_buffer); 58 | if(available_space < data_amount){ 59 | fprintf(stderr, "ringbuffer (%s) overrun\n", ring_buffer->id); 60 | } 61 | 62 | int iterable_spaces = available_space < data_amount ? available_space : data_amount; 63 | 64 | for (int i = 0; i < iterable_spaces; i++) 65 | { 66 | ring_buffer->buffer[ring_buffer->head_index++] = data[i]; 67 | ring_buffer->head_index %= ring_buffer->size; 68 | } 69 | 70 | if (ring_buffer->empty) 71 | { 72 | // After first put, buffer is no longer empty 73 | ring_buffer->empty = false; 74 | } 75 | } 76 | 77 | /** 78 | * Check if there is enough amount of data between head and tail 79 | */ 80 | int ring_buffer_get_avail(ring_buffer_t *ring_buffer) 81 | { 82 | if (ring_buffer->head_index == ring_buffer->tail_index) 83 | { 84 | return 0; 85 | } 86 | return (ring_buffer->size - ring_buffer->tail_index + ring_buffer->head_index) % ring_buffer->size; 87 | } 88 | 89 | /** 90 | * Check if there is enough space between tail and head 91 | */ 92 | int ring_buffer_put_avail(ring_buffer_t *ring_buffer) 93 | { 94 | if (ring_buffer->tail_index == ring_buffer->head_index) 95 | { 96 | return ring_buffer->size; 97 | } 98 | return (ring_buffer->size - ring_buffer->head_index + ring_buffer->tail_index) % ring_buffer->size; 99 | } -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sound.h" 3 | #include "ring_buffer.h" 4 | 5 | snd_pcm_t *playback_handle; 6 | snd_pcm_t *capture_handle; 7 | 8 | void setscheduler(void) 9 | { 10 | struct sched_param sched_param; 11 | 12 | if (sched_getparam(0, &sched_param) < 0) 13 | { 14 | printf("Scheduler getparam failed...\n"); 15 | return; 16 | } 17 | sched_param.sched_priority = sched_get_priority_max(SCHED_RR); 18 | if (!sched_setscheduler(0, SCHED_RR, &sched_param)) 19 | { 20 | printf("Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority); 21 | fflush(stdout); 22 | return; 23 | } 24 | printf("Scheduler set to Round Robin with priority %i FAILED\n", sched_param.sched_priority); 25 | } 26 | 27 | /** 28 | * Main 29 | */ 30 | int main(int argc, char *argv[]) 31 | { 32 | // Get parameters from command arguments 33 | int opt; 34 | stream_params params = {}; 35 | 36 | params.delay_time = -1.0; 37 | params.dry_wet = -1.0; 38 | params.feedback = -1.0; 39 | 40 | char *capture_device = NULL; 41 | char *playback_device = NULL; 42 | while ((opt = getopt(argc, argv, ":p:c:t:f:w:")) != -1) 43 | { 44 | switch (opt) 45 | { 46 | case 'p': 47 | playback_device = optarg; 48 | break; 49 | case 'c': 50 | capture_device = optarg; 51 | break; 52 | case 't': 53 | params.delay_time = (float)strtod(optarg, NULL); 54 | break; 55 | case 'f': 56 | params.feedback = (float)strtod(optarg, NULL); 57 | break; 58 | case 'w': 59 | params.dry_wet = (float)strtod(optarg, NULL); 60 | break; 61 | case ':': 62 | printf("option needs a value\n"); 63 | break; 64 | case '?': 65 | printf("unknown option: %c\n", optopt); 66 | break; 67 | } 68 | } 69 | 70 | if (params.delay_time == -1 || 71 | params.feedback == -1 || 72 | params.dry_wet == -1 || 73 | capture_device == NULL || 74 | playback_device == NULL) 75 | { 76 | printf("Usage: delay_processor -p -c -t -f -w \n"); 77 | return 0; 78 | } 79 | 80 | // Set max priority to the process in order to reduce latency 81 | setscheduler(); 82 | 83 | int err; 84 | int numBytes; 85 | 86 | // Init capture device 87 | if ((err = snd_pcm_open(&capture_handle, capture_device, SND_PCM_STREAM_CAPTURE, 0)) < 0) 88 | { 89 | fprintf(stderr, "cannot open audio device '%s'. Error: %s\n", capture_device, snd_strerror(err)); 90 | exit(1); 91 | } 92 | setup_alsa_handle(capture_handle, SND_PCM_STREAM_CAPTURE); 93 | 94 | if ((err = snd_pcm_start(capture_handle)) < 0) 95 | { 96 | fprintf(stderr, "cannot prepare audio interface for use(%s)\n", 97 | snd_strerror(err)); 98 | return err; 99 | } 100 | 101 | // Init playback device 102 | if ((err = snd_pcm_open(&playback_handle, playback_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) 103 | { 104 | fprintf(stderr, "cannot open audio device '%s'. Error: %s\n", playback_device, snd_strerror(err)); 105 | exit(1); 106 | } 107 | setup_alsa_handle(playback_handle, SND_PCM_STREAM_PLAYBACK); 108 | 109 | if ((err = snd_pcm_prepare(playback_handle)) < 0) 110 | { 111 | fprintf(stderr, "cannot prepare audio interface for use(%s)\n", 112 | snd_strerror(err)); 113 | return err; 114 | } 115 | 116 | // Dump pcm settings info 117 | snd_output_t *output = NULL; 118 | snd_output_stdio_attach(&output, stderr, 0); 119 | snd_pcm_dump(capture_handle, output); 120 | snd_pcm_dump(playback_handle, output); 121 | 122 | // Start stream 123 | start_stream(playback_handle, capture_handle, ¶ms); 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /sound.cpp: -------------------------------------------------------------------------------- 1 | #include "ring_buffer.h" 2 | #include "sound.h" 3 | #include "processor.h" 4 | 5 | /** 6 | * Setup an alsa handle 7 | */ 8 | int setup_alsa_handle(snd_pcm_t *handle, snd_pcm_stream_t stream) 9 | { 10 | 11 | int err = 0; 12 | snd_pcm_hw_params_t *hw_params; 13 | snd_pcm_sw_params_t *sw_params; 14 | 15 | if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) 16 | { 17 | fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n", 18 | snd_strerror(err)); 19 | exit(1); 20 | } 21 | 22 | if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) 23 | { 24 | fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n", 25 | snd_strerror(err)); 26 | exit(1); 27 | } 28 | 29 | if ((err = snd_pcm_hw_params_set_access(handle, hw_params, 30 | SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) 31 | { 32 | fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err)); 33 | exit(1); 34 | } 35 | 36 | if ((err = snd_pcm_hw_params_set_format(handle, hw_params, AUDIO_FORMAT)) < 0) 37 | { 38 | fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err)); 39 | exit(1); 40 | } 41 | 42 | unsigned int rate = SAMPLE_RATE; 43 | if ((err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rate, 0)) < 44 | 0) 45 | { 46 | fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err)); 47 | exit(1); 48 | } 49 | 50 | snd_pcm_uframes_t buffer_size = FRAMES_PER_BUFFER; 51 | if ((err = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffer_size)) < 52 | 0) 53 | { 54 | fprintf(stderr, "cannot set buffer size (%s)\n", snd_strerror(err)); 55 | exit(1); 56 | } 57 | 58 | int dir; 59 | snd_pcm_uframes_t period_size = PERIOD_SIZE_IN_FRAMES; 60 | if ((err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, &dir)) < 61 | 0) 62 | { 63 | fprintf(stderr, "cannot set period size (%s)\n", snd_strerror(err)); 64 | exit(1); 65 | } 66 | 67 | int channels; 68 | if (stream == SND_PCM_STREAM_CAPTURE) 69 | { 70 | channels = CAPTURE_CHANNELS; 71 | } 72 | else 73 | { 74 | channels = PLAYBACK_CHANNELS; 75 | } 76 | if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, channels)) < 0) 77 | { 78 | fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err)); 79 | exit(1); 80 | } 81 | 82 | if ((err = snd_pcm_hw_params(handle, hw_params)) < 0) 83 | { 84 | fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err)); 85 | exit(1); 86 | } 87 | 88 | snd_pcm_hw_params_free(hw_params); 89 | 90 | if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) 91 | { 92 | fprintf(stderr, "cannot allocate software parameters structure(%s)\n", 93 | snd_strerror(err)); 94 | return err; 95 | } 96 | if ((err = snd_pcm_sw_params_current(handle, sw_params)) < 0) 97 | { 98 | fprintf(stderr, "cannot initialize software parameters structure(%s)\n", 99 | snd_strerror(err)); 100 | return err; 101 | } 102 | if ((err = snd_pcm_sw_params_set_avail_min(handle, sw_params, AVAILABLE_MIN)) < 0) 103 | { 104 | fprintf(stderr, "cannot set minimum available count(%s)\n", 105 | snd_strerror(err)); 106 | return err; 107 | } 108 | if ((err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, 0U)) < 0) 109 | { 110 | fprintf(stderr, "cannot set start mode(%s)\n", 111 | snd_strerror(err)); 112 | return err; 113 | } 114 | if ((err = snd_pcm_sw_params(handle, sw_params)) < 0) 115 | { 116 | fprintf(stderr, "cannot set software parameters(%s)\n", 117 | snd_strerror(err)); 118 | return err; 119 | } 120 | return 0; 121 | } 122 | 123 | /** 124 | * Start stream 125 | */ 126 | void start_stream(snd_pcm_t *_playback_handle, 127 | snd_pcm_t *_capture_handle, 128 | stream_params *params) 129 | { 130 | // Setup buffers 131 | audio_sample *in_buffer; 132 | audio_sample *out_buffer; 133 | audio_sample *processed_out_buffer; 134 | 135 | int in_buffer_samples = FRAMES_PER_BUFFER * CAPTURE_CHANNELS; 136 | in_buffer = (audio_sample *)malloc(in_buffer_samples * sizeof(audio_sample)); 137 | 138 | int out_buffer_samples = FRAMES_PER_BUFFER * PLAYBACK_CHANNELS; 139 | out_buffer = (audio_sample *)malloc(out_buffer_samples * sizeof(audio_sample)); 140 | processed_out_buffer = (audio_sample *)malloc(out_buffer_samples * sizeof(audio_sample)); 141 | 142 | ring_buffer_t ring_buffer = {}; 143 | ring_buffer_init(&ring_buffer, RING_BUFFER_SIZE, "stream"); 144 | 145 | // Setup delay processor 146 | setup_processor(SAMPLE_RATE, params->delay_time, params->dry_wet, params->feedback); 147 | 148 | // Start stream loop 149 | int err; 150 | int avail; 151 | while (1) 152 | { 153 | if ((err = snd_pcm_wait(_capture_handle, 1000)) < 0) 154 | { 155 | fprintf(stderr, "poll failed(%s)\n", strerror(errno)); 156 | break; 157 | } 158 | 159 | avail = snd_pcm_avail_update(_capture_handle); 160 | if (avail > 0) 161 | { 162 | if (avail > FRAMES_PER_BUFFER) 163 | avail = FRAMES_PER_BUFFER; 164 | 165 | snd_pcm_readi(_capture_handle, in_buffer, avail); 166 | 167 | // Save samples in our ring buffer 168 | ring_buffer_put(&ring_buffer, in_buffer, avail); 169 | } 170 | 171 | if ((err = snd_pcm_wait(_playback_handle, 1000)) < 0) 172 | { 173 | fprintf(stderr, "poll failed(%s)\n", strerror(errno)); 174 | break; 175 | } 176 | 177 | avail = snd_pcm_avail_update(_playback_handle); 178 | if (avail > 0) 179 | { 180 | if (avail > FRAMES_PER_BUFFER) 181 | avail = FRAMES_PER_BUFFER; 182 | 183 | // Get samples from our ring buffer 184 | ring_buffer_get(&ring_buffer, out_buffer, avail); 185 | 186 | // Process signal before it is written 187 | process_buffer(out_buffer, processed_out_buffer, avail); 188 | 189 | // Write it 190 | snd_pcm_writei(_playback_handle, processed_out_buffer, avail); 191 | } 192 | } 193 | 194 | ring_buffer_destroy(&ring_buffer); 195 | } 196 | --------------------------------------------------------------------------------