LINUX.ORG.RU

alsa уровень сигнала


0

1

Как с помощью ALSA API получить уровень сигнала при записи?
Необходимо активировать запись при появлении голоса на входе микрофона, возможно есть другие решения, но я вижу только одно - это задать порог уровня сигнала при превышении,которого бы активировалась запись. Заранее спасибо!



Последнее исправление: Dronnikkl (всего исправлений: 1)

Как я уже ни раз тут говорил, используй Jack, ибо альса слишком уродлива. А по теме: ты получаешь звуковой сигнал в виде массива точек, бери каждую сотую например, и проверяй выше ли значение сигнала в этой точке, чем определённый порог.

kranky ★★★★★
()
Ответ на: комментарий от kranky

Не так уж и уродлива.

По теме: я бы смотрел на уровень частот, соответствующих голосу, а не на отдельные отсчёты.

Zenom ★★★
()
Ответ на: комментарий от drakmail

drakmail Огромное спасибо!!, вот что мне удалось взять из исходников mumble:

          for (i=0;i<iFrameSize;i++)
  sum += static_cast<float>(psMic[i] * psMic[i]);
        dPeakMiic=2i0.0f*log10f( sqrtf(sum / static_cast<float>(iFrameSize)) / 32768.0f);
        if (dPeakMic < -96.0f)
                dPeakMic = -96.0f;

        max = 1;
        for (i=0;i<iFrameSize;i++)
                max = static_cast<short>(abs(psMic[i]) > max ? abs(psMic[i]) : max);
        dMaxMic = max;
        
float level = (g.s.vsVAD == Settings::SignalToNoise) ? fSpeechProb : (1.0f + dPeakMic / 96.0f);

Изменил под свою программу:


#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>
struct wav_header {
char chunk_id[4];
unsigned int chunk_size;
char format[4];
char subchunk1_id[4];
unsigned int subchunk1_size;
unsigned short int audio_format;
unsigned short int channels;
unsigned int sample_rate;
unsigned int byte_range;
unsigned short int block_align;
unsigned short int bits_per_sample;
char subchunk2_id[4];
unsigned int subchunk2_size;
};
int main() {
  long loops;
  int rc;
  int size;
  snd_pcm_t *handle;
  snd_pcm_hw_params_t *params;
  unsigned int val;
  int dir;
  snd_pcm_uframes_t frames;
  char *buffer;
   
   
   
struct wav_header header;
FILE *output;

/* init wave header */
   struct wav_header {
      char chunk_id[4];
      unsigned int chunk_size;
      char format[4];
      char subchunk1_id[4];
      unsigned int subchunk1_size;
      unsigned short int audio_format;
      unsigned short int channels;
      unsigned int sample_rate;
      unsigned int byte_range;
      unsigned short int block_align;
      unsigned short int bits_per_sample;
      char subchunk2_id[4];
      unsigned int subchunk2_size;
   };
memcpy(header.chunk_id,"RIFF",4);
header.chunk_size = 0;
memcpy(header.format, "WAVE",4);
memcpy(header.subchunk1_id, "fmt ", 4);
header.subchunk1_size = 16;
header.audio_format = 1;
header.channels = 2;
header.sample_rate = 44100;
header.bits_per_sample = 16;
header.block_align = header.channels * header.bits_per_sample/8;
header.byte_range = header.sample_rate * header.channels *
header.bits_per_sample/8;
memcpy(header.subchunk2_id, "data", 4);
header.subchunk2_size = 0;
output = fopen("/mnt/record.wav","w+");
fwrite(&header, sizeof(struct wav_header), 1, output);
   
   
  /* Open PCM device for recording (capture). */
  rc = snd_pcm_open(&handle, "default",
                    SND_PCM_STREAM_CAPTURE, 0);
  if (rc < 0) {
    fprintf(stderr,
            "unable to open pcm device: %s\n",
            snd_strerror(rc));
    exit(1);
  }


  snd_pcm_hw_params_alloca(&params);
  snd_pcm_hw_params_any(handle, params);

  snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED);
  snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_S16_LE);
  snd_pcm_hw_params_set_channels(handle, params, 2);
  val = 44100;
  snd_pcm_hw_params_set_rate_near(handle, params,&val, &dir);
  frames = 32;
  snd_pcm_hw_params_set_period_size_near(handle,params, &frames, &dir);
  rc = snd_pcm_hw_params(handle, params);
  if (rc < 0) {
    fprintf(stderr,
            "unable to set hw parameters: %s\n",
            snd_strerror(rc));
    exit(1);
  }
  snd_pcm_hw_params_get_period_size(params,&frames, &dir);
  size = frames * 4; /* 2 bytes/sample, 2 channels */
  buffer = (char *) malloc(size);



  /* пишем 5 секунд */
  snd_pcm_hw_params_get_period_time(params,&val, &dir);
  loops = 5000000 / val;
  snd_output_t *output_stat = NULL;
  snd_pcm_status_t *status_stat;
  rc = snd_output_stdio_attach(&output_stat, stdout, 0);
   if (rc < 0) {
      printf("Output failed: %s\n", snd_strerror(rc));
      return 0;
   }
  while (loops > 0) {
    loops--;
    rc = snd_pcm_readi(handle, buffer, frames);
    if (rc == -EPIPE) {
      /* EPIPE means overrun */
      fprintf(stderr, "overrun occurred\n");
      snd_pcm_prepare(handle);
    } else if (rc < 0) {
      fprintf(stderr,
              "error from read: %s\n",
              snd_strerror(rc));
    } else if (rc != (int)frames) {
      fprintf(stderr, "short read, read %d frames\n", rc);
    }else{
header.subchunk2_size += frames * 4;
fwrite(buffer, frames * 4, 1, output);

                    /*ИЗМЕРЕНИЕ УРОВНЯ СИГНАЛА*/

                     sum=0;
                      for (i=0;i<41;i++){
                            sum += (buffer[i] * buffer[i]);
                      }            
                       dPeakMic=20.0f*log10f(sqrtf(sum /41.) / 32768.0f);
                       if (dPeakMic < -96.0f)
                       dPeakMic = -96.0f;
                       level=1.0f + dPeakMic / 96.0f;
                       printf ("%f \n",level);


}
    
  }

  snd_pcm_drain(handle);
  snd_pcm_close(handle);
/* save rest of the data */
header.chunk_size = ftell(output) - 8;
fseek(output, 0, SEEK_SET);
fwrite(&header, sizeof(struct wav_header), 1, output);
fclose(output);
printf("File %d bytes, audio length: %d samples\n",header.chunk_size,header.subchunk2_size / header.channels / (header.bits_per_sample /8)); 
 free(buffer);

  return 0;
}
Теперь если тишина в фикрофоне то выводятся 0.00000 а при появлении голоса значения увеличиаются, тоесть теперь программа улавливает уровень сигнала.
Четсно, говоря мало что понял из куска кода mumble ) , кто нибудь может объяснить как теоритически у меня вычисляется уровень сигнала, и правильные ли у меня значения для моих параметров alsы?

kranky спасибо!
snd_pcm_readi(handle, buffer, frames); 
у меня точка хранится в buffer правильно?

Zenom спасибо! можно поподробнее, как получить уровень частот соотвествующих голосу из вышепривиденного исходника?

Dronnikkl
() автор топика
Ответ на: комментарий от Dronnikkl

Спектр можно получить с помощью дискретного (в данном случае) преобразования Фурье. Для этого есть хорошая библиотека libfftw. Если интересует несколько конкретных частот, то лучше воспользоваться алгоритмом Герцеля. Он реализуется в несколько строчек. Вот выдрал из какого-то своего исходника стопицотлетней давности:

double goertzel(int rate, int frequency, double* samples, int len) {
        double n_freq = (double) frequency / rate;
        double k = 2 * cos(2 * M_PI * n_freq);
        double s_prev = 0;
        double s_prev2 = 0;
        double s;
        int i;
        for (i = 0; i < len; i++) {
                s = samples[i] + k * s_prev - s_prev2;
                s_prev2 = s_prev;
                s_prev = s;
        }
        return sqrt(s_prev2*s_prev2 + s_prev*s_prev - k*s_prev2*s_prev) / len * 2;
}

Здесь rate — это частота дискретизации, frequency — интересующая нас частота, samples — массив отсчётов, len — длина массива. Функция возвращает абсолютную амплитуду интересующей нас гармоники. Т. е. в децибелы надо переводить самостоятельно.

Но в данном случае, я думаю, Герцель нам не поможет. Слишком много частот надо анализировать, а в такой ситуации БПФ эффективнее.

Дискретное преобразование Фурье даст нам массив комплексных амплитуд гармоник. Первый элемент соответствует гармонике с одним колебанием за период, второй — с двумя колебаниями и т. д. до середины. Элементы во второй половине являются зеркальным отражением первой и не несут смысловой нагрузки. Модуль комплексного числа — это амплитуда, а аргумент — фаза.

Т. е. задача сводится к тому, что нужно проверить, приходится ли гармоника с максимальной амплитудой на часть спектра, соответствующую речи. А также, что за пределами этой части спектра амплитуды гармоник намного меньше, иначе за голос можно принять шум.

И ещё, я думаю, что частота дискретизации в 44100 Гц является перебором в данном случае. 8 КГц вполне должно хватить.

Zenom ★★★
()
Ответ на: комментарий от Zenom

Сложновато) как можно применить Герца, а лучше наверное БПФ в моей программе?
О БПФ при построении спектра слышал, даже библиотеку собрал),но думал есть другие решения-проще

Dronnikkl
() автор топика
Ответ на: комментарий от Dronnikkl

Применить довольно просто. В том месте, где стоит комментарий про измерение уровня сигнала надо скормить буфер libfftw. Здесь я это рассматривать не буду, т. к. наизусть не помню, а у fftw хорошая документация. Допустим, результат преобразования у нас в массиве fourier. Нужно сделать что-то типа такого:

int max_freq = 0;
double max_ampl = 0;
double avg_apl = 0;
double curr_ampl = 0;

for (i = 0; i < buf_size / 2; i++) {
    curr_ampl = cabs(fourier[i]);
    avg_apml += curr_apl * curr_amppl;
    if (curr_ampl > max_ampl) {
        max_freq = i;
        max_ampl = curr_ampl;
    }
}

max_ampl = to_dB(max_ampl)
avg_ampl = to_dB(sqrt(avg_ampl / (buf_size / 2)))

if (max_freq >= VOICE_MIN_FREQ && max_freq <= VOICE_MAX_FREQ && max_ampl - avg_ampl <= 10)
   printf("С нами кто-то говорит!\n")

Надеюсь, в 3 часа ночи ничего не напутал и нигде не накосячил, гг. Но в случае чего пусть товарищи меня поправят.

В этом исходнике я использовал cabs, функцию из complex.h По-моему, результат fftw совместим с тем, что она ожидает увидеть. Далее, to_dB, очевидно, функция для перевода абсолютного значения в децибелы, с ней тоже всё понятно, её надо реализовать. VOICE_MIN_FREQ и VOICE_MAX_FREQ — это не собственно частоты, а номера гармоник этим частотам соответствующих. Думаю, разности максимума и средней амплитуды в 10 децибел будет достаточно для надёжного детектирования голоса, но лучше предусмотреть возможность настройки.

Zenom ★★★
()
Ответ на: комментарий от Zenom

Ага, ну конечно же накосячил.

Во-первых, fftw даёт нам ненормализованные значения, а значит их надо делить на размер буфера пополам. Во-вторых, я сравниваю действующее значение сигнала с амплитудным значением интересующей нас гармоники. И в-третьих, нужно ещё проверить, что амплитуда гармоники с частотой max_freq превышает некий пороговый уровень. Ну и точки с запятой позабывал.

Zenom ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.