mirror of
https://github.com/bloeys/wavy.git
synced 2025-12-29 09:28:19 +00:00
Put spaces after //
This commit is contained in:
@ -25,7 +25,7 @@ func (ws *OggStreamer) Read(outBuf []byte) (floatsRead int, err error) {
|
|||||||
|
|
||||||
func (ws *OggStreamer) Seek(offset int64, whence int) (int64, error) {
|
func (ws *OggStreamer) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
|
||||||
//This is because ogg expects position in samples not bytes
|
// This is because ogg expects position in samples not bytes
|
||||||
offset /= BytesPerSample
|
offset /= BytesPerSample
|
||||||
|
|
||||||
switch whence {
|
switch whence {
|
||||||
@ -49,7 +49,7 @@ func (ws *OggStreamer) Seek(offset int64, whence int) (int64, error) {
|
|||||||
return ws.Dec.Position() * BytesPerSample, nil
|
return ws.Dec.Position() * BytesPerSample, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Size returns number of bytes
|
// Size returns number of bytes
|
||||||
func (ws *OggStreamer) Size() int64 {
|
func (ws *OggStreamer) Size() int64 {
|
||||||
return ws.Dec.Length() * BytesPerSample
|
return ws.Dec.Length() * BytesPerSample
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Pre-defined errors
|
// Pre-defined errors
|
||||||
var (
|
var (
|
||||||
ErrInvalidWhence = errors.New("invalid whence value. Must be: io.SeekStart, io.SeekCurrent, or io.SeekEnd")
|
ErrInvalidWhence = errors.New("invalid whence value. Must be: io.SeekStart, io.SeekCurrent, or io.SeekEnd")
|
||||||
ErrNegativeSeekPos = errors.New("negative seeker position")
|
ErrNegativeSeekPos = errors.New("negative seeker position")
|
||||||
@ -16,11 +16,11 @@ var _ io.ReadSeeker = &SoundBuffer{}
|
|||||||
type SoundBuffer struct {
|
type SoundBuffer struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
|
|
||||||
//Pos is the starting position of the next read
|
// Pos is the starting position of the next read
|
||||||
Pos int64
|
Pos int64
|
||||||
}
|
}
|
||||||
|
|
||||||
//Read only returns io.EOF when bytesRead==0 and no more input is available
|
// Read only returns io.EOF when bytesRead==0 and no more input is available
|
||||||
func (sb *SoundBuffer) Read(outBuf []byte) (bytesRead int, err error) {
|
func (sb *SoundBuffer) Read(outBuf []byte) (bytesRead int, err error) {
|
||||||
|
|
||||||
bytesRead = copy(outBuf, sb.Data[sb.Pos:])
|
bytesRead = copy(outBuf, sb.Data[sb.Pos:])
|
||||||
@ -32,10 +32,10 @@ func (sb *SoundBuffer) Read(outBuf []byte) (bytesRead int, err error) {
|
|||||||
return bytesRead, nil
|
return bytesRead, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Seek returns the new position.
|
// Seek returns the new position.
|
||||||
//An error is only returned if the whence is invalid or if the resulting position is negative.
|
// An error is only returned if the whence is invalid or if the resulting position is negative.
|
||||||
//
|
//
|
||||||
//If the resulting position is >=len(SoundBuffer.Data) then future Read() calls will return io.EOF
|
// If the resulting position is >=len(SoundBuffer.Data) then future Read() calls will return io.EOF
|
||||||
func (sb *SoundBuffer) Seek(offset int64, whence int) (int64, error) {
|
func (sb *SoundBuffer) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
|
||||||
newPos := sb.Pos
|
newPos := sb.Pos
|
||||||
@ -58,10 +58,10 @@ func (sb *SoundBuffer) Seek(offset int64, whence int) (int64, error) {
|
|||||||
return sb.Pos, nil
|
return sb.Pos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy returns a new SoundBuffer that uses the same `Data` but with an independent ReadSeeker.
|
// Copy returns a new SoundBuffer that uses the same `Data` but with an independent ReadSeeker.
|
||||||
//This allows you to have many readers all reading from different positions of the same buffer.
|
// This allows you to have many readers all reading from different positions of the same buffer.
|
||||||
//
|
//
|
||||||
//The new buffer will have its starting position set to io.SeekStart (`Pos=0`)
|
// The new buffer will have its starting position set to io.SeekStart (`Pos=0`)
|
||||||
func (sb *SoundBuffer) Copy() *SoundBuffer {
|
func (sb *SoundBuffer) Copy() *SoundBuffer {
|
||||||
return &SoundBuffer{
|
return &SoundBuffer{
|
||||||
Data: sb.Data,
|
Data: sb.Data,
|
||||||
|
|||||||
@ -26,14 +26,14 @@ func (ws *WavStreamer) Read(outBuf []byte) (bytesRead int, err error) {
|
|||||||
|
|
||||||
func (ws *WavStreamer) Seek(offset int64, whence int) (int64, error) {
|
func (ws *WavStreamer) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
|
||||||
//This will only seek the underlying file but not the actual decoder because it can't seek
|
// This will only seek the underlying file but not the actual decoder because it can't seek
|
||||||
n, err := ws.Dec.Seek(offset, whence)
|
n, err := ws.Dec.Seek(offset, whence)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Since underlying decoder can't seek back, if the requested movement is back we have to rewind the decoder
|
// Since underlying decoder can't seek back, if the requested movement is back we have to rewind the decoder
|
||||||
//then seek forward to the requested position.
|
// then seek forward to the requested position.
|
||||||
if n < ws.Pos {
|
if n < ws.Pos {
|
||||||
|
|
||||||
err = ws.Dec.Rewind()
|
err = ws.Dec.Rewind()
|
||||||
@ -41,7 +41,7 @@ func (ws *WavStreamer) Seek(offset int64, whence int) (int64, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Anything before PCMStart is not valid sound, so the minimum seek back we allow is PCMStart
|
// Anything before PCMStart is not valid sound, so the minimum seek back we allow is PCMStart
|
||||||
if n < ws.PCMStart {
|
if n < ws.PCMStart {
|
||||||
n = ws.PCMStart
|
n = ws.PCMStart
|
||||||
} else {
|
} else {
|
||||||
@ -56,7 +56,7 @@ func (ws *WavStreamer) Seek(offset int64, whence int) (int64, error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Size returns number of bytes
|
// Size returns number of bytes
|
||||||
func (ws *WavStreamer) Size() int64 {
|
func (ws *WavStreamer) Size() int64 {
|
||||||
return ws.Dec.PCMLen()
|
return ws.Dec.PCMLen()
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ func NewWavStreamer(f *os.File, wavDec *wav.Decoder) (*WavStreamer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//The actual data starts somewhat within the file, not at 0
|
// The actual data starts somewhat within the file, not at 0
|
||||||
currPos, err := wavDec.Seek(0, 1)
|
currPos, err := wavDec.Seek(0, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
138
wavy.go
138
wavy.go
@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/jfreymuth/oggvorbis"
|
"github.com/jfreymuth/oggvorbis"
|
||||||
)
|
)
|
||||||
|
|
||||||
//SoundInfo contains static info about a loaded sound file
|
// SoundInfo contains static info about a loaded sound file
|
||||||
type SoundInfo struct {
|
type SoundInfo struct {
|
||||||
Type SoundType
|
Type SoundType
|
||||||
Mode SoundMode
|
Mode SoundMode
|
||||||
@ -29,18 +29,18 @@ type Sound struct {
|
|||||||
PlayerSeeker io.Seeker
|
PlayerSeeker io.Seeker
|
||||||
Info SoundInfo
|
Info SoundInfo
|
||||||
|
|
||||||
//File is the file descriptor of the sound file being streamed.
|
// File is the file descriptor of the sound file being streamed.
|
||||||
//This is only set if sound is streamed, and is kept to ensure GC doesn't hit it
|
// This is only set if sound is streamed, and is kept to ensure GC doesn't hit it
|
||||||
File *os.File
|
File *os.File
|
||||||
|
|
||||||
//Data is an io.ReadSeeker over an open file or over a buffer containing the uncompressed sound file.
|
// Data is an io.ReadSeeker over an open file or over a buffer containing the uncompressed sound file.
|
||||||
//Becomes nil after close
|
// Becomes nil after close
|
||||||
Data io.ReadSeeker
|
Data io.ReadSeeker
|
||||||
|
|
||||||
IsLooping bool
|
IsLooping bool
|
||||||
}
|
}
|
||||||
|
|
||||||
//Those values are set after Init
|
// Those values are set after Init
|
||||||
var (
|
var (
|
||||||
Ctx *oto.Context
|
Ctx *oto.Context
|
||||||
|
|
||||||
@ -52,13 +52,13 @@ var (
|
|||||||
BytesPerSecond int64
|
BytesPerSecond int64
|
||||||
)
|
)
|
||||||
|
|
||||||
//Pre-defined errors
|
// Pre-defined errors
|
||||||
var (
|
var (
|
||||||
errUnknownSoundType = errors.New("unknown sound type. Sound file extension must be one of: .mp3")
|
errUnknownSoundType = errors.New("unknown sound type. Sound file extension must be one of: .mp3")
|
||||||
)
|
)
|
||||||
|
|
||||||
//Init prepares the default audio device and does any required setup.
|
// Init prepares the default audio device and does any required setup.
|
||||||
//It must be called before loading any sounds
|
// It must be called before loading any sounds
|
||||||
func Init(sr SampleRate, chanCount SoundChannelCount, bitDepth SoundBitDepth) error {
|
func Init(sr SampleRate, chanCount SoundChannelCount, bitDepth SoundBitDepth) error {
|
||||||
|
|
||||||
otoCtx, readyChan, err := oto.NewContext(int(sr), int(chanCount), int(bitDepth))
|
otoCtx, readyChan, err := oto.NewContext(int(sr), int(chanCount), int(bitDepth))
|
||||||
@ -78,30 +78,30 @@ func Init(sr SampleRate, chanCount SoundChannelCount, bitDepth SoundBitDepth) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Wait blocks until sound finishes playing. If the sound is not playing Wait returns immediately.
|
// Wait blocks until sound finishes playing. If the sound is not playing Wait returns immediately.
|
||||||
//In the worst case (Wait sleeping then sound immediately paused), Wait will block ~4% of the total play time.
|
// In the worst case (Wait sleeping then sound immediately paused), Wait will block ~4% of the total play time.
|
||||||
//In most other cases Wait should be accurate to ~1ms.
|
// In most other cases Wait should be accurate to ~1ms.
|
||||||
//
|
//
|
||||||
//If you want to wait for all loops to finish then use WaitLoop
|
// If you want to wait for all loops to finish then use WaitLoop
|
||||||
func (s *Sound) Wait() {
|
func (s *Sound) Wait() {
|
||||||
|
|
||||||
if !s.IsPlaying() {
|
if !s.IsPlaying() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//We wait the remaining time in 25 chunks so that if the sound was paused since wait was called we don't keep blocking
|
// We wait the remaining time in 25 chunks so that if the sound was paused since wait was called we don't keep blocking
|
||||||
sleepTime := s.RemainingTime() / 25
|
sleepTime := s.RemainingTime() / 25
|
||||||
for s.Player.IsPlaying() {
|
for s.Player.IsPlaying() {
|
||||||
time.Sleep(sleepTime)
|
time.Sleep(sleepTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
//If there is anything left it should be tiny so we check frequently
|
// If there is anything left it should be tiny so we check frequently
|
||||||
for s.Player.IsPlaying() {
|
for s.Player.IsPlaying() {
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//WaitLoop waits until the sound is no longer looping
|
// WaitLoop waits until the sound is no longer looping
|
||||||
func (s *Sound) WaitLoop() {
|
func (s *Sound) WaitLoop() {
|
||||||
|
|
||||||
for s.IsLooping {
|
for s.IsLooping {
|
||||||
@ -109,21 +109,21 @@ func (s *Sound) WaitLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//PlayAsync plays the sound in the background and returns.
|
// PlayAsync plays the sound in the background and returns.
|
||||||
func (s *Sound) PlayAsync() {
|
func (s *Sound) PlayAsync() {
|
||||||
s.Player.Play()
|
s.Player.Play()
|
||||||
}
|
}
|
||||||
|
|
||||||
//PlaySync calls PlayAsync() followed by Wait()
|
// PlaySync calls PlayAsync() followed by Wait()
|
||||||
func (s *Sound) PlaySync() {
|
func (s *Sound) PlaySync() {
|
||||||
s.PlayAsync()
|
s.PlayAsync()
|
||||||
s.Wait()
|
s.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
//LoopAsync plays the sound 'timesToPlay' times.
|
// LoopAsync plays the sound 'timesToPlay' times.
|
||||||
//If timesToPlay<0 then it is played indefinitely until paused
|
// If timesToPlay<0 then it is played indefinitely until paused
|
||||||
//If timesToPlay==0 then the sound is not played.
|
// If timesToPlay==0 then the sound is not played.
|
||||||
//If a sound is already playing then it will be paused then resumed in a looping manner
|
// If a sound is already playing then it will be paused then resumed in a looping manner
|
||||||
func (s *Sound) LoopAsync(timesToPlay int) {
|
func (s *Sound) LoopAsync(timesToPlay int) {
|
||||||
|
|
||||||
if timesToPlay == 0 {
|
if timesToPlay == 0 {
|
||||||
@ -151,7 +151,7 @@ func (s *Sound) LoopAsync(timesToPlay int) {
|
|||||||
|
|
||||||
s.Wait()
|
s.Wait()
|
||||||
|
|
||||||
//Check is here because we don't want to seek back if we got paused
|
// Check is here because we don't want to seek back if we got paused
|
||||||
if !s.IsLooping {
|
if !s.IsLooping {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ func (s *Sound) LoopAsync(timesToPlay int) {
|
|||||||
timesToPlay--
|
timesToPlay--
|
||||||
s.Wait()
|
s.Wait()
|
||||||
|
|
||||||
//Check is here because we don't want to seek back if we got paused
|
// Check is here because we don't want to seek back if we got paused
|
||||||
if !s.IsLooping {
|
if !s.IsLooping {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -181,14 +181,14 @@ func (s *Sound) LoopAsync(timesToPlay int) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
//TotalTime returns the time taken to play the entire sound.
|
// TotalTime returns the time taken to play the entire sound.
|
||||||
//Safe to use after close
|
// Safe to use after close
|
||||||
func (s *Sound) TotalTime() time.Duration {
|
func (s *Sound) TotalTime() time.Duration {
|
||||||
return PlayTimeFromByteCount(s.Info.Size)
|
return PlayTimeFromByteCount(s.Info.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
//RemainingTime returns the time left in the clip, which is affected by pausing/resetting/seeking of the sound.
|
// RemainingTime returns the time left in the clip, which is affected by pausing/resetting/seeking of the sound.
|
||||||
//Returns zero after close
|
// Returns zero after close
|
||||||
func (s *Sound) RemainingTime() time.Duration {
|
func (s *Sound) RemainingTime() time.Duration {
|
||||||
|
|
||||||
if s.IsClosed() {
|
if s.IsClosed() {
|
||||||
@ -201,8 +201,8 @@ func (s *Sound) RemainingTime() time.Duration {
|
|||||||
return PlayTimeFromByteCount(s.Info.Size - currBytePos)
|
return PlayTimeFromByteCount(s.Info.Size - currBytePos)
|
||||||
}
|
}
|
||||||
|
|
||||||
//SetVolume must be between 0 and 1 (both inclusive). Other values will panic.
|
// SetVolume must be between 0 and 1 (both inclusive). Other values will panic.
|
||||||
//The default volume is 1.
|
// The default volume is 1.
|
||||||
func (s *Sound) SetVolume(newVol float64) {
|
func (s *Sound) SetVolume(newVol float64) {
|
||||||
|
|
||||||
if newVol < 0 || newVol > 1 {
|
if newVol < 0 || newVol > 1 {
|
||||||
@ -212,7 +212,7 @@ func (s *Sound) SetVolume(newVol float64) {
|
|||||||
s.Player.SetVolume(newVol)
|
s.Player.SetVolume(newVol)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Volume returns the current volume
|
// Volume returns the current volume
|
||||||
func (s *Sound) Volume() float64 {
|
func (s *Sound) Volume() float64 {
|
||||||
return s.Player.Volume()
|
return s.Player.Volume()
|
||||||
}
|
}
|
||||||
@ -226,24 +226,24 @@ func (s *Sound) IsPlaying() bool {
|
|||||||
return s.Player.IsPlaying()
|
return s.Player.IsPlaying()
|
||||||
}
|
}
|
||||||
|
|
||||||
//SeekToPercent moves the current position of the sound to the given percentage of the total sound length.
|
// SeekToPercent moves the current position of the sound to the given percentage of the total sound length.
|
||||||
//For example, if a sound is 10s long and percent=0.5 then when the sound is played it will start from 5s.
|
// For example, if a sound is 10s long and percent=0.5 then when the sound is played it will start from 5s.
|
||||||
//
|
//
|
||||||
//This can be used while the sound is playing.
|
// This can be used while the sound is playing.
|
||||||
//
|
//
|
||||||
//percent is clamped [0,1], so passing <0 is the same as zero, and >1 is the same as 1
|
// percent is clamped [0,1], so passing <0 is the same as zero, and >1 is the same as 1
|
||||||
func (s *Sound) SeekToPercent(percent float64) {
|
func (s *Sound) SeekToPercent(percent float64) {
|
||||||
|
|
||||||
percent = clamp01F64(percent)
|
percent = clamp01F64(percent)
|
||||||
s.PlayerSeeker.Seek(int64(float64(s.Info.Size)*percent), io.SeekStart)
|
s.PlayerSeeker.Seek(int64(float64(s.Info.Size)*percent), io.SeekStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
//SeekToTime moves the current position of the sound to the given duration.
|
// SeekToTime moves the current position of the sound to the given duration.
|
||||||
//For example if you use t=5*time.Second then play you will start from 5th second.
|
// For example if you use t=5*time.Second then play you will start from 5th second.
|
||||||
//
|
//
|
||||||
//This can be used while the sound is playing.
|
// This can be used while the sound is playing.
|
||||||
//
|
//
|
||||||
//t is clamped between [0, totalTime]
|
// t is clamped between [0, totalTime]
|
||||||
func (s *Sound) SeekToTime(t time.Duration) {
|
func (s *Sound) SeekToTime(t time.Duration) {
|
||||||
|
|
||||||
byteCount := ByteCountFromPlayTime(t)
|
byteCount := ByteCountFromPlayTime(t)
|
||||||
@ -260,8 +260,8 @@ func (s *Sound) IsClosed() bool {
|
|||||||
return s.Data == nil
|
return s.Data == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Close will clean underlying resources, and the 'Ctx' and 'Bytes' fields will be made nil.
|
// Close will clean underlying resources, and the 'Ctx' and 'Bytes' fields will be made nil.
|
||||||
//Repeated calls are no-ops
|
// Repeated calls are no-ops
|
||||||
func (s *Sound) Close() error {
|
func (s *Sound) Close() error {
|
||||||
|
|
||||||
if s.IsClosed() {
|
if s.IsClosed() {
|
||||||
@ -291,10 +291,10 @@ func (s *Sound) Close() error {
|
|||||||
return fdErr
|
return fdErr
|
||||||
}
|
}
|
||||||
|
|
||||||
//CopyInMemSound returns a new sound object that has identitcal info and uses the same underlying data, but with independent play controls (e.g. one playing at the start while one is in the middle).
|
// CopyInMemSound returns a new sound object that has identitcal info and uses the same underlying data, but with independent play controls (e.g. one playing at the start while one is in the middle).
|
||||||
//Since the sound data is not copied this function is very fast.
|
// Since the sound data is not copied this function is very fast.
|
||||||
//
|
//
|
||||||
//Panics if the sound is not in-memory
|
// Panics if the sound is not in-memory
|
||||||
func CopyInMemSound(s *Sound) *Sound {
|
func CopyInMemSound(s *Sound) *Sound {
|
||||||
|
|
||||||
if s.Info.Mode != SoundMode_Memory {
|
if s.Info.Mode != SoundMode_Memory {
|
||||||
@ -315,8 +315,8 @@ func CopyInMemSound(s *Sound) *Sound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ClipInMemSoundPercent is like CopyInMemSound but produces a sound that plays only between from and to.
|
// ClipInMemSoundPercent is like CopyInMemSound but produces a sound that plays only between from and to.
|
||||||
//fromPercent and toPercent must be between 0 and 1
|
// fromPercent and toPercent must be between 0 and 1
|
||||||
func ClipInMemSoundPercent(s *Sound, fromPercent, toPercent float64) *Sound {
|
func ClipInMemSoundPercent(s *Sound, fromPercent, toPercent float64) *Sound {
|
||||||
|
|
||||||
if s.Info.Mode != SoundMode_Memory {
|
if s.Info.Mode != SoundMode_Memory {
|
||||||
@ -352,8 +352,8 @@ func ResumeAllSounds() {
|
|||||||
Ctx.Resume()
|
Ctx.Resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewSoundStreaming plays sound by streaming from a file, so no need to load the entire file into memory.
|
// NewSoundStreaming plays sound by streaming from a file, so no need to load the entire file into memory.
|
||||||
//Good for large sound files
|
// Good for large sound files
|
||||||
func NewSoundStreaming(fpath string) (s *Sound, err error) {
|
func NewSoundStreaming(fpath string) (s *Sound, err error) {
|
||||||
|
|
||||||
soundType := GetSoundFileType(fpath)
|
soundType := GetSoundFileType(fpath)
|
||||||
@ -361,7 +361,7 @@ func NewSoundStreaming(fpath string) (s *Sound, err error) {
|
|||||||
return nil, errUnknownSoundType
|
return nil, errUnknownSoundType
|
||||||
}
|
}
|
||||||
|
|
||||||
//We read file but don't close so the player can stream the file any time later
|
// We read file but don't close so the player can stream the file any time later
|
||||||
file, err := os.Open(fpath)
|
file, err := os.Open(fpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -429,7 +429,7 @@ func soundFromFile(f *os.File, s *Sound) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewSoundMem loads the entire sound file into memory
|
// NewSoundMem loads the entire sound file into memory
|
||||||
func NewSoundMem(fpath string) (s *Sound, err error) {
|
func NewSoundMem(fpath string) (s *Sound, err error) {
|
||||||
|
|
||||||
soundType := GetSoundFileType(fpath)
|
soundType := GetSoundFileType(fpath)
|
||||||
@ -462,8 +462,8 @@ func getLoadingErr(fpath string, err error) error {
|
|||||||
return fmt.Errorf("failed to load '%s' with err '%s'", fpath, err.Error())
|
return fmt.Errorf("failed to load '%s' with err '%s'", fpath, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
//decodeSoundFromReaderSeeker reads and decodes till EOF, and places the final
|
// decodeSoundFromReaderSeeker reads and decodes till EOF, and places the final
|
||||||
//PCM16 data in a buffer, thus producing an in-memory sound
|
// PCM16 data in a buffer, thus producing an in-memory sound
|
||||||
func decodeSoundFromReaderSeeker(r io.ReadSeeker, s *Sound) error {
|
func decodeSoundFromReaderSeeker(r io.ReadSeeker, s *Sound) error {
|
||||||
|
|
||||||
if s.Info.Type == SoundType_MP3 {
|
if s.Info.Type == SoundType_MP3 {
|
||||||
@ -537,17 +537,17 @@ func GetSoundFileType(fpath string) SoundType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ReadAllFromReader takes an io.Reader and reads until error or io.EOF.
|
// ReadAllFromReader takes an io.Reader and reads until error or io.EOF.
|
||||||
//
|
//
|
||||||
//If io.EOF is reached then read bytes are returned with a nil error.
|
// If io.EOF is reached then read bytes are returned with a nil error.
|
||||||
//If the reader returns an error that's not io.EOF then everything read till that point is returned along with the error
|
// If the reader returns an error that's not io.EOF then everything read till that point is returned along with the error
|
||||||
//
|
//
|
||||||
//readingBufSize is the buffer used to read from reader.Read(). Bigger values might read more efficiently.
|
// readingBufSize is the buffer used to read from reader.Read(). Bigger values might read more efficiently.
|
||||||
//If readingBufSize<4096 then readingBufSize is set to 4096
|
// If readingBufSize<4096 then readingBufSize is set to 4096
|
||||||
//
|
//
|
||||||
//ouputBufSize is used to set the capacity of the final buffer to be returned. This can greatly improve performance
|
// ouputBufSize is used to set the capacity of the final buffer to be returned. This can greatly improve performance
|
||||||
//if you know the size of the output. It is allowed to have an outputBufSize that's smaller or larger than what the reader
|
// if you know the size of the output. It is allowed to have an outputBufSize that's smaller or larger than what the reader
|
||||||
//ends up returning
|
// ends up returning
|
||||||
func ReadAllFromReader(reader io.Reader, readingBufSize, ouputBufSize uint64) ([]byte, error) {
|
func ReadAllFromReader(reader io.Reader, readingBufSize, ouputBufSize uint64) ([]byte, error) {
|
||||||
|
|
||||||
if readingBufSize < 4096 {
|
if readingBufSize < 4096 {
|
||||||
@ -570,19 +570,19 @@ func ReadAllFromReader(reader io.Reader, readingBufSize, ouputBufSize uint64) ([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//PlayTimeFromByteCount returns the time taken to play this many bytes
|
// PlayTimeFromByteCount returns the time taken to play this many bytes
|
||||||
func PlayTimeFromByteCount(byteCount int64) time.Duration {
|
func PlayTimeFromByteCount(byteCount int64) time.Duration {
|
||||||
//timeToPlayInMs = timeToPlayInSec * 1000 = byteCount / bytesPerSecond * 1000
|
// timeToPlayInMs = timeToPlayInSec * 1000 = byteCount / bytesPerSecond * 1000
|
||||||
lenInMs := float64(byteCount) / float64(BytesPerSecond) * 1000
|
lenInMs := float64(byteCount) / float64(BytesPerSecond) * 1000
|
||||||
return time.Duration(lenInMs) * time.Millisecond
|
return time.Duration(lenInMs) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
//PlayTimeFromByteCount returns how many bytes are needed to produce a sound that takes t time to play
|
// PlayTimeFromByteCount returns how many bytes are needed to produce a sound that takes t time to play
|
||||||
func ByteCountFromPlayTime(t time.Duration) int64 {
|
func ByteCountFromPlayTime(t time.Duration) int64 {
|
||||||
return t.Milliseconds() * BytesPerSecond / 1000
|
return t.Milliseconds() * BytesPerSecond / 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
//clampF64 [min,max]
|
// clampF64 [min,max]
|
||||||
func clamp01F64(x float64) float64 {
|
func clamp01F64(x float64) float64 {
|
||||||
|
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
@ -596,8 +596,8 @@ func clamp01F64(x float64) float64 {
|
|||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
//F32ToUnsignedPCM16 takes PCM data stored as float32 between [-1, 1]
|
// F32ToUnsignedPCM16 takes PCM data stored as float32 between [-1, 1]
|
||||||
//and returns a byte array of uint16, where each two subsequent bytes represent one uint16.
|
// and returns a byte array of uint16, where each two subsequent bytes represent one uint16.
|
||||||
func F32ToUnsignedPCM16(fs []float32, outBuf []byte) []byte {
|
func F32ToUnsignedPCM16(fs []float32, outBuf []byte) []byte {
|
||||||
|
|
||||||
if outBuf == nil {
|
if outBuf == nil {
|
||||||
@ -606,9 +606,9 @@ func F32ToUnsignedPCM16(fs []float32, outBuf []byte) []byte {
|
|||||||
|
|
||||||
for i := 0; i < len(fs); i++ {
|
for i := 0; i < len(fs); i++ {
|
||||||
|
|
||||||
//Remap [-1,1]->[-32768, 32767], then re-interprets the int16 as a uint16.
|
// Remap [-1,1]->[-32768, 32767], then re-interprets the int16 as a uint16.
|
||||||
//With this, the negative values are mapped into the higher half of the uint16 range,
|
// With this, the negative values are mapped into the higher half of the uint16 range,
|
||||||
//while positive values remain unchanged
|
// while positive values remain unchanged
|
||||||
x := fs[i]
|
x := fs[i]
|
||||||
var u16 uint16
|
var u16 uint16
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
|
|||||||
18
wavy_test.go
18
wavy_test.go
@ -29,7 +29,7 @@ func MP3Subtest(t *testing.T) {
|
|||||||
const tadaFilepath = "./test_audio_files/tada.mp3"
|
const tadaFilepath = "./test_audio_files/tada.mp3"
|
||||||
const fatihaLenMS = 55484
|
const fatihaLenMS = 55484
|
||||||
|
|
||||||
//Mp3 streaming
|
// Mp3 streaming
|
||||||
s, err := wavy.NewSoundStreaming(fatihaFilepath)
|
s, err := wavy.NewSoundStreaming(fatihaFilepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to load streaming sound with path '%s'. Err: %s\n", fatihaFilepath, err)
|
t.Errorf("Failed to load streaming sound with path '%s'. Err: %s\n", fatihaFilepath, err)
|
||||||
@ -57,7 +57,7 @@ func MP3Subtest(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Mp3 in-memory
|
// Mp3 in-memory
|
||||||
s, err = wavy.NewSoundMem(fatihaFilepath)
|
s, err = wavy.NewSoundMem(fatihaFilepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to load memory sound with path '%s'. Err: %s\n", fatihaFilepath, err)
|
t.Errorf("Failed to load memory sound with path '%s'. Err: %s\n", fatihaFilepath, err)
|
||||||
@ -85,7 +85,7 @@ func MP3Subtest(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//'tada.mp3' memory
|
// 'tada.mp3' memory
|
||||||
s, err = wavy.NewSoundMem(tadaFilepath)
|
s, err = wavy.NewSoundMem(tadaFilepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to load memory sound with path '%s'. Err: %s\n", tadaFilepath, err)
|
t.Errorf("Failed to load memory sound with path '%s'. Err: %s\n", tadaFilepath, err)
|
||||||
@ -93,17 +93,17 @@ func MP3Subtest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
s.PlaySync()
|
s.PlaySync()
|
||||||
|
|
||||||
//Test repeat playing
|
// Test repeat playing
|
||||||
s2 := wavy.CopyInMemSound(s)
|
s2 := wavy.CopyInMemSound(s)
|
||||||
s2.SetVolume(0.25)
|
s2.SetVolume(0.25)
|
||||||
|
|
||||||
//Already finished, should not play
|
// Already finished, should not play
|
||||||
s.PlaySync()
|
s.PlaySync()
|
||||||
|
|
||||||
//Should play from beginning
|
// Should play from beginning
|
||||||
s2.PlaySync()
|
s2.PlaySync()
|
||||||
|
|
||||||
//Test seek and play
|
// Test seek and play
|
||||||
s2.SeekToPercent(0.2)
|
s2.SeekToPercent(0.2)
|
||||||
s2.PlaySync()
|
s2.PlaySync()
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ func WavSubtest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
s.PlaySync()
|
s.PlaySync()
|
||||||
|
|
||||||
//Wav streaming
|
// Wav streaming
|
||||||
s, err = wavy.NewSoundStreaming(wavFPath)
|
s, err = wavy.NewSoundStreaming(wavFPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to load streaming sound with path '%s'. Err: %s\n", wavFPath, err)
|
t.Errorf("Failed to load streaming sound with path '%s'. Err: %s\n", wavFPath, err)
|
||||||
@ -147,7 +147,7 @@ func OggSubtest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
s.PlaySync()
|
s.PlaySync()
|
||||||
|
|
||||||
//Ogg streaming
|
// Ogg streaming
|
||||||
s, err = wavy.NewSoundStreaming(oggFPath)
|
s, err = wavy.NewSoundStreaming(oggFPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to load streaming sound with path '%s'. Err: %s\n", oggFPath, err)
|
t.Errorf("Failed to load streaming sound with path '%s'. Err: %s\n", oggFPath, err)
|
||||||
|
|||||||
Reference in New Issue
Block a user