wave.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build !wasm
  5. package audio
  6. import (
  7. "fmt"
  8. "github.com/g3n/engine/audio/al"
  9. "os"
  10. )
  11. // WaveSpecs describes the characteristics of the audio encoded in a wave file.
  12. type WaveSpecs struct {
  13. Format int // OpenAl Format
  14. Type int // Type field from wave header
  15. Channels int // Number of channels
  16. SampleRate int // Sample rate in hz
  17. BitsSample int // Number of bits per sample (8 or 16)
  18. DataSize int // Total data size in bytes
  19. BytesSec int // Bytes per second
  20. TotalTime float64 // Total time in seconds
  21. }
  22. const (
  23. waveHeaderSize = 44
  24. fileMark = "RIFF"
  25. fileHead = "WAVE"
  26. )
  27. // WaveCheck checks if the specified filepath corresponds to a an audio wave file.
  28. // If the file is a valid wave file, return a pointer to WaveSpec structure
  29. // with information about the encoded audio data.
  30. func WaveCheck(filepath string) (*WaveSpecs, error) {
  31. // Open file
  32. f, err := os.Open(filepath)
  33. if err != nil {
  34. return nil, err
  35. }
  36. defer f.Close()
  37. // Reads header
  38. header := make([]uint8, waveHeaderSize)
  39. n, err := f.Read(header)
  40. if err != nil {
  41. return nil, err
  42. }
  43. if n < waveHeaderSize {
  44. return nil, fmt.Errorf("File size less than header")
  45. }
  46. // Checks file marks
  47. if string(header[0:4]) != fileMark {
  48. return nil, fmt.Errorf("'RIFF' mark not found")
  49. }
  50. if string(header[8:12]) != fileHead {
  51. return nil, fmt.Errorf("'WAVE' mark not found")
  52. }
  53. // Decodes header fields
  54. var ws WaveSpecs
  55. ws.Format = -1
  56. ws.Type = int(header[20]) + int(header[21])<<8
  57. ws.Channels = int(header[22]) + int(header[23])<<8
  58. ws.SampleRate = int(header[24]) + int(header[25])<<8 + int(header[26])<<16 + int(header[27])<<24
  59. ws.BitsSample = int(header[34]) + int(header[35])<<8
  60. ws.DataSize = int(header[40]) + int(header[41])<<8 + int(header[42])<<16 + int(header[43])<<24
  61. // Sets OpenAL format field if possible
  62. if ws.Channels == 1 {
  63. if ws.BitsSample == 8 {
  64. ws.Format = al.FormatMono8
  65. } else if ws.BitsSample == 16 {
  66. ws.Format = al.FormatMono16
  67. }
  68. } else if ws.Channels == 2 {
  69. if ws.BitsSample == 8 {
  70. ws.Format = al.FormatStereo8
  71. } else if ws.BitsSample == 16 {
  72. ws.Format = al.FormatStereo16
  73. }
  74. }
  75. // Calculates bytes/sec and total time
  76. var bytesChannel int
  77. if ws.BitsSample == 8 {
  78. bytesChannel = 1
  79. } else {
  80. bytesChannel = 2
  81. }
  82. ws.BytesSec = ws.SampleRate * ws.Channels * bytesChannel
  83. ws.TotalTime = float64(ws.DataSize) / float64(ws.BytesSec)
  84. return &ws, nil
  85. }