Sunday, May 29, 2011

Metronome for Linux

A solution for "Metoronome" is a very simple algorithm

 def metronome(bpm, how_long):  
while how_long > 0:
beep()
time.sleep( 60.0 / bpm )
how_long -= 60.0/bpm


Unfortunately, this wouldn't work on regular OS like Linux or Windows on PC, since they are not real time OS. To make the matter worse, sleep() operation will definitely context switch out the process, and coming back will not be in consistent interval.

I needed a metronome. I knew that there are softwares doing "Metronome", mostly commercial.

So, dig through, and found one open source solution (open metronome), written in MFC. Since my computer for music is Linux, I just read and learned its brilliant idea. It generates WAV file for beeping. I wrote a program, doing so.

 #include <stdio.h>  
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
char empty_sound[8] = { 4, 0, 0, 0, 6, 0, 6, 0 };
struct wav_header {
// "RIFF" chunk descriptor
int32_t chunk_id;
int32_t chunk_sz;
int32_t format;
// "fmt (header)" sub-chunk
int32_t sub_chunk1_id;
int32_t sub_chunk1_sz;
int16_t audio_format;
int16_t num_channel;
int32_t sample_rate;
int32_t byte_rate;
int16_t block_align;
int16_t bits_per_sample;
// "data (easily, music)" sub-chunk
int32_t sub_chunk2_id;
int32_t sub_chunk2_sz;
char *data;
};
#define ONE_SEC 88200 // bytes
static void print_header(struct wav_header *hdr)
{
printf("(0) chunk id : %x\n", hdr->chunk_id);
printf("(4) chunk size : %d\n", hdr->chunk_sz);
printf("(8) format : %x\n\n", hdr->format);
printf("(12) sub chunk1 id : %d\n", hdr->sub_chunk1_id);
printf("(16) sub chunk1 size : %d\n", hdr->sub_chunk1_sz);
printf("(20) audio format : %d\n", hdr->audio_format);
printf("(22) num channels : %d\n", hdr->num_channel);
printf("(24) sample rate : %d\n", hdr->sample_rate);
printf("(28) byte rate : %d\n", hdr->byte_rate);
printf("(32) block align : %d\n", hdr->block_align);
printf("(34) bits per sample : %d\n\n", hdr->bits_per_sample);
printf("(36) sub chunk2 id : %d\n", hdr->sub_chunk2_id);
printf("(40) sub chunk2 size : %d\n", hdr->sub_chunk2_sz);
printf("(44 -- ) DATA\n\n");
}
static int bytes_for_sec(int bpm)
{
float ratio = bpm/60.0;
float bytes_per_beep = ONE_SEC / ratio;
return (int)bytes_per_beep;
}
int main(int argc, char **argv)
{
char *sample_fname;
int tempo, dura, bps;
int rfd; // Sample file descriptor
int wfd; // Output file descriptor
int rc, i, j, tot_beats;
char buf[512 + 1]; // Sample hdr buffer
char *sample_buf, *ptr;
struct wav_header *hdr;
struct wav_header new_hdr;
if (argc < 3) {
printf("USAGE: %s <sample_wav> <tempo in BPM> <duration in sec>\n", argv[0] );
printf(" * sample_wav must be aligned by sample. Random clip of data may make a noise.\n");
printf(" example: $ %s s3.wav 120 60\n", argv[0]);
printf(" This will make an output of 'a.wav', using s3.wav as sample, 120 bps for 60sec.\n");
exit(0);
}
sample_fname = argv[1];
tempo = atoi(argv[2]);
if (tempo < 40) {
printf("Invalid tempo: Make it between 40 - MAX, which is the fastest from sample\n");
exit(1);
}
dura = atoi(argv[3]);
if (dura == 0) {
printf("Invalid duration: Make it greater than 0.\n");
exit(2);
}
rfd = open(sample_fname, O_RDONLY);
rc = read(rfd, buf, 44);
if (rc < 0) {
perror("Sample read error\n");
exit(3);
}
hdr = (struct wav_header *)buf;
if ( NULL == (sample_buf = malloc( hdr->sub_chunk2_sz )) ) {
perror("Mem alloc failed (1)\n");
exit(4);
}
// copying sample data
ptr = sample_buf;
while ( 0 < (rc = read(rfd, ptr, 512)) ) {
ptr += rc;
}
wfd = open("t.wav" , O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (wfd < 0) {
perror("Error opening write file");
}
tot_beats = dura * (tempo/60.0);
bps = bytes_for_sec(tempo);
int tot_bytes = 0;
for (i =0; i<tot_beats; ++i) {
write(wfd, sample_buf, hdr->sub_chunk2_sz);
tot_bytes += hdr->sub_chunk2_sz;
for (j = 0; j< (bps - hdr->sub_chunk2_sz)/8; ++j) {
write(wfd, empty_sound, 8);
tot_bytes += 8;
}
}
close(wfd);
memcpy(&new_hdr, hdr, 44);
new_hdr.sub_chunk2_sz=tot_bytes;
wfd = open("h.wav", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
write(wfd, &new_hdr, 44);
close(wfd);
system("/bin/cat h.wav t.wav > a.wav");
return 0;
}


Finally, I generated 30 sec of each speed (40, 44, 48, .... 208), and converted them into mp3. For example, 160 bpm wav (160.wav) is converted to 160.mp3 in 128 bit encoding.

 $ lame -h -b 128  160.wav  160.mp3



Now, if I need 5 min of 160 bpm, I just feed this 10 times in mpg123.

My favorite application of this was "speed trainer." For example, made 30 secs of 160, 161, 162, 163, 164 bpm and played it.

$ mpg123 16[0-4].mp3

Or making a 15 minute mp3 beat file isn't bad. Roughly 1 meg for 1 min, so 15 min is less than 15 meg byte. If it is encoded 64bit, the size will be even smaller.

Friday, February 11, 2011

First Youtube upload

Yesterday, I stayed home. Enjoyed a relaxed day, with a little excuse of my son's school meeting. During 1 spare hour before the meeting, I made this. I always wish that I have more time to practice my guitar.

Monday, January 17, 2011

python unittest skipping test.

I just found this feature, and I love it.

http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures

Friday, November 12, 2010

Neglecting upgrade

As I neglected the blog, I neglected my OS upgrades, too. Most of my machines are running Fedora Core 12. Two versions behind. As long as kernel is up-to-date, I probably won't upgrade FC12. At least for a while. I realized that only thing I need is up-to-date version of gcc w/ libc, python(2/3), emacs.

Friday, August 6, 2010

Drupal menu disappeared.

I am not an expert of Drupal. Actually, I am new to Drupal. Somehow, someway, our team selected Drupal for our interactive method to collaborate with entire company. Make sense because we don't want to spend whole lot of time just for method. We want the result.

While I was enabling "Views" module in drupal, it acted up. Views did not show up. So, I disabled Views and tried to re-install. But, right after disabling Views, menu is screwed up. Like this.





I couldn't administer menu, modules, and theme any more! That is a disaster for admin user. I was so tempted to re-install drupal, but then, migrating old database to new one is another challenge. Old one was in dev, but we already collected some data. And, old site were configured too.
So, only option is "FIX". But, fixing a software that I never deal with was a quite discouraging moment. I prepared fresh install of drupal, and diff'd the files. Files looked similar. Then, all modification must be in the database.
So, I wrote a database diff tool.

Successfully, I found it is because menu_router table is corrupted. I restored.
Here is the sample code that I used:


#!/usr/bin/env python

import MySQLdb
import os
import sys

def main():
try:
cn1 = MySQLdb.connect(host="172.16.136.114", db="drupal_prod")
cn2 = MySQLdb.connect(host="172.16.136.114", db="drupal_dev")
except:
print >>sys.stderr, "ERROR: connection"
sys.exit(1)

while 1:
table = raw_input("Table name: ")
sql = "select count(*) from %s" % table

cur1 = cn1.cursor()
cur1.execute(sql)
cur2 = cn2.cursor()
cur2.execute(sql)

r1 = cur1.fetchone()
r2 = cur2.fetchone()
if r1[0] == r2[0] == 0:
print "SAME!"
continue

if r1[0] == r2[0]:
print "Same COUNT! "
else:
print "DIFF Found! "

inp = raw_input("specific sql? ")
if inp.strip() == '':
sql = "select * from %s" % table
else:
sql = inp
print "SQL used: %s; " % sql
cur1.execute(sql)
cur2.execute(sql)

try:
while 1:
r1 = cur1.fetchone()
r2 = cur2.fetchone()

if r1 is None and r2 is None:
break

if r1.__str__() != r2.__str__():
print "* ", r1, "\n", "# ", r2
except:
pass

print "Repeat- SQL Used:\n%s;" % sql


if __name__ == '__main__':
main()

Wednesday, May 5, 2010

PostgreSQL 9.0 beta released

Finally, they supported the first version of built-in replication. Very exciting feature.

http://www.postgresql.org/docs/9.0/static/high-availability.html

Tuesday, April 27, 2010

Begin to learn tcl

tcl is a very interesting language. I don't want to spend time to learn through it, but I need to learn to go through my work. It looks useful tool. However, although inefficient in python about what tcl does, I am producing code faster in python.