| |
| #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; |
| } |