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

I recently read this article Using Fuzzy Matching to Search by Sound with Python, which uses Fuzzy library. I didnt have any need to use this but I just found this interesting. So, I wrote this quick script:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import fuzzy

dmeta = fuzzy.DMetaphone()
fmt_dmeta = lambda s: %4s, %4s % tuple(dmeta(s))

funcs = [fuzzy.Soundex(8), fmt_dmeta, fuzzy.nysiis]
names = [
    [
      [en,    Tom Hanks,       Tom Hanks],
      [en,    Thomas Hanks,    Thomas Hanks],
      [zh_TW, Tangmu hankesi,  湯姆漢克斯],
      [jp,    Tomu hankusu,    トムハンクス],
      [kr,    tom haengkeuseu, 톰 행크스],
    ],
    [
      [en,    Barack Obama,    Barack Obama],
      [zh_TW, Balake Oubama,   巴拉克歐巴馬],
      [jp,    Baraku Obama,    バラクオバマ],
      [ar,    barak awbama,    باراك أوباما,],
    ],
    [
      [en,    Rose Kennedy,    Rose Kennedy],
      [zh_TW, Luosi Gannaidi,  蘿絲甘迺迪],
      [jp,    Rozu kenedi,     ローズケネディ],
    ],
  ]


for name in names:
  for lang in name:
    results = %8s / %10s / %-10s % tuple(f(lang[1]) for f in funcs)
    print %-16s %s %s % (lang[1], results, lang[2])
  print -*78

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