“去隔行” Scala 中的列表

发布于 2024-12-13 13:56:30 字数 994 浏览 1 评论 0原文

我有一个字节列表,表示从音频接口读取的原始样本。根据用例和硬件,每个样本的长度可以是 1 到 4 个字节,并且“流”中的通道总数可以或多或少是任意的。每个样本的通道数量和位数在运行时都是已知的。

我将举一个例子来说明我的意思。流中有四个通道,每个样本是两个字节。

列表(A1,A2,B1,B2,C1,C2,D1,D2,A3,A4,B3,B4,C3,C4,D3,D4)

所以A1是通道A第一个样本的第一个字节,A2是同一样本的第二个字节,依此类推。

我需要做的是将每个通道的样本提取到自己的列表中,如下所示:

List(List(A1, A2, A3, A4), List(B1, B2, B3, B4), List(C1, C2) , C3, C4), List(D1, D2, D3, D4))

我将如何在惯用的 Scala 中做到这一点?我几个小时前刚刚开始学习 Scala,我想出的唯一非命令式解决方案显然不是最佳的:

def uninterleave(samples: Array[Byte], numChannels: Int, bytesPerSample: Int) = {
val dropAmount = numChannels * bytesPerSample
  def extractChannel(n: Int) = {
    def extrInner(in: Seq[Byte], acc: Seq[Byte]): Seq[Byte] = {
      if(in == List()) acc
      else extrInner(in.drop(dropAmount), in.take(bytesPerSample) ++ acc)
    }
    extrInner(samples.drop(n * bytesPerSample), Nil)
  }

  for(i <- 0 until numChannels) yield extractChannel(i)
}

I have a list of bytes that represent raw samples read in from an audio interface. Depending on the use case and H/W, each sample can be anywhere from 1 to 4 bytes long, and the total number of channels in the "stream" can be more or less arbitrary. The amount of channels and bits per sample are both known at runtime.

I'll give an example of what I mean. There are four channels in the stream and each sample is two bytes.

List(A1, A2, B1, B2, C1, C2, D1, D2, A3, A4, B3, B4, C3, C4, D3, D4)

so A1 is the first byte of channel A's first sample, A2 is the second byte of the same sample and so on.

What I need to do is extract each channel's samples into their own lists, like this:

List(List(A1, A2, A3, A4), List(B1, B2, B3, B4), List(C1, C2, C3, C4), List(D1, D2, D3, D4))

How would I go about doing this in idiomatic Scala? I just started learning Scala a few hours ago, and the only non-imperative solution I've come up with is clearly nonoptimal:

def uninterleave(samples: Array[Byte], numChannels: Int, bytesPerSample: Int) = {
val dropAmount = numChannels * bytesPerSample
  def extractChannel(n: Int) = {
    def extrInner(in: Seq[Byte], acc: Seq[Byte]): Seq[Byte] = {
      if(in == List()) acc
      else extrInner(in.drop(dropAmount), in.take(bytesPerSample) ++ acc)
    }
    extrInner(samples.drop(n * bytesPerSample), Nil)
  }

  for(i <- 0 until numChannels) yield extractChannel(i)
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

淡写薰衣草的香 2024-12-20 13:56:30

但我不会

samples.grouped(bytesPerSample).grouped(numChannels).toList
  .transpose.map(_.flatten)

保证它的性能。我宁愿避免列表,不幸的是 grouped 产生它们。

也许

samples.grouped(bytesPerSample).map(_.toArray)
  .grouped(numChannels).map(_.toArray)
  .toArray
  .transpose
  .map(flatten)

仍然有很多清单。

I would do

samples.grouped(bytesPerSample).grouped(numChannels).toList
  .transpose.map(_.flatten)

I would not vouch for its performance though. I would rather avoid lists, unfortunately grouped produces them.

Maybe

samples.grouped(bytesPerSample).map(_.toArray)
  .grouped(numChannels).map(_.toArray)
  .toArray
  .transpose
  .map(flatten)

Still, lots of lists.

-柠檬树下少年和吉他 2024-12-20 13:56:30

didierd答案几乎是完美的,但是,唉,我认为可以稍微改进一下。他关心所有列表的创建,转置也是一个相当繁重的操作。如果您可以同时处理所有数据,那就足够了。

不过,我将使用 Stream,并使用一些小技巧来避免转置。

首先,分组是相同的,只是我将内容转换为流:

def getChannels[T](input: Iterator[T], elementsPerSample: Int, numOfChannels: Int) =
  input.toStream.grouped(elementsPerSample).toStream.grouped(numOfChannels).toStream

接下来,我将为您提供一个从中提取一个通道的函数:

def streamN[T](s: Stream[Stream[Stream[T]]])(channel: Int) = s flatMap (_(channel))

有了这些,我们可以解码流像这样:

// Sample input
val input = List('A1, 'A2, 'B1, 'B2, 'C1, 'C2, 'D1, 'D2, 'A3, 'A4, 'B3, 'B4, 'C3, 'C4, 'D3, 'D4)

// Save streams to val, to avoid recomputing the groups
val streams = getChannels(input.iterator, elementsPerSample = 2, numOfChannels = 4)

// Decode each one
def demuxer = streamN(streams) _
val aa = demuxer(0)
val bb = demuxer(1)
val cc = demuxer(2)
val dd = demuxer(3)

这将为每个通道返回单独的流手头没有整个流。如果您需要实时处理输入,这可能很有用。这是一些输入源,用于测试它读取输入的深度以获取特定元素:

def source(elementsPerSample: Int, numOfChannels: Int) = Iterator.from(0).map { x =>
  "" + ('A' + x / elementsPerSample % numOfChannels).toChar +
  (x % elementsPerSample 
   + (x / (numOfChannels * elementsPerSample)) * elementsPerSample 
   + 1)
}.map { x => println("Saw "+x); x }

然后您可以尝试如下操作:

val streams = getChannels(source(2, 4), elementsPerSample = 2, numOfChannels = 4)
def demuxer = streamN(streams) _
val cc = demuxer(2)
println(cc take 20 toList)
val bb = demuxer(1)
println(bb take 30 toList)

didierd's answer is just about perfect, but, alas, I think one can improve it a bit. He is concerned with all the list creation, and transpose is a rather heavy operation as well. If you can process all the data at the same time, it might well be good enough.

However, I'm going with Stream, and use a little trick to avoid transposing.

First of all, the grouping is the same, only I'm turning stuff into streams:

def getChannels[T](input: Iterator[T], elementsPerSample: Int, numOfChannels: Int) =
  input.toStream.grouped(elementsPerSample).toStream.grouped(numOfChannels).toStream

Next, I'll give you a function to extract one channel from that:

def streamN[T](s: Stream[Stream[Stream[T]]])(channel: Int) = s flatMap (_(channel))

With those, we can decode the streams like this:

// Sample input
val input = List('A1, 'A2, 'B1, 'B2, 'C1, 'C2, 'D1, 'D2, 'A3, 'A4, 'B3, 'B4, 'C3, 'C4, 'D3, 'D4)

// Save streams to val, to avoid recomputing the groups
val streams = getChannels(input.iterator, elementsPerSample = 2, numOfChannels = 4)

// Decode each one
def demuxer = streamN(streams) _
val aa = demuxer(0)
val bb = demuxer(1)
val cc = demuxer(2)
val dd = demuxer(3)

This will return separate streams for each channel without having the whole stream at hand. This might be useful if you need to process the input in real time. Here's some input source to test how far into the input it reads to get at a particular element:

def source(elementsPerSample: Int, numOfChannels: Int) = Iterator.from(0).map { x =>
  "" + ('A' + x / elementsPerSample % numOfChannels).toChar +
  (x % elementsPerSample 
   + (x / (numOfChannels * elementsPerSample)) * elementsPerSample 
   + 1)
}.map { x => println("Saw "+x); x }

You can then try stuff like this:

val streams = getChannels(source(2, 4), elementsPerSample = 2, numOfChannels = 4)
def demuxer = streamN(streams) _
val cc = demuxer(2)
println(cc take 20 toList)
val bb = demuxer(1)
println(bb take 30 toList)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文