mirror of
https://github.com/tcsenpai/ollama.git
synced 2025-06-10 13:07:08 +00:00
138 lines
3.1 KiB
Go
138 lines
3.1 KiB
Go
package recorder
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"golang.org/x/sys/unix"
|
|
"golang.org/x/term"
|
|
|
|
"github.com/gordonklaus/portaudio"
|
|
)
|
|
|
|
const (
|
|
sampleRate = 16000
|
|
numChannels = 1
|
|
bitsPerSample = 16
|
|
)
|
|
|
|
func RecordAudio(f *os.File) error {
|
|
fmt.Print("Recording. Press any key to stop.\n\n")
|
|
|
|
sig := make(chan os.Signal, 1)
|
|
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
|
|
|
|
portaudio.Initialize()
|
|
defer portaudio.Terminate()
|
|
|
|
in := make([]int16, 64)
|
|
stream, err := portaudio.OpenDefaultStream(numChannels, 0, sampleRate, len(in), in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer stream.Close()
|
|
|
|
err = stream.Start()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write WAV header with placeholder sizes
|
|
writeWavHeader(f, sampleRate, numChannels, bitsPerSample)
|
|
|
|
var totalSamples uint32
|
|
|
|
// Set up terminal input reading
|
|
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer term.Restore(int(os.Stdin.Fd()), oldState)
|
|
|
|
// Create a channel to handle the stop signal
|
|
stop := make(chan struct{})
|
|
|
|
go func() {
|
|
_, err := unix.Read(int(os.Stdin.Fd()), make([]byte, 1))
|
|
if err != nil {
|
|
fmt.Println("Error reading from stdin:", err)
|
|
return
|
|
}
|
|
// Send signal to stop recording
|
|
stop <- struct{}{}
|
|
}()
|
|
|
|
loop:
|
|
for {
|
|
err = stream.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = binary.Write(f, binary.LittleEndian, in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
totalSamples += uint32(len(in))
|
|
|
|
select {
|
|
case <-stop:
|
|
break loop
|
|
case <-sig:
|
|
break loop
|
|
default:
|
|
}
|
|
}
|
|
|
|
err = stream.Stop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update WAV header with actual sizes
|
|
updateWavHeader(f, totalSamples, numChannels, bitsPerSample)
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeWavHeader(f *os.File, sampleRate int, numChannels int, bitsPerSample int) {
|
|
subchunk1Size := 16
|
|
audioFormat := 1
|
|
byteRate := sampleRate * numChannels * (bitsPerSample / 8)
|
|
blockAlign := numChannels * (bitsPerSample / 8)
|
|
|
|
// Write the RIFF header
|
|
f.Write([]byte("RIFF"))
|
|
binary.Write(f, binary.LittleEndian, uint32(0)) // Placeholder for file size
|
|
f.Write([]byte("WAVE"))
|
|
|
|
// Write the fmt subchunk
|
|
f.Write([]byte("fmt "))
|
|
binary.Write(f, binary.LittleEndian, uint32(subchunk1Size))
|
|
binary.Write(f, binary.LittleEndian, uint16(audioFormat))
|
|
binary.Write(f, binary.LittleEndian, uint16(numChannels))
|
|
binary.Write(f, binary.LittleEndian, uint32(sampleRate))
|
|
binary.Write(f, binary.LittleEndian, uint32(byteRate))
|
|
binary.Write(f, binary.LittleEndian, uint16(blockAlign))
|
|
binary.Write(f, binary.LittleEndian, uint16(bitsPerSample))
|
|
|
|
// Write the data subchunk header
|
|
f.Write([]byte("data"))
|
|
binary.Write(f, binary.LittleEndian, uint32(0)) // Placeholder for data size
|
|
}
|
|
|
|
func updateWavHeader(f *os.File, totalSamples uint32, numChannels int, bitsPerSample int) {
|
|
fileSize := 36 + (totalSamples * uint32(numChannels) * uint32(bitsPerSample/8))
|
|
dataSize := totalSamples * uint32(numChannels) * uint32(bitsPerSample/8)
|
|
|
|
// Seek to the start of the file and write updated sizes
|
|
f.Seek(4, 0)
|
|
binary.Write(f, binary.LittleEndian, uint32(fileSize))
|
|
|
|
f.Seek(40, 0)
|
|
binary.Write(f, binary.LittleEndian, uint32(dataSize))
|
|
}
|