experiments

All kinds of coding experiments
Log | Files | Refs | Submodules

wavtest.go (4429B)


      1 package main
      2 
      3 import (
      4 	"encoding/binary"
      5 	"fmt"
      6 	"io"
      7 	"os"
      8 	"time"
      9 )
     10 
     11 // Thanks: https://wavefilegem.com/how_wave_files_work.html
     12 type Wave struct {
     13 	ChunkID       [4]byte
     14 	ChunkSize     uint32
     15 	Format        [4]byte
     16 	Subchunk1ID   [4]byte
     17 	Subchunk1Size uint32
     18 	AudioFormat   uint16
     19 	NumChannels   uint16
     20 	SampleRate    uint32
     21 	ByteRate      uint32
     22 	BlockAlign    uint16
     23 	BitsPerSample uint16
     24 	Subchunk2ID   [4]byte
     25 	Subchunk2Size uint32
     26 	Data          [][]float64
     27 }
     28 
     29 // Descriptor returns a string of the data that should be common
     30 func (w *Wave) Descriptor() string {
     31 	return fmt.Sprintf("ChunkID: '%s', Format: '%s': Subchunk1ID: '%s', Subchunk2ID: '%s'",
     32 		w.ChunkID, w.Format, w.Subchunk1ID, w.Subchunk2ID)
     33 }
     34 
     35 // CheckFormat checks that the common bytes (chunk IDs and format code) are like expected
     36 func (w *Wave) CheckFormat() error {
     37 	return nil
     38 }
     39 
     40 func (w *Wave) FormatString() string {
     41 	var format string
     42 	switch w.AudioFormat {
     43 	case 1:
     44 		format = "integer PCM"
     45 	case 2:
     46 		format = "ADPCM"
     47 	case 3:
     48 		format = "floating point PCM"
     49 	case 6:
     50 		format = "A-law"
     51 	case 7:
     52 		format = "mu-law"
     53 	case 65534:
     54 		format = "WaveFormatExtensible"
     55 	default:
     56 		format = fmt.Sprintf("unknown (code %v)", w.AudioFormat)
     57 	}
     58 	return fmt.Sprintf("AudioFormat: %v, Channels: %v, Sample rate: %v, Bits/sample: %v", format, w.NumChannels, w.SampleRate, w.BitsPerSample)
     59 }
     60 
     61 //func (w *Wave) String() string {
     62 //	return fmt.Sprintf("WAVE, fs=%v,
     63 //}
     64 
     65 func (wav *Wave) LoadMetaData(file *os.File) {
     66 	// can use nil instead of BigEndian when reading into []byte (endianness is ignored)
     67 	binary.Read(file, binary.BigEndian, &wav.ChunkID)
     68 	binary.Read(file, binary.LittleEndian, &wav.ChunkSize)
     69 	binary.Read(file, binary.BigEndian, &wav.Format)
     70 	binary.Read(file, binary.BigEndian, &wav.Subchunk1ID)
     71 	binary.Read(file, binary.LittleEndian, &wav.Subchunk1Size)
     72 	binary.Read(file, binary.LittleEndian, &wav.AudioFormat)
     73 	binary.Read(file, binary.LittleEndian, &wav.NumChannels)
     74 	binary.Read(file, binary.LittleEndian, &wav.SampleRate)
     75 	binary.Read(file, binary.LittleEndian, &wav.ByteRate)
     76 	binary.Read(file, binary.LittleEndian, &wav.BlockAlign)
     77 	binary.Read(file, binary.LittleEndian, &wav.BitsPerSample)
     78 	binary.Read(file, binary.BigEndian, &wav.Subchunk2ID)
     79 	binary.Read(file, binary.LittleEndian, &wav.Subchunk2Size)
     80 }
     81 
     82 func LoadFile1(filename string) *Wave {
     83 	file, err := os.Open(filename)
     84 	if err != nil {
     85 		fmt.Println("error when opening file:", err)
     86 		panic(err)
     87 	}
     88 	wav := Wave{}
     89 	wav.LoadMetaData(file)
     90 	numSamples := wav.Subchunk2Size / (uint32(wav.NumChannels) * uint32(wav.BitsPerSample) / 8)
     91 	waveBytes := make([][][]byte, numSamples)
     92 	for sample := range waveBytes {
     93 		waveBytes[sample] = make([][]byte, wav.NumChannels)
     94 		for channel := range waveBytes[sample] {
     95 			waveBytes[sample][channel] = make([]byte, wav.BitsPerSample/8)
     96 		}
     97 	}
     98 	var sample int
     99 	var channel uint16
    100 	for err == nil && sample < len(waveBytes) {
    101 		err = binary.Read(file, binary.LittleEndian, &waveBytes[sample][channel])
    102 		sample++
    103 		channel++
    104 		if uint16(channel) >= wav.NumChannels {
    105 			channel = 0
    106 		}
    107 	}
    108 	if err != nil && err != io.EOF {
    109 		fmt.Println("error when reading wave data:", err)
    110 	}
    111 	return &wav
    112 }
    113 
    114 func LoadFile2(filename string) *Wave {
    115 	file, err := os.Open(filename)
    116 	if err != nil {
    117 		fmt.Println("error when opening file:", err)
    118 		panic(err)
    119 	}
    120 	wav := Wave{}
    121 	wav.LoadMetaData(file)
    122 	waveBytes := make([]byte, wav.Subchunk2Size)
    123 	err = binary.Read(file, nil, &waveBytes)
    124 	if err != nil && err != io.EOF {
    125 		fmt.Println("error when reading wave data:", err)
    126 	}
    127 	for _, b := range waveBytes[:30] {
    128 		fmt.Printf("%x ", b)
    129 	}
    130 	fmt.Println("")
    131 	return &wav
    132 }
    133 
    134 func LoadFile3(filename string) *Wave {
    135 	file, err := os.Open(filename)
    136 	if err != nil {
    137 		fmt.Println("error when opening file:", err)
    138 		panic(err)
    139 	}
    140 	wav := Wave{}
    141 	err = binary.Read(file, nil, wav)
    142 	if err != nil && err != io.EOF {
    143 		fmt.Println("error when reading wave data:", err)
    144 	}
    145 	return &wav
    146 }
    147 
    148 func main() {
    149 	if len(os.Args) < 2 {
    150 		fmt.Println("No filename given")
    151 		os.Exit(1)
    152 	}
    153 	start := time.Now()
    154 	LoadFile1(os.Args[1])
    155 	fmt.Printf("LoadFile1 finished in %s\n", time.Since(start))
    156 	start = time.Now()
    157 	LoadFile2(os.Args[1])
    158 	fmt.Printf("LoadFile2 finished in %s\n", time.Since(start))
    159 	//fmt.Println(wav.FormatString())
    160 	//fmt.Println(wav.Descriptor())
    161 	start = time.Now()
    162 	LoadFile3(os.Args[1])
    163 	fmt.Printf("LoadFile3 finished in %s\n", time.Since(start))
    164 }