4

私が取り組んでいる音声合成アプリについて質問があります。オーディオ ファイルを読み込んで、グラニュラー合成技術を使用してランダム化された「グレイン」を作成し、それらを出力バッファーに配置してから、OpenAL を使用してユーザーに再生できるようにしようとしています。テスト目的で、出力バッファーをファイルに書き込んで、後でリッスンできるようにします。

私の結果から判断すると、私は正しい方向に進んでいますが、エイリアシングの問題がいくつか発生しており、再生音はまったく正しくないようです。通常、出力ファイルの途中でかなり大きなポップ音があり、音量レベルが非常に大きくなることがあります。

必要な結果を得るために行った手順は次のとおりですが、いくつかのこと、つまり AudioStreamBasicDescription に指定している形式について少し混乱しています。

  1. .aiff 形式のモノラル ファイルである私の mainBundle からオーディオ ファイルを読み込みます。

    ExtAudioFileRef extAudioFile;
    CheckError(ExtAudioFileOpenURL(loopFileURL,
                               &extAudioFile),
           "couldn't open extaudiofile for reading");
    memset(&player->dataFormat, 0, sizeof(player->dataFormat));
    
    player->dataFormat.mFormatID = kAudioFormatLinearPCM;
    player->dataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    player->dataFormat.mSampleRate = S_RATE;
    player->dataFormat.mChannelsPerFrame = 1;
    player->dataFormat.mFramesPerPacket = 1;
    player->dataFormat.mBitsPerChannel = 16;
    player->dataFormat.mBytesPerFrame = 2;
    player->dataFormat.mBytesPerPacket = 2;
    
    // tell extaudiofile about our format
    CheckError(ExtAudioFileSetProperty(extAudioFile,
                                   kExtAudioFileProperty_ClientDataFormat,
                                   sizeof(AudioStreamBasicDescription),
                                   &player->dataFormat),
           "couldnt set client format on extaudiofile");
    
    SInt64 fileLengthFrames;
    UInt32 propSize = sizeof(fileLengthFrames);
    ExtAudioFileGetProperty(extAudioFile,
                        kExtAudioFileProperty_FileLengthFrames,
                        &propSize,
                        &fileLengthFrames);
    
    player->bufferSizeBytes = fileLengthFrames * player->dataFormat.mBytesPerFrame;
    
  2. 次に、AudioBufferList を宣言し、さらにいくつかのプロパティを設定します。

    AudioBufferList *buffers;
    UInt32 ablSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * 1);
    buffers = (AudioBufferList *)malloc(ablSize);
    
    player->sampleBuffer = (SInt16 *)malloc(sizeof(SInt16) * player->bufferSizeBytes);
    
    buffers->mNumberBuffers = 1;
    buffers->mBuffers[0].mNumberChannels = 1;
    buffers->mBuffers[0].mDataByteSize = player->bufferSizeBytes;
    buffers->mBuffers[0].mData = player->sampleBuffer;
    
  3. 私の理解では、.mData は formatFlags で指定されたものになります (この場合は、SInt16 と入力します)。タイプ (void * ) であるため、これをオーディオ操作で明らかな float データに変換したいと考えています。バッファを反復処理して各サンプルを float* にキャストする for ループを設定する前に。これは不要に思えたので、作成した関数に .mData バッファーを渡し、オーディオを粒状化します。

        float *theOutBuffer = [self granularizeWithData:(float *)buffers->mBuffers[0].mData with:framesRead];
    
  4. この関数では、いくつかのバッファーを動的に割り当て、ランダムなサイズのグレインを作成し、ハミング ウィンドウを使用してウィンドウ処理した後に出力バッファーに配置し、そのバッファー (フロート データ) を返します。この時点まですべてがクールです。

  5. 次に、すべての出力ファイル ASBD などを設定します。

    AudioStreamBasicDescription outputFileFormat;
    
    bzero(audioFormatPtr, sizeof(AudioStreamBasicDescription));
    
    outputFileFormat->mFormatID = kAudioFormatLinearPCM;
    outputFileFormat->mSampleRate = 44100.0;
    outputFileFormat->mChannelsPerFrame = numChannels;
    outputFileFormat->mBytesPerPacket = 2 * numChannels;
    outputFileFormat->mFramesPerPacket = 1;
    outputFileFormat->mBytesPerFrame = 2 * numChannels;
    outputFileFormat->mBitsPerChannel = 16;
    outputFileFormat->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
    
    UInt32 flags = kAudioFileFlags_EraseFile;
    ExtAudioFileRef outputAudioFileRef = NULL;
    NSString *tmpDir = NSTemporaryDirectory();
    NSString *outFilename = @"Decomp.caf";
    NSString *outPath = [tmpDir stringByAppendingPathComponent:outFilename];
    NSURL *outURL = [NSURL fileURLWithPath:outPath];
    
    
    AudioBufferList *outBuff;
    UInt32 abSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * 1);
    outBuff = (AudioBufferList *)malloc(abSize);
    
    outBuff->mNumberBuffers = 1;
    outBuff->mBuffers[0].mNumberChannels = 1;
    outBuff->mBuffers[0].mDataByteSize = abSize;
    outBuff->mBuffers[0].mData = theOutBuffer;
    
    CheckError(ExtAudioFileCreateWithURL((__bridge CFURLRef)outURL,
                                     kAudioFileCAFType,
                                     &outputFileFormat,
                                     NULL,
                                     flags,
                                     &outputAudioFileRef),
           "ErrorCreatingURL_For_EXTAUDIOFILE");
    
    CheckError(ExtAudioFileSetProperty(outputAudioFileRef,
                                   kExtAudioFileProperty_ClientDataFormat,
                                   sizeof(outputFileFormat),
                                   &outputFileFormat),
           "ErrorSettingProperty_For_EXTAUDIOFILE");
    
    CheckError(ExtAudioFileWrite(outputAudioFileRef,
                             framesRead,
                             outBuff),
           "ErrorWritingFile");
    

ファイルは CAF 形式で正しく書き込まれます。私の質問は次のとおりです。サンプルをフロート データにキャストし、さまざまなウィンドウ サイズを操作 (粒状化) してから、ExtAudioFileWrite (CAF 形式) を使用してファイルに書き込むという点で、.mData バッファーを正しく処理していますか? ASBD formatFlag を kAudioFlagIsFloat として宣言するなど、これを行うためのよりエレガントな方法はありますか? 私の出力 CAF ファイルにはいくつかのクリックがあり、Logic で開くと、多くのエイリアシングがあるように見えます。これは、float データを送信しようとしている場合には理にかなっていますが、私が気付いていない何らかの変換が行われています。

この件に関するアドバイスをお寄せいただきありがとうございます。私は、コア オーディオ ブック、さまざまなブログ、チュートリアルなど、ほぼすべてのソース マテリアルをオンラインで熱心に読んでいます。私のアプリの最終的な目標は、グラニュラライズされたオーディオをリアルタイムでユーザーにヘッドフォンで再生することです。ファイルへの書き込みは、現時点ではテストに使用されているだけです。ありがとう!

4

1 に答える 1

2

ステップ3についてあなたが言ったことは、ショートの配列をフロートの配列として解釈していることを私に示唆していますか? もしそうなら、私たちはあなたの問題の原因を見つけました。短い値を 1 つずつ float の配列に割り当てることができますか? それはそれを修正する必要があります。

ショーツの配列を指しているmDataように見えます。void *このポインターを にキャストしてfloat *も、基になるデータは に変更されませんfloatが、オーディオ処理関数はそれらをあたかもそうであるかのように扱います。ただし、floatshortはまったく異なる方法で保存されるため、その関数で行う計算は、実際の入力信号とは関係のない非常に異なる値で動作します。これを実験的に調べるには、次のことを試してください。

short data[4] = {-27158, 16825, 23024, 15};
void *pData = data;

ポインターは、voidそれが指しているデータの種類を示していないため、誤って値を指していると誤って想定することができfloatます。shortaは 2 バイト幅ですが、aは 4 バイト幅であることに注意してくださいfloat。コードがアクセス違反でクラッシュしなかったのは偶然です。float上記の配列は、2 つの値に対して十分な長さしかないと解釈されます。最初の値を見てみましょう。

float *pfData = (float *)pData;
printf("%d == %f\n", data[0], pfData[0]);

これの出力は-27158 == 23.198200、予想されるのではなく、-27158.0fおおよそ取得する方法を示しています23.2f。2つの問題が発生しました。まず、でsizeof(float)はありませんsizeof(short)。第 2 に、浮動小数点数の「1 と 0」は、整数とは大きく異なる方法で格納されます。http://en.wikipedia.org/wiki/Single_precision_floating-point_formatを参照してください。

問題を解決するには?少なくとも 2 つの簡単な解決策があります。まず、オーディオ プロセッサにフィードする前に、配列の各要素を変換できます。

int k;
float *pfBuf = (float *)malloc(n_data * sizeof(float));
short *psiBuf = (short *)buffers->mBuffers[0].mData[k];
for (k = 0; k < n_data; k ++)
{
    pfBuf[k] = psiBuf[k];
}
[self granularizeWithData:pfBuf with:framesRead];
for (k = 0; k < n_data; k ++)
{
    psiBuf[k] = pfBuf[k];
}
free(pfBuf);

shortほとんどの場合、 への呼び出しの後にすべてを に戻す必要があることがわかりますgranularizeWithData: with:。したがって、2番目の解決策は、すべての処理を行うshortことですが、あなたが書いたものからすると、後者のアプローチは気に入らないと思います。

于 2013-01-08T04:48:54.840 に答える