Lesson 10. Advance Audio Buffer


Lesson 9 : Audio Buffers | Lesson 11 : Wave Files Basic | Contents


In this lesson, we will generate a 16-bit stereo audio. Its left channel would be a square wave signal of amplitude 30000 and frequency 4kHz while its right channel would be a square wave signal of amplitude 30000 and frequency 8kHz. The sampling rate would be 44.1kHz.

We'll do it in a twisted but cute way in order to illustrate the philosophy of audio buffers. We will first generate a 2kHz mono square wave signal. Then we'll extract the 4kHz and 8kHz ones from it.

First we load the package DvmBasic and allocate memory for the 2kHz signal of length 16 seconds.

package require DvmBasic
set sampling_rate 44100
set signal2k [audio_16_new [expr 16*$sampling_rate]]

Next we initialize the 2kHz signal. The way we do it is by using (offset, stride) pairs supported by commands ending with _some (e.g., audio_16_set_some). The first line in the for loop is setting samples in audio to 30000 every period samples. After half_period of iterations, this line would set the first half of each wavelength to 30000. The second line sets the second half of each wavelength to -30000 in a similar way.

set period [expr ($sampling_rate / 2000)]
set half_period [expr $period / 2]
for {set i 0} {$i < $half_period} {incr i} {
    audio_16_set_some $signal2k 30000 $i $period
    audio_16_set_some $signal2k -30000 [expr $i+$half_period] $period
}

We now have the 2kHz signal already! Can't wait to hear the result? We need the DvmWave package and the following procedure to play audio buffer. For now don't worry about the details. Just abstract the following procedures as playing the audio buffer with specified format, number of channels, sampling rate and bit rate. You'll learn about how to deal with wave files and how to do control wave output device in Lesson 11.

package require DvmWave
proc play_audio {audio format nchan samprate bits} {
    set wavehdr [wave_hdr_new]
    wave_hdr_set_format $wavehdr $format
    wave_hdr_set_num_of_chan $wavehdr $nchan
    wave_hdr_set_samples_per_sec $wavehdr $samprate
    wave_hdr_set_bytes_per_sec $wavehdr [expr $samprate*$nchan*$bits/8]
    wave_hdr_set_block_align $wavehdr [expr $nchan*$bits/8]
    wave_hdr_set_bits_per_sample $wavehdr $bits
    wave_out_open $wavehdr
    wave_audio_prep_play $audio
    wave_audio_play [expr [audio_get_num_of_samples $audio]*$bits/8]
    while {![wave_out_done]} {}
    wave_hdr_free $wavehdr
    wave_out_close
}

Now we can play the 2kHz signal (better turn down the volume before you do this one!)

play_audio $signal2k 1 1 $sampling_rate 16

With the 2kHz signal, we could extract the 4kHz and 8kHz from it by *copy_some commands. The 4kHz signal is half the length of the 2kHz signal while the 8kHz is 1/4 the length of the 2kHz one. We would create this stereo buffer by having a stereo audio buffer with each channel having half the number of samples of signal2k. That means the stereo buffer have the same total number of samples as signal2k when both channels are added together.

set signal [audio_16_new [audio_get_num_of_samples $signal2k]]

The way to get the 4kHz signal is copying one sample out from the 2kHz signal every 2 samples. We directly put the result into the left channel of signal.

audio_16_copy_some $signal2k $signal 0 2 0 2

We get the 8kHz one by copying one sample every 4 samples. We directly put the result into the right channel of signal.

audio_16_copy_some $signal2k $signal 0 4 1 2

Finally we need to chop off the last half of the signal because we only filled first half of the right channel. (Note that the command right before has unequal source and destination stride, which means the destination cannot be completely filled by data from the source buffer.)

set signal2 [audio_16_clip $signal 0 [expr [audio_get_num_of_samples $signal]/2]]

Now let's listen the the result.

play_audio $signal2 1 2 $sampling_rate 16

We would write the data to file as raw PCM format so as to examine the buffer contents.

set file [open signal.pcm w]
fconfigure $file -translation binary -buffersize 65536

We can directly cast the audio buffer into bitstream without doing any copying.

set bs [audio_16_cast_to_bitstream $signal2]

Then we write out the bitstream and free up resources.

bitstream_channel_write $bs $file 0
close $file
bitstream_free $bs
audio_free $signal2k
audio_free $signal
audio_free $signal2

You should get a pcm file identical to this : signal.pcm

Source code for this lesson : l10.tcl


Lesson 9 : Audio Buffers | Lesson 11 : Wave Files Basic | Contents


Last Updated :