diff --git a/examples/localization.go b/examples/localization.go index fe077b3..e33de5a 100644 --- a/examples/localization.go +++ b/examples/localization.go @@ -9,7 +9,7 @@ import ( ) func main() { - file, err := os.OpenFile("M1F1-Alaw-AFsp.wav", os.O_RDWR, 066) + file, err := os.OpenFile("guns/gun_war-2-stereo.wav", os.O_RDWR, 066) if err != nil { fmt.Println(err) } @@ -19,7 +19,7 @@ func main() { fmt.Println(err) } - f, err := os.Create("M1F1-Alaw-AFsp_localized.wav") + f, err := os.Create("15_left_gun_war-2.wav") defer f.Close() if err != nil { fmt.Println(err) @@ -30,39 +30,55 @@ func main() { agraph.SampleRate(int(reader.Fmt.Data.SampleRate)), agraph.BitsPerSample(int(reader.Fmt.Data.BitsPerSample))) - localizationNode, _ := agraph.NewNode(agraph.LocalizationFilter, + localizationNode, err := agraph.NewNode(agraph.LocalizationFilter, "localization", - agraph.Angle(0)) + agraph.Angle(15)) + + if err != nil { + panic(err) + } localizationNode.SetSink(make(chan []uint16, 0)) go localizationNode.Process() start := time.Now() + i := 0 + var originalFirstSample uint16 for { + //fmt.Printf("i: %v\n", i) + if i == 1885 { + fmt.Printf("Original First Sample: %v\n", originalFirstSample) + //panic("PAUSE") + } + data, err := reader.ReadSampleInt16() if err != nil { fmt.Println(err) break } + if i == 0 { + originalFirstSample = data[0] + } + localizationNode.Source() <- data modifiedData := <-localizationNode.Sink() //fmt.Printf("Localized: %v\n", modifiedData) - leftByte := make([]byte, 2) rightByte := make([]byte, 2) binary.LittleEndian.PutUint16(leftByte, modifiedData[0]) binary.LittleEndian.PutUint16(rightByte, modifiedData[1]) -/* + + /* fmt.Print("Packet Info: \n") fmt.Printf(" - Original [%v]uint16 = %v \n", len(data), data) - // fmt.Printf(" - Modified [%v]uint16 = %v \n", len(data), data) + fmt.Printf(" - Modified [%v]uint16 = %v \n", len(modifiedData), modifiedData) fmt.Printf(" - L: [%v]byte = %v \n - R: [%v]byte = %v \n", len(leftByte), leftByte, len(rightByte), rightByte) -*/ + */ // pack all the bytes into the correct ordering fullByte := make([]byte, 4) @@ -76,8 +92,8 @@ func main() { // fmt.Println() - writer.Write(fullByte) + i++ } writer.Close() diff --git a/localization_filter.go b/localization_filter.go index 8add875..0441c6c 100644 --- a/localization_filter.go +++ b/localization_filter.go @@ -4,6 +4,9 @@ import ( "time" "math" "strconv" + "container/list" + "fmt" + "strings" ) /* @@ -27,12 +30,13 @@ import ( x x x x x x - 180°/-180° + -180°/180° */ const ( R = 9 // radius of the head in cm C = 345 // speed of sound in m/s + sampleRate = 22050 // won't work with audio that isn't 8000 (won't give the proper delay length) ) type Localization struct { @@ -41,6 +45,9 @@ type Localization struct { Name string Angle float64 itd time.Duration + sampleDelay int + delayChannel int + delayBuf *list.List } func newLocalization(name string, angle float64) (Node, error) { @@ -48,12 +55,30 @@ func newLocalization(name string, angle float64) (Node, error) { if err != nil { return nil, err } + + delayChannel := 0 + + if angle > 0 { + delayChannel = 1 + } + + // number of samples to buffer + sampleDelay := int(math.Floor(itd.Seconds() * sampleRate)) + + fmt.Printf("sample delay info: \n" + + " - itd seconds: %v \n" + + " - sampleRate: %v \n" + + " - sample delay %v \n", itd.Seconds(), sampleRate, sampleDelay) + return &Localization{ source: make(chan []uint16, SOURCE_SIZE), sink: nil, Name: name, Angle: angle, itd: itd, + sampleDelay: sampleDelay, + delayChannel: delayChannel, + delayBuf: list.New(), }, nil } @@ -85,19 +110,47 @@ func (n *Localization) Process() error { } func (n *Localization) do(data []uint16) ([]uint16, error) { + // add samples to the buffer if it isn't full + if n.delayBuf.Len() < n.sampleDelay { + n.delayBuf.PushFront(data[n.delayChannel]) + } + //fmt.Println(n.sampleDelay) + //fmt.Printf("LENGH OF DELAY BUF: %v\n", n.delayBuf.Len()) + + // if the buffer is full, replace the sample with the value + if n.delayBuf.Len() == n.sampleDelay { + samp := n.delayBuf.Back().Value.(uint16) + //fmt.Printf("Reading from buffer! \n - samp uint16: %v \n", samp) + n.delayBuf.Remove(n.delayBuf.Back()) + data[n.delayChannel] = samp + } else { + // otherwise, set that sample in the data stream to 0 + data[n.delayChannel] = 0 + } + return data, nil } // interaural time difference (ITD) // ∆t = r/c(θ + sin(θ)) +// angle is in degrees coming in func itd(angle float64, radius float64, c float64) (time.Duration, error) { + + angRad := angle * 0.0174533 + // calculate the itd - itd := (radius / c) * (angle + math.Sin(angle)) + itd := math.Abs((radius / c) * (angRad + math.Sin(angle))) // prepare the itd to be parsed as a duration itdStrConv := strconv.FormatFloat(itd, 'f', -1, 64) + fmt.Println(itdStrConv) + unitIdent := "s" + itdStrArr := []string{itdStrConv, unitIdent} + + + itdStr := strings.Join(itdStrArr, "") // duration - return time.ParseDuration(itdStrConv) + return time.ParseDuration(itdStr) }