修改 ALSA arecord 函数以将音频电平输出到 Raspberry Pi 3 RGB LED
Modify ALSA arecord function to output audio levels to Raspberry Pi 3 RGB LED
我目前可以使用我的 raspberry pi 3
将 USB Audio input
重定向到 icecast stream
。目前,当我在 Raspbian Jessie Lite 上 pipe arecord from USB audio hw input to avconv
(相当于 ffmpeg)时,它在小型测试中运行良好。
当您使用详细设置时,Arecord 有一个内置的文本 vu 表用于音频级别。
我认为代码位于附加函数中。我想知道是否可以重写此函数以输出到 Raspberry Pi 3 RGB LED - 以某种方式根据音量级别发送 red/yellow/green - 使用函数的峰值变量?
我已经包含了整个函数 - 以及打印函数。如果可以的话,我认为代码可能会取代 print_vu_meter(perc, maxperc);
是否可以修改arecord让Raspi 3来处理pipe过程中的处理?有没有办法使用另一个线程?
这完全超出了我的范围 - 只是在寻找一个开始,或者一些想法来摆脱我的想法,或者说这是可能的。
peak handler
static void compute_max_peak(u_char *data, size_t count)
{
signed int val, max, perc[2], max_peak[2];
static int run = 0;
size_t ocount = count;
int format_little_endian = snd_pcm_format_little_endian(hwparams.format);
int ichans, c;
if (vumeter == VUMETER_STEREO)
ichans = 2;
else
ichans = 1;
memset(max_peak, 0, sizeof(max_peak));
switch (bits_per_sample) {
case 8: {
signed char *valp = (signed char *)data;
signed char mask = snd_pcm_format_silence(hwparams.format);
c = 0;
while (count-- > 0) {
val = *valp++ ^ mask;
val = abs(val);
if (max_peak[c] < val)
max_peak[c] = val;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 16: {
signed short *valp = (signed short *)data;
signed short mask = snd_pcm_format_silence_16(hwparams.format);
signed short sval;
count /= 2;
c = 0;
while (count-- > 0) {
if (format_little_endian)
sval = __le16_to_cpu(*valp);
else
sval = __be16_to_cpu(*valp);
sval = abs(sval) ^ mask;
if (max_peak[c] < sval)
max_peak[c] = sval;
valp++;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 24: {
unsigned char *valp = data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
count /= 3;
c = 0;
while (count-- > 0) {
if (format_little_endian) {
val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
} else {
val = (valp[0]<<16) | (valp[1]<<8) | valp[2];
}
/* Correct signed bit in 32-bit value */
if (val & (1<<(bits_per_sample-1))) {
val |= 0xff<<24; /* Negate upper bits too */
}
val = abs(val) ^ mask;
if (max_peak[c] < val)
max_peak[c] = val;
valp += 3;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 32: {
signed int *valp = (signed int *)data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
count /= 4;
c = 0;
while (count-- > 0) {
if (format_little_endian)
val = __le32_to_cpu(*valp);
else
val = __be32_to_cpu(*valp);
val = abs(val) ^ mask;
if (max_peak[c] < val)
max_peak[c] = val;
valp++;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
default:
if (run == 0) {
fprintf(stderr, _("Unsupported bit size %d.\n"), (int)bits_per_sample);
run = 1;
}
return;
}
max = 1 << (bits_per_sample-1);
if (max <= 0)
max = 0x7fffffff;
for (c = 0; c < ichans; c++) {
if (bits_per_sample > 16)
perc[c] = max_peak[c] / (max / 100);
else
perc[c] = max_peak[c] * 100 / max;
}
if (interleaved && verbose <= 2) {
static int maxperc[2];
static time_t t=0;
const time_t tt=time(NULL);
if(tt>t) {
t=tt;
maxperc[0] = 0;
maxperc[1] = 0;
}
for (c = 0; c < ichans; c++)
if (perc[c] > maxperc[c])
maxperc[c] = perc[c];
putchar('\r');
print_vu_meter(perc, maxperc);
fflush(stdout);
}
else if(verbose==3) {
printf(_("Max peak (%li samples): 0x%08x "), (long)ocount, max_peak[0]);
for (val = 0; val < 20; val++)
if (val <= perc[0] / 5)
putchar('#');
else
putchar(' ');
printf(" %i%%\n", perc[0]);
fflush(stdout);
}
}
print_vu_meter
static void print_vu_meter_mono(int perc, int maxperc)
{
const int bar_length = 50;
char line[80];
int val;
for (val = 0; val <= perc * bar_length / 100 && val < bar_length; val++)
line[val] = '#';
for (; val <= maxperc * bar_length / 100 && val < bar_length; val++)
line[val] = ' ';
line[val] = '+';
for (++val; val <= bar_length; val++)
line[val] = ' ';
if (maxperc > 99)
sprintf(line + val, "| MAX");
else
sprintf(line + val, "| %02i%%", maxperc);
fputs(line, stdout);
if (perc > 100)
printf(_(" !clip "));
}
看起来人们对此并不感兴趣,但我想我会 post 我未来的答案,以防其他人试图深入研究此类项目。
调查结果:
- arecord 只是 aplay 的链接副本
- 你可以使用令人难以置信的 wiringPi c 库为 C 代码添加线程
- 通过向代码中添加静态 volatile int 变量 - 它在线程和主程序之间共享
- 将变量设置为 perc 值意味着它会在线程程序中立即更新
我能够使用以下代码模拟带有 LED 的 6 级音频电平表:
我使用面包板上的按钮在显示和不显示的 LED 级别之间切换。
setAudioLEDS线程函数:
PI_THREAD (setAudioLEDs)
{
// Only update LEDS if button is pressed
// Gets Audio Level from global var: globalAudioLevel
// Wiring Pi Constants for led and button
// Pin number declarations. We're using the Broadcom chip pin numbers.
#define CYCLE_UPDATE '0'
#define CYCLE_STEADY '1'
int last_button;
int last_cycle;
int this_cycle;
// Button is released if this returns 1
// HIGH or LOW (1 or 0)
last_button = digitalRead(butPin);
last_cycle = CYCLE_STEADY;
this_cycle = last_cycle;
while (1)
{
if (digitalRead(butPin) != last_button) {
if (last_cycle == CYCLE_UPDATE)
this_cycle = CYCLE_STEADY;
else
this_cycle = CYCLE_UPDATE;
last_cycle = this_cycle;
}
switch (this_cycle) {
case CYCLE_UPDATE:
// Set LEDS based on audio level
if (globalAudioLevel > 20)
digitalWrite(led1, HIGH); // Turn LED ON
else
digitalWrite(led1, LOW); // Turn LED OFF
if (globalAudioLevel > 40)
digitalWrite(led2, HIGH); // Turn LED ON
else
digitalWrite(led2, LOW); // Turn LED OFF
if (globalAudioLevel > 60)
digitalWrite(led3, HIGH); // Turn LED ON
else
digitalWrite(led3, LOW); // Turn LED OFF
if (globalAudioLevel > 70)
digitalWrite(led4, HIGH); // Turn LED ON
else
digitalWrite(led4, LOW); // Turn LED OFF
if (globalAudioLevel > 80)
digitalWrite(led5, HIGH); // Turn LED ON
else
digitalWrite(led5, LOW); // Turn LED OFF
if (globalAudioLevel > 90)
digitalWrite(led6, HIGH); // Turn LED ON
else
digitalWrite(led6, LOW); // Turn LED OFF
break;
default:
/* Button hasn't been pressed */
digitalWrite(led1, LOW); // Turn LED OFF
digitalWrite(led2, LOW); // Turn LED OFF
digitalWrite(led3, LOW); // Turn LED OFF
digitalWrite(led4, LOW); // Turn LED OFF
digitalWrite(led5, LOW); // Turn LED OFF
digitalWrite(led6, LOW); // Turn LED OFF
}
}
}
我有一些东西可以作为 ALSA 示波器插件使用,所以播放的任何音频都会启动我的 led vu 表。您所要做的就是安装插件并编辑 /etc/asound.conf 以包含插件。
我找到了 pivumeter (https://github.com/pimoroni/pivumeter) which is based on ameter (http://laugeo.free.fr/ameter.html) 并为使用 GPIO 引脚的 2 通道立体声 16 LED 和另一个使用两个移位寄存器和 SPI 的 LED 创建了自己的设备。
我的叉子可以在这里找到:https://github.com/linuxgnuru/pivumeter
要添加您自己的设备,您需要编辑 Makefile.am、src/pivumeter.c(在底部添加您的函数)、src/devices/all.h 和您的代码在 src/devices/YOURDEVICE.c 和 src/devices/YOURDEVICE.h
我计划为范围插件浏览 ALSA C 库并从头开始制作我自己的插件。
https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___scope.html
我目前可以使用我的 raspberry pi 3
将 USB Audio input
重定向到 icecast stream
。目前,当我在 Raspbian Jessie Lite 上 pipe arecord from USB audio hw input to avconv
(相当于 ffmpeg)时,它在小型测试中运行良好。
当您使用详细设置时,Arecord 有一个内置的文本 vu 表用于音频级别。
我认为代码位于附加函数中。我想知道是否可以重写此函数以输出到 Raspberry Pi 3 RGB LED - 以某种方式根据音量级别发送 red/yellow/green - 使用函数的峰值变量?
我已经包含了整个函数 - 以及打印函数。如果可以的话,我认为代码可能会取代 print_vu_meter(perc, maxperc);
是否可以修改arecord让Raspi 3来处理pipe过程中的处理?有没有办法使用另一个线程?
这完全超出了我的范围 - 只是在寻找一个开始,或者一些想法来摆脱我的想法,或者说这是可能的。
peak handler
static void compute_max_peak(u_char *data, size_t count)
{
signed int val, max, perc[2], max_peak[2];
static int run = 0;
size_t ocount = count;
int format_little_endian = snd_pcm_format_little_endian(hwparams.format);
int ichans, c;
if (vumeter == VUMETER_STEREO)
ichans = 2;
else
ichans = 1;
memset(max_peak, 0, sizeof(max_peak));
switch (bits_per_sample) {
case 8: {
signed char *valp = (signed char *)data;
signed char mask = snd_pcm_format_silence(hwparams.format);
c = 0;
while (count-- > 0) {
val = *valp++ ^ mask;
val = abs(val);
if (max_peak[c] < val)
max_peak[c] = val;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 16: {
signed short *valp = (signed short *)data;
signed short mask = snd_pcm_format_silence_16(hwparams.format);
signed short sval;
count /= 2;
c = 0;
while (count-- > 0) {
if (format_little_endian)
sval = __le16_to_cpu(*valp);
else
sval = __be16_to_cpu(*valp);
sval = abs(sval) ^ mask;
if (max_peak[c] < sval)
max_peak[c] = sval;
valp++;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 24: {
unsigned char *valp = data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
count /= 3;
c = 0;
while (count-- > 0) {
if (format_little_endian) {
val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
} else {
val = (valp[0]<<16) | (valp[1]<<8) | valp[2];
}
/* Correct signed bit in 32-bit value */
if (val & (1<<(bits_per_sample-1))) {
val |= 0xff<<24; /* Negate upper bits too */
}
val = abs(val) ^ mask;
if (max_peak[c] < val)
max_peak[c] = val;
valp += 3;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 32: {
signed int *valp = (signed int *)data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
count /= 4;
c = 0;
while (count-- > 0) {
if (format_little_endian)
val = __le32_to_cpu(*valp);
else
val = __be32_to_cpu(*valp);
val = abs(val) ^ mask;
if (max_peak[c] < val)
max_peak[c] = val;
valp++;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
default:
if (run == 0) {
fprintf(stderr, _("Unsupported bit size %d.\n"), (int)bits_per_sample);
run = 1;
}
return;
}
max = 1 << (bits_per_sample-1);
if (max <= 0)
max = 0x7fffffff;
for (c = 0; c < ichans; c++) {
if (bits_per_sample > 16)
perc[c] = max_peak[c] / (max / 100);
else
perc[c] = max_peak[c] * 100 / max;
}
if (interleaved && verbose <= 2) {
static int maxperc[2];
static time_t t=0;
const time_t tt=time(NULL);
if(tt>t) {
t=tt;
maxperc[0] = 0;
maxperc[1] = 0;
}
for (c = 0; c < ichans; c++)
if (perc[c] > maxperc[c])
maxperc[c] = perc[c];
putchar('\r');
print_vu_meter(perc, maxperc);
fflush(stdout);
}
else if(verbose==3) {
printf(_("Max peak (%li samples): 0x%08x "), (long)ocount, max_peak[0]);
for (val = 0; val < 20; val++)
if (val <= perc[0] / 5)
putchar('#');
else
putchar(' ');
printf(" %i%%\n", perc[0]);
fflush(stdout);
}
}
print_vu_meter
static void print_vu_meter_mono(int perc, int maxperc)
{
const int bar_length = 50;
char line[80];
int val;
for (val = 0; val <= perc * bar_length / 100 && val < bar_length; val++)
line[val] = '#';
for (; val <= maxperc * bar_length / 100 && val < bar_length; val++)
line[val] = ' ';
line[val] = '+';
for (++val; val <= bar_length; val++)
line[val] = ' ';
if (maxperc > 99)
sprintf(line + val, "| MAX");
else
sprintf(line + val, "| %02i%%", maxperc);
fputs(line, stdout);
if (perc > 100)
printf(_(" !clip "));
}
看起来人们对此并不感兴趣,但我想我会 post 我未来的答案,以防其他人试图深入研究此类项目。
调查结果:
- arecord 只是 aplay 的链接副本
- 你可以使用令人难以置信的 wiringPi c 库为 C 代码添加线程
- 通过向代码中添加静态 volatile int 变量 - 它在线程和主程序之间共享
- 将变量设置为 perc 值意味着它会在线程程序中立即更新
我能够使用以下代码模拟带有 LED 的 6 级音频电平表:
我使用面包板上的按钮在显示和不显示的 LED 级别之间切换。
setAudioLEDS线程函数:
PI_THREAD (setAudioLEDs)
{
// Only update LEDS if button is pressed
// Gets Audio Level from global var: globalAudioLevel
// Wiring Pi Constants for led and button
// Pin number declarations. We're using the Broadcom chip pin numbers.
#define CYCLE_UPDATE '0'
#define CYCLE_STEADY '1'
int last_button;
int last_cycle;
int this_cycle;
// Button is released if this returns 1
// HIGH or LOW (1 or 0)
last_button = digitalRead(butPin);
last_cycle = CYCLE_STEADY;
this_cycle = last_cycle;
while (1)
{
if (digitalRead(butPin) != last_button) {
if (last_cycle == CYCLE_UPDATE)
this_cycle = CYCLE_STEADY;
else
this_cycle = CYCLE_UPDATE;
last_cycle = this_cycle;
}
switch (this_cycle) {
case CYCLE_UPDATE:
// Set LEDS based on audio level
if (globalAudioLevel > 20)
digitalWrite(led1, HIGH); // Turn LED ON
else
digitalWrite(led1, LOW); // Turn LED OFF
if (globalAudioLevel > 40)
digitalWrite(led2, HIGH); // Turn LED ON
else
digitalWrite(led2, LOW); // Turn LED OFF
if (globalAudioLevel > 60)
digitalWrite(led3, HIGH); // Turn LED ON
else
digitalWrite(led3, LOW); // Turn LED OFF
if (globalAudioLevel > 70)
digitalWrite(led4, HIGH); // Turn LED ON
else
digitalWrite(led4, LOW); // Turn LED OFF
if (globalAudioLevel > 80)
digitalWrite(led5, HIGH); // Turn LED ON
else
digitalWrite(led5, LOW); // Turn LED OFF
if (globalAudioLevel > 90)
digitalWrite(led6, HIGH); // Turn LED ON
else
digitalWrite(led6, LOW); // Turn LED OFF
break;
default:
/* Button hasn't been pressed */
digitalWrite(led1, LOW); // Turn LED OFF
digitalWrite(led2, LOW); // Turn LED OFF
digitalWrite(led3, LOW); // Turn LED OFF
digitalWrite(led4, LOW); // Turn LED OFF
digitalWrite(led5, LOW); // Turn LED OFF
digitalWrite(led6, LOW); // Turn LED OFF
}
}
}
我有一些东西可以作为 ALSA 示波器插件使用,所以播放的任何音频都会启动我的 led vu 表。您所要做的就是安装插件并编辑 /etc/asound.conf 以包含插件。
我找到了 pivumeter (https://github.com/pimoroni/pivumeter) which is based on ameter (http://laugeo.free.fr/ameter.html) 并为使用 GPIO 引脚的 2 通道立体声 16 LED 和另一个使用两个移位寄存器和 SPI 的 LED 创建了自己的设备。
我的叉子可以在这里找到:https://github.com/linuxgnuru/pivumeter
要添加您自己的设备,您需要编辑 Makefile.am、src/pivumeter.c(在底部添加您的函数)、src/devices/all.h 和您的代码在 src/devices/YOURDEVICE.c 和 src/devices/YOURDEVICE.h
我计划为范围插件浏览 ALSA C 库并从头开始制作我自己的插件。
https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___scope.html