2013年7月9日 星期二

Digital audio essential, what I learned these days.

1. ADC & DAC concept

  PCM interface

2. sample rate
Fig. 1

Fig. 2
In these figs, we have two tracks of audio. The upper one and downside one are the same music, but with different sample rate. The upper one with 44.1khz and the downside one with 8khz.
  It's hard to see the difference between these two tracks from fig. 1, because the waveform is too small. But, when we zoom in many times, you can see the difference between these tracks in Fig. 2. With higher sample rate, more details of the wareform can be preserve; and with lower sample rate, we lose more details of the wareform of music. 
  Yes, we human have very sensible ears. Try it, you can listen and feels the difference of music with sample rate 8khz and 44.1khz.

3. how to convert music from one sample rate to another
  Audacity is a good recording tool and a good audio editing tool. But I didn't find out how to use it to convert music from one sample rate to another. Using google, I find sox can do this magic. Under ubuntu:
$ sudo apt-get install sox
 and then
$ sox AUDA.wav -t raw -r 8000 -s -c 1 auda.sln
You can get a raw file with 8khz sample rate. More detail about sox can be find on Convert WAV for Asterisk.

4. c code to play 8khz sample rate music
  p.s.: I export the music from audacity to wav file(with 16bit pcm), and then using sox to convert that file(AUDA.wav) to 8khz sample rate.

// this code modify from: using-bluetooth
// and is *NOT* a robust code
// sometimes it will broken and can't play all of the music
    char scobuffer[255];
    int num_frames = 24;
    int len;

    // open the default sound device
    if (snd_pcm_open((snd_pcm_t **)&sndhandlep, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0)
    {
        perror("snd_pcm_open playback");
        return -1;
    }

    // initialize the device to handle an 8khz, single channel,
    // little endian audio data stream
    if (snd_pcm_set_params((snd_pcm_t *)sndhandlep, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 1,
            8000, 0, 0) < 0)
    {
        perror("set_params ");
        return -1;
    }
   
    // open file of audio data
    if ((fd = open("auda.sln", O_RDONLY)) < 0) {
        perror("open");
        return -1;
    }

    while ((len = read(fd, scobuffer, num_frames * 2)) > 0)
    {
        int i;

        // send the sound data to the sound device
        // the sound device expects "frames".  since we have 16 bit, single
        // channel data, each 2 bytes is one "frame"
        snd_pcm_writei((snd_pcm_t *)sndhandlep, scobuffer, len / 2);
    }
   
    snd_pcm_close((snd_pcm_t *)sndhandlep);
    close(fd);

5. python code to send 8khz music to bluetooth headset
  p.s. : 1. these codes modify from "Bluetooth Essential" book
           2. there are two codes, using slc.py then sco-client.py
           3. I just test sony bluetooth headset HBH-DS205
           4. this code play music for 100 seconds.

  **  slc.py  **

#!/usr/bin/env python

from bluetooth import *
from select import *

sock=BluetoothSocket(RFCOMM)
# you should find the correct channel number, and replace 2 here
try: sock.connect(("00:12:34:56:78:9A", 2))
except: pass

while True:
    print "waiting ..."
   
    data = sock.recv(1024)
    print ">> %s" % data

    sock.send("\r\n+BRSF=55\r\n")
    print "<< %s" % "+BRSF=55"

    sock.send("\r\nOK\r\n")
    print "<< %s" % "OK"

    data = raw_input()
    if len(data) == 0: break

sock.close()
# end of slc.py


  ** sco-client.py **
The origin code can be find here: sco-client.py
Because blog tool can't display some characters, some code are missing.
Please add it by yourself, by copy them from the origin code link above.
#!/usr/bin/env python

import struct
import time
from bluetooth import *
import bluetooth._ as bt
# check voice settings. switch to S16 LE mono 8kHz CVSD if needed
# please add code from origin code link ********
#determine the maximum packet size
# please add code from origin code link ********
server_address = "00:12:34:56:78:9A"  # DS-205
if sco_mtu > 48:
    sco_mtu = 48
sock = BluetoothSocket(SCO)
sock.connect((server_address,))

with open('auda.sln','r') as f:
    tosend = f.read()
#send one second's worth of silence
#tosend = "\0" * 16000
sent = 0

data = ""
while sent < 1600000:
    sent += sock.send(tosend[sent:sent+sco_mtu])
    print "< %d" % sent
    data = data + sock.recv(1600000 - len(data))
    print "> %d" % len(data)

time.sleep(2)
sock.close()



張貼留言