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つのfloatが含まれ、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);
void close_handle_buffer_traverse(HANDLE_TYPE h);

typedef struct
{{
    double*ソース;
    size_t source_length;
    size_t位置;
}トラバーサル;

#define INVALID_HANDLE 0
/ *
 *新しいトラバーサルハンドルを返します。失敗した場合は0(INVALID_HANDLE)を返します。
 *
 *トラバーサル状態を含むようにメモリを割り当てます。
 *トラバーサル状態をソースバッファの先頭にリセットします。
 * /
HANDLE_TYPE init_buffer_traverse(double * src、size_t src_len)
{{
    TRAVERSAL * trav = malloc(sizeof(TRAVERSAL));

    if(NULL == trav)
        INVALID_HANDLEを返します。

    trav-> source = src;
    trav-> source_len = src_len;
    trav-> position = 0;

    return(HANDLE_TYPE)trav;
}
/ *
 *トラバーサルハンドルに関連付けられているシステムリソース(メモリ)を返します。
 * /
void close_handle_buffer_traverse(HANDLE_TYPE h)
{{
    TRAVERSAL * trav = NULL;

    if(INVALID_HANDLE!= h)
        free((TRAVERSAL *)h);
}
int copy_buffer(HANDLE_TYPE h、
                float * dest、int dest_length)
{{
    TRAVERSAL * trav = NULL;

    if(INVALID_HANDLE == h)
        -1を返します。

    trav =(TRAVERSAL *)h;

    int copy_length = trav-> source_length --trav-> position;
    if(dest_length <copy_length)
        copy_length = dest_length;

    for(int i = 0;i*強調テキスト*<copy_length;i ++)
        dest [i] = trav-> source [trav-> position + i];

    //次にcopy_buffer()が呼び出されたときに続行する場所を覚えておいてください
    trav-> position + = copy_length;

    copy_lengthを返します。
}

この種のスタイルは、C++が登場する前に一部のCコーダーが使用していたものです。このスタイルには、「クラス」のすべてのデータ要素を含むデータ構造が含まれます。クラスのほとんどのAPIは、最初の引数として、これらの構造体の1つへのポインターを取ります。このポインタはポインタに似てい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_traversedeleteC++オブジェクトです。もちろんdelete、のクラス実装の一部ではなくTRAVERSALdelete言語が付属しています。

C ++バージョンでは、「不透明な」ハンドルはありません。

Cバージョンはより明確であり、プログラムの実行に応じてハードウェアによって実行されている操作をより明確にする可能性があります。

HANDLE_TYPECバージョンは、ポインター型ではなく「不透明なID」を作成するためにキャストを使用する方が適しています。C ++バージョンは、別のレイヤーを追加しながら同じことを実現するAPIに「ラップ」することができます。現在の例では、このクラスのユーザーは、のコピーを保持しますがTRAVERSAL *、これは完全に「不透明」ではありません。

関数copy_buffer()では、C ++バージョンはtravポインターについて言及する必要はありません。代わりに、コンパイラーが提供するthisポインターを暗黙的に逆参照するためです。

sizeof(TRAVERSAL)CとC++の両方の例で同じである必要があります-vtableがなく、C ++の実行時型識別がオフになっていると仮定すると、C++クラスには最初の例のC構造体と同じメモリレイアウトのみが含まれます。

C ++では「透明性」のペナルティが低くなるため、C++で「不透明ID」スタイルを使用することはあまり一般的ではありません。のデータメンバーはclass TRAVERSALであるprivateためTRAVERSAL *、APIユーザーとのAPI契約を破るために誤って使用することはできません。

不透明なIDとクラスポインタの両方が悪意のあるAPIユーザーからの悪用に対して脆弱であることに注意してください。たとえば、不透明なIDまたはクラスポインタのいずれかを直接キャストして、IDの所有者がを介してメンバーを直接double **変更できるようにすることができます。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++を習得したことがあるかどうかを知るのに役立ちます。

PS

これまでの実装で次のものが使用されていることに気づきました。

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

バイトをコピーするとき。destとは両方ともsourcedouble *つまり、両方ともdouble値を指している)ので、ここでキャストする必要はありません。また、からにキャストすると、浮動小数点表現の精度が低下する可能性がありますdoublefloatしたがって、これは削除して次のように言い換えるのが最適です。

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 に答える