Hello,
I am ripping a bunch of MP3s from some CDs. Some of the segments were split into separate tracks on subsequent CDs. I am creating wave files of the split tracks and would like to concatenate the 2 wave files so that I could do an MP3 from the single wave file.
I tried using StandardFileStream and #nextPutAll: to write the 2 wave files into a single then I got an error because #nextPutAll: accepts only strings. (I forgot) :)
From looking at the File and Stream classes I didn't find how to write binary data to a file.
I am sure this is simple and I'm just not seeing/understanding how to proceed.
Any help or pointers on how to accomplish this is greatly appreciated.
Thanks.
Jimmie Houchin
Jimmie Houchin jhouchin@texoma.net wrote:
I tried using StandardFileStream and #nextPutAll: to write the 2 wave files into a single then I got an error because #nextPutAll: accepts only strings. (I forgot) :)
This is not always true. As soon as you have created the stream, you should send it the message binary. This messages replaces the standard buffer (an instance of String) with a buffer that is an instance of ByteArray. As soon as you have this buffer, you should be able to file out instances of ByteArray.
(Have a look at AbstractSound>>storeWAVOnFileNamed)
Hope this helps. Boris
Boris Gaertner wrote:
Jimmie Houchin jhouchin@texoma.net wrote:
I tried using StandardFileStream and #nextPutAll: to write the 2 wave files into a single then I got an error because #nextPutAll: accepts only strings. (I forgot) :)
This is not always true. As soon as you have created the stream, you should send it the message binary. This messages replaces the standard buffer (an instance of String) with a buffer that is an instance of ByteArray. As soon as you have this buffer, you should be able to file out instances of ByteArray.
(Have a look at AbstractSound>>storeWAVOnFileNamed)
Hope this helps. Boris
Hello Boris,
Thanks for the help. Unfortunately I'm still having problems.
Below is the latest code I've attempted in a workspace. I get this error: StandardFileStream(Object)>>error: error: aString "Throw a generic Error exception." ^Error new signal: aString
"Concatenate 2 Wave Files" | fileDir wave1 wave2 wave3 file1 file2 file3 | fileDir := FileDirectory on: 'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. file1 := fileDir fullName, '\Genesis 37a.wav'. file2 := fileDir fullName, '\Genesis 37b.wav'. file3 := fileDir fullName, '\Genesis 37k.wav'. wave1 := (StandardFileStream fileNamed: file1) binary. wave1 := ReadStream on: wave1 contentsOfEntireFile. wave2 := (StandardFileStream fileNamed: file2) binary. wave2 := ReadStream on: wave2 contentsOfEntireFile. wave3 := (StandardFileStream fileNamed: file3) binary. wave3 nextPutAll: wave1. wave3 nextPutAll: wave2. wave3 flush. wave3 close. wave2 close. wave1 close.
I've browsed the WAV code. I don't understand most of it. I've browsed lots of code to find examples of writing binary data to files. I haven't succeded in learning yet. I don't know what I'm missing.
I've banged my head on this for a few hours browsing code trying to learn.
I'm no Python expert but in less that 5 minutes I did the below and was successful at accomplishing my task.
I am very pro Squeak. It is frustrating that I was able to accomplish this so easily in Python, but have heretofore failed in Squeak. :(
Anybody know where I'm failing above?
Thanks,
Jimmie Houchin
Python shell session.
cdir = 'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\' file1name = cdir + 'Genesis 37a.wav' file2name = cdir + 'Genesis 37b.wav' file3name = cdir + 'Genesis 37.wav' file1 = file(file1name,'rb') file2 = file(file2name,'rb') file3 = file(file3name,'wb') file1contents = file1.read() file2contents = file2.read() file3.write(file1contents + file2contents) file3.flush() file3.close() file2.close() file1.close()
On Monday 31 March 2003 12:59 pm, Jimmie Houchin wrote:
Boris Gaertner wrote:
Jimmie Houchin jhouchin@texoma.net wrote:
I tried using StandardFileStream and #nextPutAll: to write the 2 wave files into a single then I got an error because #nextPutAll: accepts only strings. (I forgot) :)
This is not always true. As soon as you have created the stream, you should send it the message binary. This messages replaces the standard buffer (an instance of String) with a buffer that is an instance of ByteArray. As soon as you have this buffer, you should be able to file out instances of ByteArray.
(Have a look at AbstractSound>>storeWAVOnFileNamed)
Hope this helps. Boris
Hello Boris,
Thanks for the help. Unfortunately I'm still having problems.
Below is the latest code I've attempted in a workspace. I get this error: StandardFileStream(Object)>>error: error: aString "Throw a generic Error exception." ^Error new signal: aString
"Concatenate 2 Wave Files"
| fileDir wave1 wave2 wave3 file1 file2 file3 |
fileDir := FileDirectory on: 'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. file1 := fileDir fullName, '\Genesis 37a.wav'. file2 := fileDir fullName, '\Genesis 37b.wav'. file3 := fileDir fullName, '\Genesis 37k.wav'. wave1 := (StandardFileStream fileNamed: file1) binary. wave1 := ReadStream on: wave1 contentsOfEntireFile. wave2 := (StandardFileStream fileNamed: file2) binary. wave2 := ReadStream on: wave2 contentsOfEntireFile. wave3 := (StandardFileStream fileNamed: file3) binary. wave3 nextPutAll: wave1. wave3 nextPutAll: wave2. wave3 flush. wave3 close. wave2 close. wave1 close.
I've browsed the WAV code. I don't understand most of it. I've browsed lots of code to find examples of writing binary data to files. I haven't succeded in learning yet. I don't know what I'm missing.
I've banged my head on this for a few hours browsing code trying to learn.
I'm no Python expert but in less that 5 minutes I did the below and was successful at accomplishing my task.
I am very pro Squeak. It is frustrating that I was able to accomplish this so easily in Python, but have heretofore failed in Squeak. :(
Anybody know where I'm failing above?
Yes, when you go "wave3 nextPutAll: wave1" you're sending a ReadStream to a StandardFileStream when it wants a ByteArray.
That is, you're doing too much work <g>.
Try this (untested) code:
"Concatenate 2 Wave Files" | fileDir wave1 wave2 wave3 file1 file2 file3 | fileDir := FileDirectory on: 'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. file1 := fileDir fullName, '\Genesis 37a.wav'. file2 := fileDir fullName, '\Genesis 37b.wav'. file3 := fileDir fullName, '\Genesis 37k.wav'. wave1 := (StandardFileStream fileNamed: file1) binary contentsOfEntireFile. wave2 := (StandardFileStream fileNamed: file2) binary contentsOfEntireFile. wave3 := (StandardFileStream fileNamed: file3) binary. wave3 nextPutAll: wave1. wave3 nextPutAll: wave2. wave3 close. wave2 close. wave1 close.
Hello Ned,
Thanks for the reply. I'll study your comments and learn. :) Nice to know I'm doing to much work.
I tried your code and it got an MNU: binary.
I banged my head into that earlier too. I don't understand why it didn't understand the #binary message? My skills with the debugger are non-existent.
Thanks again.
Jimmie Houchin
Ned Konz wrote:
On Monday 31 March 2003 12:59 pm, Jimmie Houchin wrote:
Boris Gaertner wrote:
Jimmie Houchin jhouchin@texoma.net wrote:
I tried using StandardFileStream and #nextPutAll: to write the 2 wave files into a single then I got an error because #nextPutAll: accepts only strings. (I forgot) :)
This is not always true. As soon as you have created the stream, you should send it the message binary. This messages replaces the standard buffer (an instance of String) with a buffer that is an instance of ByteArray. As soon as you have this buffer, you should be able to file out instances of ByteArray.
(Have a look at AbstractSound>>storeWAVOnFileNamed)
Hope this helps. Boris
Hello Boris,
Thanks for the help. Unfortunately I'm still having problems.
Below is the latest code I've attempted in a workspace. I get this error: StandardFileStream(Object)>>error: error: aString "Throw a generic Error exception." ^Error new signal: aString
"Concatenate 2 Wave Files"
| fileDir wave1 wave2 wave3 file1 file2 file3 |
fileDir := FileDirectory on: 'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. file1 := fileDir fullName, '\Genesis 37a.wav'. file2 := fileDir fullName, '\Genesis 37b.wav'. file3 := fileDir fullName, '\Genesis 37k.wav'. wave1 := (StandardFileStream fileNamed: file1) binary. wave1 := ReadStream on: wave1 contentsOfEntireFile. wave2 := (StandardFileStream fileNamed: file2) binary. wave2 := ReadStream on: wave2 contentsOfEntireFile. wave3 := (StandardFileStream fileNamed: file3) binary. wave3 nextPutAll: wave1. wave3 nextPutAll: wave2. wave3 flush. wave3 close. wave2 close. wave1 close.
I've browsed the WAV code. I don't understand most of it. I've browsed lots of code to find examples of writing binary data to files. I haven't succeded in learning yet. I don't know what I'm missing.
I've banged my head on this for a few hours browsing code trying to learn.
I'm no Python expert but in less that 5 minutes I did the below and was successful at accomplishing my task.
I am very pro Squeak. It is frustrating that I was able to accomplish this so easily in Python, but have heretofore failed in Squeak. :(
Anybody know where I'm failing above?
Yes, when you go "wave3 nextPutAll: wave1" you're sending a ReadStream to a StandardFileStream when it wants a ByteArray. That is, you're doing too much work <g>.
Try this (untested) code:
"Concatenate 2 Wave Files" | fileDir wave1 wave2 wave3 file1 file2 file3 | fileDir := FileDirectory on: 'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. file1 := fileDir fullName, '\Genesis 37a.wav'. file2 := fileDir fullName, '\Genesis 37b.wav'. file3 := fileDir fullName, '\Genesis 37k.wav'. wave1 := (StandardFileStream fileNamed: file1) binary contentsOfEntireFile. wave2 := (StandardFileStream fileNamed: file2) binary contentsOfEntireFile. wave3 := (StandardFileStream fileNamed: file3) binary. wave3 nextPutAll: wave1. wave3 nextPutAll: wave2. wave3 close. wave2 close. wave1 close.
On Monday 31 March 2003 02:10 pm, Jimmie Houchin wrote:
Hello Ned,
Thanks for the reply. I'll study your comments and learn. :) Nice to know I'm doing to much work.
I tried your code and it got an MNU: binary.
I banged my head into that earlier too. I don't understand why it didn't understand the #binary message?
Because it was the wrong type of object to be sending a #binary to (#binary wasn't defined for that object's class).
My skills with the debugger are non-existent.
Tim's suggestions are very good, especially if you don't want to read the entire file into memory.
But you really should learn to use the debugger; it'll save you lots of time. You can even fix a method and continue your program in many cases.
If you just pop up the debugger, it'll show you the call stack. You can click on the various stack frames (the topmost list) and see where you are in each method.
You can also examine variables.
So in this case it would be good to find out what didn't understand #binary.
Ned Konz wrote:
On Monday 31 March 2003 02:10 pm, Jimmie Houchin wrote:
Hello Ned,
Thanks for the reply. I'll study your comments and learn. :) Nice to know I'm doing to much work.
I tried your code and it got an MNU: binary.
I banged my head into that earlier too. I don't understand why it didn't understand the #binary message?
Because it was the wrong type of object to be sending a #binary to (#binary wasn't defined for that object's class).
That's what I don't understand. When I browse the StandardFileStream class I see the #binary message.
My bad. (After attempting again...)
I failed to edit the email end of of lines on the fileDir. So I guess this cause the StandardFileStream to fail initialization? Nevertheless, when I corrected it your code worked with the exception of the MNU: close on 'wave2'. According to the debugger it was a 'aByteArray'. I imagine I can remove the close messages for wave1 and wave2.
My skills with the debugger are non-existent.
Tim's suggestions are very good, especially if you don't want to read the entire file into memory.
But you really should learn to use the debugger; it'll save you lots of time. You can even fix a method and continue your program in many cases.
I always click on 'debug' sometimes I do learn why my code fails. Sometimes I just learn I don't know very much. :)
I regularly learn I didn't place a '.' at the end of a statement.
If you just pop up the debugger, it'll show you the call stack. You can click on the various stack frames (the topmost list) and see where you are in each method.
You can also examine variables.
So in this case it would be good to find out what didn't understand #binary.
When I am the debugger window for this code it shows the 'wave...' variables to be 'nil'. I guess this goes back to a failed initialization I spoke of above.
Thanks again for your help. I am getting an education. :)
Jimmie Houchin
On Monday 31 March 2003 02:51 pm, Jimmie Houchin wrote:
I failed to edit the email end of of lines on the fileDir. So I guess this cause the StandardFileStream to fail initialization?
Yes, you probably don't have a directory with a CR in the middle of its name.
In production code you should check for whether opens succeeded or not. Using Tim's code:
fileDir := FileDirectory default directoryNamed:'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. fileDir exists ifFalse: [ ... do something ... ]. wave3 := fileDir fileNamed: 'Genesis 37k.wav'. "Will create/clobber a file" wave2 := fileDir oldFileNamed: 'Genesis 37b.wav'. "Will pop up a requester" wave1 := fileDir oldFileNamed: 'Genesis 37a.wav'. "Will pop up a requester"
Nevertheless, when I corrected it your code worked with the exception of the MNU: close on 'wave2'. According to the debugger it was a 'aByteArray'. I imagine I can remove the close messages for wave1 and wave2.
Yes, you're right. I was working too fast.
My skills with the debugger are non-existent.
Tim's suggestions are very good, especially if you don't want to read the entire file into memory.
But you really should learn to use the debugger; it'll save you lots of time. You can even fix a method and continue your program in many cases.
I always click on 'debug' sometimes I do learn why my code fails. Sometimes I just learn I don't know very much. :)
I regularly learn I didn't place a '.' at the end of a statement.
At least you don't have to worry about indentation!
If you just pop up the debugger, it'll show you the call stack. You can click on the various stack frames (the topmost list) and see where you are in each method.
You can also examine variables.
So in this case it would be good to find out what didn't understand #binary.
When I am the debugger window for this code it shows the 'wave...' variables to be 'nil'. I guess this goes back to a failed initialization I spoke of above.
If you're in the code prior to the assignment to the wave* variables, they will be nil.
Ned Konz wrote:
On Monday 31 March 2003 02:51 pm, Jimmie Houchin wrote:>
I failed to edit the email end of of lines on the fileDir. So I guess this cause the StandardFileStream to fail initialization?
Yes, you probably don't have a directory with a CR in the middle of its name.
In production code you should check for whether opens succeeded or not. Using Tim's code:
fileDir := FileDirectory default directoryNamed:'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. fileDir exists ifFalse: [ ... do something ... ]. wave3 := fileDir fileNamed: 'Genesis 37k.wav'. "Will create/clobber a file" wave2 := fileDir oldFileNamed: 'Genesis 37b.wav'. "Will pop up a requester" wave1 := fileDir oldFileNamed: 'Genesis 37a.wav'. "Will pop up a requester"
[snip]
Thanks for the education. I got busy at work and haven't had the opportunity to analyse Tim's code yet. Thanks for the pointer about production code and the requesters. More best practice principles. :)
Maybe I'm not the only one learning from my mistakes. :)
Jimmie Houchin
"Concatenate 2 Wave Files" | fileDir wave1 wave2 wave3 file1 file2 file3 | fileDir := FileDirectory on: 'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'.
First problem; you should use FileDirectory default directoryNamed:'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. This will get you a proper file directroy for your platform rather than the generic one. Much more useful.
file1 := fileDir fullName, '\Genesis 37a.wav'.
Second problem; using strings like this. It will work but it's really bad practice. After all, there is a fair bit of code already written to make sure filenames are acceptable. Why work around it? Use fileDir fileNamed: 'Genesis.wav' etc. instead.
file2 := fileDir fullName, '\Genesis 37b.wav'. file3 := fileDir fullName, '\Genesis 37k.wav'. wave1 := (StandardFileStream fileNamed: file1) binary. wave1 := ReadStream on: wave1 contentsOfEntireFile.
Third problem; why are you makinga ReadStream on the contents of the file? The stuff is already accesible from the file stream and if it is a big file you will rapidly run out of memory.
wave2 := (StandardFileStream fileNamed: file2) binary. wave2 := ReadStream on: wave2 contentsOfEntireFile. wave3 := (StandardFileStream fileNamed: file3) binary. wave3 nextPutAll: wave1.
Fourth problem; you're trying to add a ReadStream to another Stream. You could get away with 'wave3 nextPutAll: wave1 contents' in most cases.
wave3 nextPutAll: wave2. wave3 flush. wave3 close. wave2 close. wave1 close.
Take a look at FileDirectory copyFileNamed:to: to see a similar process.
You might find this more successful:- | fileDir wave3 wave2 wave1 buffer| fileDir := FileDirectory default directoryNamed:'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. wave3 := fileDir fileNamed: 'Genesis 37k.wav'. wave2 := fileDir oldFileNamed: 'Genesis 37b.wav'. wave1 := fileDir oldFileNamed: 'Genesis 37a.wav'. buffer := String new:5000. wave3 setToEnd. [wave2 atEnd] whileFalse: [wave3 nextPutAll: (wave2 nextInto: buffer)]. [wave1 atEnd] whileFalse: [wave3 nextPutAll: (wave1 nextInto: buffer)]. wave1 close. wave2 close. wave3 close.
WARNING I didn't test this. Left as an exercise for the student.
tim
Hello Tim,
Thanks for the reply.
I appreciate the best practice information. I am slowly working through Beck's book. I'll take some time reading your information below so that I learn it well.
In the mean time I copied and pasted your code and it worked right out of the box. (after editing some email end of lines on the fileDir string)
It had the wave order backwords, but that is insignificant to the lesson.
Thanks again for the education. Now off to learn the lesson. :)
Jimmie Houchin
Tim Rowledge wrote:
"Concatenate 2 Wave Files" | fileDir wave1 wave2 wave3 file1 file2 file3 | fileDir := FileDirectory on: 'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'.
First problem; you should use FileDirectory default directoryNamed:'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. This will get you a proper file directroy for your platform rather than the generic one. Much more useful.
file1 := fileDir fullName, '\Genesis 37a.wav'.
Second problem; using strings like this. It will work but it's really bad practice. After all, there is a fair bit of code already written to make sure filenames are acceptable. Why work around it? Use fileDir fileNamed: 'Genesis.wav' etc. instead.
file2 := fileDir fullName, '\Genesis 37b.wav'. file3 := fileDir fullName, '\Genesis 37k.wav'. wave1 := (StandardFileStream fileNamed: file1) binary. wave1 := ReadStream on: wave1 contentsOfEntireFile.
Third problem; why are you makinga ReadStream on the contents of the file? The stuff is already accesible from the file stream and if it is a big file you will rapidly run out of memory.
wave2 := (StandardFileStream fileNamed: file2) binary. wave2 := ReadStream on: wave2 contentsOfEntireFile. wave3 := (StandardFileStream fileNamed: file3) binary. wave3 nextPutAll: wave1.
Fourth problem; you're trying to add a ReadStream to another Stream. You could get away with 'wave3 nextPutAll: wave1 contents' in most cases.
wave3 nextPutAll: wave2. wave3 flush. wave3 close. wave2 close. wave1 close.
Take a look at FileDirectory copyFileNamed:to: to see a similar process.
You might find this more successful:- | fileDir wave3 wave2 wave1 buffer| fileDir := FileDirectory default directoryNamed:'C:\Documents and Settings\Jimmie\My Documents\My Music\MP3\Alexander Scourby\waves'. wave3 := fileDir fileNamed: 'Genesis 37k.wav'. wave2 := fileDir oldFileNamed: 'Genesis 37b.wav'. wave1 := fileDir oldFileNamed: 'Genesis 37a.wav'. buffer := String new:5000. wave3 setToEnd. [wave2 atEnd] whileFalse: [wave3 nextPutAll: (wave2 nextInto: buffer)]. [wave1 atEnd] whileFalse: [wave3 nextPutAll: (wave1 nextInto: buffer)]. wave1 close. wave2 close. wave3 close.
WARNING I didn't test this. Left as an exercise for the student.
tim
squeak-dev@lists.squeakfoundation.org