ALSA PCM capture, ALSA recoding
https://github.com/muabow/home/tree/main/src/cpp/pcm_capture
Linux ALSA driver를 이용한 audio capture 프로그램 샘플을 작성하였다.
코드 내에 주어진 파라미터 대로 오디오를 읽어 capture event handler를 통해 pcm raw 파일로 저장한다.
동작을 위한 내용은 README.md 를 참고하면 되고,
capture 파라미터의 수정은 src/main.cpp 파일을 참고하면 된다.
C++ Class로 구성하여서 단일 또는 복수개의 instance를 생성하여 N 채널 입력의 처리도 가능하다.
코드와 동작 내용을 포스팅하려 했지만 코드 블록을 썼음에도 너무 길어져서 위의 git repository를 공유한다.
아래는 PCM_CaptureHandler class 내 ALSA parameter를 세팅하는 set_pcm_driver method이다.
ALSA 제어는 어떠한 포맷의 데이터를 어떻게 capture interrupt/playback feeding의 빈도를 맞추어 읽고 쓰는지가 중요하기 때문에 세팅 부분이 가장 중요하다 볼 수 있다.
bool PCM_CaptureHandler::set_pcm_driver(void) {
int err;
snd_pcm_hw_params_t *hw_params = NULL;
snd_pcm_hw_params_alloca(&hw_params);
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
if( this->t_pcm_handler != NULL ) {
this->print_debug_info("set_pcm_driver() resetting capture parameter\n");
snd_pcm_drop(this->t_pcm_handler);
snd_pcm_drain(this->t_pcm_handler);
snd_pcm_close(this->t_pcm_handler);
this->t_pcm_handler = NULL;
}
if( (err = snd_pcm_open(&this->t_pcm_handler, this->device_name, stream, 0)) < 0 ) {
this->print_debug_info("set_pcm_driver() cannot open audio device [%s] : %s\n",this->device_name , snd_strerror(err));
return false;
}
if( (err = snd_pcm_hw_params_any(this->t_pcm_handler, hw_params)) < 0 ) {
this->print_debug_info("set_pcm_driver() cannot initialize hardware parameter structure : %s\n", snd_strerror(err));
return false;
}
if( (err = snd_pcm_hw_params_set_access(this->t_pcm_handler, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0 ) {
this->print_debug_info("set_pcm_driver() cannot set access type : %s\n", snd_strerror(err));
return false;
}
if( (err = snd_pcm_hw_params_set_channels(this->t_pcm_handler, hw_params, this->channels)) < 0 ) {
this->print_debug_info("set_pcm_driver() cannot set channel count : %s\n", snd_strerror(err));
this->print_debug_info("set_pcm_driver() near set channel..\n");
return false;
}
if( (err = snd_pcm_hw_params_set_format(this->t_pcm_handler, hw_params, format)) < 0 ) {
this->print_debug_info("set_pcm_driver() cannot set sample format : %s\n", snd_strerror(err));
return false;
}
if( (err = snd_pcm_hw_params_set_rate(this->t_pcm_handler, hw_params, this->sample_rate, 0) ) < 0) {
this->print_debug_info("set_pcm_driver() cannot set near sample rate : %s\n", snd_strerror(err));
return false;
}
if( (err = snd_pcm_hw_params_set_periods(this->t_pcm_handler, hw_params, this->size_pcm_periods, 0)) < 0) {
this->print_debug_info("set_pcm_driver() error setting periods : %s\n", snd_strerror(err));
return false;
}
if( (err = snd_pcm_nonblock(this->t_pcm_handler, 0)) < 0 ) {
this->print_debug_info("set_pcm_driver() nonblock failed : %s\n", snd_strerror(err));
return false;
}
snd_pcm_uframes_t t_buffer_size = (this->chunk_size / this->channels * this->size_pcm_periods) >> 2;
if( (err = snd_pcm_hw_params_set_buffer_size_near(this->t_pcm_handler, hw_params, &t_buffer_size)) < 0 ) {
this->print_debug_info("set_pcm_driver() buffer size failed : %s\n", snd_strerror(err));
return false;
}
if( (err = snd_pcm_hw_params(this->t_pcm_handler, hw_params)) < 0 ) {
this->print_debug_info("set_pcm_driver() cannot set parameters : %s\n", snd_strerror(err));
return false;
}
if( (err = snd_pcm_prepare(this->t_pcm_handler)) < 0 ) {
this->print_debug_info("set_pcm_driver() cannot prepare audio interface for use : %s\n", snd_strerror(err));
return false;
}
snd_pcm_hw_params_get_channels(hw_params, &this->channels);
snd_pcm_hw_params_get_buffer_time(hw_params, &this->buffer_size, 0);
snd_pcm_hw_params_get_period_time(hw_params, &this->period_size, 0);
snd_pcm_uframes_t t_info_buffer_size, t_info_period_size;
snd_pcm_hw_params_get_buffer_size(hw_params, &t_info_buffer_size);
snd_pcm_hw_params_get_period_size(hw_params, &t_info_period_size, 0);
this->frame_bytes = snd_pcm_frames_to_bytes(this->t_pcm_handler, 1);
this->frame_latency = (double)(this->chunk_size / this->channels) / this->frame_bytes / this->sample_rate;
this->print_debug_info("set_pcm_driver() h/w params set information..\n");
this->print_debug_info("set_pcm_driver() h/w params set - sample rate : [%d]\n", this->sample_rate);
this->print_debug_info("set_pcm_driver() h/w params set - channels : [%d]\n", this->channels);
this->print_debug_info("set_pcm_driver() h/w params set - pcm buffer time : [%d]\n", this->buffer_size);
this->print_debug_info("set_pcm_driver() h/w params set - pcm period time : [%d]\n", this->period_size);
this->print_debug_info("set_pcm_driver() h/w params set - pcm buffer size : [%d]\n", t_info_buffer_size);
this->print_debug_info("set_pcm_driver() h/w params set - pcm period size : [%d]\n", t_info_period_size);
this->print_debug_info("set_pcm_driver() h/w params set - pcm frame bytes : [%d]\n", this->frame_bytes);
this->print_debug_info("set_pcm_driver() h/w params set - frame latency : [%lf]\n",this->frame_latency);
this->print_debug_info("set_pcm_driver() h/w params set - pcm width : [%d]\n", snd_pcm_format_width(SND_PCM_FORMAT_S16_LE));
return true;
}
ALSA와 관련된 정보 및 파라미터는 아래에서 자세히 확인할 수 있다.
https://www.alsa-project.org/wiki/Main_Page
'IT > programming' 카테고리의 다른 글
[C/C++] readdir을 활용한 파일 목록 읽기 예제, 파일 탐색기 (0) | 2022.01.07 |
---|---|
[C/C++] parse mp3 header/C언어 MP3 헤더 분석 함수 공유 (0) | 2022.01.07 |
[C/C++] signal handler library 활용과 소스코드 공유 (0) | 2021.12.24 |
[C/C++] C언어 main 함수 전/후에 함수를 실행 하는 방법 (0) | 2021.12.21 |
[C/C++] pthread condition 설명 (0) | 2020.03.30 |
댓글