| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- // Copyright 2016 The G3N Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // +build !wasm
- package audio
- import (
- "fmt"
- "github.com/g3n/engine/audio/al"
- "github.com/g3n/engine/audio/ov"
- "io"
- "os"
- "unsafe"
- )
- // AudioInfo represents the information associated to an audio file
- type AudioInfo struct {
- Format int // OpenAl Format
- Channels int // Number of channels
- SampleRate int // Sample rate in hz
- BitsSample int // Number of bits per sample (8 or 16)
- DataSize int // Total data size in bytes
- BytesSec int // Bytes per second
- TotalTime float64 // Total time in seconds
- }
- // AudioFile represents an audio file
- type AudioFile struct {
- wavef *os.File // Pointer to wave file opened filed (nil for vorbis)
- vorbisf *ov.File // Pointer to vorbis file structure (nil for wave)
- info AudioInfo // Audio information structure
- looping bool // Looping flag
- }
- // NewAudioFile creates and returns a pointer to a new audio file object and an error
- func NewAudioFile(filename string) (*AudioFile, error) {
- // Checks if file exists
- _, err := os.Stat(filename)
- if err != nil {
- return nil, err
- }
- af := new(AudioFile)
- // Try to open as a wave file
- if af.openWave(filename) == nil {
- return af, nil
- }
- // Try to open as an ogg vorbis file
- if af.openVorbis(filename) == nil {
- return af, nil
- }
- return nil, fmt.Errorf("Unsuported file type")
- }
- // Close closes the audiofile
- func (af *AudioFile) Close() error {
- if af.wavef != nil {
- return af.wavef.Close()
- }
- return ov.Clear(af.vorbisf)
- }
- // Read reads decoded data from the audio file
- func (af *AudioFile) Read(pdata unsafe.Pointer, nbytes int) (int, error) {
- // Slice to access buffer
- bs := (*[1 << 30]byte)(pdata)[0:nbytes:nbytes]
- // Reads wave file directly
- if af.wavef != nil {
- n, err := af.wavef.Read(bs)
- if err != nil {
- return 0, err
- }
- if !af.looping {
- return n, nil
- }
- if n == nbytes {
- return n, nil
- }
- // EOF reached. Position file at the beginning
- _, err = af.wavef.Seek(int64(waveHeaderSize), 0)
- if err != nil {
- return 0, nil
- }
- // Reads next data into the remaining buffer space
- n2, err := af.wavef.Read(bs[n:])
- if err != nil {
- return 0, err
- }
- return n + n2, err
- }
- // Decodes Ogg vorbis
- decoded := 0
- for decoded < nbytes {
- n, _, err := ov.Read(af.vorbisf, unsafe.Pointer(&bs[decoded]), nbytes-decoded, false, 2, true)
- // Error
- if err != nil {
- return 0, err
- }
- // EOF
- if n == 0 {
- if !af.looping {
- break
- }
- // Position file at the beginning
- err = ov.PcmSeek(af.vorbisf, 0)
- if err != nil {
- return 0, err
- }
- }
- decoded += n
- }
- if nbytes > 0 && decoded == 0 {
- return 0, io.EOF
- }
- return decoded, nil
- }
- // Seek sets the file reading position relative to the origin
- func (af *AudioFile) Seek(pos uint) error {
- if af.wavef != nil {
- _, err := af.wavef.Seek(int64(waveHeaderSize+pos), 0)
- return err
- }
- return ov.PcmSeek(af.vorbisf, int64(pos))
- }
- // Info returns the audio info structure for this audio file
- func (af *AudioFile) Info() AudioInfo {
- return af.info
- }
- // CurrentTime returns the current time in seconds for the current file read position
- func (af *AudioFile) CurrentTime() float64 {
- if af.vorbisf != nil {
- pos, _ := ov.TimeTell(af.vorbisf)
- return pos
- }
- pos, err := af.wavef.Seek(0, 1)
- if err != nil {
- return 0
- }
- return float64(pos) / float64(af.info.BytesSec)
- }
- // Looping returns the current looping state of this audio file
- func (af *AudioFile) Looping() bool {
- return af.looping
- }
- // SetLooping sets the looping state of this audio file
- func (af *AudioFile) SetLooping(looping bool) {
- af.looping = looping
- }
- // openWave tries to open the specified file as a wave file
- // and if succesfull, sets the file pointer positioned after the header.
- func (af *AudioFile) openWave(filename string) error {
- // Open file
- osf, err := os.Open(filename)
- if err != nil {
- return err
- }
- // Reads header
- header := make([]uint8, waveHeaderSize)
- n, err := osf.Read(header)
- if err != nil {
- osf.Close()
- return err
- }
- if n < waveHeaderSize {
- osf.Close()
- return fmt.Errorf("File size less than header")
- }
- // Checks file marks
- if string(header[0:4]) != fileMark {
- osf.Close()
- return fmt.Errorf("'RIFF' mark not found")
- }
- if string(header[8:12]) != fileHead {
- osf.Close()
- return fmt.Errorf("'WAVE' mark not found")
- }
- // Decodes header fields
- af.info.Format = -1
- af.info.Channels = int(header[22]) + int(header[23])<<8
- af.info.SampleRate = int(header[24]) + int(header[25])<<8 + int(header[26])<<16 + int(header[27])<<24
- af.info.BitsSample = int(header[34]) + int(header[35])<<8
- af.info.DataSize = int(header[40]) + int(header[41])<<8 + int(header[42])<<16 + int(header[43])<<24
- // Sets OpenAL format field if possible
- if af.info.Channels == 1 {
- if af.info.BitsSample == 8 {
- af.info.Format = al.FormatMono8
- } else if af.info.BitsSample == 16 {
- af.info.Format = al.FormatMono16
- }
- } else if af.info.Channels == 2 {
- if af.info.BitsSample == 8 {
- af.info.Format = al.FormatStereo8
- } else if af.info.BitsSample == 16 {
- af.info.Format = al.FormatStereo16
- }
- }
- if af.info.Format == -1 {
- osf.Close()
- return fmt.Errorf("Unsupported OpenAL format")
- }
- // Calculates bytes/sec and total time
- var bytesChannel int
- if af.info.BitsSample == 8 {
- bytesChannel = 1
- } else {
- bytesChannel = 2
- }
- af.info.BytesSec = af.info.SampleRate * af.info.Channels * bytesChannel
- af.info.TotalTime = float64(af.info.DataSize) / float64(af.info.BytesSec)
- // Seeks after the header
- _, err = osf.Seek(waveHeaderSize, 0)
- if err != nil {
- osf.Close()
- return err
- }
- af.wavef = osf
- return nil
- }
- // openVorbis tries to open the specified file as an ogg vorbis file
- // and if succesfull, sets up the player for playing this file
- func (af *AudioFile) openVorbis(filename string) error {
- // Try to open file as ogg vorbis
- vf, err := ov.Fopen(filename)
- if err != nil {
- return err
- }
- // Get info for opened vorbis file
- var info ov.VorbisInfo
- err = ov.Info(vf, -1, &info)
- if err != nil {
- return err
- }
- if info.Channels == 1 {
- af.info.Format = al.FormatMono16
- } else if info.Channels == 2 {
- af.info.Format = al.FormatStereo16
- } else {
- return fmt.Errorf("Unsupported number of channels")
- }
- totalSamples, err := ov.PcmTotal(vf, -1)
- if err != nil {
- ov.Clear(vf)
- return nil
- }
- timeTotal, err := ov.TimeTotal(vf, -1)
- if err != nil {
- ov.Clear(vf)
- return nil
- }
- af.vorbisf = vf
- af.info.SampleRate = info.Rate
- af.info.BitsSample = 16
- af.info.Channels = info.Channels
- af.info.DataSize = int(totalSamples) * info.Channels * 2
- af.info.TotalTime = timeTotal
- return nil
- }
|