关于数组:追加函数会覆盖切片中的现有数据

Append function overwrites existing data in slice

我写了一个小应用程序,它记录声卡中的数据,并将数据存储在一个数组中,以便以后处理。

每当有新数据可用时,portaudio执行回调record。在回调中,我将数据附加到数组RecData.data中。

golang内置函数append按预期将另一个元素添加到切片中,但出于任何原因,也会使用完全相同的数据覆盖数组中的所有现有元素。

我已经试图孤立这个问题两天多了,但没有成功。

下面是代码的简化版本,它可以工作并显示问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package main

import (
   "fmt"
   "time"
//   "reflect"

   "github.com/gordonklaus/portaudio"
)

type RecData struct{
    data [][][]float32
}

func main() {

    var inputChs int = 1
    var outputChs int = 0
    var samplingRate float64 = 48000
    var framesPerBuffer int = 3 //for test purpose that low. Would normally be 1024 or 2048

    rec := RecData{make([][][]float32, 0, 1000)}

    portaudio.Initialize()

    stream, err := portaudio.OpenDefaultStream(inputChs, outputChs, samplingRate, framesPerBuffer, rec.record)
    if err != nil {
        fmt.Println(err)
    }

    defer stream.Close()
    stream.Start()
    for {
        time.Sleep(time.Millisecond * 10)
    }
}

// callback which gets called when new data is in the buffer
func (re *RecData)record(in [][]float32) {
    fmt.Println("Received sound sample:")
    fmt.Println(in)
    re.data = append(re.data, in)
    fmt.Println("Content of RecData.data after adding received sound sample:")
    fmt.Println(re.data,"
")
    time.Sleep(time.Millisecond * 500) //limit temporarily the amount of data read
    // iterate over all recorded data and compare them
    /*
    for i, d := range re.data{
        if reflect.DeepEqual(d, in){
                fmt.Printf("Data at index %d is the same as the recorded one, but should not be!
", i )
        }
    }*/
}

2。更新

这是应用程序输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Received sound sample:
[[0.71575254 1.0734825 0.7444282]]
Content of RecData.data after adding received sound sample:
[[[0.71575254 1.0734825 0.7444282]]]

Received sound sample:
[[0.7555193 0.768355 0.6575008]]
Content of RecData.data after adding received sound sample:
[[[0.7555193 0.768355 0.6575008]] [[0.7555193 0.768355 0.6575008]]]

Received sound sample:
[[0.7247052 0.68471473 0.6843796]]
Content of RecData.data after adding received sound sample:
[[[0.7247052 0.68471473 0.6843796]] [[0.7247052 0.68471473 0.6843796]] [[0.7247052 0.68471473 0.6843796]]]

Received sound sample:
[[0.6996536 0.66283375 0.67252487]]
Content of RecData.data after adding received sound sample:
[[[0.6996536 0.66283375 0.67252487]] [[0.6996536 0.66283375 0.67252487]] [[0.6996536 0.66283375 0.67252487]] [[0.6996536 0.66283375 0.67252487]]]


.... etc ....

正如我们所看到的,随着时间的推移,切片的大小在增加,但数组中的数据也会被覆盖,而不仅仅是追加数据。

这不应该发生。portaudio在回调中提供[][]float32和从声卡记录的音频样本。正如你所看到的,它们总是不同的。

如前所述,上面的代码是我的应用程序的精简版。通常我会记录5秒,然后对样本执行快速傅立叶变换(FFT)来计算光谱。我把这部分忘了,因为它对这个问题没有影响。

我非常感谢你的帮助。也许有人能指出我做错了什么。

谢谢!


传递到回调的缓冲区被portaudio包重用,因此您要将相同的切片结构附加到data切片。每次portaudio分配的缓冲区覆盖数据时,您都会在data片的每个元素中看到结果。

您需要分配新的切片并复制数据:

1
2
3
4
5
6
func (re *RecData) record(in [][]float32) {
    buf := make([][]float32, len(in))
    for i, v := range in {
        buf[i] = append([]float32(nil), v...)
    }
    re.data = append(re.data, buf)

例子:https://play.golang.org/p/cf57lqizfu