追加函数会覆盖切片中的现有数据。

huangapple go评论98阅读模式
英文:

Append function overwrites existing data in slice

问题

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

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

Golang内置函数append按预期向切片添加另一个元素,但由于某种原因,它还会用完全相同的数据覆盖数组中的所有现有元素

我已经尝试了两天多来分离出问题,但没有成功。

以下是一个简化版本的代码,它可以工作并展示了问题:

package main

import (
    "fmt"
    "time"

    "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, "\n")
    time.Sleep(time.Millisecond * 500) //limit temporarily the amount of data read
}

2. 更新

这是应用程序的输出:

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]]]

.... 等等 ....

可以看到,随着时间的推移,切片的大小在增长,但是数据不仅仅是追加,而是覆盖了数组中的数据。

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

如上所述,上面的代码是我应用程序的简化版本。通常情况下,我会录制5秒钟的音频,然后对样本执行快速傅里叶变换(FFT)以计算频谱。我省略了这部分,因为它对这个特定的问题没有影响。

我非常感谢任何帮助。也许有人可以指出我做错了什么。

谢谢!

<details>
<summary>英文:</summary>

I wrote a small application which records data from a sound card and stores the data in an array for later processing.

Whenever new data is available, portaudio executes the callback `record`. Within the callback I append the data to the array `RecData.data`.

The golang builtin function `append` adds as expected another element to the slice, **but for whatever reason also overwrites all existing elements within the array with exactly the same data**.

I have been trying to isolate the problem for more than two days, without success.

Here is a stripped down version of the code, which works and shows the problem: 

    package main
    
    import (
        &quot;fmt&quot;
        &quot;time&quot;
    //    &quot;reflect&quot;
    
        &quot;github.com/gordonklaus/portaudio&quot;
    )
    
    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(&quot;Received sound sample: &quot;)
        fmt.Println(in)
        re.data = append(re.data, in)
        fmt.Println(&quot;Content of RecData.data after adding received sound sample:&quot;)
        fmt.Println(re.data, &quot;\n&quot;)
        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(&quot;Data at index %d is the same as the recorded one, but should not be!\n&quot;, i )
            }
        }*/
    }


**2. Update**

This is the application output:

    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 ....

As we one can see, over time, the size of the slice is growing, but instead of just appending the data, the data in the array gets also overwritten. 

This should not happen. `portaudio` provides in the callback a `[][]float32` with the audio sample recorded from the sound card. As you can see they are always different. 

As mentioned, the code above is a stripped down version of my application. Usually I would record lets say 5 seconds, and then perform a Fast Fourier Transformation (FFT) over the samples to calculate the spectrum. I left this part away since it has no impact on this particular problem.


I would very much appreciate any help. Maybe somebody can point me out what I&#39;m doing wrong.

Thanks!


</details>


# 答案1
**得分**: 5

回调函数中传入的缓冲区是由portaudio包重用的,所以你将相同的切片结构追加到你的`data`切片中。每次由portaudio分配的缓冲区覆盖数据时,你会在`data`切片的每个元素中看到结果。

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

```go
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

英文:

The buffer passed into the callback is reused by the portaudio package, so you are appending the same slice structure to your data slice. Each time the buffer allocated by portaudio overwrites the data, you see the results in every element of your data slice.

You will need to allocate new slices and make a copy of the data:

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)

Example:
https://play.golang.org/p/cF57lQIZFU

huangapple
  • 本文由 发表于 2016年10月31日 22:15:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/40343987.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定