Lesson 10 : Advance Audio Buffer | Lesson 12 : Audio Map | Contents
To work with Wave Files, we need the DvmWave as well as the DvmBasic package. If you are using Tcl shell instead of Dvm Shell, you need to load these packages by :
cd C:/Dali/doc/tutorial/ package require DvmBasic package require DvmWave
Then we're going to do the following things:
We will work with the file testsnd.wav. It is a 16-bit stereo Wave file in PCM format. You could substitute it with any other 16-bit stereo Wave file in PCM format (although it is likely to work for most other formats too). Just change the filename in red below to the one that you are using. But make sure the file is situated in the current directory of Tcl shell or Dvm Shell.
First we open the Wave file and read the whole file into a bitstream bs.
set size [file size testsnd.wav] set bs [bitstream_new $size] set bp [bitparser_new] bitparser_wrap $bp $bs set infile [open testsnd.wav r] fconfigure $infile -translation binary -buffersize 65536 bitstream_channel_read $bs $infile 0 close $infile
Now the bitstream bs contains the whole Wave file and is referenced by bitparser bp. Every Windows Wave file comprises header information before the actual audio data. These information are abstracted as Wave Header in Dali. So before reading the actual audio data, we need to parse the header information into the Wave Header first.
set hdr [wave_hdr_new] set len [wave_hdr_parse $bp $hdr]
Note that we pass in the BitParser to the command wave_hdr_parse instead of the BitStream. wave_hdr_parse return the number of bytes read from the BitParser. (This is the case for many primitives that uses the BitStream.)
Now we can retrive the information stored in the Wave Header. The following information are in the Wave Header. For the commands to inquire these fields, see the WAV package specification:
The wave file we are working with is in raw PCM format. We would like to check if it is the case. We can do this by getting the format field from the Wave Header. The format code for raw PCM is 1.
if {[wave_hdr_get_format $hdr] != 1} { puts "input file is not in pcm format." }
We can compute the total number of samples of both channels in the wave file by the formula (Note: if there are 10 samples in the left channel, 10 samples in the right channel, total number of samples of both channels is 20) :
numOfSamples = numOfBytes / (bitsPerSample / 8)
set bytes_per_sample [expr [wave_hdr_get_bits_per_sample $hdr] / 8] set nbytes [wave_hdr_get_data_len $hdr] set nsamples [expr $nbytes / $bytes_per_sample]
Now we can cast the read bitstream into 16-bit audio buffer. Generally we need to check the bits_per_sample field to find out whether to use bitstream_cast_to_audio_8 or bs_cast_to_audio_16. Here we assume it's 16-bit audio.
set audio [bitstream_cast_to_audio_16 $bs [expr $len/$bytes_per_sample] $nsamples]
We have casted the audio data portion of the bitstream into an audio buffer. It is particularly important to note that audio is just a virtual buffer sharing the same location as bitstream bs. That means you should not free bs when you still need to use audio.
We are done with the reading of Wave file. What we have now is a Wave Header containing all the neccessary information of the audio data, and a 16-bit audio buffer containing the audio data stored in the Wave file.
We are now going to play the audio data to the speaker. First we open the wave output device. The Wave Header is needed as an argument because the wave output device need to know the type of the audio data.
wave_out_open $hdr
Before we can actually play the audio buffer in audio, we need to prepare it.
wave_audio_prep_play $audio wave_audio_play $nbytes
Hear it? That's what you need to do to play a wave file in Dali. Note that the concept of wave_audio commands is different from that of the audio buffers. It takes argument in number of bytes, but not number of samples.
The command wave_out_done tells us whether the wave output device finished playing the audio data already. wave_out_done returns 0 if and only if it is playing some audio data. The following procedure uses this command to loop until the currently playing audio is finished.
proc wait {} { while {![wave_out_done]} {} }
Since the audio buffer is prepared, we could play the first half of it pretty easily. We just tell wave_audio_play that we only want to play half the number of bytes in the prepared audio.
wait wave_audio_play [expr $nbytes/2]
Playing the second half is a little more work. we need to clip out the second half of the audio and prepare it again.
set secondhalf [audio_16_clip $audio [expr $nsamples/2] [expr $nsamples/2]] wait wave_audio_prep_play $secondhalf wave_audio_play [expr $nbytes/2]
Now let's play the left and right channel individually. Note that we need to allocate memory for both channels before we could put the splited audio into them by audio_16_split
set left [audio_16_new [expr $nsamples/2]] set right [audio_16_new [expr $nsamples/2]] audio_16_split $audio $left $right
We have the left channel in left and right channel in right here. It's always safe to call wait before playing an audio
wait wave_audio_prep_play $left wave_audio_play [expr $nbytes/2] wait wave_audio_prep_play $right wave_audio_play [expr $nbytes/2]
Doesn't sound right? Yes, it's because we opened the device for playing 16-bit stereo audio. To play channels individually, we have to reopen the device for 16-bit mono audio.
wave_hdr_set_num_of_chan $hdr 1 wave_hdr_set_bytes_per_sec $hdr [expr [wave_hdr_get_bytes_per_sec $hdr] / 2] wave_hdr_set_block_align $hdr [expr [wave_hdr_get_block_align $hdr] / 2] wave_hdr_set_data_len $hdr [expr [wave_hdr_get_data_len $hdr] / 2] wait wave_out_close wave_out_open $hdr wave_audio_prep_play $left wave_audio_play [expr $nbytes/2] wait wave_audio_prep_play $right wave_audio_play [expr $nbytes/2] wait
Okay, you might have been sick of this wave audio clip already. Sorry about that. We're not going to play it any more. We'll now write its right channel to a file. But to do it correctly, we have to get a correct Wave Header. Fortunately, we have set the Wave Header to appropriate values already
Let's open the target output file.
set outfile [open testsndr.wav w] fconfigure $outfile -translation binary -buffersize 65536
Then we make a bitstream and wrap a bitparser to it. After that we can encode the header to the bitstream with the bitparser and write the bitstream to the target output file.
set hdrbs [bitstream_new 100] set hdrbp [bitparser_new] bitparser_wrap $hdrbp $hdrbs set hdrlen [wave_hdr_encode $hdr $hdrbp] bitstream_channel_write_segment $hdrbs $outfile 0 $hdrlen
Finally we cast the audio buffer containing the right channel audio data into bitstream and write the bitstream to file.
set rightbs [audio_16_cast_to_bs $right] bitstream_channel_write $rightbs $outfile 0
Remember the wave out device we opened is among one of the resources that we need to free.
wave_out_close wave_hdr_free $hdr audio_free $audio audio_free $left audio_free $right audio_free $secondhalf bitstream_free $bs bitstream_free $hdrbs bitstream_free $rightbs bitparser_free $bp bitparser_free $hdrbp close $outfile
This is the result you should get : testsndr.wav
Lesson 10 : Advance Audio Buffer | Lesson 12 : Audio Map | Contents
Last Updated : 06/09/2025 00:34:01