| #ifdef HAVE_CONFIG_H | |
| #include "config.h" | |
| #endif | |
| #include <unistd.h> | |
| #include <fcntl.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <sys/ioctl.h> | |
| #include <glib.h> | |
| #ifdef HAVE_OSS_INCLUDE_IN_SYS | |
| # include <sys/soundcard.h> | |
| #else | |
| # ifdef HAVE_OSS_INCLUDE_IN_ROOT | |
| # include <soundcard.h> | |
| # else | |
| # ifdef HAVE_OSS_INCLUDE_IN_MACHINE | |
| # include <machine/soundcard.h> | |
| # else | |
| # error "What to include?" | |
| # endif /* HAVE_OSS_INCLUDE_IN_MACHINE */ | |
| # endif /* HAVE_OSS_INCLUDE_IN_ROOT */ | |
| #endif /* HAVE_OSS_INCLUDE_IN_SYS */ | |
| typedef struct _Probe Probe; | |
| struct _Probe | |
| { | |
| int fd; | |
| int format; | |
| int n_channels; | |
| GArray *rates; | |
| int min; | |
| int max; | |
| }; | |
| typedef struct _Range Range; | |
| struct _Range | |
| { | |
| int min; | |
| int max; | |
| }; | |
| static gboolean probe_check (Probe * probe); | |
| static int check_rate (Probe * probe, int irate); | |
| static void add_range (GQueue * queue, int min, int max); | |
| static void add_rate (GArray * array, int rate); | |
| static int int_compare (gconstpointer a, gconstpointer b); | |
| int | |
| main (int argc, char *argv[]) | |
| { | |
| int fd; | |
| int i; | |
| Probe *probe; | |
| fd = open ("/dev/dsp", O_RDWR); | |
| if (fd < 0) { | |
| perror ("/dev/dsp"); | |
| exit (1); | |
| } | |
| probe = g_new0 (Probe, 1); | |
| probe->fd = fd; | |
| probe->format = AFMT_S16_LE; | |
| probe->n_channels = 2; | |
| probe_check (probe); | |
| g_array_sort (probe->rates, int_compare); | |
| for (i = 0; i < probe->rates->len; i++) { | |
| g_print ("%d\n", g_array_index (probe->rates, int, i)); | |
| } | |
| g_array_free (probe->rates, TRUE); | |
| g_free (probe); | |
| #if 0 | |
| probe = g_new0 (Probe, 1); | |
| probe->fd = fd; | |
| probe->format = AFMT_S16_LE; | |
| probe->n_channels = 1; | |
| probe_check (probe); | |
| for (i = 0; i < probe->rates->len; i++) { | |
| g_print ("%d\n", g_array_index (probe->rates, int, i)); | |
| } | |
| probe = g_new0 (Probe, 1); | |
| probe->fd = fd; | |
| probe->format = AFMT_U8; | |
| probe->n_channels = 2; | |
| probe_check (probe); | |
| for (i = 0; i < probe->rates->len; i++) { | |
| g_print ("%d\n", g_array_index (probe->rates, int, i)); | |
| } | |
| probe = g_new0 (Probe, 1); | |
| probe->fd = fd; | |
| probe->format = AFMT_U8; | |
| probe->n_channels = 1; | |
| probe_check (probe); | |
| for (i = 0; i < probe->rates->len; i++) { | |
| g_print ("%d\n", g_array_index (probe->rates, int, i)); | |
| } | |
| #endif | |
| return 0; | |
| } | |
| static gboolean | |
| probe_check (Probe * probe) | |
| { | |
| Range *range; | |
| GQueue *ranges; | |
| int exact_rates = 0; | |
| gboolean checking_exact_rates = TRUE; | |
| int n_checks = 0; | |
| gboolean result = TRUE; | |
| ranges = g_queue_new (); | |
| probe->rates = g_array_new (FALSE, FALSE, sizeof (int)); | |
| probe->min = check_rate (probe, 1000); | |
| n_checks++; | |
| probe->max = check_rate (probe, 100000); | |
| n_checks++; | |
| add_range (ranges, probe->min + 1, probe->max - 1); | |
| while ((range = g_queue_pop_head (ranges))) { | |
| int min1; | |
| int max1; | |
| int mid; | |
| int mid_ret; | |
| g_print ("checking [%d,%d]\n", range->min, range->max); | |
| mid = (range->min + range->max) / 2; | |
| mid_ret = check_rate (probe, mid); | |
| n_checks++; | |
| if (mid == mid_ret && checking_exact_rates) { | |
| int max_exact_matches = 100; | |
| exact_rates++; | |
| if (exact_rates > max_exact_matches) { | |
| g_print ("got %d exact rates, assuming all are exact\n", | |
| max_exact_matches); | |
| result = FALSE; | |
| g_free (range); | |
| break; | |
| } | |
| } else { | |
| checking_exact_rates = FALSE; | |
| } | |
| /* Assume that the rate is arithmetically rounded to the nearest | |
| * supported rate. */ | |
| if (mid == mid_ret) { | |
| min1 = mid - 1; | |
| max1 = mid + 1; | |
| } else { | |
| if (mid < mid_ret) { | |
| min1 = mid - (mid_ret - mid); | |
| max1 = mid_ret + 1; | |
| } else { | |
| min1 = mid_ret - 1; | |
| max1 = mid + (mid - mid_ret); | |
| } | |
| } | |
| add_range (ranges, range->min, min1); | |
| add_range (ranges, max1, range->max); | |
| g_free (range); | |
| } | |
| while ((range = g_queue_pop_head (ranges))) { | |
| g_free (range); | |
| } | |
| g_queue_free (ranges); | |
| return result; | |
| } | |
| static void | |
| add_range (GQueue * queue, int min, int max) | |
| { | |
| g_print ("trying to add [%d,%d]\n", min, max); | |
| if (min <= max) { | |
| Range *range = g_new0 (Range, 1); | |
| range->min = min; | |
| range->max = max; | |
| g_queue_push_tail (queue, range); | |
| //g_queue_push_head (queue, range); | |
| } | |
| } | |
| static int | |
| check_rate (Probe * probe, int irate) | |
| { | |
| int rate; | |
| int format; | |
| int n_channels; | |
| rate = irate; | |
| format = probe->format; | |
| n_channels = probe->n_channels; | |
| ioctl (probe->fd, SNDCTL_DSP_SETFMT, &format); | |
| ioctl (probe->fd, SNDCTL_DSP_CHANNELS, &n_channels); | |
| ioctl (probe->fd, SNDCTL_DSP_SPEED, &rate); | |
| g_print ("rate %d -> %d\n", irate, rate); | |
| if (rate == irate - 1 || rate == irate + 1) { | |
| rate = irate; | |
| } | |
| add_rate (probe->rates, rate); | |
| return rate; | |
| } | |
| static void | |
| add_rate (GArray * array, int rate) | |
| { | |
| int i; | |
| int val; | |
| for (i = 0; i < array->len; i++) { | |
| val = g_array_index (array, int, i); | |
| if (val == rate) | |
| return; | |
| } | |
| g_print ("supported rate: %d\n", rate); | |
| g_array_append_val (array, rate); | |
| } | |
| static int | |
| int_compare (gconstpointer a, gconstpointer b) | |
| { | |
| const int *va = (const int *) a; | |
| const int *vb = (const int *) b; | |
| if (*va < *vb) | |
| return -1; | |
| if (*va > *vb) | |
| return 1; | |
| return 0; | |
| } |