Lesson 9 : Audio Buffers
| Lesson 11 : Wave Files Basic | ContentsIn 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 | ContentsLast Updated :