OS X / iOS - conversion de fréquence d'Échantillonnage pour la mémoire tampon à l'aide AudioConverterFillComplexBuffer
Je suis en train d'écrire un CoreAudio backend pour un bibliothèque audio appelé XAL. Tampons d'entrée peut être de différents taux d'échantillonnage. Je suis à l'aide d'un seul appareil audio pour la sortie. L'idée est de convertir les tampons et les mélanger avant de les envoyer à l'unité audio.
Tout fonctionne tant que le tampon d'entrée possède les mêmes propriétés (fréquence d'échantillonnage, nombre de voies, etc) que la sortie audio de l'unité. Par conséquent, le mix fonctionne.
Cependant, je suis coincé avec le taux d'échantillonnage et le nombre de voies de conversion. De ce que j'ai compris, c'est le plus facile à faire avec le Convertisseur Audio API de Services. J'ai réussi à construire un convertisseur; l'idée est que le format de sortie est la même que la sortie de l'unité de format, mais peut-être ajusté pour l'application du convertisseur.
Audio converter est construit avec succès, mais lors de l'appel de AudioConverterFillComplexBuffer()
, je reçois de l'état de la sortie d'erreur -50.
J'aimerais, si je pouvais obtenir une autre série de globes oculaires sur ce code. Le problème est probablement quelque part en dessous de AudioConverterNew()
. Variable stream
contient entrant et sortant) de la mémoire tampon de données, et streamSize
contient la taille en octets d'entrée (et de sortie) de la mémoire tampon de données.
Qu'ai-je fait de mal?
void CoreAudio_AudioManager::_convertStream(Buffer* buffer, unsigned char** stream, int *streamSize)
{
if (buffer->getBitsPerSample() != unitDescription.mBitsPerChannel ||
buffer->getChannels() != unitDescription.mChannelsPerFrame ||
buffer->getSamplingRate() != unitDescription.mSampleRate)
{
printf("INPUT STREAM SIZE: %d\n", *streamSize);
//describe the input format's description
AudioStreamBasicDescription inputDescription;
memset(&inputDescription, 0, sizeof(inputDescription));
inputDescription.mFormatID = kAudioFormatLinearPCM;
inputDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
inputDescription.mChannelsPerFrame = buffer->getChannels();
inputDescription.mSampleRate = buffer->getSamplingRate();
inputDescription.mBitsPerChannel = buffer->getBitsPerSample();
inputDescription.mBytesPerFrame = (inputDescription.mBitsPerChannel * inputDescription.mChannelsPerFrame) / 8;
inputDescription.mFramesPerPacket = 1; //*streamSize /inputDescription.mBytesPerFrame;
inputDescription.mBytesPerPacket = inputDescription.mBytesPerFrame * inputDescription.mFramesPerPacket;
printf("INPUT : %lu bytes per packet for sample rate %g, channels %d\n", inputDescription.mBytesPerPacket, inputDescription.mSampleRate, inputDescription.mChannelsPerFrame);
//copy conversion output format's description from the
//output audio unit's description.
//then adjust framesPerPacket to match the input we'll be passing.
//framecount of our input stream is based on the input bytecount.
//output stream will have same number of frames, but different
//number of bytes.
AudioStreamBasicDescription outputDescription = unitDescription;
outputDescription.mFramesPerPacket = 1; //inputDescription.mFramesPerPacket;
outputDescription.mBytesPerPacket = outputDescription.mBytesPerFrame * outputDescription.mFramesPerPacket;
printf("OUTPUT : %lu bytes per packet for sample rate %g, channels %d\n", outputDescription.mBytesPerPacket, outputDescription.mSampleRate, outputDescription.mChannelsPerFrame);
//create an audio converter
AudioConverterRef audioConverter;
OSStatus acCreationResult = AudioConverterNew(&inputDescription, &outputDescription, &audioConverter);
printf("Created audio converter %p (status: %d)\n", audioConverter, acCreationResult);
if(!audioConverter)
{
//bail out
free(*stream);
*streamSize = 0;
*stream = (unsigned char*)malloc(0);
return;
}
//calculate number of bytes required for output of input stream.
//allocate buffer of adequate size.
UInt32 outputBytes = outputDescription.mBytesPerPacket * (*streamSize / inputDescription.mBytesPerFrame); //outputDescription.mFramesPerPacket * outputDescription.mBytesPerFrame;
unsigned char *outputBuffer = (unsigned char*)malloc(outputBytes);
memset(outputBuffer, 0, outputBytes);
printf("OUTPUT BYTES : %d\n", outputBytes);
//describe input data we'll pass into converter
AudioBuffer inputBuffer;
inputBuffer.mNumberChannels = inputDescription.mChannelsPerFrame;
inputBuffer.mDataByteSize = *streamSize;
inputBuffer.mData = *stream;
//describe output data buffers into which we can receive data.
AudioBufferList outputBufferList;
outputBufferList.mNumberBuffers = 1;
outputBufferList.mBuffers[0].mNumberChannels = outputDescription.mChannelsPerFrame;
outputBufferList.mBuffers[0].mDataByteSize = outputBytes;
outputBufferList.mBuffers[0].mData = outputBuffer;
//set output data packet size
UInt32 outputDataPacketSize = outputDescription.mBytesPerPacket;
//convert
OSStatus result = AudioConverterFillComplexBuffer(audioConverter, /* AudioConverterRef inAudioConverter */
CoreAudio_AudioManager::_converterComplexInputDataProc, /* AudioConverterComplexInputDataProc inInputDataProc */
&inputBuffer, /* void *inInputDataProcUserData */
&outputDataPacketSize, /* UInt32 *ioOutputDataPacketSize */
&outputBufferList, /* AudioBufferList *outOutputData */
NULL /* AudioStreamPacketDescription *outPacketDescription */
);
printf("Result: %d wheee\n", result);
//change "stream" to describe our output buffer.
//even if error occured, we'd rather have silence than unconverted audio.
free(*stream);
*stream = outputBuffer;
*streamSize = outputBytes;
//dispose of the audio converter
AudioConverterDispose(audioConverter);
}
}
OSStatus CoreAudio_AudioManager::_converterComplexInputDataProc(AudioConverterRef inAudioConverter,
UInt32* ioNumberDataPackets,
AudioBufferList* ioData,
AudioStreamPacketDescription** ioDataPacketDescription,
void* inUserData)
{
printf("Converter\n");
if(*ioNumberDataPackets != 1)
{
xal::log("_converterComplexInputDataProc cannot provide input data; invalid number of packets requested");
*ioNumberDataPackets = 0;
ioData->mNumberBuffers = 0;
return -50;
}
*ioNumberDataPackets = 1;
ioData->mNumberBuffers = 1;
ioData->mBuffers[0] = *(AudioBuffer*)inUserData;
*ioDataPacketDescription = NULL;
return 0;
}
OriginalL'auteur Ivan Vučica | 2011-07-07
Vous devez vous connecter pour publier un commentaire.
Code de travail pour le Core Audio conversion du taux d'échantillonnage et le nombre de voies de conversion, à l'aide de Convertisseur Audio Services (maintenant disponible comme une partie de la Sous licence BSD XAL bibliothèque audio):
Dans l'en-tête, dans le cadre de la
CoreAudio_AudioManager
classe, ici sont pertinentes variables d'instance:Quelques mois plus tard, je regarde ça et j'ai réalisé que je n'avais pas de documenter les changements.
Si vous êtes intéressé à ce que les changements ont été:
CoreAudio_AudioManager::_converterComplexInputDataProc
ioNumberDataPackets
inUserData
) et l'entrée de description (utilisé pour calculer le nombre de paquets à être alimenté en Core Audio converter)Nous espérons que cette édition va aider un futur lecteur (moi y compris)!
Cette bibliothèque n'a pas besoin ni servir pour arbitraire de conversion; la seule raison que c'est même la conversion est ainsi il peut lire des fichiers audio sur la "sortie audio de l'unité" -- c'est à dire des enceintes ou un casque, pour la plupart. Donc, lors de la conversion, c'est la copie de l'intégralité de la sortie audio description à partir de la sortie audio de l'unité, et de modifier uniquement le
mFramesPerPacket
. Si vous avez besoin pour obtenir un taux différent, essayez de changer de champ approprié dans laoutputDescription
(qui est unAudioStreamBasicDescription
tapé struct). Et être prudent; je ai eu un temps terrible avec ce au plus tard à l'expérimentation.mais ce qui arrive quand les octets par paquet, le format d'entrée contient = 0, comme c'est souvent le cas avec les formats VBR? vous ne pouvez pas diviser par zéro, sauf si vous êtes chuck norris! :p
je l'ai eu.. dans le livre apprentissage core audio exemple de code ch6-ils répondre à cette question en prenant le maxPacketValue le convertisseur peut offrir à la place: CheckResult(AudioConverterGetProperty(streamer->audioConverter, kAudioConverterPropertyMaximumOutputpacketsize, &taille, &sizePerPacket);
Peut AudioConverterServices être utilisé, même si j'ai simplement voulu la convertir 2 mono flux de même longueur et de la fréquence d'échantillonnage dans un seul fichier stéréo? Il ne me semble pas clair comment je peux passer dans deux flux d'entrée dans un seul tampon, puis la faire devenir une mémoire tampon de sortie qui sera une chaîne stéréo.
OriginalL'auteur Ivan Vučica