// Registers a DataFlavor/FE pair
void nsDataObjCollection::AddDataFlavor(const char * aDataFlavor,
                                        LPFORMATETC aFE)
{
  // Add the FormatEtc to our list if it's not already there.  We don't care
  // about the internal aDataFlavor because nsDataObj handles that.
  IEnumFORMATETC * ifEtc;
  FORMATETC fEtc;
  ULONG num;
  if (S_OK != this->EnumFormatEtc(DATADIR_GET, &ifEtc))
    return;
  while (S_OK == ifEtc->Next(1, &fEtc, &num)) {
    NS_ASSERTION(1 == num,
         "Bit off more than we can chew in nsDataObjCollection::AddDataFlavor");
    if (FormatsMatch(fEtc, *aFE)) {
      ifEtc->Release();
      return;
    }
  } // If we didn't find a matching format, add this one
  ifEtc->Release();
  m_enumFE->AddFormatEtc(aFE);
}
AudioConverterStream::AudioConverterStream(WaveFormat sourceFormat, WaveFormat destFormat, WaveFormat** prevSourceFormats , int numPrevSourceFormats)
    : outBuffer(nullptr), outSize(0), outBufferAllocated(0), stream(nullptr), isProxy(false), m_failed(false), subConverter(nullptr), startOffset(0)
{
    if (sourceFormat.GetSize() == destFormat.GetSize())
    {
        LPWAVEFORMATEX src = static_cast<LPWAVEFORMATEX>(sourceFormat);
        LPWAVEFORMATEX dest = static_cast<LPWAVEFORMATEX>(destFormat);

        if (memcmp(src, dest, sourceFormat.GetSize()) == 0)
        {
            // Source and destination formats are identical, so we'll just pass the input through to
            // the output
            isProxy = true;
            return;
        }
    }

    MMRESULT mm = acmStreamOpen(&stream, nullptr, sourceFormat, destFormat, nullptr, 0, 0, ACM_STREAMOPENF_NONREALTIME);

    if (mm != MMSYSERR_NOERROR)
    {
        // Not supported directly, so try letting ACM suggest an intermediate format, so we can
        // perform the conversion in multiple stages.
        WaveFormat intermediateFormat;

        // This weird looping structure is because we must try all combinations of four flags, and
        // even when a given combination yields a valid suggestion, we must be able to backtrack and
        // continue looping (tryMoreSuggestions) if the conversion based on that suggestion later
        // fails or dead-ends.
        bool foundSuggest = false;
        int chan, samp, bits, form, done;

        for (done = 0; done < 1 && !foundSuggest; foundSuggest ? 0 : ++done)
        {
            for (chan = 1; chan >= 0 && !foundSuggest; foundSuggest ? 0 : --chan)
            {
                for (samp = 1; samp >= 0 && !foundSuggest; foundSuggest ? 0 : --samp)
                {
                    for (bits = 0; bits <= 1 && !foundSuggest; foundSuggest ? 0 : ++bits)
                    {
                        for (form = 0; form <= 1 && !foundSuggest; foundSuggest ? 0 : ++form)
                        {
                            int flags = 0;
                            flags |= chan ? ACM_FORMATSUGGESTF_NCHANNELS      : 0;
                            flags |= samp ? ACM_FORMATSUGGESTF_NSAMPLESPERSEC : 0;
                            flags |= bits ? ACM_FORMATSUGGESTF_WBITSPERSAMPLE : 0;
                            flags |= form ? ACM_FORMATSUGGESTF_WFORMATTAG     : 0;

                            intermediateFormat = destFormat;
                            MMRESULT mmSuggest = acmFormatSuggest(NULL, sourceFormat, intermediateFormat, intermediateFormat.GetSize(), flags);
                            if (mmSuggest == MMSYSERR_NOERROR)
                            {
                                // Got a possibly-valid suggestion, but it might be a suggestion to
                                // do absolutely nothing (which would be bad), so we first make sure
                                // there's some sort of change involved:
                                if (!FormatsMatch(sourceFormat, intermediateFormat))
                                {
                                    // We got a suggestion
                                    foundSuggest = true;

                                    // Now check to see if it's identical to a previous conversion
                                    // state. If it is, then we'll revert foundSuggest to false to
                                    // prevent endless conversion cycles.
                                    for (int prev = 0; prev < numPrevSourceFormats && prevSourceFormats && foundSuggest; prev++)
                                    {
                                        WaveFormat& oldFormat = *prevSourceFormats[prev];

                                        if (FormatsMatch(oldFormat, intermediateFormat))
                                        {
                                            // We already went through this exact format
                                            foundSuggest = false;
                                        }
                                    }
                                }
                            }
tryMoreSuggestions:
                            continue;
                        }
                    }
                }
            }
        }

        if (!foundSuggest)
        {
            m_failed = true;
            return;
        }

        // we'll handle conversion to the intermediate format
        mm = acmStreamOpen(&stream, nullptr, sourceFormat, intermediateFormat, nullptr, 0, 0, ACM_STREAMOPENF_NONREALTIME);
        if (mm != MMSYSERR_NOERROR)
        {
            if (!done)
            {
                foundSuggest = false;
                goto tryMoreSuggestions; // continue the search
            }

            // reached dead end
            m_failed = true;
            return;
        }

        // create temporary updated conversion history for cycle prevention
        size_t prevSize = sizeof(WaveFormat*) * (numPrevSourceFormats + 1);
        WaveFormat** prevFormats = static_cast<WaveFormat**>(alloca(prevSize));

        if (prevSourceFormats)
        {
            memcpy(prevFormats, prevSourceFormats, prevSize);
        }

        prevFormats[numPrevSourceFormats] = &sourceFormat;

        // delegate the rest of the conversion to a new converter (recursive construction)
        subConverter = new AudioConverterStream(intermediateFormat, destFormat, prevFormats, numPrevSourceFormats + 1);

        if (subConverter->m_failed)
        {
            delete subConverter;
            subConverter = nullptr;

            if (!done)
            {
                foundSuggest = false;
                goto tryMoreSuggestions; // continue the search
            }

            // reached dead end
            m_failed = true;
            return;
        }
    }

    // prepare the stream header
    memset(&header, 0, sizeof(ACMSTREAMHEADER));
    header.cbStruct = sizeof(ACMSTREAMHEADER);
    header.pbSrc = inWorkBuffer;
    header.cbSrcLength = sizeof(inWorkBuffer);
    header.pbDst = outWorkBuffer;
    header.cbDstLength = sizeof(outWorkBuffer);
    mm = acmStreamPrepareHeader(stream, &header, 0);

    if (mm != MMSYSERR_NOERROR)
    {
        m_failed = true;
    }
}