3

一部の人々は、オーディオ データを最初から最後まで読み取り、最初から最後まで書き込まれたコピーを作成し、その逆のオーディオ データを単純に再生することを提案しました。

これがどのように行われるか、iOSの既存の例はありますか?

MixerHost というサンプル プロジェクトを見つけました。これは、ある時点で AudioUnitSampleType、ファイルから読み取ったオーディオ データを保持し、それをバッファに割り当てることを使用します。

これは次のように定義されます。

typedef SInt32 AudioUnitSampleType;
#define kAudioUnitSampleFractionBits 24

そしてAppleによると:

iPhone OS でのオーディオ ユニットおよびその他のオーディオ処理の正規のオーディオ サンプル タイプは、8.24 ビットの固定小数点サンプルを使用するインターリーブされていないリニア PCM です。

つまり、インターリーブされていないリニア PCM オーディオ データを保持します。

しかし、このデータがどこに読み込まれているのか、どこに保存されているのかわかりません。オーディオ データをロードしてバッファリングするコードは次のとおりです。

- (void) readAudioFilesIntoMemory {

    for (int audioFile = 0; audioFile < NUM_FILES; ++audioFile)  {

        NSLog (@"readAudioFilesIntoMemory - file %i", audioFile);

        // Instantiate an extended audio file object.
        ExtAudioFileRef audioFileObject = 0;

        // Open an audio file and associate it with the extended audio file object.
        OSStatus result = ExtAudioFileOpenURL (sourceURLArray[audioFile], &audioFileObject);

        if (noErr != result || NULL == audioFileObject) {[self printErrorMessage: @"ExtAudioFileOpenURL" withStatus: result]; return;}

        // Get the audio file's length in frames.
        UInt64 totalFramesInFile = 0;
        UInt32 frameLengthPropertySize = sizeof (totalFramesInFile);

        result =    ExtAudioFileGetProperty (
                        audioFileObject,
                        kExtAudioFileProperty_FileLengthFrames,
                        &frameLengthPropertySize,
                        &totalFramesInFile
                    );

        if (noErr != result) {[self printErrorMessage: @"ExtAudioFileGetProperty (audio file length in frames)" withStatus: result]; return;}

        // Assign the frame count to the soundStructArray instance variable
        soundStructArray[audioFile].frameCount = totalFramesInFile;

        // Get the audio file's number of channels.
        AudioStreamBasicDescription fileAudioFormat = {0};
        UInt32 formatPropertySize = sizeof (fileAudioFormat);

        result =    ExtAudioFileGetProperty (
                        audioFileObject,
                        kExtAudioFileProperty_FileDataFormat,
                        &formatPropertySize,
                        &fileAudioFormat
                    );

        if (noErr != result) {[self printErrorMessage: @"ExtAudioFileGetProperty (file audio format)" withStatus: result]; return;}

        UInt32 channelCount = fileAudioFormat.mChannelsPerFrame;

        // Allocate memory in the soundStructArray instance variable to hold the left channel, 
        //    or mono, audio data
        soundStructArray[audioFile].audioDataLeft =
            (AudioUnitSampleType *) calloc (totalFramesInFile, sizeof (AudioUnitSampleType));

        AudioStreamBasicDescription importFormat = {0};
        if (2 == channelCount) {

            soundStructArray[audioFile].isStereo = YES;
            // Sound is stereo, so allocate memory in the soundStructArray instance variable to  
            //    hold the right channel audio data
            soundStructArray[audioFile].audioDataRight =
                (AudioUnitSampleType *) calloc (totalFramesInFile, sizeof (AudioUnitSampleType));
            importFormat = stereoStreamFormat;

        } else if (1 == channelCount) {

            soundStructArray[audioFile].isStereo = NO;
            importFormat = monoStreamFormat;

        } else {

            NSLog (@"*** WARNING: File format not supported - wrong number of channels");
            ExtAudioFileDispose (audioFileObject);
            return;
        }

        // Assign the appropriate mixer input bus stream data format to the extended audio 
        //        file object. This is the format used for the audio data placed into the audio 
        //        buffer in the SoundStruct data structure, which is in turn used in the 
        //        inputRenderCallback callback function.

        result =    ExtAudioFileSetProperty (
                        audioFileObject,
                        kExtAudioFileProperty_ClientDataFormat,
                        sizeof (importFormat),
                        &importFormat
                    );

        if (noErr != result) {[self printErrorMessage: @"ExtAudioFileSetProperty (client data format)" withStatus: result]; return;}

        // Set up an AudioBufferList struct, which has two roles:
        //
        //        1. It gives the ExtAudioFileRead function the configuration it 
        //            needs to correctly provide the data to the buffer.
        //
        //        2. It points to the soundStructArray[audioFile].audioDataLeft buffer, so 
        //            that audio data obtained from disk using the ExtAudioFileRead function
        //            goes to that buffer

        // Allocate memory for the buffer list struct according to the number of 
        //    channels it represents.
        AudioBufferList *bufferList;

        bufferList = (AudioBufferList *) malloc (
            sizeof (AudioBufferList) + sizeof (AudioBuffer) * (channelCount - 1)
        );

        if (NULL == bufferList) {NSLog (@"*** malloc failure for allocating bufferList memory"); return;}

        // initialize the mNumberBuffers member
        bufferList->mNumberBuffers = channelCount;

        // initialize the mBuffers member to 0
        AudioBuffer emptyBuffer = {0};
        size_t arrayIndex;
        for (arrayIndex = 0; arrayIndex < channelCount; arrayIndex++) {
            bufferList->mBuffers[arrayIndex] = emptyBuffer;
        }

        // set up the AudioBuffer structs in the buffer list
        bufferList->mBuffers[0].mNumberChannels  = 1;
        bufferList->mBuffers[0].mDataByteSize    = totalFramesInFile * sizeof (AudioUnitSampleType);
        bufferList->mBuffers[0].mData            = soundStructArray[audioFile].audioDataLeft;

        if (2 == channelCount) {
            bufferList->mBuffers[1].mNumberChannels  = 1;
            bufferList->mBuffers[1].mDataByteSize    = totalFramesInFile * sizeof (AudioUnitSampleType);
            bufferList->mBuffers[1].mData            = soundStructArray[audioFile].audioDataRight;
        }

        // Perform a synchronous, sequential read of the audio data out of the file and
        //    into the soundStructArray[audioFile].audioDataLeft and (if stereo) .audioDataRight members.
        UInt32 numberOfPacketsToRead = (UInt32) totalFramesInFile;

        result = ExtAudioFileRead (
                     audioFileObject,
                     &numberOfPacketsToRead,
                     bufferList
                 );

        free (bufferList);

        if (noErr != result) {

            [self printErrorMessage: @"ExtAudioFileRead failure - " withStatus: result];

            // If reading from the file failed, then free the memory for the sound buffer.
            free (soundStructArray[audioFile].audioDataLeft);
            soundStructArray[audioFile].audioDataLeft = 0;

            if (2 == channelCount) {
                free (soundStructArray[audioFile].audioDataRight);
                soundStructArray[audioFile].audioDataRight = 0;
            }

            ExtAudioFileDispose (audioFileObject);            
            return;
        }

        NSLog (@"Finished reading file %i into memory", audioFile);

        // Set the sample index to zero, so that playback starts at the 
        //    beginning of the sound.
        soundStructArray[audioFile].sampleNumber = 0;

        // Dispose of the extended audio file object, which also
        //    closes the associated file.
        ExtAudioFileDispose (audioFileObject);
    }
}

反転する必要があるオーディオ サンプルの配列を含むのはどの部分ですか? ですかAudioUnitSampleType

bufferList->mBuffers[0].mData = soundStructArray[audioFile].audioDataLeft;

注: audioDataLeft はAudioUnitSampleType、配列ではなく SInt32 である として定義されます。

Core Audio Mailing listで手がかりを見つけました:

まあ、私の知る限り、iPh*n* とは何の関係もありません (一部のオーディオ API が省略されていない限り、私はそのプログラムのメンバーではありません)。AFAIR、AudioFile.h、および ExtendedAudioFile.h は、caf の読み取りまたは書き込み、およびそのストリーム/チャネルへのアクセスに必要なものを提供する必要があります。基本的に、各チャネル/ストリームを逆方向に読み取りたいので、オーディオ ファイルのプロパティが必要ない場合は、そのチャネルのデータを処理できれば、それが圧縮されていないと仮定すると、非常に簡単です。caf が表現できる形式の数を考慮すると、これには、考えているよりも数行多くのコードが必要になる可能性があります。圧縮されていないデータを扱えるようになると、文字列を逆にするのと同じくらい簡単になります。次に、もちろん、ファイルのデータを逆のデータに置き換えます。

これは私が試したものですが、リバースされたバッファを両方のチャネルの mData に割り当てると、何も聞こえません:

AudioUnitSampleType *leftData = soundStructArray[audioFile].audioDataLeft;
AudioUnitSampleType *reversedData = (AudioUnitSampleType *) calloc (totalFramesInFile, sizeof (AudioUnitSampleType));
UInt64 j = 0;
for (UInt64 i = (totalFramesInFile - 1); i > -1; i--) {
    reversedData[j] = leftData[i];
    j++;
}
4

3 に答える 3

2

I have worked on a sample app, which records what user says and plays them backwards. I have used CoreAudio to achieve this. Link to app code.

/* As each sample is 16-bits in size(2 bytes)(mono channel). You can load each sample at a time by copying it into a different buffer by starting at the end of the recording and reading backwards. When you get to the start of the data you have reversed the data and playing will be reversed. */

// set up output file
AudioFileID outputAudioFile;

AudioStreamBasicDescription myPCMFormat;
myPCMFormat.mSampleRate = 16000.00;
myPCMFormat.mFormatID = kAudioFormatLinearPCM ;
myPCMFormat.mFormatFlags =  kAudioFormatFlagsCanonical;
myPCMFormat.mChannelsPerFrame = 1;
myPCMFormat.mFramesPerPacket = 1;
myPCMFormat.mBitsPerChannel = 16;
myPCMFormat.mBytesPerPacket = 2;
myPCMFormat.mBytesPerFrame = 2;


AudioFileCreateWithURL((__bridge CFURLRef)self.flippedAudioUrl,
                       kAudioFileCAFType,
                       &myPCMFormat,
                       kAudioFileFlags_EraseFile,
                       &outputAudioFile);
// set up input file
AudioFileID inputAudioFile;
OSStatus theErr = noErr;
UInt64 fileDataSize = 0;

AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);

theErr = AudioFileOpenURL((__bridge CFURLRef)self.recordedAudioUrl, kAudioFileReadPermission, 0, &inputAudioFile);

thePropertySize = sizeof(fileDataSize);
theErr = AudioFileGetProperty(inputAudioFile, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);

UInt32 dataSize = fileDataSize;
void* theData = malloc(dataSize);

//Read data into buffer
UInt32 readPoint  = dataSize;
UInt32 writePoint = 0;
while( readPoint > 0 )
{
    UInt32 bytesToRead = 2;

    AudioFileReadBytes( inputAudioFile, false, readPoint, &bytesToRead, theData );
    AudioFileWriteBytes( outputAudioFile, false, writePoint, &bytesToRead, theData );

    writePoint += 2;
    readPoint -= 2;
}

free(theData);
AudioFileClose(inputAudioFile);
AudioFileClose(outputAudioFile);

Hope this helps.

于 2013-02-06T07:54:10.503 に答える
0

通常、ASBDが使用されている場合、フィールドは、この説明で表されるバッファー内のサンプルデータの完全なレイアウトを記述します。通常、これらのバッファーは、AudioBufferListに含まれるAudioBufferで表されます。

ただし、ASBDにkAudioFormatFlagIsNonInterleavedフラグがある場合、AudioBufferListの構造とセマンティクスは異なります。この場合、ASBDフィールドは、リストに含まれているAudioBufferの1つの形式を記述し、リスト内の各AudioBufferは、オーディオデータの単一(モノ)チャネルを持つと判断されます。次に、ASBDのmChannelsPerFrameは、AudioBufferList内に含まれるAudioBufferの総数を示します。各バッファーには1つのチャネルが含まれます。これは主に、このリストのAudioUnit(およびAudioConverter)表現で使用され、この構造のAudioHardwareの使用法には含まれていません。

于 2012-08-19T14:29:45.227 に答える
0

リバースされたデータを格納するために別のバッファを割り当てる必要はありません。サウンドの長さによっては、かなりの量の CPU が必要になる場合があります。サウンドを逆方向に再生するには、sampleNumber カウンターを totalFramesInFile - 1 から開始するだけです。

このように MixerHost を変更して、目的の効果を得ることができます。

soundStructArray[audioFile].sampleNumber = 0;と 置き換えますsoundStructArray[audioFile].sampleNumber = totalFramesInFile - 1;

UInt32 の代わりに sampleNumber を SInt32 にします。

サンプルを書き出すループをこれに置き換えます。

for (UInt32 frameNumber = 0; frameNumber < inNumberFrames; ++frameNumber) {
    outSamplesChannelLeft[frameNumber]                 = dataInLeft[sampleNumber];
    if (isStereo) outSamplesChannelRight[frameNumber]  = dataInRight[sampleNumber];

    if (--sampleNumber < 0) sampleNumber = frameTotalForSound - 1;
}

これにより、効果的に逆方向に再生されます。んー。MixerHost の音楽を聞くのは久しぶりです。私はそれがとても楽しいと思うことを認めなければなりません。

于 2012-12-28T04:24:44.787 に答える