As of 2016-02-26, there will be no more posts for this blog. s/blog/pba/
Showing posts with label ALSA. Show all posts

When I was trying to watch a live streaming, VLC only burped once and no more audio output. The Message dialog spat lots of warning messages:
main warning: audio drift is too big (170368), dropping buffer
main warning: audio drift is too big (147148), dropping buffer
main warning: audio drift is too big (134086), dropping buffer
main warning: output date isn't PTS date, requesting resampling (1536590)
VLC can play other media files normally with default preferences, but not with that live streaming. I searched for the error message and read something about changing audio output module to OSS. Before I tried to re-emerge VLC with pulseaudio USE flag, I changed
  • Audio Output Module from Default to ALSA audio output.
Still no luck with it. At this moment, I had issued re-emerge command for PulseAudio support in VLC, while waiting, I made another change for ALSA audio output's settings
  • Audio Device from Default to HDA Intel: STAC92xx Analog (hw:0,0).
That fixes the problem. Somehow the Default audio device would cause the "audio drift too big..., dropping buffer" error. By specifying the device resolving the issue, I have no clue why this didn't happen on other regular media files.

The live streaming is GOMTV, a stream of GSL, a StarCraft II tournament. Watching GSL requires a Windows-only custom media player. I found a Python script, GOMstreamer, which can extract the streaming URL and feed stream data to VLC automatically using just one command.

I installed VLC just for watching this stream because GOMstreamer only officially supports playing using VLC. I am sure you can play it using MPlayer, but I haven't tried to know what exactly behind the scene. Since VLC also uses Qt as SMPlayer does, so I might keep it on my disk for a while. The interface is quite nice.

I don't know if the error is related to the presence of PulseAudio (PA). I have PA installed months ago, because I need to record the system audio output, my sound card does not provide such recording channel. That's how PA got into my computer, I generally do not want a second program does the same things as ALSA has already taken care all tasks except the recording need.
After the installation of PA, I honestly didn't feel much difference than before as in performance and memory consumption. I kept PA around even I now have no need for recording.

I wrote a script cap-vol-meter.py, which display sound level of default capture device (microphone). Here is a video.

Thats all for the script, you can read more how to use it in that page or use -h option.

1   Original idea

I was planning to show the current sound system output, in my case, its ALSA. I dont know how many attempts I have tried to get the final output of sound system. I always failed to do so since ALSA doesnt seem to give you that data.

I wanted to show the spectrum of current output. At first, I tried to show the sound level using PyAudio. Just like getting system out, I cant get any via PyAudio. There is nothing returns. So, I switched to pyalsaaudio, which I have used before. I want to use PyAudio at first place because its cross-platform. If it worked, at least my script might also work on Mac. With a little tweak, it would work on Windows.

And thats the moment I recalled, I cant get the system output. However, I still finished to show a volume meter. I wont do more for spectrum because I dont do recording. I just want to have a terminal screensaver.

Anyway, I did so little research. I found this nice page about how to use Capture device to get system output. In its Record playing sound, I know there is no chance for my device. I dont want to use physical wire to loop back. Unfortunately, I dont have Mix. But I have Mux, which does loop back the sound, but the quality is really awful and the sound level is very low.

As for the ALSA file plugin, I had hope for it. But I cant get it work, the output file never gets created. I need to do more research on asoundrc. I was thinking to write a listener which utilizes the support of pipe, let file pipe output to my listener program, then this program will will write a number of samples to a file, it overwrites every time. So whatever program needs the output, it just open and read those samples, then re-open and read again. Of course, there will be a number prefix before those samples, so same content wont be process twice.

However, file is output per application, when a process opens a playback, ALSA creates the file. Therefore, its not truly the final output. In Audacitys wiki, there is a section explains how to create files for each process.

I dont know if other popular sound systems gives you the final output data. If they do, its no use for me, because I dont know to install and configure for another. ALSA only fails me for this final output.

Sorry, my dearest terminal, you wont have sweet eye-candy to show off.

I tried to make JACK running on my Gentoo for a day or two, but I kept getting this:

% jackd -v -R -d alsa
jackd 0.118.0
Copyright 2001-2009 Paul Davis, Stephane Letz, Jack O'Quinn, Torben Hohn and others.
jackd comes with ABSOLUTELY NO WARRANTY
This is free software, and you are welcome to redistribute it
under certain conditions; see the file COPYING for details

getting driver descriptor from /usr/lib64/jack/jack_dummy.so
getting driver descriptor from /usr/lib64/jack/jack_net.so
getting driver descriptor from /usr/lib64/jack/jack_alsa.so
JACK compiled with System V SHM support.
server `default' registered
registered builtin port type 32 bit float mono audio
registered builtin port type 8 bit raw midi
clock source = system clock via clock_gettime
loading driver ..
start poll on 3 fd's
new client: alsa_pcm, id = 1 type 1  0x608fc0 fd = -1
creating alsa driver ... hw:0|hw:0|1024|2|48000|0|0|nomon|swmeter|-|32bit
control device hw:0
configuring for 48000Hz, period = 1024 frames (21.3 ms), buffer = 2 periods
ALSA: final selected sample format for capture: 32bit integer little-endian
ALSA: use 2 periods for capture
ALSA: final selected sample format for playback: 32bit integer little-endian
ALSA: use 2 periods for playback
new buffer size 1024
registered port system:capture_1, offset = 4096
registered port system:capture_2, offset = 8192
registered port system:playback_1, offset = 0
registered port system:playback_2, offset = 0
++ jack_sort_graph
++ jack_rechain_graph():
+++ client is now alsa_pcm active ? 1
client alsa_pcm: internal client, execution_order=0.
-- jack_rechain_graph()
-- jack_sort_graph
5485 waiting for signals
[Ten seconds later]
6624 waiting for signals
jackd watchdog: timeout - killing jackd

I googled it and found a lot are about realtime, but its not my case. After many tries, I still couldnt solve my problem so I asked for help, and someones reply helped me indirectly.

By default, JACK would try to open two ports, one for playback, the other for capture. The problem was with capture, I guess jackd couldnt receive data, so it timed out. I have to enable Capture in alsamixer or only use the playback by specifying -P. If I dont do either, the timeout always occurs.

I also tried to use TiMidity++ to create a virtual MIDI device, but there is a problem:

% timidity -iA -Oj -B2,7
TiMidity starting in ALSA server mode
ALSA lib seq_hw.c:457:(snd_seq_hw_open) open /dev/snd/seq failed: No such file or directory
error in snd_seq_open

modprobe snd-seq loads the kernel module TiMidity++ needs. Put it into /etc/modules.autoload.d/kernel-2.6 would be helpful for loading at boot time.

I just wrote a simple C program. Its been quite a long time, at least years, my skill has become very rusty. Any tips or tricks are welcome!

Here is the code, you can also get it on Google Code:

// get-volume is a small utility for ALSA mixer volume, written for being used
// in Conky.
//
// This code is in Public Domain
//
// Reference:
//  http://www.alsa-project.org/alsa-doc/alsa-lib/index.html
//
// Author:
//  2009 Yu-Jie Lin
//
// Compile using:
//  gcc -lasound -o get-volume get-volume.c

#include <stdio.h>
#include "alsa/asoundlib.h"

const char *ATTACH = "default";
const snd_mixer_selem_channel_id_t CHANNEL = SND_MIXER_SCHN_FRONT_LEFT;
const char *SELEM_NAME = "Master";

void error_close_exit(char *errmsg, int err, snd_mixer_t *h_mixer) {
        if (err == 0)
                fprintf(stderr, errmsg);
        else
                fprintf(stderr, errmsg, snd_strerror(err));
        if (h_mixer != NULL)
                snd_mixer_close(h_mixer);
        exit(EXIT_FAILURE);
        }

int main(int argc, char** argv) {
        int err;
        long vol;
        long vol_min, vol_max;
        int switch_value;
        snd_mixer_t *h_mixer;
        snd_mixer_selem_id_t *sid;
        snd_mixer_elem_t *elem ;

        if ((err = snd_mixer_open(&h_mixer, 1)) < 0)
                error_close_exit("Mixer open error: %s\n", err, NULL);

        if ((err = snd_mixer_attach(h_mixer, ATTACH)) < 0)
                error_close_exit("Mixer attach error: %s\n", err, h_mixer);

        if ((err = snd_mixer_selem_register(h_mixer, NULL, NULL)) < 0)
                error_close_exit("Mixer simple element register error: %s\n", err, h_mixer);

        if ((err = snd_mixer_load(h_mixer)) < 0)
                error_close_exit("Mixer load error: %s\n", err, h_mixer);

        snd_mixer_selem_id_alloca(&sid);
        snd_mixer_selem_id_set_index(sid, 0);
        snd_mixer_selem_id_set_name(sid, SELEM_NAME);

        if ((elem = snd_mixer_find_selem(h_mixer, sid)) == NULL)
                error_close_exit("Cannot find simple element\n", 0, h_mixer);

        if (argc != 2)
                error_close_exit("Missing (switch|volume) as argument\n", 0, NULL);

        if (strcmp(argv[1], "volume") == 0) {
                snd_mixer_selem_get_playback_volume(elem, CHANNEL, &vol);
                snd_mixer_selem_get_playback_volume_range(elem, &vol_min, &vol_max);
                printf("%3.0f%%", 100.0 * vol / vol_max);
                }
        else if (strcmp(argv[1], "switch") == 0) {
                snd_mixer_selem_get_playback_switch(elem, CHANNEL, &switch_value);
                printf("%s", (switch_value == 1) ? "ON" : "OFF");
                }
        else if (strcmp(argv[1], "on") == 0) {
                snd_mixer_selem_get_playback_switch(elem, CHANNEL, &switch_value);
                printf("%s", (switch_value == 1) ? "ON" : "");
                }
        else if (strcmp(argv[1], "off") == 0) {
                snd_mixer_selem_get_playback_switch(elem, CHANNEL, &switch_value);
                printf("%s", (switch_value == 1) ? "" : "OFF");
                }
        else
                error_close_exit("Invalid argument. Using (switch|volume)\n", 0, NULL);

        snd_mixer_close(h_mixer);
        return 0;
        }

This code is written for being used with Conky. Before this, I had the following in my .conkyrc:

${color lightblue}Volume:$color ${exec amixer get Master | egrep -o "[0-9]+%" | head -1 | egrep -o "[0-9]*"} % $alignr${color blue}${exec amixer get Master | grep -o "\[on\]" | head -1}$color${color red}${exec amixer get Master | grep -o "\[off\]" | head -1}$color

amixer, (e)grep, and head may not consume too much resource and I have never been aware of them running background. But, one simple C program, definitely run faster and efficient than those, because its just written for that purpose.

I also thought of Python. I love Python, but I personally prefer compiled program if I need to make a choice. So, I decided to use C. Python is really (usually) easy to code and (almost) fun to program, but it really consumes much more memory.

With this small program, then that part become:

${color lightblue}Volume:$color ${exec ~/bin/get-volume volume}$alignr${color blue}${exec ~/bin/get-volume on}$color${color red}${exec ~/bin/get-volume off}$color