diff --git a/src/alsa.c b/src/alsa.c new file mode 100644 index 0000000..f938c9d --- /dev/null +++ b/src/alsa.c @@ -0,0 +1,159 @@ +#include "settings.h" + +/** TODO + * 1. Convert audio_data in proper buffers + * 2. Treat stream as mono + * 3. Modify main_event to support polling from this audio interface API +*/ + +static void initialize_audio_parameters(snd_pcm_t** handle, struct audio_data* audio, +snd_pcm_uframes_t* frames) { + + // open alsa device to capture + int err = snd_pcm_open(handle, audio->source, SND_PCM_STREAM_CAPTURE, 0); + if (err < 0) { + fprintf(stderr, "error opening stream: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + + snd_pcm_hw_params_t* params; + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(*handle, params); + + // interleaved + snd_pcm_hw_params_set_access(*handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + + // set 16bit + snd_pcm_hw_params_set_format(*handle, params, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_channels(*handle, params, ALSA_CHANNELS); + + // set sample rate + unsigned int sample_rate = ALSA_SAMPLE_RATE; + snd_pcm_hw_params_set_rate_near(*handle, params, &sample_rate, NULL); + + // number of frames pr read + snd_pcm_hw_params_set_period_size_near(*handle, params, frames, NULL); + + if((err = snd_pcm_hw_params(*handle, params)) < 0); // attempting to set params + fprintf(stderr, "[ALSA]: Cannot set parameters: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + + if ((err = snd_pcm_prepare (*handle)) < 0) { + fprintf (stderr, "[ALSA] Cannot prepare audio interface: (%s)\n", snd_strerror (err)); + exit (EXIT_FAILURE); + } + + // getting actual format + snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)&sample_rate); + + // converting result to number of bits + if (sample_rate <= 5) audio->format = 16; + else if (sample_rate <= 9 ) audio->format = 24; + else audio->format = 32; + + snd_pcm_hw_params_get_rate(params, &audio->rate, NULL); + snd_pcm_hw_params_get_period_size(params, frames, NULL); +} + +static int get_certain_frame(signed char* buffer, int buffer_index, int adjustment) { + // using the 10 upper bits this would give me a vert res of 1024, enough... + int temp = buffer[buffer_index + adjustment - 1] << 2; + int lo = buffer[buffer_index + adjustment - 2] >> 6; + if (lo < 0) + lo = abs(lo) + 1; + if (temp >= 0) + temp += lo; + else + temp -= lo; + return temp; +} + +static void fill_audio_outs(struct audio_data* audio, signed char* buffer, +const int size) { + int radj = audio->format / 4; // adjustments for interleaved + int ladj = audio->format / 8; + static int audio_out_buffer_index = 0; + // sorting out one channel and only biggest octet + for (int buffer_index = 0; buffer_index < size; buffer_index += ladj * 2) { + // first channel + int tempr = get_certain_frame(buffer, buffer_index, radj); + // second channel + int templ = get_certain_frame(buffer, buffer_index, ladj); + + // mono: adding channels and storing it in the buffer + if (audio->channels == 1) + audio->audio_out_l[audio_out_buffer_index] = (templ + tempr) / 2; + else { // stereo storing channels in buffer + audio->audio_out_l[audio_out_buffer_index] = templ; + audio->audio_out_r[audio_out_buffer_index] = tempr; + } + + ++audio_out_buffer_index; + audio_out_buffer_index %= 2048; + } +} + +#define FRAMES_NUMBER 256 + + +void* input_alsa(void* buffer) { + int err; + snd_pcm_sframes_t* audio = (snd_pcm_sframes_t*)buffer; + snd_pcm_t* handle; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; + snd_pcm_uframes_t frames = FRAMES_NUMBER; + + initialize_audio_parameters(&handle, audio, &frames); + snd_pcm_get_params(handle, &buffer_size, &period_size); + + int16_t buf[period_size]; + frames = period_size / ((audio->format / 8) * ALSA_CHANNELS); + //printf("period size: %lu\n", period_size); + //exit(0); + + // frames * bits/8 * channels + //const int size = frames * (audio->format / 8) * ALSA_CHANNELS; + signed char* buffer = malloc(period_size); + int n = 0; + + while (1) { + switch (audio->format) { + case 16: + err = snd_pcm_readi(handle, buf, frames); + for (int i = 0; i < frames * 2; i += 2) { + buffer[n] = (buf[i] + buf[i + 1]) / 2; + break; + default: + err = snd_pcm_readi(handle, buffer, frames); + fill_audio_outs(audio, buffer, period_size); + break; + } + + if (err == -EPIPE) { + /* EPIPE means overrun */ + #ifdef DEBUG + fprintf(stderr, "overrun occurred\n"); + #endif + snd_pcm_prepare(handle); + } else if (err < 0) { + #ifdef DEBUG + fprintf(stderr, "error from read: %s\n", snd_strerror(err)); + #endif + } else if (err != (int)frames) { + #ifdef DEBUG + fprintf(stderr, "short read, read %d %d frames\n", err, (int)frames); + #endif + } + + + + + if (audio->terminate == 1) { + free(buffer); + snd_pcm_close(handle); + return NULL; + } + } +} diff --git a/src/alsa.h b/src/alsa.h new file mode 100644 index 0000000..97e868f --- /dev/null +++ b/src/alsa.h @@ -0,0 +1,3 @@ +#pragma once + +void* input_alsa(void* data); diff --git a/src/fft.c b/src/fft.c index 46209f3..1c2c76a 100644 --- a/src/fft.c +++ b/src/fft.c @@ -1,4 +1,5 @@ #include "fft.h" +#include "settings.h" /* if flag is EVEN (0), it takes only the even elements * otherwise if flag is ODD (1) it takes only the odd ones @@ -120,12 +121,12 @@ int NADJ=4; int boost=8; step=1; + for(i=0; i= 0 && i+NADJ < k && \ fftBuf[i] >= fftBuf[i-NADJ] + 2 && \ fftBuf[i] >= fftBuf[i+NADJ] + 2) { fftBuf[i] += boost; - printf("ciao"); for(j=1; jsong && status->song->duration_sec) { print_rate_info(sampleRate, nsamples, maxC, status->song->duration_sec); + cnt_over = 0; } - print_mpd_status(status, maxC, statusHeight+maxR/2+maxR/6, statusCol); + print_mpd_status(status, maxC, statusHeight+maxR/6, statusCol); } #endif + box(mainwin, 0, 0); // refresh screen refresh(); getmaxyx(stdscr, maxR, maxC); @@ -254,6 +254,9 @@ free(fftEHist); free(sEnergy); close(fifo); + if(cnt_over) return 1; + + return 0; } int @@ -271,6 +274,7 @@ if((mainwin = curses_init()) == NULL){ exit(EXIT_FAILURE); } + #ifdef STATUS_CHECK setlocale(LC_ALL, ""); import_var_from_settings(); @@ -288,10 +292,14 @@ // call the fifo processor - main_event(fifo); + int res = main_event(fifo, mainwin); // free resources endwin(); delwin(mainwin); + + if(res) fprintf(stderr, "No data in %s\nExited.\n", MPD_FIFO); + else fprintf(stdout, "Received exit command.\n"); + return 0; } diff --git a/src/settings.h b/src/settings.h index 0bc447c..697bc3c 100644 --- a/src/settings.h +++ b/src/settings.h @@ -1,29 +1,4 @@ -/* - **** MPD options -**/ -#ifdef STATUS_CHECK - -// same of mpd.conf -static char defHost[] = "localhost"; - -static unsigned int defPort = 6600; - -// database location, needed for libmpdclient -static char defDBlocation[] = "~/.mpd/mpd.db"; - -// timeout for MPD connection to fail -#define TIMEOUT 1000 - -#endif -/* - **** VISUALIZER options -**/ - -// define characters used while printing on screen -// FULL == 1 (char is set) -// EMPTY == 0 (char is not set) -#define FULL 'X' -#define EMPTY '.' +/* =========== Generic Options ============ */ // path to the MPD named pipe // (defined: 'mpd.conf') @@ -39,6 +14,48 @@ // set to 0 to disable #define ADAPTIVE_SAMPLING 1 + +/* =========== MPD Options ============ */ + +#ifdef STATUS_CHECK + +// same of mpd.conf +static char defHost[] = "localhost"; + +static unsigned int defPort = 6600; + +// database location, needed for libmpdclient +static char defDBlocation[] = "~/.mpd/mpd.db"; + +// timeout for MPD connection to fail +#define TIMEOUT 1000 + +#endif + +/* =========== Visualizer Options ============ */ + +// define characters used while printing on screen +// FULL == 1 (char is set) +// EMPTY == 0 (char is not set) +// +// PRESET 1: vertical sharp +//#define FULL '|' +//#define HALFFULL '|' +//#define HALFEMPTY '.' +//#define EMPTY ' ' + +//// PRESET 2: shadow +#define FULL ':' +#define HALFFULL '|' +#define HALFEMPTY ' ' +#define EMPTY '.' + +// PRESET 3: colorful +//#define FULL 'X' +//#define HALFFULL 'x' +//#define HALFEMPTY '^' +//#define EMPTY '.' + // seconds between each mpd status refresh #define STATUS_REFRESH 1 @@ -50,14 +67,22 @@ // etc (accuracy decreases visibly after niceness = 4) #define NICENESS 0 +// define output window +// roughly corresponds to the number of bars of the display replicated +#define FOCUS 2 + // subtracted to each component (visualized column) // adjust according to screen height -#define Y_CORRECTION 28 +#define Y_CORRECTION 25 // adjust lateral shift on screen // some optimal values are provided // adjust based on terminal dimensions - +#define X_CORRECTION 1 // 1280x1024 screens (5:4) //#define X_CORRECTION 8 // 1920x1080 screens (16:9) -#define X_CORRECTION 1 // 1280x1024 screens (5:4) +/* =========== ALSA Support (WIP) ============ */ + +#define USE_ALSA +#define ALSA_CHANNELS 1 +#define ALSA_SAMPLE_RATE 44100 diff --git a/src/utils_curses.c b/src/utils_curses.c index 8289361..8bbbf9f 100644 --- a/src/utils_curses.c +++ b/src/utils_curses.c @@ -47,30 +47,34 @@ case CURVE: if (row > maxR-l && row < maxR-l/3) { // center of the screen mvaddch(row, col, FULL); - } else { + } else if (row == maxR-l) { // center of the screen + mvaddch(row, col, HALFFULL); + } else if (row == maxR-l/3) { // center of the screen + mvaddch(row, col, HALFEMPTY); + } else if(EMPTY != ' ') { mvaddch(row, col, EMPTY); } break; case MOUTH: - if (row > l && row < maxR-l/3) { // center of the screen - mvaddch(row, col, EMPTY); - } else { + if (!(row > l && row < maxR-l/3)) { // center of the screen mvaddch(row, col, FULL); + } else if(EMPTY != ' ') { + mvaddch(row, col, EMPTY); } break; case MOUTH_REV: if (row > l && row < maxR-l/3) { // center of the screen mvaddch(row, col, FULL); - } else { + } else if(EMPTY != ' ') { mvaddch(row, col, EMPTY); } break; case LINE: if (row > (maxR-l) && row < maxR) { // center of the screen mvaddch(row, col, FULL); - } else { + } else if(EMPTY != ' ') { mvaddch(row, col, EMPTY); } break; @@ -81,9 +85,9 @@ } else if (seed < 28 && seed > 24) { mvaddch(row,col,EMPTY); } else if (seed < 14 && seed > 10) { - mvaddch(row,col,EMPTY); + mvaddch(row,col,HALFEMPTY); } else if (seed < 10) { - mvaddch(row,col,FULL); + mvaddch(row,col,HALFFULL); } else { mvaddch(row,col,' '); }