Reading individual bytes of a file (VBscript?)

Technical support and scripting issues

Moderators: Dorian (MJT support), JRL

Post Reply
Socks

Reading individual bytes of a file (VBscript?)

Post by Socks » Tue Oct 12, 2004 1:07 am

I have a situation where I need to grab a certain range of bytes from a WAV file and save it as a different file. Since there's nothing for handling individial bytes in Macro Scheduler, I thought to use VBScript for this purpose. But I have no idea how to program in VBScript and personally I don't want to learn a whole new programming language just to write a couple lines of code for a single macro. Basically what I need it to do is this:

read the byte at decimal offset X from file Y and store it in variable Z
append the byte in variable Z to the end of file W (and create the file if it does not exist)

Macro Scheduler would pass X, Y, and W into the VBScript section; assuming the VBScript does its job, it wouldn't need to pass anything back to Macro Scheduler. Variable Z could be completely exclusive to the VBScript area, since it's just a temporary storage variable and doesn't need to be accessed at any other point.

Of course, it would be even better if I could snag the entire byte range at once, instead of doing it one byte at a time using an incrementing counter. But I doubt this would be possible.

User avatar
Bob Hansen
Automation Wizard
Posts: 2475
Joined: Tue Sep 24, 2002 3:47 am
Location: Salem, New Hampshire, US
Contact:

Post by Bob Hansen » Tue Oct 12, 2004 3:20 am

I am not into audio files, but I have to wonder if there isn't already a program out there that will do that editing. You could have Macro Scheduler pen that program and then execute the appropriate mouse/key commands to do the editing. Just a thought..........
Hope this was helpful..................good luck,
Bob
A humble man and PROUD of it!

Socks

Post by Socks » Tue Oct 12, 2004 9:12 pm

In fact, not only do I have a program capable of doing that, the current version of my macro does exactly what you described; it loads the sound-editing program and executes the keystrokes and mouseclicks that select a sample range and save it as a different file. But I found three problems with this method:

1. The source file in question is a GIANT wave file consisting of some 1500+ sounds. Separating each individual sound and saving it using only simulated user input takes somewhere close to forever. According to my tests, you could theoretically just leave it running overnight and it would finish by morning, but here comes problem #2.

2. Sometimes the hard drive has a hiccup and it takes about 5 seconds longer than normal to save a new file. This throws off the entire timing routine and causes the macro to get stuck in an endless loop of saving the same sound over and over. I could increase the wait time after saving to avoid this, but it would more than quadruple the total time needed to extract all the sounds. There is a little "Saving..." progress bar that comes up, but WaitWindowClosed doesn't work on it and neither does WaitReady.

3. If I compile the macro as an EXE and give it to other people who want to do the same thing as me, it would require them to have a registered version of the sound editing program I used in the macro: Cool Edit Pro 2. They probably won't because Adobe acquired the company that made it a while ago and turned it into Adobe Audition. Forcing people to purchase a discontinued product just to use my program seems a tad mean-spirited.

User avatar
Bob Hansen
Automation Wizard
Posts: 2475
Joined: Tue Sep 24, 2002 3:47 am
Location: Salem, New Hampshire, US
Contact:

Post by Bob Hansen » Tue Oct 12, 2004 9:22 pm

Hmmm, will have to do some more thinking now...

Re your problem #2, the delay or hiccup of 5 seconds. I have found that Wait will frequently be too generic for dynamic operations and some other method is required.

Can you use a loop to look at file size, and just loop until file size does not change for over 30 seconds (or some other value?)...Or check to see if a file exists? Or wait to see it the work "Copying In Process" (or some other phrase) has disappeared? Or if a certain window has closed or opened?

Hmmm, I just reread your last message and saw this:
There is a little "Saving..." progress bar that comes up, but WaitWindowClosed doesn't work on it and neither does WaitReady.
I am curious about that. Can you provide some sample code where that is used. That is usually a good method for control. Would like to know better why it does not work for you.
Hope this was helpful..................good luck,
Bob
A humble man and PROUD of it!

Guest

Post by Guest » Wed Oct 13, 2004 12:54 am

Well, here's the entire giant block of code, for your viewing pleasure. I've sprinkled comments throughout so you can tell how it works. Also, I doubt that waiting to see if the file exists yet will work, because the file system creates the file first and then adds the content to it as saving progresses.


// Set some variables beforehand

Let>SK_DELAY=10
Let>back=\
Let>k=0
Let>biglist=

// Get some file locations from the user.

// The "uber-wave" is the giant sound file that contains all the sounds to be split,
// and the IDX file is an index of sound names and sample numbers that goes with the uber-wave.

// Each line of the IDX file looks like this:
// sound_name.wav 2839002 57842 22050 16
// First is the sound name, second is the sample number it starts at,
// and third is the sound length in samples. (4th and 5th don't matter)

// This section includes checks to make sure the provided files exist,
// and a check for the sound output directory that offers to create it
// if it doesn't already exist.

Input>cool,Location of Cool Edit Pro 2.0 executable?,c:\program files\coolpro2\coolpro2.exe
IfFileExists>%cool%,cont1
Goto>filebad
Label>cont1
Input>wave,Location of uber-wave to be split?,bigsound.wav
IfFileExists>%wave%,cont2
Goto>filebad
Label>cont2
Input>inde,Location of IDX file to read from?,bigsound.idx
IfFileExists>%inde%,cont3
Goto>filebad
Label>cont3
Input>save,Location to save sound files in? (no trailing backslash),c:\splitsounds
IfDirExists>%save%,cont4
Goto>dirbad
Label>cont4

// This section is a loop that reads every line from the IDX file
// and then displays it in a window alongside a column of numbers.
// This way the user can choose to start the extraction at any sound
// they want by typing in the corresponding number.

// "Biglist" is what will eventually be displayed for the user to see.
// Each iteration, it reads the next line from the IDX file and
// separates it into entries divided by the space character.
// The only one we're concerned about for now is the first entry
// (the sound name).

// Through a series of ConCat commands, the value of K
// (which increments every line) is added to the end of Biglist,
// then a space, then the sound name (from entry_1), then
// a line break. When the contents of Biglist are displayed in
// a window, the result is a column of numbers on the left and
// their corresponding sound names on the right.

MessageModal>The IDX file will now be read. This takes about 10 seconds for the largest file. Click Okay to start reading.
Label>idxreadloop
Let>k=k+1
ReadLn>%inde%,k,line
If>line=##EOF##,cont5
Separate>line, ,entry
ConCat>biglist,k
ConCat>biglist,
ConCat>biglist,entry_1
ConCat>biglist,%CRLF%
Goto>idxreadloop
Label>cont5
MessageModal>Pick a sound to start at and input its number into the next dialog box. (you can expand this window horizontally to prevent wrap-around and vertically to view more lines at once) %CRLF% %CRLF% %biglist%

// Once the user picks a sound number from Biglist to
// start at, they type it into this dialog box.

// In the largest wave file that could be used with this
// program, there's a bitdepth shift that prevents Cool
// Edit Pro from reading the file properly within a certain
// area. If the given sound number is within this area,
// the program exits with a warning. If it's not, the warning
// is still given, but the program continues on its way.
// (as the message says, there's a Goto command that
// makes the program skip over this "bad section")

Input>k,Sound number to start at?,1
If>k>1126,check
Goto>keepgoing
Label>check
If>kkeepgoing
MessageModal>Warning: Due to a bitdepth shift, sounds 1127 through 1436 in bigsound.wav cannot be extracted. These sounds will be skipped over if they are encountered during extraction.
Run Program>%cool%
WaitWindowOpen>Cool Edit Pro -*
Wait>2
Press CTRL
Send Character/Text>o
Release CTRL
WaitReady>0
Send Character/Text>%wave%
Press ENTER
Ask>Click Yes to start the loop or No if the program/wave did not load.,asked
If>asked=NO,end

// This is the main loop of the program. It starts off with a quick
// check for that "bad section" I mentioned earlier. Then it reads
// the next line from the IDX file and separates the entries we need;
// the sound name, start sample number, and length in samples.
// These last two values need to be divided by 2 because of the way
// Cool Edit Pro reads the sound file. Once that's done, we have the
// variables "filename", "startsample", and "lengthsample".

// If the line is beyond the end of the file, the program terminates.

Label>loop
If>k=1127,replace
Label>resume
ReadLn>%inde%,k,line
If>line=##EOF##,end
Separate>line, ,entry
Let>filename=entry_1
Let>startsample=entry_2/2
Let>lengthsample=entry_3/2

// This section puts the appropriate values in the clipboard
// and pastes them into the "Selection Start" and "Selection
// Length" boxes in Cool Edit Pro. It assumes the window is
// maximized at 1024x768 resolution. The short delays
// between keypresses and mouse clicks are to avoid a glitch
// where Windows doesn't finish processing the previous action
// before the next one is performed, causing the second action
// to not register at all.

SetFocus>Cool Edit Pro*
PutClipBoard>startsample
MouseMove>820,640
Wait>0.1
LClick
Wait>0.1
Press Backspace
Wait>0.1
Press CTRL
Send Character/Text>v
Release CTRL
Wait>0.1
Press Enter
PutClipBoard>lengthsample
MouseMove>960,640
Wait>0.1
LClick
Wait>0.1
Press Backspace
Wait>0.1
Press CTRL
Send Character/Text>v
Release CTRL
Wait>0.1
Press Enter
Wait>0.1

// This section copies the newly-created section to the
// clipboard and pastes it into a new file (Ctrl-Shift-N).
// The delays have a wide safety margin to avoid the
// aforementioned hard drive hiccups.

Press CTRL
Send Character/Text>c
Release CTRL
Wait>1
Press CTRL
Press SHIFT
Send Character/Text>n
Release SHIFT
Release CTRL
Wait>1.5

// Now we save the copied sound. Thankfully, Macro Scheduler
// can identify the Save Waveform As box, and simply type in
// the filename as soon as it comes up. To get the sound file
// into the directory the user gave at the start of the program,
// that directory is concatenated with a backslash and then with
// the filename (I defined "back" at the top of the program).

// The double ENTER press 0.2 seconds apart means that if a
// "this file already exists" dialog box comes up, it will choose
// the default option "Overwrite". If the box doesn't come up,
// the ENTER press doesn't seem to affect anything else.

Press CTRL
Send Character/Text>s
Release CTRL
WaitWindowOpen>Save Waveform As
Let>conc=save
ConCat>conc,back
ConCat>conc,filename
PutClipBoard>conc
Press CTRL
Send Character/Text>v
Release CTRL
Press Enter
Wait>0.2
Press Enter

// After a short delay to allow for the save time, the newly-saved
// sound file is closed with Ctrl-W, bringing Cool Edit Pro back to
// the giant sound file. Home abandons the current selection and
// returns the editing cursor to the start of the file.

// If the program is going to screw up, it does so here.
// The occasional hard drive hiccup will cause the macro
// to wait for too short of a time and so the Ctrl-W keypress
// is ignored. This causes the program to enter a loop of saving
// the exact same sound file under the names of all the following
// sounds in the IDX file.


// All that's left to do at this point is increment the counter variable
// and go back to the beginning.

Wait>2
Press CTRL
Send Character/Text>w
Release CTRL
Wait>1
Press Home
Let>k=k+1
Goto>loop

// Here's the quick "skip over the bad spot" routine.

Label>replace
Let>k=1437
Goto>resume

// The error that occurs if the user inputs a nonexistant file.

Label>filebad
MessageModal>Error: That file does not exist.
Goto>end

// The error that occurs if the user inputs a nonexistant directory.
// Asks if the user wants to create it.

Label>dirbad
Ask>Error: That directory does not exist. Would you like to create it?,asked
If>asked=NO,end
CreateDir>%save%
Goto>cont4

// And lastly, here's the "bad spot" error message
// and the all-powerful "end" label.

Label>error
MessageModal>Error: Sounds 1127 through 1436 in 3dsounds.wav cannot be extracted due to a bitdepth shift. Please choose a number lower than 1127 or higher than 1436; this area will be automatically skipped over if it is encountered during extraction.
Label>end
Message>Uber-Wave Splitter has terminated.

User avatar
Bob Hansen
Automation Wizard
Posts: 2475
Joined: Tue Sep 24, 2002 3:47 am
Location: Salem, New Hampshire, US
Contact:

Post by Bob Hansen » Wed Oct 13, 2004 1:18 am

for your viewing pleasure
Someone has a funny sense of humor. :D

Can you provide a log file shot for this section of the code when it fails?
// The double ENTER press 0.2 seconds apart means that if a
// "this file already exists" dialog box comes up, it will choose
// the default option "Overwrite". If the box doesn't come up,
// the ENTER press doesn't seem to affect anything else.

Press CTRL
Send Character/Text>s
Release CTRL
WaitWindowOpen>Save Waveform As
Let>conc=save
ConCat>conc,back
ConCat>conc,filename
PutClipBoard>conc
Press CTRL
Send Character/Text>v
Release CTRL
Press Enter
Wait>0.2
Press Enter

// After a short delay to allow for the save time, the newly-saved
// sound file is closed with Ctrl-W, bringing Cool Edit Pro back to
// the giant sound file. Home abandons the current selection and
// returns the editing cursor to the start of the file.

// If the program is going to screw up, it does so here.
// The occasional hard drive hiccup will cause the macro
// to wait for too short of a time and so the Ctrl-W keypress
// is ignored. This causes the program to enter a loop of saving
// the exact same sound file under the names of all the following
// sounds in the IDX file.

// All that's left to do at this point is increment the counter variable
// and go back to the beginning.

Wait>2
Press CTRL
Send Character/Text>w
Release CTRL
Wait>1
Press Home
Let>k=k+1
Goto>loop
Last edited by Bob Hansen on Fri Oct 15, 2004 1:36 am, edited 1 time in total.
Hope this was helpful..................good luck,
Bob
A humble man and PROUD of it!

User avatar
support
Automation Wizard
Posts: 1450
Joined: Sat Oct 19, 2002 4:38 pm
Location: London
Contact:

Post by support » Wed Oct 13, 2004 12:38 pm

You can read/write binary data from to files with VBScript and the ADODB.Stream object.

The following example copies the first n bytes of a file to another. I doubt this will work with .wav files since I expect there is information in the end of the file that is needed. However, it gives you some idea of what is possible and I see no reason why you can't use ADODB.Stream to grab certain bytes from a file. Also look at MidB and LenB functions.


VBSTART

Sub CopyNBytes(SourceFile,DestFile,Bytes)
Const adTypeBinary = 1
Const adSaveCreateOverWrite = 2
Dim BinaryStream

'Create the BinaryStream object
Set BinaryStream = CreateObject("ADODB.Stream")

'Set it up and load in the source file
BinaryStream.Type = adTypeBinary
BinaryStream.Open
BinaryStream.LoadFromFile SourceFile

'Truncate the stream
BinaryStream.Position = Bytes
BinaryStream.SetEOS

'Save the stream to the destination file
BinaryStream.SaveToFile DestFile, adSaveCreateOverWrite
End Sub

VBEND

VBRun>CopyNBytes,c:\file.ext,c:\newfile.ext,5000
MJT Net Support
[email protected]

Socks

Post by Socks » Wed Oct 13, 2004 7:23 pm

Thanks a lot for that script! Just saving the range of bytes does work for WAV files, this format is usually called "raw" because it has no header (most media players can't handle it). Many sound editing programs out there (including some that are free) will do a batch conversion of the files where you give it a list of raw wave files and tell it what the sample rate and bitdepth is, and it tacks the appropriate header onto the top of each file. If I was feeling adventurous, I could probably write another program that does the header-adding business, since the header only needs to contain a very small amount of data that is easy to calculate for the most part.

Looking at your script, it looks like there's only one more command that I need. I'm assuming that "BinaryStream.SetEOS" cuts off the rest of the stream beyond the current position. All I need is another command that cuts off whatever part of the stream is before the current position, and my day will be made. :)

User avatar
support
Automation Wizard
Posts: 1450
Joined: Sat Oct 19, 2002 4:38 pm
Location: London
Contact:

Post by support » Wed Oct 13, 2004 8:06 pm

Set Position, then read from there into a new stream. Check out MidB and LenB functions. You should check the docs for ADODB too - stream has other properties and methods.
MJT Net Support
[email protected]

User avatar
support
Automation Wizard
Posts: 1450
Joined: Sat Oct 19, 2002 4:38 pm
Location: London
Contact:

Post by support » Wed Oct 13, 2004 9:05 pm

Try this example. This takes an extra parameter, StartPos (byte position to copy from), and copies out n Bytes from that position:


VBSTART

Sub CopyNNBytes(SourceFile,DestFile,StartPos,Bytes)
Const adTypeBinary = 1
Const adSaveCreateOverWrite = 2
Dim BinaryStream
Dim OutStream

'Create the BinaryStream object
Set BinaryStream = CreateObject("ADODB.Stream")

'Set it up and load in the source file
BinaryStream.Type = adTypeBinary
BinaryStream.Open
BinaryStream.LoadFromFile SourceFile

'Create OutStream
Set OutStream = CreateObject("ADODB.Stream")
OutStream.Type = adTypeBinary
OutStream.Open

'Copy Bytes of input stream to output stream from StartPos
BinaryStream.Position = StartPos
BinaryStream.CopyTo OutStream, Bytes

'Save the stream to the destination file
OutStream.SaveToFile DestFile, adSaveCreateOverWrite
End Sub

VBEND

VBRun>CopyNNBytes,c:\source.wav,c:\dest.wav,5000,5000


A good way to test that this works is to run it against a simple text file with one line "1234567890". Set StartPos to 5 and Bytes to 2 and the resultant file will contain only "67".

So I think this should do the trick for copying a segment from within a .wav file.
MJT Net Support
[email protected]

Guest

Post by Guest » Thu Oct 14, 2004 11:54 pm

Thanks again, it does indeed do the trick. I just discovered that, although the WAV header contains data for the length of the file, most if not all media applications don't even use it (going to the file system is probably faster). Using a hex editor, I took one of the raw wave files created by the macro, copied the header off the original uber-wave, and slapped it onto the start. It worked perfectly fine, despite the fact that the lengths in the header were completely wrong. So basically I'm trying to just add on exactly the same header to all the wave files before I write out their contents.

I tried to put in another "BinaryStream.CopyTo Outstream" command by copying and pasting the header in text format, but that caused a compile error (I didn't really expect it to work anyway). Is there a way to write out an arbitrary string of bytes without getting it from another source? Methinks it would involve setting a constant beforehand, but I have no idea what its contents would look like. Certainly not how it looks in Notepad or somesuch, it most likely returns an "Invalid Character" error no matter where the character is.

Socks

Post by Socks » Thu Oct 14, 2004 11:58 pm

Ach, that was stupid of me. The above post is authored by yours truly. :wink:

rullbandspelare
Pro Scripter
Posts: 149
Joined: Tue Mar 23, 2004 9:11 pm

Post by rullbandspelare » Sat Jun 09, 2012 11:06 am

Hi!
A follow up on this one:

The script works very well, but I dont know the start position!
I would like to know the position of a certain string in the binary file.
Is this possible with VBSCRIPT?

Thanks for any input!

rullbandspelare
Pro Scripter
Posts: 149
Joined: Tue Mar 23, 2004 9:11 pm

Post by rullbandspelare » Wed Jun 13, 2012 5:43 pm

No one got any idea?

Now I had to do a solution using gsar.exe to find the position of the string.
http://gnuwin32.sourceforge.net/packages/gsar.htm

Run>cmd /c gsar.exe -bhsHEADER "%PathFileName%">gsar.out
ReadFile>gsar.out,HexGsar
MidStr>HexGsar,3,4,HexVal
VBEval>CInt("&H" & "%HexVal%"),Ofset

I realy would like a cleaner solutioon.

Post Reply
Sign up to our newsletter for free automation tips, tricks & discounts