Skip to content

Commit

Permalink
feat: add volume slider, try switch to native file dialogs
Browse files Browse the repository at this point in the history
  • Loading branch information
dpolakovics committed Dec 14, 2024
1 parent 8015d7d commit 9e86682
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 58 deletions.
10 changes: 2 additions & 8 deletions internal/logic/sync-5.1.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package logic

import (
"fmt"
)
import "fmt"

func get5_1Arguments(volume float64) []string {
return []string{
// best audio format so far on ios
"-filter_complex", "[1:a]apad[a2];[0:a]volume=" + fmt.Sprintf("%f", volume / 100) + "[a1];[a1][a2]amerge=inputs=2[out]",
"-filter_complex", fmt.Sprintf("[0:a]volume=%f,apad[a2];[1:a][a2]amerge=inputs=2[out]", volume),
"-c:a", "aac", "-b:a", "654k",
// mp4 output
// "-filter_complex", "[1:a]apad[a2];[0:a][a2]amerge=inputs=2,pan=5.1|c0=c0+c6|c1=c1+c7|c2=c2|c3=c3|c4=c4|c5=c5[out]",
// "-c:a", "eac3", "-ac", "6",
}
}
6 changes: 4 additions & 2 deletions internal/logic/sync-7.1.2.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package logic

func get7_1_2Arguments() []string {
import "fmt"

func get7_1_2Arguments(volume float64) []string {
return []string{
"-filter_complex",
"[1:a]apad[a2];[0:a][a2]amerge=inputs=2,pan=7.1.2|FL=c0+c10|FR=c1+c11|FC=c2|LFE=c3|BL=c6|BR=c7|SL=c4|SR=c5|TFL=c8|TFR=c9[out]",
fmt.Sprintf("[0:a]volume=%f,apad[a2];[1:a][a2]amerge=inputs=2,pan=7.1.2|FL=c0+c10|FR=c1+c11|FC=c2|LFE=c3|BL=c6|BR=c7|SL=c4|SR=c5|TFL=c8|TFR=c9[out]", volume),
}
}
6 changes: 2 additions & 4 deletions internal/logic/sync-7.1.4.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package logic

import (
"fmt"
)
import "fmt"

func get7_1_4Arguments(volume float64) []string {
return []string{
"-filter_complex",
"[1:a]apad[a2];[0:a]volume=" + fmt.Sprintf("%f", volume / 100) + "[a1];[a1][a2]amerge=inputs=2,pan=7.1.4|FL=c0+c12|FR=c1+c13|FC=c2|LFE=c3|BL=c6|BR=c7|SL=c4|SR=c5|TFL=c8|TFR=c9|TBL=c10|TBR=c11[out]",
fmt.Sprintf("[0:a]volume=%f,apad[a2];[1:a][a2]amerge=inputs=2,pan=7.1.4|FL=c0+c12|FR=c1+c13|FC=c2|LFE=c3|BL=c6|BR=c7|SL=c4|SR=c5|TFL=c8|TFR=c9|TBL=c10|TBR=c11[out]", volume),
}
}
6 changes: 2 additions & 4 deletions internal/logic/sync-stereo.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package logic

import (
"fmt"
)
import "fmt"

func getStereoArguments(volume float64) []string {
return []string{
"-filter_complex", "[1:a]apad[a2];[0:a]volume=" + fmt.Sprintf("%f", volume / 100) + "[a1];[a1][a2]amerge=inputs=2,pan=stereo|c0<c0+c2|c1<c1+c3[out]",
"-filter_complex", fmt.Sprintf("[0:a]volume=%f,apad[a2];[1:a][a2]amerge=inputs=2,pan=stereo|c0<c0+c2|c1<c1+c3[out]", volume),
}
}
17 changes: 12 additions & 5 deletions internal/logic/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ func getAudioFiles(folder string) ([]string, error) {
return audioFiles, nil
}

func CombineFiles(folder1 string, folder2 string, outputFolder string, progress *widget.ProgressBar, volume float64) error {
func CombineFiles(folder1 string, folder2 string, outputFolder string, progress *widget.ProgressBar, soundscapeVolume float64) error {
// Adjust volume to range [0.5, 1.0]
soundscapeVolume = 0.5 + (soundscapeVolume / 100.0 * 0.5)

// Get list of audio files from both folders
files1, err := getAudioFiles(folder1)
if err != nil {
Expand Down Expand Up @@ -69,11 +72,11 @@ func CombineFiles(folder1 string, folder2 string, outputFolder string, progress
return err
}

channelArguments, err := getChannelArguments(channel, volume)
channelArguments, err := getChannelArguments(channel, soundscapeVolume)
if err != nil {
return err
}

// Get output file name
ext := filepath.Ext(file)
newFileName := outputFolder + "/" + filepath.Base(files2[index])
Expand All @@ -87,7 +90,9 @@ func CombineFiles(folder1 string, folder2 string, outputFolder string, progress
}
arguments = append(arguments, channelArguments...)
arguments = append(arguments, getBaseArguments()...)
arguments = append(arguments, getCoverArtArguments(file, files2[index])...)
if ext == ".mp3" || ext == ".flac" {
arguments = append(arguments, getCoverArtArguments(file, files2[index])...)
}
arguments = append(arguments, newFileName)
cmd := exec.CommandContext(ctx, ffmpeg, arguments...)
cmd.SysProcAttr = getSysProcAttr()
Expand Down Expand Up @@ -169,10 +174,12 @@ func getChannelArguments(channels int, volume float64) ([]string, error) {
return getStereoArguments(volume), nil
case 6:
return get5_1Arguments(volume), nil
case 10:
return get7_1_2Arguments(volume), nil
case 12:
return get7_1_4Arguments(volume), nil
}
return nil, fmt.Errorf("Currently only stereo, 5.1 and 7.1.4 Soundscapes are supported")
return nil, fmt.Errorf("Currently only stereo, 5.1, 7.1.2 and 7.1.4 Soundscapes are supported")
}

func getCoverArtArguments(file1 string, file2 string) []string {
Expand Down
141 changes: 106 additions & 35 deletions internal/ui/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package ui
import (
_ "embed"
"net/url"
"os/exec"
"path/filepath"
"runtime"
"strings"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
Expand All @@ -16,6 +19,95 @@ import (
//go:embed bmc.png
var bmcPng []byte

// tryLinuxNativeFolderDialog attempts to open a native OS folder selection dialog on Linux.
// It first tries zenity (common on GNOME), then kdialog (common on KDE).
// If neither works, it returns an empty string.
func tryLinuxNativeFolderDialog() string {
// Try zenity first
cmd := exec.Command("zenity", "--file-selection", "--directory")
out, err := cmd.Output()
if err == nil {
folderPath := strings.TrimSpace(string(out))
if folderPath != "" {
return folderPath
}
}

// If zenity fails, try kdialog
cmd = exec.Command("kdialog", "--getexistingdirectory", "$HOME")
out, err = cmd.Output()
if err == nil {
folderPath := strings.TrimSpace(string(out))
if folderPath != "" {
return folderPath
}
}

// If both fail, return empty string
return ""
}

// tryNativeFolderDialog attempts to open a native OS folder selection dialog.
// If successful, it returns the selected path. If not available or fails, it returns an empty string.
func tryNativeFolderDialog() string {
switch runtime.GOOS {
case "windows":
// Use PowerShell to prompt for a folder
cmd := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command",
"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null; "+
"$f = New-Object System.Windows.Forms.FolderBrowserDialog; "+
"if($f.ShowDialog() -eq 'OK'){ $f.SelectedPath }")
out, err := cmd.Output()
if err == nil {
folderPath := strings.TrimSpace(string(out))
if folderPath != "" {
return folderPath
}
}
return ""

case "darwin":
// On macOS, use AppleScript to choose a folder
cmd := exec.Command("osascript", "-e", `tell application "System Events" to activate`, "-e", `POSIX path of (choose folder)`)
out, err := cmd.Output()
if err == nil {
folderPath := strings.TrimSpace(string(out))
if folderPath != "" {
return folderPath
}
}
return ""

case "linux":
return tryLinuxNativeFolderDialog()

default:
return ""
}
}

// showFolderSelection attempts to show a native file dialog first. If it fails, fallback to Fyne's dialog.
func showFolderSelection(win fyne.Window, callback func(string)) {
// Try native first
nativePath := tryNativeFolderDialog()
if nativePath != "" {
callback(nativePath)
return
}

// Fallback to Fyne dialog if native is not available
dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) {
if err != nil {
dialog.ShowError(err, win)
return
}
if uri == nil {
return
}
callback(uri.Path())
}, win)
}

func CreateMainContent(app fyne.App, window fyne.Window) fyne.CanvasObject {
var folder1, folder2, folderOutput string
// Create folder selection buttons
Expand All @@ -36,50 +128,29 @@ func CreateMainContent(app fyne.App, window fyne.Window) fyne.CanvasObject {
progressBar := widget.NewProgressBar()
progressBar.Hide() // Hide initially

// Set up folder selection dialogs
// Set up folder selection actions with fallback logic
folder1Button.OnTapped = func() {
dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) {
if err != nil {
dialog.ShowError(err, fyne.CurrentApp().Driver().AllWindows()[0])
return
}
if uri == nil {
return
}
folder1 = uri.Path()
folder1Label.SetText(filepath.Base(uri.Path()))
showFolderSelection(window, func(path string) {
folder1 = path
folder1Label.SetText(filepath.Base(path))
updateStartButton(folder1Label, folder2Label, folderOutputLabel, startButton)
}, fyne.CurrentApp().Driver().AllWindows()[0])
})
}

folder2Button.OnTapped = func() {
dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) {
if err != nil {
dialog.ShowError(err, fyne.CurrentApp().Driver().AllWindows()[0])
return
}
if uri == nil {
return
}
folder2 = uri.Path()
folder2Label.SetText(filepath.Base(uri.Path()))
showFolderSelection(window, func(path string) {
folder2 = path
folder2Label.SetText(filepath.Base(path))
updateStartButton(folder1Label, folder2Label, folderOutputLabel, startButton)
}, fyne.CurrentApp().Driver().AllWindows()[0])
})
}

folderOutputButton.OnTapped = func() {
dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) {
if err != nil {
dialog.ShowError(err, fyne.CurrentApp().Driver().AllWindows()[0])
return
}
if uri == nil {
return
}
folderOutput = uri.Path()
folderOutputLabel.SetText(filepath.Base(uri.Path()))
showFolderSelection(window, func(path string) {
folderOutput = path
folderOutputLabel.SetText(filepath.Base(path))
updateStartButton(folder1Label, folder2Label, folderOutputLabel, startButton)
}, fyne.CurrentApp().Driver().AllWindows()[0])
})
}

// Volume slider
Expand All @@ -104,7 +175,7 @@ func CreateMainContent(app fyne.App, window fyne.Window) fyne.CanvasObject {
startButton.Enable()
}()
}

// Create an image for the Buy Me a Coffee button
bmcResource := fyne.NewStaticResource("bmc.png", bmcPng)
// Create the Buy Me a Coffee button with an image
Expand Down

0 comments on commit 9e86682

Please sign in to comment.