You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
4.1 KiB
177 lines
4.1 KiB
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"embed"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
"flag"
|
|
"bufio"
|
|
"os"
|
|
|
|
"github.com/chzyer/readline"
|
|
"github.com/gopxl/beep"
|
|
"github.com/gopxl/beep/speaker"
|
|
"github.com/gopxl/beep/wav"
|
|
)
|
|
|
|
//go:embed sound/*
|
|
var soundFS embed.FS
|
|
|
|
const (
|
|
pauseBetweenCharacters = 500 * time.Millisecond
|
|
longPause = 1600 * time.Millisecond
|
|
)
|
|
|
|
func main() {
|
|
// Define and parse the -r flag
|
|
repeat := flag.Bool("r", false, "Repeat each section separated by space, '-', '_', or a new line")
|
|
flag.Parse()
|
|
|
|
// Create a map to store sound data for each character
|
|
soundMap := make(map[rune][]byte)
|
|
|
|
// Load all sound files into the map
|
|
for _, c := range "abcdefghijklmnopqrstuvwxyz0123456789" {
|
|
filename := fmt.Sprintf("sound/sam%c.wav", c)
|
|
data, err := soundFS.ReadFile(filename)
|
|
if err == nil {
|
|
soundMap[c] = data
|
|
}
|
|
}
|
|
|
|
var input string
|
|
|
|
if *repeat {
|
|
fmt.Println("Repeat mode is ON")
|
|
}
|
|
|
|
// Check if input is being redirected from a file
|
|
if isInputFromPipe() {
|
|
// Read from stdin (file input)
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
var inputBuilder strings.Builder
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
inputBuilder.WriteString(line + "\n")
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
fmt.Printf("Error reading input: %s\n", err)
|
|
return
|
|
}
|
|
input = inputBuilder.String()
|
|
|
|
// Display the input, filtered to uppercase, before playback
|
|
fmt.Println("Input:\n")
|
|
fmt.Println(strings.ToUpper(input))
|
|
} else {
|
|
// Use readline for interactive input
|
|
rl, err := readline.NewEx(&readline.Config{
|
|
Prompt: "> ", // Use a simple prompt for each line input
|
|
InterruptPrompt: "^C",
|
|
EOFPrompt: "exit",
|
|
FuncFilterInputRune: uppercaseFilter, // Filter to show uppercase
|
|
})
|
|
if err != nil {
|
|
fmt.Printf("Error initializing readline: %s\n", err)
|
|
return
|
|
}
|
|
defer rl.Close()
|
|
|
|
// Inform the user about how to enter input
|
|
fmt.Println("Enter text to be read (A-Z and 0-9 only). Press Enter on a blank line to process:")
|
|
|
|
var inputBuilder strings.Builder
|
|
for {
|
|
line, err := rl.Readline()
|
|
if err != nil {
|
|
if err.Error() == "EOF" {
|
|
fmt.Println("Exiting.")
|
|
return
|
|
}
|
|
fmt.Printf("Error reading input: %s\n", err)
|
|
return
|
|
}
|
|
|
|
// Check if the user entered a blank line to finish input
|
|
if strings.TrimSpace(line) == "" {
|
|
break
|
|
}
|
|
|
|
// Append the line to the input builder
|
|
inputBuilder.WriteString(line + "\n")
|
|
}
|
|
|
|
input = inputBuilder.String()
|
|
}
|
|
|
|
// Process the input
|
|
input = strings.ToLower(strings.TrimSpace(input))
|
|
sections := strings.FieldsFunc(input, func(r rune) bool {
|
|
return r == ' ' || r == '-' || r == '_' || r == '\n' || r == '\r'
|
|
})
|
|
|
|
fmt.Print("Beginning playback... ")
|
|
|
|
for _, section := range sections {
|
|
numRepeats := 1
|
|
if *repeat {
|
|
numRepeats = 2
|
|
}
|
|
|
|
// Read and optionally repeat each section
|
|
for i := 0; i < numRepeats; i++ {
|
|
for _, c := range section {
|
|
if soundData, ok := soundMap[c]; ok {
|
|
playSound(soundData)
|
|
} else {
|
|
fmt.Printf("Invalid character (skipping): %c\n", c)
|
|
}
|
|
time.Sleep(pauseBetweenCharacters)
|
|
}
|
|
// Pause between repeats if repeating
|
|
if *repeat && i == 0 {
|
|
time.Sleep(longPause)
|
|
}
|
|
}
|
|
// Long pause between sections
|
|
time.Sleep(longPause)
|
|
}
|
|
|
|
fmt.Println("Playback finished. Exiting.")
|
|
}
|
|
|
|
// Function to check if input is from a pipe or file
|
|
func isInputFromPipe() bool {
|
|
fileInfo, _ := os.Stdin.Stat()
|
|
return (fileInfo.Mode() & os.ModeCharDevice) == 0
|
|
}
|
|
|
|
// Function to play sound
|
|
func playSound(soundData []byte) {
|
|
streamer, format, err := wav.Decode(bytes.NewReader(soundData))
|
|
if err != nil {
|
|
fmt.Printf("Could not decode sound data: %s\n", err)
|
|
return
|
|
}
|
|
defer streamer.Close()
|
|
|
|
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
|
|
done := make(chan bool)
|
|
speaker.Play(beep.Seq(streamer, beep.Callback(func() {
|
|
done <- true
|
|
})))
|
|
|
|
<-done
|
|
}
|
|
|
|
// Filter function to convert all typed input to uppercase for display
|
|
func uppercaseFilter(r rune) (rune, bool) {
|
|
if r >= 'a' && r <= 'z' {
|
|
return r - ('a' - 'A'), true // Convert to uppercase
|
|
}
|
|
return r, true // Keep other characters unchanged
|
|
}
|
|
|