Hello all,
Our A/D board is getting closer to service. Since we are recording audio of sorts, it would be prudent to listen to the data. That often starts with a .wav file, which I don't have. Are there any simple ways to start with 16 bit samples and a frequency and end up producing sound? At present, I can write files from Dolphin, and then read them in Squeak to create and play a SampledSound, but it would be nice to do the whole job from Dolphin. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Bill,
> Our A/D board is getting closer to service. Since we are recording audio > of sorts, it would be prudent to listen to the data. That often starts > with a .wav file, which I don't have. Creating a wav file manually is not too difficult, it just consists of a shortish header followed by the sampled data. It's some time since I dabbled in this area but, if you want, I can have a look at some old packages to see what I can find. ISTR that the header/file format is called RIFF, a MSDN search might turn up something. -- Ian Use the Reply-To address to contact me. Mail sent to the From address is ignored. |
Ian,
>>Our A/D board is getting closer to service. Since we are recording audio >>of sorts, it would be prudent to listen to the data. That often starts >>with a .wav file, which I don't have. > > > Creating a wav file manually is not too difficult, it just consists of a > shortish header followed by the sampled data. It's some time since I > dabbled in this area but, if you want, I can have a look at some old > packages to see what I can find. Google found various independent statements of the same sentiment, along with file formats. However, they don't agree on all details, and I lost a little steam when Squeak's files would not play in Media Player. Did you manage to make it work? I would certainly be able to make use of something that writes .wav format data. If you think you have something, I would greatly appreciate your searching for it. I stumbled on something intersting: there is a flag for PlaySound() that allows it to take data directly from memory. It needs to be the byte image of a .wav file, but it would allow us to create a way to play sounds w/o a file. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Bill,
> Did you manage to make it work? I would certainly be able to make use of > something that writes .wav format data. If you think you have something, > I would greatly appreciate your searching for it. OK, the following workspace code is a bit rough but it seems to work. I've fixed all the parameters (sample rate, channels etc.) so you'll obviously have to adjust them for your A/D feed. You will also need to add a WriteStream>>nextWORDPut: method to your image. All it does is create a 5 sec wav file containing a continuous 2205Hz tone (22050 samples/sec / 10 samples per cycle = 2205 Hz). I did try to make the sound a bit more interesting but decided it was a bit too difficult in a workspace :-) NB. It's a bit hazy but I seem to recall that the samplesPerSec parameter is restricted to a specific set of values.? format := 1 "WAVE_FORMAT_PCM". channels := 2. samplesPerSec := 22050. bitsPerSample := 16. blockAlign := channels * (bitsPerSample / 8). "i.e. bytes per sample" avgBytesPerSec := samplesPerSec * blockAlign. sampleCount := samplesPerSec * 5. " 5 seconds long" byteCount := sampleCount * blockAlign. "bytes needed" stream := ByteArray writeStream. stream nextPutAll: 'RIFF' asByteArray; nextDWORDPut: 4 + 8 + 16 + 8 + byteCount; nextPutAll: 'WAVE' asByteArray; nextPutAll: 'fmt ' asByteArray; nextDWORDPut: 16; nextWORDPut: format; nextWORDPut: channels; nextDWORDPut: samplesPerSec; nextDWORDPut: avgBytesPerSec; nextWORDPut: blockAlign; nextWORDPut: bitsPerSample; nextPutAll: 'data' asByteArray; nextDWORDPut: byteCount. 5 timesRepeat: ["each second" 2205 timesRepeat: ["22050 / 10" "16 bits per sample - 2 channels" stream nextDWORDPut: 16r00000000; nextDWORDPut: 16r10001000; nextDWORDPut: 16r20002000; nextDWORDPut: 16r10001000; nextDWORDPut: 16r00000000; nextDWORDPut: 16r00000000; nextDWORDPut: 16rF000F000; nextDWORDPut: 16rE000E000; nextDWORDPut: 16rF000F000; nextDWORDPut: 16r00000000]]. fs := FileStream write: 'test.wav' text: false. [fs nextPutAll: stream contents] ensure: [fs close] -- Ian Use the Reply-To address to contact me. Mail sent to the From address is ignored. |
In reply to this post by Schwab,Wilhelm K
Bill,
> [...] it would be prudent to listen to the data. That often > starts with a .wav file, which I don't have. Are there any simple ways > to start with 16 bit samples and a frequency and end up producing sound? A couple of things I've wanted to do for a long time have needed the ability to play generated waveforms -- ideally in an arbitrarily long stream, so I've been bugged by the same thing. All the easy stuff seems to require WAV files, and when you dig into how to play pure buffers of generated sound in -- say -- DirectSound it becomes insanely complicated [*]. In fact I couldn't get it to work at all. So your post prompted me to do what I should have done in the first place -- go back to Petzold and see what the older APIs are like. It turns out that they are not too bad at all (more messing than I'd like, but a lot less than there might be -- given that it's Windows code). A lot more straightforward than the "high level" stuff, in fact (typical!). I've put together a small package that turns this into a (reasonably) nice little player object. It's /totally/ brand new code, but it does seem to work on both Win2K and XP. I've put it at a temporary home here: http://ephemera.metagnostic.org/code/WOP.zip where it'll stay until I remember to remove it (which, at the latest will be when I next update my "real" website -- but that could be weeks away), or until it turns out to have fatal flaws... I repeat the caution: this is brand new code. Hardly tested at all, and never yet used in anger. But if you, or anyone, would like to take a look at it, and either use it "as is" or as a basis for something better/different, then please feel free. See the class-side of WaveOutPlayer for a few example uses. -- chris ([*] And the Java sound APIs are no better !) |
Chris,
>>[...] it would be prudent to listen to the data. That often >>starts with a .wav file, which I don't have. Are there any simple ways >>to start with 16 bit samples and a frequency and end up producing sound? > > > A couple of things I've wanted to do for a long time have needed the ability to > play generated waveforms -- ideally in an arbitrarily long stream, so I've > been bugged by the same thing. All the easy stuff seems to require WAV files, > and when you dig into how to play pure buffers of generated sound in -- say -- > DirectSound it becomes insanely complicated [*]. In fact I couldn't get it to > work at all. > > So your post prompted me to do what I should have done in the first place -- go > back to Petzold and see what the older APIs are like. It turns out that they > are not too bad at all (more messing than I'd like, but a lot less than there > might > be -- given that it's Windows code). A lot more straightforward than the "high > level" stuff, in fact (typical!). [ WaveOutPlayer bangHeadOnWall play. ] repeat. "When will I learn!!!! :)" So far I have only loaded the package and run the examples, but I'm more or less sold on it. I will exit and backup before continuing with it loaded. There is probably room for a sampled sound abstraction that holds the format and (perhaps is??) the buffer. There might be subclasses for stereo and mono to idiot proof the indexing. Question: might #mono, #mono: (and likewise for stereo) cause confusion? It might be better to encounter #monoSize:samplesPerSecond: to emphasize what is being decided. Perhaps I am missing something related to streaming?? This looks great. Thanks!!! Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Bill,
> There is probably room for a sampled sound abstraction that holds the > format and (perhaps is??) the buffer. There might be subclasses for > stereo and mono to idiot proof the indexing. There's certainly a /lot/ of room for more abstraction. It's very "Windowsy" as it is. That's mostly because I didn't (don't) have enough experience of what works and how it works to be confident that I was creating an appropriate abstraction. For instance, I only discovered late yesterday that the layout of >8-bit buffers uses signed integers. My code had worked fine with 8-bit buffers assuming unsigned bytes, but it sounded "odd" using 16-bit buffers. Only when I'd found out about that inconsistency (and fixed the code) did it work. And that's the problem: up until yesterday afternoon I'd had no idea that the abstraction might want to hide the difference between 8- and 16- bit sound. Now I'm tempted to put a layer around the buffers that makes them "look" as if they all contain data in the [-1.0, +1.0] range. But a problem with that is efficiency, the floating point calculations are expensive, and /need/ to be optimised -- which makes abstraction difficult. In any case, I'd imagine that in most applications (mine, anyway ;-) the data will come from somewhere else, such as being generated in a helper DLL, rather than created directly from Smalltalk code, so it's not too clear that the abstraction would pay for itself. I'm not saying it wouldn't -- I just don't know yet. Right now, if I were re-engineering this, I think I'd have a SoundStage (which basically holds a WAVEFORMAT and a number of internal Players) to which you could add SoundSources (which could be single buffers, lists of buffers, files, or automatically recycling rings of buffers). The notion of a buffer would almost vanish, or at least would loose its Windowsy flavour, and the Players would be hidden entirely. This is, of course, a bad case of "second system effect". What's more it could become almost as complicated as the DirectSound stuff that I was trying to avoid in the first place ;-) > Question: might #mono, #mono: (and likewise for stereo) cause confusion? Possibly. Those are only convenient short-hand methods anyway, since there isn't a preset list of valid buffer configurations. The choice (in my mind) was actually between: WAVEHDR mono: 16. or: WAVEHDR mono16. or: WAVEHDR mono16bit. The first seemed short and sweet. If you want to be explicit then: (WAVEHDR new) nBitsPerSample: 16 yourself. is even better, and would be better still if I bothered to provide sensible aliases for the Windows accessor names (which I really /ought/ to do...). BTW, I found a bug in the device capabilities stuff. I've updated the temporary webpage with the new version (1.03), but probably won't make any more changes (barring major bugs) till it gets put on the real website (where there's a chance it'll include some of the above improvements, and whatever else occurs to me as I start actually /using/ it...). -- chris |
In reply to this post by Ian Bartholomew-19
Ian,
in my 5.1.4 I get a walkback telling: WriteStream does not understand #nextWORDPut: And really: I see only a nextSDWORDPut: anInteger Am I missing some extensions? Thanks, Janos |
Janos,
> Am I missing some extensions? I mentioned in the post that adding a #nextWORDPut: method would be needed, it doesn't come with the Dolphin image. It's just .... WriteStream>>nextWORDPut: anInteger self nextPutAll: ((ByteArray new: 2) wordAtOffset: 0 put: anInteger; yourself). ^anInteger -- Ian Use the Reply-To address to contact me. Mail sent to the From address is ignored. |
Ian,
I know it is only a side branch from the main stream... NextWordPut: is not a problem. The problem is the nextDWORDPut: with the value nextDWORDPut: 16rF000F000 I just took the nextSDWORDPut: instead, and unfortunately it calls sdwordAtOffset: 0 put: anInteger; and the anInteger must not be bigger than 16r3FFFFFFF, because it is supposed to be signed integer. I tried to call sqwordAtOffset: 0 put: instead, it gave of course Out of Bound with a ByteArray of size 4. With size 8 the wav file was not playable. Do we have a kind of unsigned integer, where we can use the whole range of the 4 bytes? Thanks, Janos |
Janos,
> NextWordPut: is not a problem. The problem is the nextDWORDPut: with > the value > nextDWORDPut: 16rF000F000 This is the implementation I use: ================= nextDWORDPut: anInteger "Append a 32-bit unsigned integer in 2's complement representation as the next 4 bytes on the receiver." #CUadded. "#dwordAtOffset:put: doesn't check for negative numbers" (anInteger < 0) ifTrue: [^ self errorCantHold: anInteger]. self nextPutAll: ((ByteArray new: 4) dwordAtOffset: 0 put: anInteger; yourself). ^anInteger. ================= -- chris |
Chris, Ian,
it was my ignorance. Yes, using dwordAtOffset: 0 put: instead of sdwordAtOffset: 0 put: has solved it. Only one character difference on the face of it. Yes, after that also studying the ByteArray methods is enlightening, and I see the difference... Now it works fine. Many thanks for your patience with the struggling of a newbie, Janos |
In reply to this post by Janos Kazsoki
Janos,
> NextWordPut: is not a problem. The problem is the nextDWORDPut: with > the value > nextDWORDPut: 16rF000F000 Sorry, my mistake. Dolphin 6 does have a #nextDWORDPut: in the basic image and I incorrectly assumed it was present in Dolphin 5. Chris has posted the missing method (thanks Chris). -- Ian Use the Reply-To address to contact me. Mail sent to the From address is ignored. |
In reply to this post by Janos Kazsoki
Janos,
> Do we have a kind of unsigned integer, where we can use the whole range > of the 4 bytes? FWIW, see the code below. Please let me know if you have any problems with it. It should probably be higher in the hierarchy; suggestions are welcome. Have a good one, Bill !WriteStream methodsFor! nextDWORDPut: anInteger "Append a 32-bit unsigned integer in 2's complement representation as the next 4 bytes on the receiver." self nextPutAll: ((ByteArray new: 4) dwordAtOffset: 0 put: anInteger; yourself)! ! !WriteStream categoriesFor: #nextDWORDPut:!binary filing!public! ! -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Free forum by Nabble | Edit this page |