Программирование alsa (щелкает что-то)

Модератор: Модераторы разделов

Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Программирование alsa

Сообщение eddy »

Пытаюсь написать программку, генерирующую синусоиду (а вообще - еще и другие сигналы). На OSS все работало, но не факт, что oss будет поддерживаться в дальнейшем, поэтому пытаюсь сделать аналог на ALSA.
Компилирую, запускаю - раздаются какие-то щелчки и гул. Подскажите, пожалуйста, кто программировал на ALSA, как сгенерировать нормальную синусоиду? Может быть, здесь:
s1 = c1+A1*cos((time+((double)i)*rate)*_2pi*freq1);
что-то не так?

Код: Выделить всё

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include <alsa/asoundlib.h>
#include <sys/time.h>
#include <math.h>
static char *device = "plughw:0,0"; /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; /* sample format */
#define METHOD SND_PCM_ACCESS_RW_INTERLEAVED
static unsigned int rate = 44100; /* stream rate */
static unsigned int channels = 2; /* count of channels */
static unsigned int period_time; /* period time in us */
double freq1 = 440; /* sinusoidal wave frequency in Hz */
double freq2 = 440; /* sinusoidal wave frequency in Hz */
short c1 = 0, c2 = 0, A1 = 32700, A2 = 32700;
int size;
static int verbose = 0; /* verbose flag */
static snd_output_t *output = NULL;
double dtime(){
    struct timeval ct;
    struct timezone tz;
    gettimeofday(&ct, &tz);
    return (ct.tv_sec + ct.tv_usec/1e6);
}
static int xrun_recovery(snd_pcm_t *handle, int err){
    if (err == -EPIPE) { /* under-run */
        err = snd_pcm_prepare(handle);
        if (err < 0)
         printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
        return 0;
    } else if (err == -ESTRPIPE) {
        while ((err = snd_pcm_resume(handle)) == -EAGAIN)
         usleep(1); /* wait until the suspend flag is released */
        if (err < 0) {
         err = snd_pcm_prepare(handle);
         if (err < 0)
         printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
        }
        return 0;
    }
    return err;
}
static int write_loop(snd_pcm_t *handle_o){
    double time0, time, _2pi = 2.*M_PI;
    unsigned char tmp;
    int i, s1, s2, _ss;
    snd_pcm_sframes_t err;
    double rnd1, rnd2, rate;
    time0 = dtime();
    char *rdata = (char *) malloc(size);
    _ss = size / 4;
    rate = period_time / _ss;
    short *data = (short*) rdata;
    snd_pcm_nonblock(handle_o, 1);
    while(1){
        time = dtime() - time0;
        for(i = 0; i < _ss; i+=2){
            s1 = c1+A1*cos((time+((double)i)*rate)*_2pi*freq1);
            s2 = c2+A2*cos((time+((double)i)*rate)*_2pi*freq2);
            if(s1 < -32700)    data[i] = -32700;
            else if(s1 > 32700) data[i] = 32700;
            else data[i] = s1;
            if(s2 < -32700)    data[i+1] = -32700;
            else if(s2 > 32700) data[i+1] = 32700;
            else data[i+1] = s2;
        }
        err = snd_pcm_writei(handle_o, rdata, size);
        if (err < 0)
            err = snd_pcm_recover(handle_o, err, 0);
        if (err < 0){
            printf("snd_pcm_writei failed\n");
            break;
        }
    }
}
int main(int argc, char *argv[]){
    snd_pcm_t *handle_o;
    int err, morehelp;
    snd_pcm_hw_params_t *hwparams_o;
    snd_pcm_sw_params_t *swparams_o;
    snd_pcm_uframes_t frames;
    unsigned int chn, val;
    if(argc==2) freq1 = freq2 = atof(argv[1]);
    else if(argc == 3){
        freq1 = atof(argv[1]);
        freq2 = atof(argv[2]);
    }
    printf("Playback device is %s\n", device);
    printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
    printf("Sine wave rate is (%.4f/%.4f)Hz\n", freq1, freq2);
    if ((err = snd_pcm_open(&handle_o, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        printf("Playback open error: %s\n", snd_strerror(err));
        return 0;
    }
    snd_pcm_hw_params_alloca(&hwparams_o);
    snd_pcm_hw_params_any(handle_o, hwparams_o);
    snd_pcm_hw_params_set_access(handle_o, hwparams_o, METHOD);
    snd_pcm_hw_params_set_format(handle_o, hwparams_o, format);
    snd_pcm_hw_params_set_channels(handle_o, hwparams_o, 2);
    val = 44100;
    snd_pcm_hw_params_set_rate_near(handle_o, hwparams_o, &val, &err);
    frames = 128;
    snd_pcm_hw_params_set_period_size_near(handle_o, hwparams_o, &frames, &err);
    printf("set period size = %d: %s\n", frames, snd_strerror(err));
    err = snd_pcm_hw_params(handle_o, hwparams_o);
    if (err < 0) {
        printf("unable to set hw parameters: %s\n", snd_strerror(err));
        exit(1);
    }
    snd_pcm_hw_params_get_period_size(hwparams_o, &frames, &err);
    size = frames * 4;
    snd_pcm_hw_params_get_period_time(hwparams_o, &period_time, &err);
    printf("Period size: %dx4, period time: %d\n", frames, period_time);
    if (verbose > 0){
        snd_pcm_dump(handle_o, output);
    }
    snd_pcm_prepare(handle_o);
    err = write_loop(handle_o);
    if (err < 0)
        printf("Transfer failed: %s\n", snd_strerror(err));
    snd_pcm_close(handle_o);
    return 0;
}
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
greatpower
Сообщения: 15
ОС: slackware

Re: Программирование alsa

Сообщение greatpower »

У тебя генерируется не синусоида.
Можешь результат проверить в Hex редакторе.

Записать звуковой поток можно так :

Пропатчил твой сурс код

Код: Выделить всё

// static char *device = "plughw:0,0"; /* playback device */
static char *device = "savetofile"; /* playback device */


Создал временную папку

Код: Выделить всё

mkdir $HOME/Temp


~/.asoundrc

Код: Выделить всё

pcm.savetofile {
    type file
    slave  {
                pcm "hw:0,0"
                }
    file /home/LOGIN-NAME/Temp/ALSA_savetofile.raw
    }


Проверить что записалось
"play alsa save.sh"

Код: Выделить всё

#!/bin/sh

play -r 44100 -b 16 -c 2 -e signed ALSA_savetofile.raw


Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: Программирование alsa

Сообщение eddy »

Спасибо, на ЛОРе мне уже помогли :)
Все-таки, по сравнению с OSS ALSA - жутко неудобная штука. Постоянно ей хочется, чтобы буфер был заполнен... Чтобы один раз в несколько секунд менять напряжение на ЦАПе, в OSS достаточно просто прописывать новое значение в /dev/dsp, в ALSA же надо еще и пошаманить.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали: