Gopher2600/wavwriter/wav.go
JetSetIlly 314e1d2dd4 reworked audio system
television signal split into video and audio signal. The audio signal is
only sent then a new sample from the TIA is ready

realtime mixer concept change. the mixer can be informed of a change of
TV spec. this allows the realtime mixer to make an informed judgement
about the required sample rate

the sample rate is unlikely to be ideal however unless the number of
scanlines in the TV image is the same as given in the basic TV
specification (ie. 262 lines for NTSC, 312 for PAL). because of that,
the realtime mixer can also indirectly regulate the rate of calls to
SetAudio(). the Regulate() function is called by the television which
then alters the call frequency to SetAudio() depending on the regulate
value. this effectively keeps the audio buffer nicely filled - neither
too long which would be audibly laggy, or too short which would result
in clipped audio

the values that control the regulation in both the sdlaudio package and
television package will need tweaking to find the best values
2025-01-11 16:14:47 +00:00

96 lines
2.5 KiB
Go

// This file is part of Gopher2600.
//
// Gopher2600 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Gopher2600 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
// Package wavwriter allows writing of audio data to disk as a WAV file.
package wavwriter
import (
"fmt"
"os"
"github.com/jetsetilly/gopher2600/hardware/television/signal"
tia "github.com/jetsetilly/gopher2600/hardware/tia/audio"
"github.com/jetsetilly/gopher2600/hardware/tia/audio/mix"
"github.com/go-audio/audio"
"github.com/go-audio/wav"
)
// WavWriter implements the television.AudioMixer interface
type WavWriter struct {
filename string
buffer []int16
}
// New is the preferred method of initialisation for the WavWriter type
func NewWavWriter(filename string) (*WavWriter, error) {
aw := &WavWriter{
filename: fmt.Sprintf("%s.wav", filename),
buffer: make([]int16, 0, 0),
}
return aw, nil
}
// SetAudio implements the television.AudioMixer interface.
func (aw *WavWriter) SetAudio(sig []signal.AudioSignalAttributes) error {
for _, s := range sig {
v0 := s.AudioChannel0
v1 := s.AudioChannel1
m := mix.Mono(v0, v1)
aw.buffer = append(aw.buffer, m)
}
return nil
}
const numChannels = 1
const bitDepth = 16
// EndMixing implements the television.AudioMixer interface
func (aw *WavWriter) EndMixing() error {
f, err := os.Create(aw.filename)
if err != nil {
return fmt.Errorf("wavwriter: %w", err)
}
defer f.Close()
enc := wav.NewEncoder(f, tia.AverageSampleFreq, bitDepth, numChannels, 1)
if enc == nil {
return fmt.Errorf("wavwriter: bad parameters for wav encoding")
}
defer enc.Close()
buf := audio.PCMBuffer{
Format: &audio.Format{
NumChannels: numChannels,
SampleRate: tia.AverageSampleFreq,
},
I16: aw.buffer,
DataType: audio.DataTypeI16,
SourceBitDepth: bitDepth,
}
err = enc.Write(buf.AsIntBuffer())
if err != nil {
return fmt.Errorf("wavwriter: %w", err)
}
return nil
}
// Reset implements the television.AudioMixer interface
func (aw *WavWriter) Reset() {
}