2

我有这个代码

#define BUFFER_LEN (2048)
static float buffer[BUFFER_LEN];
int readcount;

while ((readcount = sf_read_float(handle, buffer, BUFFER_LEN))) {
  // alsa play
}

它从缓冲区读取 BUFFER_LEN 浮点数,并返回它实际读取的浮点数。“handle”告诉 sf_rad_float 缓冲区有多大。

例如,如果缓冲区包含 5 个浮点数,并且 BUFFER_LEN 为 3,readcount 将首先返回 3,下一次返回 2,while 循环将退出。

我想有一个做同样的功能。

更新

经过大量编码,我认为这是解决方案。

#include <stdio.h>

int copy_buffer(double* src, int src_length, int* src_pos,
                float* dest, int dest_length) {

  int copy_length = 0;

  if (src_length - *src_pos > dest_length) {
    copy_length = dest_length;
    printf("copy_length1 %i\n", copy_length);
  } else {
    copy_length = src_length - *src_pos;
    printf("copy_length2 %i\n", copy_length);
  }

  for (int i = 0; i < copy_length; i++) {
    dest[i] = (float) src[*src_pos + i];
  }

  // remember where to continue next time the copy_buffer() is called
  *src_pos += copy_length;

  return copy_length;
}

int main() {

  double src[] = {1,2,3,4,5};
  int src_length = 5;

  float dest[] = {0,0};
  int dest_length = 2;

  int read;
  int src_pos = 0;
  read = copy_buffer(src, src_length, &src_pos, dest, dest_length);
  printf("read %i\n", read);
  printf("src_pos %i\n", src_pos);

  for (int i = 0; i < src_length; i++) {
    printf("src %f\n", src[i]);
  }

  for (int i = 0; i < dest_length; i++) {
    printf("dest %f\n", dest[i]);
  }

  return 0;

}

下次调用 copy_buffer() 时,dest 包含 3,4。再次运行copy_buffer()只会复制值“5”。所以我认为它现在有效。

虽然它不是很漂亮,但我在int src_pos = 0;外面copy_buffer()

如果我可以像 sndfile 那样给出copy_buffer()一个唯一的句柄而不是,那就更好了。&src_pos

有谁知道怎么做?

4

2 回答 2

2

如果您想创建独特的句柄,您可以使用malloc()struct

typedef intptr_t HANDLE_TYPE;

HANDLE_TYPE init_buffer_traverse(double * src, size_t src_len);
int copy_buffer(HANDLE_TYPE h_traverse, double * dest, size_t dest_len);
无效 close_handle_buffer_traverse(HANDLE_TYPE h);

类型定义结构
{
    双 * 来源;
    size_t 源长度;
    size_t 位置;
} 遍历;

#define INVALID_HANDLE 0
/*
 * 返回一个新的遍历句柄,失败时返回 0 (INVALID_HANDLE)。
 *
 * 分配内存以包含遍历状态。
 * 将遍历状态重置为源缓冲区的开头。
 */
HANDLE_TYPE init_buffer_traverse(double *src, size_t src_len)
{
    遍历 * trav = malloc(sizeof(TRAVERSAL));

    如果(NULL == trav)
        返回无效句柄;

    trav->源 = src;
    trav->source_len = src_len;
    旅游->位置= 0;

    返回(HANDLE_TYPE)trav;
}
/*
 * 返回与遍历句柄关联的系统资源(内存)。
 */
无效close_handle_buffer_traverse(HANDLE_TYPE h)
{
    遍历 * trav = NULL;

    if (INVALID_HANDLE != h)
        免费((遍历*)h);
}
int copy_buffer(HANDLE_TYPE h,
                浮动* dest, int dest_length)
{
    遍历 * trav = NULL;

    if (INVALID_HANDLE == h)
        返回-1;

    trav = (TRAVERSAL *)h;

    int copy_length = trav->source_length - trav->位置;
    如果(目标长度 < 复制长度)
        复制长度 = 目标长度;

    for (int i = 0; i*强调文本* < copy_length; i++)
        dest[i] = trav->source[trav->position + i];

    // 记住下次调用 copy_buffer() 时从哪里继续
    trav->位置+=复制长度;

    返回复制长度;
}

这种风格是一些 C 编码器在 C++ 出现之前使用的。该样式涉及一个数据结构,其中包含我们“类”的所有数据元素。该类的大多数 API 都将其作为第一个参数,即指向其中一个结构的指针。这个指针类似于this指针。在我们的示例中,此参数被命名为trav

API 的例外是那些分配句柄类型的方法;这些类似于构造函数,并将句柄类型作为返回值。在我们的例子中,namedinit_buffer_traverse还不如被称为construct_traversal_handle

除此方法外,还有许多其他方法可以实现“不透明句柄”值。事实上,一些编码人员会操纵这些位(例如,通过 XOR)以掩盖句柄的真实性质。(这种默默无闻并不能在需要的地方提供安全性。)

在给出的示例中,我不确定(没有查看 sndlib)是否将目标缓冲区指针和长度保存在句柄结构中是否最有意义。如果是这样,那将使其成为“复制缓冲区”句柄而不是“遍历”句柄,并且您希望更改此答案中的所有术语。

这些句柄仅在当前进程的生命周期内有效,因此它们不适用于必须在句柄服务器重新启动后仍然存在的句柄。为此,使用 ISAM 数据库和列 ID 作为句柄。数据库方法比内存/指针方法慢得多,但是对于持久句柄,无论如何您都不能使用内存值。

另一方面,听起来您正在实现一个将在单个进程生命周期内运行的库。在这种情况下,根据您的要求修改后,我写的答案应该是可用的。

附录

您要求澄清我上面提到的与 C++ 的相似性。具体来说,一些等价的(与上述 C 代码)C++ 代码可能是:

class TRAVERSAL
{
    double * source;
    size_t source_length;
    size_t position;

    public TRAVERSAL(double *src, size_t src_len)
    {
        source = src;
        source_length = src_len;
        position = 0;
    }

    public int copy_buffer(double * dest, size_t dest_len)
    {
        int copy_length = source_length - position;
        if (dest_length < copy_length)
            copy_length = dest_length;

        for (int i = 0; i < copy_length; i++)
            dest[i] = source[position + i];

        // remember where to continue next time the copy_buffer() is called
        position += copy_length;

        return copy_length;            
    }
}

有一些明显的差异。C++ 版本看起来不那么冗长。其中一些是虚幻的;close_handle_buffer_traverse现在相当于deleteC++ 对象。当然delete不是类实现的一部分TRAVERSALdelete是语言自带的。

在 C++ 版本中,没有“不透明”句柄。

C 版本更明确,并且可能更清楚硬件正在执行哪些操作以响应程序执行。

C 版本更适合使用强制转换HANDLE_TYPE来创建“不透明 ID”而不是指针类型。C++ 版本可以“包装”在一个 API 中,该 API 在添加另一层的同时完成了同样的事情。在当前示例中,此类的用户将维护 a 的副本TRAVERSAL *,这不是很“不透明”。

在 functioncopy_buffer()中,C++ 版本不需要提及trav指针,因为它隐式地取消引用编译器提供的this指针。

sizeof(TRAVERSAL)C 和 C++ 示例应该是相同的——没有 vtable,还假设 C++ 的运行时类型标识已关闭,C++ 类仅包含与我们第一个示例中的 C 结构相同的内存布局。

在 C++ 中使用“不透明 ID”样式不太常见,因为在 C++ 中对“透明度”的惩罚降低了。class TRAVERSALare的数据成员,private因此TRAVERSAL *不能意外地用于破坏我们与 API 用户的 API 合同。

请注意,不透明 ID 和类指针都容易受到恶意 API 用户的滥用——不透明 ID 或类指针都可以直接转换为,例如,double **允许 ID 的持有者source直接通过记忆。当然,您必须已经信任 API 调用者,因为在这种情况下,API 调用代码在同一个地址空间中。在网络文件服务器的示例中,如果基于内存地址的“不透明 ID”暴露给外部,则可能存在安全隐患。

我通常不会离题到 API 用户的可信度,但我想澄清 C++ 关键字private没有“强制执行权”,它只指定程序员之间的协议,编译器也尊重该协议,除非人类另有说明。

最后,可以将 C++ 类指针转换为不透明 ID,如下所示:

typedef intptr_t HANDLE_TYPE;

HANDLE_TYPE init_buffer_traverse(double *src, size_t src_len)
{
    return (HANDLE_TYPE)(new TRAVERSAL(src, src_len));
}

int copy_buffer(HANDLE_TYPE h_traverse, double * dest, size_t dest_len)
{
    return ((TRAVERSAL *)h_traverse)->copy_buffer(dest, dest_len);
}

void close_handle_buffer_traverse(HANDLE_TYPE h)
{
    delete ((TRAVERSAL *)h);
}

现在我们对“等效”C++ 的简洁性可能会受到进一步质疑。

我写的关于与 C++ 相关的旧式 C 编程的内容并不是说 C++ 更适合这项任务。我只是说数据的封装和实现细节的隐藏可以通过一种几乎与 C++ 风格同构的风格在 C 中完成。如果您发现自己在使用 C 编程但不幸的是首先学习了 C++,这可能会很好地了解您。

附言

我刚刚注意到我们迄今为止的实现使用了:

dest[i] = (float)source[position + i];

复制字节时。因为两者destsource都是double *(即它们都指向double值),所以这里不需要强制转换。此外,从doubleto 转换float可能会丢失浮点表示中的精度数字。因此,最好将其删除并重述为:

dest[i] = source[position + i];
于 2010-02-15T00:13:21.420 回答
0

我开始研究它,但您可能也可以这样做: libsndfile是开源的,因此可以查看其sf_read_float()工作原理并创建一个从缓冲区执行相同操作的函数。 http://www.mega-nerd.com/libsndfile/有下载链接。

于 2010-02-14T05:36:36.827 回答