audio/mp3, audio/vorbis, audio/wav: add DecodeWithoutResampling

Updates #2055
This commit is contained in:
Hajime Hoshi 2022-07-01 01:37:07 +09:00
parent 298d6488cc
commit 75e84f6eda
8 changed files with 80 additions and 12 deletions

View File

@ -57,7 +57,27 @@ func (s *Stream) Length() int64 {
return s.orig.Length()
}
// DecodeWithSampleRate decodes MP3 source and returns a decoded stream.
// DecodeWithoutResampling decodes an MP3 source and returns a decoded stream.
//
// DecodeWithoutResampling returns error when decoding fails or IO error happens.
//
// The returned Stream's Seek is available only when src is an io.Seeker.
//
// A Stream doesn't close src even if src implements io.Closer.
// Closing the source is src owner's responsibility.
func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
d, err := mp3.NewDecoder(src)
if err != nil {
return nil, err
}
s := &Stream{
orig: d,
resampling: nil,
}
return s, nil
}
// DecodeWithSampleRate decodes an MP3 source and returns a decoded stream.
//
// DecodeWithSampleRate returns error when decoding fails or IO error happens.
//

View File

@ -139,6 +139,35 @@ func decode(in io.Reader) (*decoded, int, int, error) {
return d, r.Channels(), r.SampleRate(), nil
}
// DecodeWithoutResampling decodes Ogg/Vorbis data to playable stream.
//
// DecodeWithoutResampling returns error when decoding fails or IO error happens.
//
// The returned Stream's Seek is available only when src is an io.Seeker.
//
// A Stream doesn't close src even if src implements io.Closer.
// Closing the source is src owner's responsibility.
func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
decoded, channelNum, _, err := decode(src)
if err != nil {
return nil, err
}
if channelNum != 1 && channelNum != 2 {
return nil, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", channelNum)
}
var s io.ReadSeeker = decoded
size := decoded.Length()
if channelNum == 1 {
s = convert.NewStereo16(s, true, false)
size *= 2
}
stream := &Stream{
decoded: s,
size: size,
}
return stream, nil
}
// DecodeWithSampleRate decodes Ogg/Vorbis data to playable stream.
//
// DecodeWithSampleRate returns error when decoding fails or IO error happens.

View File

@ -102,6 +102,21 @@ func (s *stream) Seek(offset int64, whence int) (int64, error) {
return n - s.headerSize, nil
}
// DecodeWithSampleRate decodes WAV (RIFF) data to playable stream.
//
// The format must be 1 or 2 channels, 8bit or 16bit little endian PCM.
// The format is converted into 2 channels and 16bit.
//
// DecodeWithSampleRate returns error when decoding fails or IO error happens.
//
// The returned Stream's Seek is available only when src is an io.Seeker.
//
// A Stream doesn't close src even if src implements io.Closer.
// Closing the source is src owner's responsibility.
func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
return decode(src, nil)
}
// DecodeWithSampleRate decodes WAV (RIFF) data to playable stream.
//
// The format must be 1 or 2 channels, 8bit or 16bit little endian PCM.
@ -116,6 +131,10 @@ func (s *stream) Seek(offset int64, whence int) (int64, error) {
// A Stream doesn't close src even if src implements io.Closer.
// Closing the source is src owner's responsibility.
func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) {
return decode(src, &sampleRate)
}
func decode(src io.Reader, sampleRate *int) (*Stream, error) {
buf := make([]byte, 12)
n, err := io.ReadFull(src, buf)
if n != len(buf) {
@ -182,9 +201,9 @@ chunks:
return nil, fmt.Errorf("wav: bits per sample must be 8 or 16 but was %d", bitsPerSample)
}
origSampleRate := int64(buf[4]) | int64(buf[5])<<8 | int64(buf[6])<<16 | int64(buf[7])<<24
if int64(sampleRate) != origSampleRate {
if sampleRate != nil && int64(*sampleRate) != origSampleRate {
sampleRateFrom = int(origSampleRate)
sampleRateTo = sampleRate
sampleRateTo = *sampleRate
}
headerSize += size
case bytes.Equal(buf[0:4], []byte("data")):

View File

@ -134,13 +134,13 @@ func NewPlayer(game *Game, audioContext *audio.Context, musicType musicType) (*P
switch musicType {
case typeOgg:
var err error
s, err = vorbis.Decode(audioContext, bytes.NewReader(raudio.Ragtime_ogg))
s, err = vorbis.DecodeWithoutResampling(bytes.NewReader(raudio.Ragtime_ogg))
if err != nil {
return nil, err
}
case typeMP3:
var err error
s, err = mp3.Decode(audioContext, bytes.NewReader(raudio.Ragtime_mp3))
s, err = mp3.DecodeWithoutResampling(bytes.NewReader(raudio.Ragtime_mp3))
if err != nil {
return nil, err
}
@ -174,7 +174,7 @@ func NewPlayer(game *Game, audioContext *audio.Context, musicType musicType) (*P
player.audioPlayer.Play()
go func() {
s, err := wav.Decode(audioContext, bytes.NewReader(raudio.Jab_wav))
s, err := wav.DecodeWithSampleRate(sampleRate, bytes.NewReader(raudio.Jab_wav))
if err != nil {
log.Fatal(err)
return

View File

@ -56,7 +56,7 @@ func (g *Game) Update() error {
// Decode an Ogg file.
// oggS is a decoded io.ReadCloser and io.Seeker.
oggS, err := vorbis.Decode(g.audioContext, bytes.NewReader(raudio.Ragtime_ogg))
oggS, err := vorbis.DecodeWithoutResampling(bytes.NewReader(raudio.Ragtime_ogg))
if err != nil {
return err
}

View File

@ -70,7 +70,7 @@ func (g *Game) initAudio() {
// Decode an Ogg file.
// oggS is a decoded io.ReadCloser and io.Seeker.
oggS, err := vorbis.Decode(g.audioContext, bytes.NewReader(raudio.Ragtime_ogg))
oggS, err := vorbis.DecodeWithoutResampling(bytes.NewReader(raudio.Ragtime_ogg))
if err != nil {
log.Fatal(err)
}

View File

@ -179,7 +179,7 @@ func (g *Game) init() {
g.audioContext = audio.NewContext(48000)
}
jumpD, err := vorbis.DecodeWithSampleRate(48000, bytes.NewReader(raudio.Jump_ogg))
jumpD, err := vorbis.DecodeWithoutResampling(bytes.NewReader(raudio.Jump_ogg))
if err != nil {
log.Fatal(err)
}
@ -188,7 +188,7 @@ func (g *Game) init() {
log.Fatal(err)
}
jabD, err := wav.DecodeWithSampleRate(48000, bytes.NewReader(raudio.Jab_wav))
jabD, err := wav.DecodeWithoutResampling(bytes.NewReader(raudio.Jab_wav))
if err != nil {
log.Fatal(err)
}

View File

@ -58,11 +58,11 @@ func NewGame() (*Game, error) {
// return err
// }
//
// d, err := wav.Decode(g.audioContext, f)
// d, err := wav.DecodeWithoutResampling(f)
// ...
// Decode wav-formatted data and retrieve decoded PCM stream.
d, err := wav.Decode(g.audioContext, bytes.NewReader(raudio.Jab_wav))
d, err := wav.DecodeWithoutResampling(bytes.NewReader(raudio.Jab_wav))
if err != nil {
return nil, err
}