W rdzeniu audio iOS jest API AudioFileWritePackets z parametrem inPacketDescriptions zdefiniowanym jako 'Wskaźnik do tablicy opisów pakietów dla danych audio.'

I wygląda to tak w sygnaturze metody:
const AudioStreamPacketDescription *inPacketDescriptions,

Teraz struktura AudioStreamPacketDescription jest zdefiniowana w następujący sposób:

struct  AudioStreamPacketDescription
{
    SInt64  mStartOffset;
    UInt32  mVariableFramesInPacket;
    UInt32  mDataByteSize;
};
typedef struct AudioStreamPacketDescription AudioStreamPacketDescription;

Chciałbym wiedzieć, jak utworzyć i wypełnić taki „wskaźnik do tablicy struktur”, a nawet po podaniu zmiennej, jak ją odczytać. Korzystając z przykładu speakHere apple Umieściłem punkt przerwania, w którym otrzymuję zmienną i próbowałem zrzucić całą jej zawartość do logu.. to jest przykład próby:

void AQRecorder::printPacketDescriptionContents(const AudioStreamPacketDescription * inPacketDescriptions, UInt32 inNumberPackets)
{
    for (int i = 0; i < inNumberPackets; ++i)
    {
        NSLog(@"\n----------------\n");
        NSLog(@"this is packetDescriptionArray[%d].mStartOffset: %lld", i, (*inPacketDescriptions).mStartOffset);
        NSLog(@"this is packetDescriptionArray[%d].mVariableFramesInPacket: %lu", i, (*inPacketDescriptions).mVariableFramesInPacket);
        NSLog(@"this is packetDescriptionArray[%d].mDataByteSize: %lu", i, (*inPacketDescriptions).mDataByteSize);
        NSLog(@"\n----------------\n");   

    }        
}

Jakieś pomysły?

Aktualizacja: tutaj jest przykładowy dziennik, w którym próbuję się z nim pokombinować.. może może to pomóc w odpowiedzi (uwaga na dole ciągle wyświetla się jako null.. to nie ma sensu całość to tylko paczka zer, ponieważ jest to zmienna zwracana przez wywołanie zwrotne, która powinna być prawidłowo wypełniona, zauważ też, że informuje mnie ona o liczbie otrzymanych pakietów...).. także jeśli uruchomię kod z ((const AudioStreamPacketDescription *)(inPacketDescriptions +i))->mDataByteSize), otrzymuję błąd EXC_BAD_ACCESS

(lldb) po **(inPacketDescriptions)
error: indirection requires pointer operand ('const AudioStreamPacketDescription' invalid)
error: 1 errors parsing expression
(lldb) po *(inPacketDescriptions)
(AudioStreamPacketDescription) $1 = [no Objective-C description available]
(lldb) po *(inPacketDescriptions).mStartOffset
error: member reference type 'const AudioStreamPacketDescription *' is a pointer; maybe you meant to use '->'?
error: indirection requires pointer operand ('SInt64' (aka 'long long') invalid)
error: 2 errors parsing expression
(lldb) po (*inPacketDescriptions).mStartOffset
(SInt64) $2 = 0 <nil>

(lldb) po (const AudioStreamPacketDescription *)(inPacketDescriptions +1)
(const class AudioStreamPacketDescription *) $3 = 0x00000010 [no Objective-C description available]
(lldb) po (const AudioStreamPacketDescription *)(inPacketDescriptions +1)->mStartOffset
error: Execution was interrupted, reason: Attempted to dereference an invalid pointer..
The process has been returned to the state before execution.
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +1))->mStartOffset
(SInt64) $5 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +1))->mDataByteSize
(UInt32) $6 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +100))->mDataByteSize
(UInt32) $7 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +500))->mDataByteSize
(UInt32) $8 = 0 <nil>

(lldb) po inPacketDescriptions[0].mStartOffset
error: parent failed to evaluate: parent is NULL
(lldb) 

Również tutaj wygląda to w inspektorze XCode:wprowadź opis obrazu tutaj

1
abbood 25 wrzesień 2012, 11:54

2 odpowiedzi

Najlepsza odpowiedź

Nie pamiętam przypadku, w którym ta konkretna struktura jest kiedykolwiek wypełniana przez Ciebie, klienta. Aby pomyślnie radzić sobie z odczytywaniem i zapisywaniem danych dźwiękowych, musisz utworzyć pamięć dla tych struktur, które następnie przekazujesz w wielu wywołaniach. Kilka formatów innych niż PCM będzie wymagało tych informacji, w zależności od sposobu przechowywania danych dźwiękowych.

Chciałbym wiedzieć, jak utworzyć i wypełnić taki „wskaźnik do tablicy struktur”, a nawet po podaniu zmiennej, jak ją odczytać.

Cóż, istnieje kilka interfejsów API w interfejsach AudioFile I/O i AudioConvertor, które wykorzystują tę strukturę. Zasadniczo sam nie wypełniasz tego typu. Podstawowy przepływ wygląda tak:

// this is not for PCM audio data
//
// we'll read up to 8 packets at a time:
const size_t MaxPacketsToRead(8);

// allocate MaxPacketsToRead ASPDs on the stack:
AudioStreamPacketDescription aspds[MaxPacketsToRead];

// audio file read function:
AudioFileID inAudioFile = ...;
Boolean inUseCache = ...;
UInt32 outNumBytes = ...;
AudioStreamPacketDescription* outPacketDescriptions(aspds);
SInt64 inStartingPacket = ...;
UInt32 ioNumPackets = MaxPacketsToRead; // << you may not get all the packets
                                        // you request, but this sets the
                                        // upper limit.
void* outBuffer = ...;


OSStatus result(AudioFileReadPackets(inAudioFile,
                                     inUseCache,
                                     &outNumBytes,
                                     outPacketDescriptions,
                                     inStartingPacket,
                                     &ioNumPackets,
                                     outBuffer
));

if (noErr != result) {
  ...uh-oh...
}

// *now* we know that we have ioNumPackets worth of valid ASPDs,
// populated by the reader. and we have the associated audio data
// in other parameters.
// we can now safely pass all this information off to a function which 
// reads the ASPDs, such as AudioFileWritePackets.

(znając problem OP bardziej szczegółowo) W wielu przypadkach możesz uniknąć całej tej złożoności i po prostu utworzyć reprezentację ExtAudioFile i określić kExtAudioFileProperty_ClientDataFormat dla próbki docelowej format — wtedy interfejsy API ExtAudioFile utworzą w Twoim imieniu wewnętrzny konwerter, który przekonwertuje wejście pliku audio dowolnego typu na określoną reprezentację PCM, której możesz użyć do odtwarzania przykładowych danych. Implementacja tego wszystkiego na tym poziomie jest w rzeczywistości dość skomplikowana, jeśli chcesz obsługiwać wiele formatów plików. ExtAudioFile sprawia, że ​​konwersja przykładowych danych jest bardzo łatwa – jeśli jest to opcja i jeśli dobrze pasuje do twojego scenariusza przesyłania strumieniowego.

Jeśli chodzi o rejestrowanie, to na podstawie wyglądu próbujesz wydrukować pola o strukturze NULL.

1
justin 25 wrzesień 2012, 13:29

Zadeklaruj tablicę AudioStreamPacketDescription:

AudioStreamPacketDescription descriptions[10];

/* Populate 'descriptions' */
descriptions[0].mVariableFramesInPacket = 4; /* For example. */

Następnie przekaż go do funkcji:

/* 'a' is an instance of AQRecorder. */
a.printPacketDescriptionContents(descriptions, 10);

Gdy tablica jest przekazywana do funkcji, rozpada się na wskaźnik (do pierwszego elementu tablicy).

Przykład printPacketDescriptionContents() to dostęp tylko do pierwszego elementu tablicy inNumberPackets razy: musisz uzyskać dostęp do każdego elementu:

NSLog(@"this is packetDescriptionArray[%d].mStartOffset: %lld",
      i,
      inPacketDescriptions[i].mStartOffset);
1
hmjd 25 wrzesień 2012, 12:04