C 如何使用ALSA在缓冲区中录制声音

C 如何使用ALSA在缓冲区中录制声音,c,audio,while-loop,recording,alsa,C,Audio,While Loop,Recording,Alsa,我开始学习linux和ALSA,我想知道是否有办法将我从麦克风a录制的声音直接存储到缓冲区。我在这里读到了如何制作我的录音程序。但我需要的是更复杂一点。我需要录音直到我按了一个键。我之所以需要它,是因为我正在摆弄一个树莓(上面有debian),想看看能否把它变成一个声音监控/检测设备 我现在的主要问题是,当我尝试使用它来记录(./Rec>name.raw)时,它什么也不做。它只是输出一个空的.raw文件 #define ALSA_PCM_NEW_HW_PARAMS_API #include &l

我开始学习linux和ALSA,我想知道是否有办法将我从麦克风a录制的声音直接存储到缓冲区。我在这里读到了如何制作我的录音程序。但我需要的是更复杂一点。我需要录音直到我按了一个键。我之所以需要它,是因为我正在摆弄一个树莓(上面有debian),想看看能否把它变成一个声音监控/检测设备

我现在的主要问题是,当我尝试使用它来记录(./Rec>name.raw)时,它什么也不做。它只是输出一个空的.raw文件

#define ALSA_PCM_NEW_HW_PARAMS_API
#include <termios.h>
#include <alsa/asoundlib.h>

struct termios stdin_orig;  // Structure to save parameters

void term_reset() {
        tcsetattr(STDIN_FILENO,TCSANOW,&stdin_orig);
        tcsetattr(STDIN_FILENO,TCSAFLUSH,&stdin_orig);
}

void term_nonblocking() {
        struct termios newt;
        tcgetattr(STDIN_FILENO, &stdin_orig);
        fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // non-blocking
        newt = stdin_orig;
        newt.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &newt);

        atexit(term_reset);
}

int main() {
  int key=0;
  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;

  /* 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);
  }

  /* Allocate a hardware parameters object. */
  snd_pcm_hw_params_alloca(&params);

  /* Fill it in with default values. */
  snd_pcm_hw_params_any(handle, params);

  /* Set the desired hardware parameters. */

  /* Interleaved mode */
  snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

  /* Signed 16-bit little-endian format */
  snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

  /* One channel (mono) */
  snd_pcm_hw_params_set_channels(handle, params, 1);

  /* 16000 bits/second sampling rate */
  val = 16000;
  snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);

  /* Set period size to 2048 frames. */
  frames = 2048;
  snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

  /* Write the parameters to the driver */
  rc = snd_pcm_hw_params(handle, params);
  if (rc < 0) {
    fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
    exit(1);
  }

  /* Use a buffer large enough to hold one period */
  snd_pcm_hw_params_get_period_size(params, &frames, &dir);
  size = frames * 2; /* 2 bytes/sample, 1 channels */
  buffer = (char *) malloc(size);

  while (key == 0) 
  {

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

    rc = write(1, buffer, size);

    if (rc != size)
      fprintf(stderr, "short write: wrote %d bytes\n", rc);
    key = getchar();
  }

  snd_pcm_drain(handle);
  snd_pcm_close(handle);
  free(buffer);

  return 0;
}
#定义ALSA_PCM_NEW_HW_PARAMS_API
#包括
#包括
struct termios stdin_orig;//结构来保存参数
无效项_重置(){
tSetAttr(标准文件号、标准名称和标准原件);
tcsetattr(标准文件号、TCSAFLUSH和标准原始文件);
}
无效项_非阻塞(){
结构术语;
tcgetattr(标准文件号和标准原件);
fcntl(标准文件号、设置符、非块);//非块
牛顿=标准偏差;
newt.c|lflag&=~(ICANON | ECHO);
tcsetattr(标准文件号、TCSANOW和NETT);
atexit(期限重置);
}
int main(){
int键=0;
长环;
int rc;
整数大小;
snd_pcm_t*手柄;
snd_pcm_hw_params_t*params;
无符号int-val;
int dir;
snd_pcm_uframes_t frames;
字符*缓冲区;
/*打开PCM设备进行记录(捕获)*/
rc=snd_pcm_open(&handle,“default”,snd_pcm_STREAM_CAPTURE,0);
if(rc<0){
fprintf(标准,“无法打开pcm设备:%s\n”,snd_strerror(rc));
出口(1);
}
/*分配硬件参数对象*/
snd_pcm_hw_params_alloca(¶ms);
/*用默认值填充它*/
snd_pcm_hw_params_any(手柄、参数);
/*设置所需的硬件参数*/
/*交织模式*/
snd_pcm_硬件_参数_设置_访问(句柄、参数、snd_pcm_访问_RW_交错);
/*有符号16位小端点格式*/
snd_pcm_hw_params_set_format(句柄、参数、snd_pcm_format_S16_LE);
/*单声道(单声道)*/
snd_pcm_硬件_参数_设置_通道(句柄,参数,1);
/*16000位/秒采样率*/
val=16000;
snd_pcm_hw_参数_set_rate_near(句柄、参数、&val、&dir);
/*将时段大小设置为2048帧*/
帧=2048;
snd_pcm_hw_params_set_period_size_near(句柄、参数、帧和目录);
/*将参数写入驱动程序*/
rc=snd_pcm_hw_参数(手柄,参数);
if(rc<0){
fprintf(stderr,“无法设置硬件参数:%s\n”,snd_strerror(rc));
出口(1);
}
/*使用足以容纳一个周期的缓冲区*/
snd_pcm_hw_params_get_period_size(参数、帧和目录);
大小=帧*2;/*2字节/样本,1个通道*/
缓冲区=(字符*)malloc(大小);
while(key==0)
{
rc=snd_pcm_readi(句柄、缓冲区、帧);
如果(rc==-EPIPE)
{
/*EPIPE意味着超限*/
fprintf(stderr,“发生溢出\n”);
snd_pcm_准备(手柄);
} 
else if(rc<0)
{
fprintf(标准,“读取错误:%s\n”,snd_strerror(rc));
} 
else if(rc!=(int)帧)
{
fprintf(stderr,“短读,读%d帧\n”,rc);
}
rc=写入(1,缓冲区,大小);
如果(rc!=尺寸)
fprintf(stderr,“短写:写入了%d字节\n”,rc);
key=getchar();
}
snd_pcm_排水管(手柄);
snd_pcm_关闭(手柄);
自由(缓冲);
返回0;
}

下面是我如何使用python实现这一点的。在我的桌面Debian上使用USB Plantronics耳机进行测试。您需要安装
python-qt4
python-pyaudio
软件包才能工作

此外,您还需要将输入设备设置为麦克风。在GNOME中,我通过
System Tools->System Settings
->
Sound
切换输入和输出设备。如果你的树莓上有
Raspbian
,而不是
Debian
,就像我一样,那会更难,因为有LXDE而不是GNOME。您可以使用那里的
alsamixer
和F6按钮来设置声卡,但问题是,ALSA是低级接口,而不是PulseAudio或JACK。您需要一些运气/时间来确保将输入/输出设备切换到麦克风/耳机

如果您使用的是通过Raspberry Pi的插孔输入插入的插孔麦克风,请注意Raspberry的插孔仅用于输入,因此您将无法播放录音,需要一些USB耳机来收听wav

就我个人而言,我觉得ALSA的记录非常糟糕(我想这是故意的),我不喜欢处理它

import pyaudio
import wave
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

# This is Qt part: we create a window, which has a "stop" flag.
# Stop flag defaults to False, but is set to True, when you press a key.
# Value of that flag is checked in main loop and loop exits when flag is True.

app = QApplication(sys.argv)
class MyWindow(QWidget):
    def __init__(self):
        super(QWidget, self).__init__()
        self.stop = False
    def keyPressEvent(self, event):
        print "keyPressedEvent caught!"
        self.stop = True

window = MyWindow()
window.show()

# This is sound processing part: we create an input stream to read from microphone.

p = pyaudio.PyAudio()
stream = p.open(format = p.get_format_from_width(2),
        channels = 2,
        rate=44100,
        input=True,
        output=False,
        frames_per_buffer=1024)

# This is main loop: we iteratively poll audio and gui: audio data are stored in output_buffer,
# whereas gui is checked for stop flag value (if keyPressedEvent happened, flag will be set
# to True and break our main loop).

output_buffer = ""
while True:
    app.processEvents()
    data = stream.read(1024)
    output_buffer += data
    if window.stop: break

stream.stop_stream()
stream.close()

# Here we output contents of output_buffer as .wav file
output_wav = wave.open("output.wav", 'w')
output_wav.setparams((2, 2, 44100, len(output_buffer),"NONE","not compressed"))
output_wav.writeframesraw(output_buffer)

p.terminate()

此代码演示如何在C++循环中从ALSA中捕获:

您可以在此处更改循环以永久记录,替换:

while (N>0){
while (N>0){

现在您需要一些额外的代码。首先,要读取非阻塞字符,请将其添加到文件顶部:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define ngetc(c) (read (0, (c), 1))


难道你不需要在while循环中以非阻塞模式读取键盘,然后退出循环吗?目前,缓冲区在每个时段都会被覆盖。如果要将所有内容都记录到缓冲区中,则必须将缓冲区变大,并按每个周期的大小将缓冲区指针向前移动。
snd\u pcm\u readi
已经做到了这一点。@CL.您能更具体一点吗?还有snd_pcm_readn做什么?
snd_pcm_readi
将样本读入缓冲区。在您展示的程序中,该变量恰巧被命名为
buffer
@CL。是的。。。是的。但是请阅读整个问题。我有点不知所措,不知道如何进行必要的修改,使循环在键盘输入时停止。你能看看这条线吗?告诉我你是否发现了什么问题?@aiureaaindicatotyo好吧,我现在没有永远用C编程,但是在你提供的代码中,
key
变量被设置为0值,在while循环中你说
while(key!=0)
,所以你永远不会进入循环。@Bob。。。亲爱的上帝。非常感谢。我讨厌容易犯的错误。
while (N>0){
while (1){
  int enter=ngetc(&ch);
  if (enter>0)
    break;