wave.go 2.5 KB

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