WAV file splitter program?
#1
Has anyone done or seen any QB/QB64/QB64PE code that takes a WAV file "MyAlbum.wav" and a text file with track times, in a format like:

0:00 song title #1
3:01 my song #2
4:18 another track
9:49 a long one
etc.

and cuts up the WAV file into separate files for each track based on the track times, with the files named after the titles, like:


MyAlbum 01 song title #1.wav
MyAlbum 02 my song #2.wav
MyAlbum 03 another track.wav
MyAlbum 04 a long one.wav
etc.



I need to go back and study the WAV file format, but it would help if there is some working code to study.
Reply
#2
Start by getting the .wav file header format
https://docs.fileformat.com/audio/wav/

Then figure out WTH? all that stuff is ;-))
b = b + ...
Reply
#3
(04-20-2023, 02:08 PM)bplus Wrote: Start by getting the .wav file header format
https://docs.fileformat.com/audio/wav/

Then figure out WTH? all that stuff is ;-))

Yep, it all starts with the WAV file format spec. 
Thanks!
Reply
#4
I have Lua code that (slowly) loads a mono 44100Hz 16-bit wave file into a table which could be split up and written to separate wave files.

However, doing it by "markers" requires being able to read chunks in the wave file. The loop point of a WAV sample is a separate chunk.

I haven't been able to completely master the ability of reading chunks beyond the "data" chunk. Also keep in mind that programs like Audacity like to save the chunk with "marker" listing before the "data" chunk. In addition, Wavosaur saves its own "saur" chunk to be able to recall a volume envelope without having to save another file that could be easily lost.

The second chunk of the file could usually be discovered after the first 40 bytes of the WAV file. The first one is "fmt " (with space as fourth character) which describes the attributes such as sampling rate, bit rate and compression technique. Compressed WAV files are a pain, need to be able to work with a codec for that. With stereo files, the difference from mono files is that the data is interlaced, ie. left side, then right side, then left side, then right side etc. I haven't looked at a WAV which has more than two channels but it should be alike, but few programs support such a thing even for 5.1 Surround sound. Dealing with 32-bit float WAV is another monster but might be necessary today, as many digital audio workstations support it.

For a QB64 program, sadly have to read the WAV file twice. Once with the programming system's means to get the sample data, and again separately in the old-fashioned way in order to go hunting for those chunks. It might be easier than expected to interpret the sample frames, taken directly from "marker" information, so that the programmer knows where in the sample data buffer to start or finish copying from.

I had to read the first post again. If it has nothing to do with "markers" already in a wave file then this should be even easier. Find the way to convert a time like 4:50 into sample frame value. IINM it should be 22050 for one second of 44100Hz sampling rate, regardless of bit rate because QB64(PE) has code to convert from a different sampling rate and perhaps from a different bit rate as well. This sample frame number is invariably needed for using with _MEMCOPY, to copy from the big WAV file into a new buffer obviously created with another _MEM variable. Could steal the WAV file header for output file but have to fill in the size of the wave file at byte position 5, and the size of the "data" chunk at the position just after that word as it appears.

EDIT: If you don't need to do this in a QB64 program, and if audiophile sound quality isn't important, convert WAV to OGG Vorbis and then use "vcut" to split:

https://github.com/xiph/vorbis-tools

This is what I recommend, although there are different tools for MP3 and maybe for FLAC.
Reply
#5
I can write it, but I need to know what format your WAV file is in. Is it mono, stereo, 16 bit, 8 bit, 32 bit? I've worked with it quite a bit in the past. You can find the header of the WAV file in my thread, for example here https://staging.qb64phoenix.com/showthread.php?tid=1491

I only work with uncompressed format.


Reply
#6
I would say that as long as you know how the header is formatted and can look for those tags, you should be able to split based on each occurrence of these headers. I had code for reading various music files and displaying the contents of the ID3 tags. Worked quite well, especially with FLACs that I ripped from Tidal. Those even had the lyrics and album art embedded as tags.
Schuwatch!
Yes, it's me. Now shut up.
Reply
#7
Welcome to the forums.

It looks like the OP is asking for a program that takes a text-file description, each line first with time hint and then with "song name", to tell a program how to slice a big WAV file. If the topic starter also expects the program to find the "marker" chunks in a WAV then, well, it gets more complicated for the programmer if he/she is not used to the format.
Reply
#8
I had to try it. I only tested it on 16 bit format. stereo, but it should work on all types. The created WAV files (cut original) have the same bitrate, the same number of channels, etc., only the length is different according to the text file.

Text file format:
First line: number specifying the number of tracks to be cut
Second line: source file name in quotes
third and following lines: destination file name in quotes followed by a comma and a time in colon format (required), eg 1:25 means minute, 25 seconds.


The new file always starts exactly after the end of the previous file. For example, if you need to cut recordings with silence, write it in a text file like this (example - the source uncompressed WAV file is called allinone.wav, you want to cut it into three WAV files, with lengths of 1:10, 2:20, 3:00 but you need to cut three seconds of silence between each of these recordings) - do it like this:

txt file content:

5
"allinone"
"Track 01", 1:10
"Silent 1", 0:3
"Track 02", 2:20
"Silent 2", 0:3
"Track 03", 3:00

end of txt file

then just delete created "silent" waves, tracks begin without this part.
Yes, This is really very good fun.


Code: (Select All)
'wav splitter
'STEP 1: verify text file
'text file format:

'first line is number - total files to create
'4
'next row contains wav source file name
'next 4 rows is new file name and track lenght  "FileName", 0:27

'wav extension is add automaticaly

Type TrackType
    Time As Single
    Song As String
End Type

Type WAVHead
    chunk As String * 4 '       4 bytes  (RIFF)
    size As _Unsigned Long '              4 bytes  (file size)  velikost souboru
    fomat As String * 4 '       4 bytes  (WAVE)
    sub1 As String * 4 '        4 bytes  (fmt )
    subchunksize As Long '      4 bytes  (lo / hi), $00000010 for PCM audio
    format As Integer '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
    channels As Integer '       2 bytes  (1 = mono, 2 = stereo)
    rate As Long '              4 bytes  (sample rate, standard is 44100)
    ByteRate As Long '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
    Block As Integer '          2 bytes  (block align = number of channels * bits per sample /8)
    Bits As Integer '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
    subchunk2 As String * 4 '   4 bytes  ("data")  contains begin audio samples
    lenght As _Unsigned Long '            4 bytes  Data block size
End Type '                     44 bytes  total
Dim WavHead As WAVHead
Dim WavNew As WAVHead

SplitTxt$ = "split.txt"
ff = FreeFile
If _FileExists(SplitTxt$) Then
    Open SplitTxt$ For Input As ff
    If LOF(ff) > 0 Then
        Input #ff, tracks$
        Tracks = Val(tracks$)
        If Tracks <= 0 Then
            Print "Can not create negative or zero new tracks.": End
        Else
            Input #ff, source$
            If LCase$(Right$(source$, 4)) <> ".wav" Then source$ = source$ + ".wav"
            If _FileExists(source$) Then

                Dim tracks(Tracks) As TrackType

                While Not EOF(ff)
                    Input #ff, TrackName$, TrackTime$
                    If LCase$(Right$(TrackName$, 4)) <> ".wav" Then TrackName$ = TrackName$ + ".wav"
                    tracks(ti).Song = TrackName$

                    separator = InStr(1, TrackTime$, ":")
                    If separator = 0 Then Print "Invalid track time. Use format Min:Sec": End
                    Min = Val(Left$(TrackTime$, separator - 1))
                    Sec = Val(Right$(TrackTime$, separator))
                    tracks(ti).Time = Min * 60 + Sec
                    ti = ti + 1
                    If ti > Tracks Then Print "Txt file contains more records than is declared on line 1 in txt file "; SplitTxt$; Tracks; ti: End
                Wend

            Else
                Print "Source file: "; source$; " not exists.": End
            End If

        End If
    Else
        Print "File lenght "; SplitTxt$; " is not valid.": End
    End If
Else
    Print "File: "; SplitTxt$; " not exists."
End If

Print "Total declared tracks:"; Tracks
Print "Source WAV sound file: "; source$

Close ff

For TimeTest = 0 To Tracks
    TotalTime = TotalTime + tracks(TimeTest).Time
Next

Print "Total Time in "; Tracks; " tracks is:"; TotalTime

ff = FreeFile
Open source$ For Binary As ff
Get ff, , WavHead

If WavHead.chunk <> "RIFF" Then Print "Invalid source file WAV format: "; WavHead.chunk
SAFLEN = WavHead.lenght / (WavHead.Bits / 8) / WavHead.channels / WavHead.rate

If SAFLEN < TotalTime Then Print "Source audio file is shorter than the total required length. Some audio tracks may therefore have silence at the end."

Print "Source audio file lenght:"; SAFLEN
Print "Source audio file format:"; WavHead.Bits
Print "Source audio file channels:"; WavHead.channels

all$ = Space$(LOF(ff))
Get ff, 1, all$
Start = InStr(1, all$, "data") + 4
Seek #ff, Start
all$ = ""


For split = 0 To Tracks - 1
    Print "Creating track "; tracks(split).Song; " ["; LTrim$(Str$(tracks(split).Time)); "S]"
    DataSize& = (WavHead.Bits / 8) * WavHead.channels * WavHead.rate * tracks(split).Time
    datas$ = Space$(DataSize&)
    Get #ff, , datas$
    WavNew.Bits = WavHead.Bits
    WavNew.channels = WavHead.channels
    WavNew.rate = WavHead.rate
    WavNew.chunk = "RIFF"
    WavNew.size = DataSize& + 44
    WavNew.fomat = WavHead.fomat
    WavNew.sub1 = WavHead.sub1
    WavNew.subchunksize = WavHead.subchunksize
    WavNew.ByteRate = WavHead.ByteRate
    WavNew.Block = WavHead.Block
    WavNew.subchunk2 = WavHead.subchunk2
    WavNew.format = WavHead.format
    WavNew.lenght = DataSize&

    '  Print "New WAV bits: "; WavNew.Bits
    '  Print "New WAV channels: "; WavNew.channels
    '  Print "New WAV sound rate: "; WavNew.rate
    '  Print "New WAV size: "; WavNew.size

    ff2 = FreeFile
    Open tracks(split).Song For Binary As ff2
    Put ff2, , WavNew
    Put ff2, , datas$
    Close ff2

    datas$ = ""
Next


in attachement my txt file as example.


Attached Files
.txt   split.txt (Size: 81 bytes / Downloads: 17)


Reply
#9
(04-21-2023, 02:26 PM)mnrvovrfc Wrote: Welcome to the forums.

It looks like the OP is asking for a program that takes a text-file description, each line first with time hint and then with "song name", to tell a program how to slice a big WAV file. If the topic starter also expects the program to find the "marker" chunks in a WAV then, well, it gets more complicated for the programmer if he/she is not used to the format.

The source file is actually MP4 video, which I would convert to WAV (to burn to audio CD) or MP3 (for mp3 players). I asked about WAV because I don't need or want to deal with the video, and WAV is the format I want to work with for editing, and anything for working with WAV files will come in handy later! 

I don't know about markers or chunks, the audio doesn't have any metadata or anything special (such as what a DAW might add or use), all I have is a separate text file with the track title listing with each track's length. Slicing up the file automatically would be nice, but actually the track times might not account for any space between songs (I'd need to check when back at the PC), and if that's the case, then I would have the program go to the end of the first track and search forward to determine when the next track begins. The track times can be used as guides, but the actual begin and end locations are approximate, if they don't include the blank space at the end of each track. 

I did google some more and found a ton of Python scripts that split up sound files, using whatever Python libraries for working with WAV and MP3. QB64PE doesn't have any WAV or MP3 file libraries that I'm aware of, so I think I would need to write all the code to handle reading/parsing/processing/writing the files.

As bplus said (and I already figured) the first step is to study the WAV file spec, which I will do as I have time, but I figured it can't hurt to ask here and see if there is any pre-existing QB-like code I could reuse or study to figure out how to do this.

(I could always just use Python, but where would the fun be?? LoL)
Reply
#10
From MP4 it might be possible with FFMPEG but the command-line syntax is very complicated and convoluted.

At around this time last year LOL I downloaded the first six episodes of "Silver Moon" Anime cartoon series from "archive-dot-org". It was slow, over six hours! It came as a bunch of VOB files that I had to put together into a single MP4 file using FFMPEG on Windows. But that's putting together the pieces, not taking the big one apart! I'm saying this because it should be possible to get smaller WAV files out of a single MP4, but the utility to do it is difficult to deal with for the average user. Because you want audio only it's easier than video to video.

As I've said in my first post in this topic, all it requires is parsing the time, from the text file that you provided as input, then find the way to turn that into a sample frame number. Then just use _SNDOPEN() to load the WAV, use _MEMSOUND() to get a _MEM variable for it, then allocate another _MEM variable [not necessary to use the new syntax for _SNDOPEN() because there's no direct way to save that to disk] for the output buffer to hold a piece of the large sample data buffer. Could use _MEMGET and _MEMPUT for the "slow" way, or if you know what you are really doing, could just use _MEMCOPY with the right size of one song from the sample data. Adding silence at front or back is up to you, will require additional programming (trickery) but not really that difficult. I wish I could cobble up an example for you.

Doing sample markers inside a WAV isn't going to get the names of the songs in many cases. It depends on a WAV prepared by the original author, or if conversion to WAV was able to go that far. That's why "your way" is better: I give you a wave file and a text file for how to chop it up. LOL.

EDIT: I had ignored Petr's contribution. That should be enough. Just fix it for the sake of absolute elapsed times over the big wave file.
Reply




Users browsing this thread: 1 Guest(s)