Granular Synthesis w iOS 6 z wykorzystaniem AudioFileServices

Mam pytanie dotyczące aplikacji do syntezy dźwięku, nad którą pracuję. Próbuję odczytać plik audio, używając losowych „ziaren”techniki syntezy ziarnistej, umieść je w buforze wyjściowym, a następnie odtwórz je z powrotem za pomocą OpenAL. Do celów testowych po prostu zapisuję bufor wyjściowy do pliku, który mogę następnie odsłuchać.

Sądząc po moich wynikach, jestem na dobrej drodze, ale mam problemy z aliasingiem i odtwarzaniem dźwięków, które nie wydają się całkiem właściwe. Zazwyczaj w środku pliku wyjściowego występuje dość głośny pop, a poziomy głośności są BARDZO głośne.

Oto kroki, które podjąłem, aby uzyskać pożądane rezultaty, ale jestem trochę zdezorientowany co do kilku rzeczy, mianowicie formatów, które określam dla mojego AudioStreamBasicDescription.

Czytaj plik audio z mojego mainBundle, który jest plikiem mono w formacie .aiff:

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;

Następnie deklaruję moją listę AudioBufferList i ustawiam więcej właściwości

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;

Zrozumiałem, że .mData będzie czymkolwiek określonym w formatFlags (w tym przypadku wpisz SInt16). Ponieważ jest typu (nieważne*) Chcę to przekonwertować, aby przesłać dane, które są oczywiste dla manipulacji dźwiękiem. Zanim ustawię pętlę for, która właśnie przeszła przez bufor i rzuciła każdą próbkę do floatu *. Wydawało się to niepotrzebne, więc teraz przekazuję mój bufor .mData do funkcji, którą stworzyłem, a następnie granuluje dźwięk:

    float *theOutBuffer = [self granularizeWithData:(float *)buffers->mBuffers[0].mData with:framesRead];

W tej funkcji dynamicznie alokuję niektóre bufory, tworzę ziarenka losowego rozmiaru, umieszczam je w moim buforze wyjściowym po ich okienkowaniu za pomocą okna Hamminga i zwracam ten bufor (który jest zmiennoprzecinkowy). Do tego momentu wszystko jest fajne.

Następnie ustawiam cały mój plik wyjściowy ASBD i takie:

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");

Plik jest zapisany poprawnie, w formacie CAF. Moje pytanie brzmi: czy poprawnie obsługuję bufor .mData w tym, że rzucam próbki, aby przesyłać dane, manipulować (granulować) różnymi rozmiarami okien, a następnie zapisywać je w pliku przy użyciu ExtAudioFileWrite (w formacie CAF)? Czy jest bardziej elegancki sposób, aby to zrobić, na przykład deklarując mój format ASBD jako kAudioFlagIsFloat? Mój wyjściowy plik CAF zawiera kilka kliknięć, a kiedy otwieram go w Logice, wygląda na to, że jest dużo aliasingu. Ma to sens, jeśli próbuję wysłać dane zmiennoprzecinkowe, ale jest jakiś rodzaj konwersji, której nie znam.

Z góry dziękuję za wszelkie porady w tej sprawie! Byłem zapalonym czytelnikiem prawie wszystkich materiałów źródłowych online, w tym książki głównej audio, różnych blogów, samouczków itp. Ostatecznym celem mojej aplikacji jest odtwarzanie ziarnistego dźwięku w czasie rzeczywistym dla użytkownika ze słuchawkami, aby zapisywanie do pliku jest obecnie używane do testowania. Dzięki!

questionAnswers(1)

yourAnswerToTheQuestion