2011-08-07 17:46:35 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2011-08-20 11:49:29 +00:00
|
|
|
#include <stdbool.h>
|
2011-08-16 21:29:38 +00:00
|
|
|
#include <string.h>
|
2011-08-07 17:46:35 +00:00
|
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
#include <alsa/asoundlib.h>
|
2012-12-25 20:43:26 +00:00
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
#include <fftw3.h>
|
2011-08-07 17:46:35 +00:00
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
/*
|
|
|
|
* Stuff related to sound card capture
|
|
|
|
*
|
|
|
|
*/
|
2011-08-07 17:46:35 +00:00
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
// Capture fresh PCM data to buffer
|
2011-08-16 21:29:38 +00:00
|
|
|
void readPcm(gint numsamples) {
|
|
|
|
|
|
|
|
int samplesread, i;
|
2012-04-02 08:59:52 +00:00
|
|
|
gint32 tmp[BUFLEN]; // Holds one or two 16-bit channels, will be ANDed to single channel
|
2011-08-16 21:29:38 +00:00
|
|
|
|
|
|
|
samplesread = snd_pcm_readi(pcm_handle, tmp, (PcmPointer == 0 ? BUFLEN : numsamples));
|
|
|
|
|
|
|
|
if (samplesread < numsamples) {
|
2013-01-13 09:40:56 +00:00
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
if (samplesread == -EPIPE) printf("ALSA: buffer overrun\n");
|
2013-01-13 18:49:42 +00:00
|
|
|
else if (samplesread < 0) {
|
|
|
|
printf("ALSA error %d (%s)\n", samplesread, snd_strerror(samplesread));
|
2013-01-13 19:18:10 +00:00
|
|
|
gtk_widget_set_tooltip_text(gui.image_devstatus, "ALSA error");
|
2013-01-13 18:49:42 +00:00
|
|
|
Abort = true;
|
|
|
|
pthread_exit(NULL);
|
|
|
|
}
|
2011-08-16 21:29:38 +00:00
|
|
|
else printf("Can't read %d samples\n", numsamples);
|
2013-01-13 09:40:56 +00:00
|
|
|
|
|
|
|
// On first appearance of error, update the status icon
|
|
|
|
if (!BufferDrop) {
|
|
|
|
gdk_threads_enter();
|
2013-01-13 19:18:10 +00:00
|
|
|
gtk_image_set_from_stock(GTK_IMAGE(gui.image_devstatus),GTK_STOCK_DIALOG_WARNING,GTK_ICON_SIZE_SMALL_TOOLBAR);
|
|
|
|
gtk_widget_set_tooltip_text(gui.image_devstatus, "Device is dropping samples");
|
2013-01-13 09:40:56 +00:00
|
|
|
gdk_threads_leave();
|
|
|
|
BufferDrop = true;
|
|
|
|
}
|
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (PcmPointer == 0) {
|
|
|
|
// Fill buffer on first run
|
2012-03-22 07:44:09 +00:00
|
|
|
for (i=0; i<BUFLEN; i++)
|
|
|
|
PcmBuffer[i] = tmp[i] & 0xffff;
|
2011-08-16 21:29:38 +00:00
|
|
|
PcmPointer = BUFLEN/2;
|
|
|
|
} else {
|
|
|
|
for (i=0; i<BUFLEN-numsamples; i++) PcmBuffer[i] = PcmBuffer[i+numsamples];
|
2011-08-20 05:51:27 +00:00
|
|
|
for (i=0; i<numsamples; i++) {
|
2012-03-22 07:44:09 +00:00
|
|
|
PcmBuffer[BUFLEN-numsamples+i] = tmp[i] & 0xffff;
|
2011-08-20 05:51:27 +00:00
|
|
|
// Keep track of max power for VU meter
|
2012-03-22 07:44:09 +00:00
|
|
|
if (abs(PcmBuffer[i]) > MaxPcm) MaxPcm = abs(PcmBuffer[i]);
|
2011-08-20 05:51:27 +00:00
|
|
|
}
|
2011-08-16 21:29:38 +00:00
|
|
|
|
|
|
|
PcmPointer -= numsamples;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-01-09 19:02:41 +00:00
|
|
|
void populateDeviceList() {
|
2011-08-08 21:36:47 +00:00
|
|
|
int card;
|
|
|
|
char *cardname;
|
2013-01-09 19:46:39 +00:00
|
|
|
int numcards, row;
|
2011-08-07 17:46:35 +00:00
|
|
|
|
2013-01-09 19:02:41 +00:00
|
|
|
gdk_threads_enter();
|
2013-01-13 19:18:10 +00:00
|
|
|
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(gui.combo_card), "default");
|
2013-01-09 19:02:41 +00:00
|
|
|
gdk_threads_leave();
|
|
|
|
|
2011-08-07 17:46:35 +00:00
|
|
|
numcards = 0;
|
|
|
|
card = -1;
|
2013-01-09 19:02:41 +00:00
|
|
|
row = 0;
|
2011-08-07 17:46:35 +00:00
|
|
|
do {
|
|
|
|
snd_card_next(&card);
|
|
|
|
if (card != -1) {
|
2013-01-09 19:02:41 +00:00
|
|
|
row++;
|
2011-08-07 17:46:35 +00:00
|
|
|
snd_card_get_name(card,&cardname);
|
2013-01-09 19:02:41 +00:00
|
|
|
gdk_threads_enter();
|
2013-01-13 19:18:10 +00:00
|
|
|
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(gui.combo_card), cardname);
|
2013-01-17 06:44:38 +00:00
|
|
|
if (strcmp(cardname,g_key_file_get_string(config,"slowrx","device",NULL)) == 0)
|
2013-01-13 19:18:10 +00:00
|
|
|
gtk_combo_box_set_active(GTK_COMBO_BOX(gui.combo_card), row);
|
2013-01-09 19:02:41 +00:00
|
|
|
|
|
|
|
gdk_threads_leave();
|
2011-08-07 17:46:35 +00:00
|
|
|
numcards++;
|
|
|
|
}
|
|
|
|
} while (card != -1);
|
|
|
|
|
|
|
|
if (numcards == 0) {
|
2013-01-09 19:02:41 +00:00
|
|
|
perror("No sound devices found!\n");
|
|
|
|
exit(EXIT_FAILURE);
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|
2013-01-09 19:02:41 +00:00
|
|
|
|
|
|
|
}
|
2011-08-07 17:46:35 +00:00
|
|
|
|
2013-01-09 19:02:41 +00:00
|
|
|
// Initialize sound card
|
|
|
|
// Return value:
|
|
|
|
// 0 = opened ok
|
|
|
|
// -1 = opened, but suboptimal
|
|
|
|
// -2 = couldn't be opened
|
|
|
|
int initPcmDevice(char *wanteddevname) {
|
2011-08-07 17:46:35 +00:00
|
|
|
|
2013-01-09 19:02:41 +00:00
|
|
|
snd_pcm_hw_params_t *hwparams;
|
|
|
|
char pcm_name[30];
|
|
|
|
unsigned int exact_rate = 44100;
|
|
|
|
int card;
|
|
|
|
bool found;
|
|
|
|
char *cardname;
|
2011-08-07 17:46:35 +00:00
|
|
|
|
2013-01-13 09:40:56 +00:00
|
|
|
BufferDrop = false;
|
|
|
|
|
2013-01-09 19:02:41 +00:00
|
|
|
snd_pcm_hw_params_alloca(&hwparams);
|
2012-12-24 00:15:46 +00:00
|
|
|
|
2013-01-09 19:02:41 +00:00
|
|
|
card = -1;
|
|
|
|
found = false;
|
|
|
|
if (strcmp(wanteddevname,"default") == 0) {
|
|
|
|
found=true;
|
|
|
|
} else {
|
|
|
|
do {
|
|
|
|
snd_card_next(&card);
|
|
|
|
if (card != -1) {
|
|
|
|
snd_card_get_name(card,&cardname);
|
|
|
|
if (strcmp(cardname, wanteddevname) == 0) {
|
|
|
|
found=true;
|
|
|
|
break;
|
|
|
|
}
|
2012-12-24 00:15:46 +00:00
|
|
|
}
|
2013-01-09 19:02:41 +00:00
|
|
|
} while (card != -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
perror("Device disconnected?\n");
|
|
|
|
return(-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(wanteddevname,"default") == 0) {
|
|
|
|
sprintf(pcm_name,"default");
|
|
|
|
} else {
|
|
|
|
sprintf(pcm_name,"hw:%d",card);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (snd_pcm_open(&pcm_handle, pcm_name, SND_PCM_STREAM_CAPTURE, 0) < 0) {
|
|
|
|
perror("ALSA: Error opening PCM device");
|
|
|
|
return(-2);
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Init hwparams with full configuration space */
|
|
|
|
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
|
2013-01-09 19:02:41 +00:00
|
|
|
perror("ALSA: Can not configure this PCM device.");
|
|
|
|
return(-2);
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
|
2013-01-09 19:02:41 +00:00
|
|
|
perror("ALSA: Error setting interleaved access.");
|
|
|
|
return(-2);
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|
|
|
|
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) {
|
2013-01-09 19:02:41 +00:00
|
|
|
perror("ALSA: Error setting format S16_LE.");
|
|
|
|
return(-2);
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|
|
|
|
if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0) < 0) {
|
2013-01-09 19:02:41 +00:00
|
|
|
perror("ALSA: Error setting sample rate.");
|
|
|
|
return(-2);
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|
|
|
|
|
2012-04-02 08:59:52 +00:00
|
|
|
// Try stereo first
|
|
|
|
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2) < 0) {
|
|
|
|
// Fall back to mono
|
|
|
|
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 1) < 0) {
|
2013-01-09 19:02:41 +00:00
|
|
|
perror("ALSA: Error setting channels.");
|
|
|
|
return(-2);
|
2012-03-22 07:44:09 +00:00
|
|
|
}
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|
|
|
|
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
|
2013-01-09 19:02:41 +00:00
|
|
|
perror("ALSA: Error setting HW params.");
|
|
|
|
return(-2);
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
PcmBuffer = calloc( BUFLEN, sizeof(gint16));
|
2013-01-09 19:02:41 +00:00
|
|
|
|
|
|
|
if (exact_rate != 44100) {
|
|
|
|
fprintf(stderr, "ALSA: Got %d Hz instead of 44100. Expect artifacts.\n", exact_rate);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
2011-08-16 21:29:38 +00:00
|
|
|
|
2011-08-07 17:46:35 +00:00
|
|
|
}
|