Skip to content

Commit

Permalink
Merge pull request #142 from bcc-code/feat/silence-detection
Browse files Browse the repository at this point in the history
Feat/silence detection
  • Loading branch information
fredrikvedvik authored Jan 11, 2024
2 parents 744afc0 + e36ab01 commit 29efcfa
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 3 deletions.
12 changes: 12 additions & 0 deletions activities/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,25 @@ type NormalizeAudioParams struct {

type NormalizeAudioResult struct {
FilePath paths.Path
IsSilent bool
InputAnalysis *common.AnalyzeEBUR128Result
OutputAnalysis *common.AnalyzeEBUR128Result
}

func NormalizeAudioActivity(ctx context.Context, params NormalizeAudioParams) (*NormalizeAudioResult, error) {
out := &NormalizeAudioResult{}

silent, err := transcode.AudioIsSilent(params.FilePath)
if err != nil {
return nil, err
}
if silent {
return &NormalizeAudioResult{
FilePath: params.FilePath,
IsSilent: true,
}, nil
}

r128Result, err := AnalyzeEBUR128Activity(ctx, AnalyzeEBUR128Params{
FilePath: params.FilePath,
TargetLoudness: params.TargetLUFS,
Expand Down
1 change: 1 addition & 0 deletions cmd/trigger_ui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ func (s *TriggerServer) triggerHandlerPOST(c *gin.Context) {
VXID: vxID,
WithFiles: c.PostForm("withFiles") == "on",
WithChapters: c.PostForm("withChapters") == "on",
IgnoreSilence: c.PostForm("ignoreSilence") == "on",
WatermarkPath: watermarkPath,
AudioSource: audioSource,
Destinations: c.PostFormArray("destinations[]"),
Expand Down
5 changes: 5 additions & 0 deletions cmd/trigger_ui/templates/vx-export.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@
<input class="ml-2 h-4 w-4 my-auto" type="checkbox" name="withChapters"
id="withChapters">
</div>
<div class="flex">
<label for="ignoreSilence" class="my-auto">Ignore silence</label>
<input class="ml-2 h-4 w-4 my-auto" type="checkbox" name="ignoreSilence"
id="ignoreSilence">
</div>

<input id="submit"
class="cursor-pointer rounded-md bg-[#6A64F1] py-3 px-8 text-center text-base font-semibold text-white outline-none"
Expand Down
74 changes: 74 additions & 0 deletions services/transcode/audio.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package transcode

import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"

Expand All @@ -13,6 +16,77 @@ import (
"github.com/bcc-code/bcc-media-flows/services/ffmpeg"
)

type SilencePeriod struct {
Start float64 `json:"start"`
End float64 `json:"end"`
}

func AudioGetSilencePeriods(path paths.Path, threshold float64) ([]SilencePeriod, error) {
params := []string{
"-loglevel", "info",
"-hide_banner",
"-i", path.Local(),
"-af", fmt.Sprintf("silencedetect=noise=-90dB:d=%f", threshold),
"-f", "null",
"-",
}

cmd := exec.Command("ffmpeg", params...)

var stderr bytes.Buffer
cmd.Stderr = &stderr

// Run the command
err := cmd.Run()
if err != nil {
fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
return nil, err
}

result := stderr.String()

var silencePeriods []SilencePeriod
r := regexp.MustCompile(`silence_(start|end): ([0-9.]+)`)

var start float64
for _, line := range strings.Split(result, "\n") {
matches := r.FindStringSubmatch(line)
if len(matches) == 3 {
if matches[1] == "start" {
start, _ = strconv.ParseFloat(matches[2], 64)
} else if matches[1] == "end" {
end, _ := strconv.ParseFloat(matches[2], 64)
silencePeriods = append(silencePeriods, SilencePeriod{Start: start, End: end})
}
}
}

return silencePeriods, nil
}

func AudioIsSilent(path paths.Path) (bool, error) {
info, err := ffmpeg.GetStreamInfo(path.Local())
if err != nil {
return false, err
}

silencePeriods, err := AudioGetSilencePeriods(path, 10)
if err != nil {
return false, err
}

var dur float64
for _, p := range silencePeriods {
dur += p.End - p.Start
}

if int(info.TotalSeconds) == int(dur) {
return true, nil
}

return false, nil
}

func AudioAac(input common.AudioInput, cb ffmpeg.ProgressCallback) (*common.AudioResult, error) {
params := []string{
"-progress", "pipe:1",
Expand Down
5 changes: 5 additions & 0 deletions services/transcode/audio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ func Test_AudioSplit(t *testing.T) {

spew.Dump(files)
}

func Test_AudioSilence(t *testing.T) {
_, err := AudioIsSilent(paths.MustParse("/private/temp/workflows/5d2ea767-6b71-44c6-a207-005d7522326c/FKTB_20210415_2000_SEQ-slv.wav"))
assert.Nil(t, err)
}
1 change: 1 addition & 0 deletions workflows/export/vx_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type VXExportParams struct {
AudioSource string
Languages []string
Subclip string
IgnoreSilence bool
}

type VXExportResult struct {
Expand Down
13 changes: 10 additions & 3 deletions workflows/export/vx_export_vod.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func VXExportToVOD(ctx workflow.Context, params VXExportChildWorkflowParams) (*V
}
}

audioFiles, err := prepareAudioFiles(ctx, params.MergeResult, params.TempDir, true)
audioFiles, err := prepareAudioFiles(ctx, params.MergeResult, params.TempDir, true, params.ParentParams.IgnoreSilence)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -142,7 +142,7 @@ type vxExportVodService struct {
errs []error
}

func prepareAudioFiles(ctx workflow.Context, mergeResult MergeExportDataResult, tempDir paths.Path, normalizeAudio bool) (map[string]paths.Path, error) {
func prepareAudioFiles(ctx workflow.Context, mergeResult MergeExportDataResult, tempDir paths.Path, normalizeAudio, ignoreSilence bool) (map[string]paths.Path, error) {
prepareFilesSelector := workflow.NewSelector(ctx)

if normalizeAudio {
Expand Down Expand Up @@ -172,7 +172,14 @@ func prepareAudioFiles(ctx workflow.Context, mergeResult MergeExportDataResult,
return nil, fmt.Errorf("failed to normalize audio for language %s: %w", lang, err)
}

mergeResult.AudioFiles[lang] = normalizedRes.FilePath
if normalizedRes.IsSilent {
if ignoreSilence {
continue
}
return nil, fmt.Errorf("audio for language %s is silent", lang)
} else {
mergeResult.AudioFiles[lang] = normalizedRes.FilePath
}
}
}

Expand Down

0 comments on commit 29efcfa

Please sign in to comment.