alsa linux c 程序输出的奇怪正弦波
Strange sine wave on output of an alsa linux c program
我正在 ubuntu 学习 alsa 编程。
我正在尝试将正弦波输出到笔记本电脑声卡的线路输出,然后通过音频线重定向到线路输入(麦克风)。
___LINE_IN(microphone)\
/ \
| \ _____________
| |soundcard-pc |
cable /
| /
\ ___LINE_OUT(speakers)/
我正在使用此代码
#include<stdio.h>
#include<stdlib.h>
#include<alsa/asoundlib.h>
#include<math.h>
#define SIZEBUF 2048
int main(void)
{
int i;
int err;
double x;
double cost;
double frequency=500;
unsigned int rate=44100;
short buf[SIZEBUF];
snd_pcm_t *phandle;
snd_pcm_hw_params_t *hw_params;
snd_pcm_open(&phandle, "default", SND_PCM_STREAM_PLAYBACK, 0);
snd_pcm_hw_params_malloc(&hw_params);
snd_pcm_hw_params_any(phandle, hw_params);
if ((err = snd_pcm_hw_params_set_access(phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
printf("Cannot set access type.\n");
exit(1);
}
snd_pcm_hw_params_set_format(phandle, hw_params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate_near(phandle, hw_params, &rate, 0);
snd_pcm_hw_params_set_channels(phandle, hw_params, 1);
snd_pcm_hw_params(phandle, hw_params);
snd_pcm_hw_params_free(hw_params);
snd_pcm_prepare(phandle);
cost = 2.0 * M_PI * frequency / (double)rate;
printf("cost=%f.\n", cost);
for (i = 1 ; i < SIZEBUF ; i++) {
x = sin(i * cost);
buf[i] = (short)(32767 * x + 32768);
}
for (i = 0 ; i < 50 ; i++) {
snd_pcm_writei(phandle, buf, SIZEBUF);
}
snd_pcm_close(phandle);
exit(0);
}
我正在使用 audacity 来查看波浪,但它看起来很奇怪,就像这张图片
好像没有正弦波的行为。
为什么?
要将浮点样本转换为 SND_PCM_FORMAT_S16_LE,请执行以下操作:
#include <endian.h>
#include <math.h>
#include <stdint.h>
// assume -1.0 <= x <= 1.0
int16_t float_to_s16le (float x)
{
int16_t s16 = trunc (x * 32767); // rounds toward zero
return htole16 (s16);
}
一些备注:
- 您正在转换为有符号数,而不是无符号数。此外,重要的是要避免剪裁或环绕大值。
- 您需要具有特定位数的类型。因此使用 int16_t,而不是短
- 通常您会选择主机字节顺序 (SND_PCM_FORMAT_S16)。但是由于您明确选择了 Little Endian,因此您需要确保这是您生成的内容(使用
htole16
或等效项)。
您正在使用 short
类型的示例,并且已将它们正确声明为 S16_LE
。
但是,您的代码随后会计算这些值,就好像它们是无符号的一样:
buf[i] = (short)(32767 * x + 32768);
您不需要补偿;对于带符号的样本,零实际上是零。使用这个:
buf[i] = (short)(32767 * x);
我正在 ubuntu 学习 alsa 编程。
我正在尝试将正弦波输出到笔记本电脑声卡的线路输出,然后通过音频线重定向到线路输入(麦克风)。
___LINE_IN(microphone)\
/ \
| \ _____________
| |soundcard-pc |
cable /
| /
\ ___LINE_OUT(speakers)/
我正在使用此代码
#include<stdio.h>
#include<stdlib.h>
#include<alsa/asoundlib.h>
#include<math.h>
#define SIZEBUF 2048
int main(void)
{
int i;
int err;
double x;
double cost;
double frequency=500;
unsigned int rate=44100;
short buf[SIZEBUF];
snd_pcm_t *phandle;
snd_pcm_hw_params_t *hw_params;
snd_pcm_open(&phandle, "default", SND_PCM_STREAM_PLAYBACK, 0);
snd_pcm_hw_params_malloc(&hw_params);
snd_pcm_hw_params_any(phandle, hw_params);
if ((err = snd_pcm_hw_params_set_access(phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
printf("Cannot set access type.\n");
exit(1);
}
snd_pcm_hw_params_set_format(phandle, hw_params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate_near(phandle, hw_params, &rate, 0);
snd_pcm_hw_params_set_channels(phandle, hw_params, 1);
snd_pcm_hw_params(phandle, hw_params);
snd_pcm_hw_params_free(hw_params);
snd_pcm_prepare(phandle);
cost = 2.0 * M_PI * frequency / (double)rate;
printf("cost=%f.\n", cost);
for (i = 1 ; i < SIZEBUF ; i++) {
x = sin(i * cost);
buf[i] = (short)(32767 * x + 32768);
}
for (i = 0 ; i < 50 ; i++) {
snd_pcm_writei(phandle, buf, SIZEBUF);
}
snd_pcm_close(phandle);
exit(0);
}
我正在使用 audacity 来查看波浪,但它看起来很奇怪,就像这张图片
好像没有正弦波的行为。 为什么?
要将浮点样本转换为 SND_PCM_FORMAT_S16_LE,请执行以下操作:
#include <endian.h>
#include <math.h>
#include <stdint.h>
// assume -1.0 <= x <= 1.0
int16_t float_to_s16le (float x)
{
int16_t s16 = trunc (x * 32767); // rounds toward zero
return htole16 (s16);
}
一些备注:
- 您正在转换为有符号数,而不是无符号数。此外,重要的是要避免剪裁或环绕大值。
- 您需要具有特定位数的类型。因此使用 int16_t,而不是短
- 通常您会选择主机字节顺序 (SND_PCM_FORMAT_S16)。但是由于您明确选择了 Little Endian,因此您需要确保这是您生成的内容(使用
htole16
或等效项)。
您正在使用 short
类型的示例,并且已将它们正确声明为 S16_LE
。
但是,您的代码随后会计算这些值,就好像它们是无符号的一样:
buf[i] = (short)(32767 * x + 32768);
您不需要补偿;对于带符号的样本,零实际上是零。使用这个:
buf[i] = (short)(32767 * x);