Saving WAV files
Blitz3D Forums/Blitz3D Programming/Saving WAV files
| ||
Hi. This site: http://www.topherlee.com/software/pcm-tut-wavformat.html shows the format for a WAV file, which I'm trying to create. However, I'm not sure how to save certain aspects of the header. Am I right that text portions like "RIFF" can be saved as: WriteString(myFile, "RIFF") Also, how would a number like 44100 be saved in four bytes, and how would numbers like 2 be save in 2 bytes? Should each byte be saved separately with WriteByte()? Would I then need to convert to hex first? Thanks! |
| ||
WriteString would add a 4 byte length header to the value being written, which would likely bork the file format. For this sort of thing you need to use banks ( http://www.blitzbasic.com/b3ddocs/command_list_2d_cat.php?show=Bank ) so that you have precise control of the data format. You can save a bank to file using WriteBytes ( http://www.blitzbasic.com/b3ddocs/command.php?name=WriteBytes&ref=2d_cat ) and read it in using ReadBytes ( http://www.blitzbasic.com/b3ddocs/command.php?name=ReadBytes&ref=2d_cat ). |
| ||
Something to consider is that even if you get the format right you may need to also know whether the file's bytes are stored in little or big endian format. Most of the time it will be fine....but every now and then.... |
| ||
I'm examining the tada.wav file in Windows\Media to see how it works as well. I've used banks before so I'll try them again as well. Thanks. |
| ||
Here's the first portion of tada.wav (interestingly it is identical to the one in the link in the OP):5249 4646 245a 0400 5741 5645 666d 7420 1000 0000 0100 0200 44ac 0000 10b1 0200 0400 1000 6461 7461 005a 0400 Here it is broken down: 5249 4646 - "RIFF" 245a 0400 - Size of file 5741 5645 - "WAVE" 666d 7420 - "fmt " 1000 0000 - 16 0100 - 1 0200 - 2 44ac 0000 - 44100 10b1 0200 - 176400 0400 - 4 1000 - 16 6461 7461 - "data" 005a 0400 - Size of data portion of file So I would guess that PokeInt would work for the four byte ints, and PokeShort would work for the two byte ints. The strings would require, perhaps, four PokeBytes with the ascii values for "R", "I", "F", "F". Does that make sense? |
| ||
Yep. |
| ||
FYI it works. The code is below, if anyone wants to save sound files, this will give you some idea of the process. You need a sound type with an array of frequency values, of course. I'm working on a random sound generator. There are lots of methods to explore in terms of coming up with frequencies to include, for example creating frequency patterns that are variations of musical instruments or other sounds. Then the spectrum has to be transformed to a waveform and output to disk. So far I've just created simple notes and chords, but now I can extend it. This function gives me the ability to save the file. Note that the sound data doesn't go from 0 to 255 as I thought, but instead wraps around from 0, so negative numbers appear below 256, so to speak. Anyway, thanks all for the help! Function SaveWav(name$,sound.Sound) Local i,j,index,totalAmps Local result Local fileName$="C:\"+name$ Local riff4$="RIFF" Local fileSize4=44+SAMPLERATE*4;sound\length*4 Local wave4$="WAVE" Local fmt4$="fmt " Local formatData4=16 Local formatType2=1 Local numChannels2=2 Local sampleRate4=44100 Local sbcd4=176400 Local bitsPerSampleTimesChannels2=4 Local bitsPerSample2=16 Local data4$="data" Local soundSize4=sound\length*4 Local b=CreateBank(fileSize4) PokeByte(b,0,Asc("R")) PokeByte(b,1,Asc("I")) PokeByte(b,2,Asc("F")) PokeByte(b,3,Asc("F")) PokeInt(b,4,fileSize4) PokeByte(b,8,Asc("W")) PokeByte(b,9,Asc("A")) PokeByte(b,10,Asc("V")) PokeByte(b,11,Asc("E")) PokeByte(b,12,Asc("f")) PokeByte(b,13,Asc("m")) PokeByte(b,14,Asc("t")) PokeByte(b,15,Asc(" ")) PokeInt(b,16,formatData4) ; 16 PokeShort(b,20,formatType2) ; 1 PokeShort(b,22,numChannels2) ; 2 PokeInt(b,24,sampleRate4) ; 44100 PokeInt(b,28,sbcd4) ; 176400 PokeShort(b,32,bitsPerSampleTimesChannels2) ; 4 PokeShort(b,34,bitsPerSample2) ; 16 PokeByte(b,36,Asc("d")) PokeByte(b,37,Asc("a")) PokeByte(b,38,Asc("t")) PokeByte(b,39,Asc("a")) PokeInt(b,40,soundSize4) ; Sound Data Local amp ; We have to convert from 128 at the center of the curve to 0 (with negative values marked as less then 256 instead of 0) For i=0 To sound\sampleRate-1 amp=sound\waveformFreq[i]-128 If amp<0 Then amp=amp+256 PokeShort(b,43+i*4,amp);sound\waveformFreq[i]) PokeShort(b,43+i*4+2,amp);sound\waveformFreq[i]) Next ; Save the file Local myFile$=WriteFile(fileName$) result=WriteBytes(b,myFile,0,fileSize4) CloseFile(myFile) Print fileName+" saved." Return result End Function |
| ||
I could be wrong, but didn't you set it to 16 bit in the header? Values -128 to 127 are 8 bit. So maybe you should instead use values of -($ffff/2) to ($ffff/2). There may also be one parameter in the header that defines whether the data is signed or unsigned integer. |
| ||
jfk & Matty: (Regarding 16 bit vs 8 bit and big vs little endianness) I got the sound working fine at 8 bit, but then I read jfk's comment and tried putting it into 16 bit format and found it was hard to get it to work. I was using PokeByte instead of PokeShort so I could have control over which byte went where, but the results were random or just weird. I finally realized that my offset is incorrect in the original program above. Note the offset 43; it should be 44 if the previous PokeInt was 4 bytes starting at at byte 40. So the 8 bit value poked as a PokeShort, but offset by one byte, seemed to work fine! Anyway I found it amusing. Now I'm using the code above, but corrected to 44 offset, and using 16 bit sound and it works. Thanks so much guys. Hopefully I'll have a random sound effect generator to post soon. |