I'm working on a Praat script to:

1- open a wav file

2- split the wav file based on silences

3- concatenate the intervals based on duration, so that the new wav segment files are <= 15 seconds each

4- write new wav segments to separate wav files

I've made some progress on getting this script to work, but I'm having 2 major problems:

1- after i concatenate the segments to create the first 15 sec clip my output stops, therefore I'm missing part of the wav file in the output

2- the clips are being concatenated in reverse order

Here is my script so far. Please help! I'm a novice in scripting in Praat, and I'm totally stumped..

Read from file... Desktop/englishTest.wav
name$ = selected$("Sound", 1)
outputDir$ = "Desktop/praat_output/"
To TextGrid (silences)... 100 0 -25 0.3 0.1 silent sounding
plus Sound 'name$'
Extract intervals where... 1 no "is equal to" sounding

n = numberOfSelected("Sound")

for i to n
    soundObject'i'=selected("Sound", i)
endfor

topSound = soundObject1
select topSound
durTop = Get total duration

i = 2
for i to n
    select soundObject'i'
    dur = Get total duration
    if durTop + dur <= 15
        select topSound
        plus soundObject'i'
        topSound = Concatenate
        select topSound
        durTop = Get total duration
    else
        select topSound
        Save as WAV file... 'outputDir$''name$'_'i'.wav
        topSound = soundObject'i'
        durTop = dur
    endif
endfor
1

There are 1 best solutions below

0
On

Let's go through your script bit by bit:

i = 2
for i to n

Here the first line will have no effect, because the for loop initialises its control variable to 1 by default. You should write instead for i from 2 to n.

select topSound
plus soundObject'i'
topSound = Concatenate

This is why your sounds are being concatenated in the wrong order. In Praat, Concatenate joins sounds in the order they appear in the Object list. Unfortunately, there are no easy ways to move objects about in the Object list. But you can solve this by copying objects, since newly created objects always appear at the bottom of the list.

selectObject: soundObject[i]           ; Select the sound
tmp = Copy: selected$("Sound")         ; Copy (= move to bottom)
removeObject: soundObject[i]           ; Remove the original
soundObject[i] = tmp                   ; Update the object ID
selectObject: topSound, soundObject[i] ; Select the new objects
topSound = Concatenate                 ; Concatenate in the right order

With these two changes, your script is almost there. The remaining problem is that, because you save your files when they exceed your maximum duration, the last part (which as the remainder, will likely be shorter) never gets saved. You need to remember to save that part separately after the loop ends.

I made some other small changes, like adding a form, changing your variables into more proper arrays and updating the syntax in general (selectObject instead of select), but I tried to annotate them when not clear. Putting all of this together, you get something like this

form Reticulate splines...
    sentence Sound_path  Desktop/englishTest.wav
    sentence Output_path Desktop/praat_output/
endform

sound = Read from file: sound_path$
sound$ = selected$("Sound")
silences = To TextGrid (silences):
  ... 100, 0, -25, 0.3, 0.1, "silent", "sounding"

selectObject: sound, silences
Extract intervals where:
    ... 1, "no", "is equal to", "sounding"
n = numberOfSelected("Sound")

for i to n
    soundObject[i] = selected("Sound", i)
endfor

topSound = soundObject[1]
selectObject: topSound
durTop = Get total duration

# new is a counter for the new objects we'll be making
new = 0
# Start for loop from second position
for i from 2 to n
    selectObject: soundObject[i]
    dur = Get total duration
    if durTop + dur <= 15
        # Rearrange objects in object list
        tmp = soundObject[i]
        selectObject: soundObject[i]
        soundObject[i] = Copy: selected$("Sound")
        removeObject: tmp
        previous = topSound
        selectObject: topSound, soundObject[i]
        topSound = Concatenate
        durTop = Get total duration

        # Remember to remove unwanted objects!
        removeObject: previous, soundObject[i]
    else
        # Save an array of new indices
        new += 1
        final[new] = topSound
        topSound = soundObject[i]
        durTop = dur
    endif
endfor
# Remember to add the last sound
new += 1
final[new] = topSound

# Clean up unwanted objects
removeObject: silences

# Loop through the array to rename them
nocheck selectObject: undefined
for i to new
    selectObject: final[i]
    Rename: sound$ + "_" + string$(i)

    ## You can save the objects automatically here
    ## but this is not the best design in my opinion
    # Save as WAV file: output_path$ + selected$("Sound")
endfor

# Select the newly extracted parts
nocheck selectObject: undefined
for i to new
    plusObject: final[i]
endfor

This could further be improved by, for instance, zero-padding the numbers in your filenames, but that's out of scope. :)

Update: Here's one possibility for improvement, with a slightly different algorithm, and breaking up longer chunks into fragments no larger than your specified maximum.