Audiowiedergabe Hardcore(Starthilfe)

  • C#

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von BitBrösel.

    Audiowiedergabe Hardcore(Starthilfe)

    Hey Leute,

    es ist soweit der gute @-Franky- hat es geschafft, der hat mich bekloppt gemacht(auf einer positiven Art) mit den ganzen COM Dingern und Interfaces. Nun will ich mal schauen wie man selbst Audio ohne extra Bibliothek abspielt(kein MCISendString oder wie das heist). Ich denke WASAPI ist das Stichwort, wo fange ich da an? (Abgesehen von Docs.microsoft)

    Hab hier noch mal einiges editiert und entfernt, einiges ist nun klar. Das einzige Problem was nun noch besteht, wie hole ich mir die Interfaces in C#. WASAPI und die MMDevice-API sind keine Hürde, mehr aus gut Dokumentiert.

    PS.
    Hab es schon mal gebacken gekriegt Audio-Devices aufzulisten in C++. Wenn du @-Franky- oder jemand anders mir nun zeigen kann, wie ich das in C# umsetzen kann, habe ich schon den Grundstein. Den Rest schaffe ich dann mit docs.microsoft.

    C-Quellcode

    1. #include <iostream>
    2. #include <Mmdeviceapi.h>
    3. #include "COMException.h"
    4. #include <propvarutil.h>
    5. #include <Functiondiscoverykeys_devpkey.h>
    6. using namespace std;
    7. const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
    8. const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
    9. IMMDeviceEnumerator* deviceEnumerator = nullptr;
    10. IMMDeviceCollection* deviceCollection = nullptr;
    11. IMMDevice* device = nullptr;
    12. IPropertyStore* propertyStore = nullptr;
    13. #define SafeRelease(ptr) if (ptr != nullptr) { ptr->Release(); ptr = nullptr; }
    14. int main()
    15. {
    16. ThrowIfFailed(CoInitialize(NULL), "");
    17. ThrowIfFailed(CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&deviceEnumerator), "");
    18. ThrowIfFailed(deviceEnumerator->EnumAudioEndpoints(EDataFlow::eRender, DEVICE_STATE_ACTIVE, &deviceCollection), "");
    19. UINT deviceCount = 0;
    20. ThrowIfFailed(deviceCollection->GetCount(&deviceCount), "");
    21. cout << "Deivces Found: " << deviceCount << endl;
    22. for (UINT i = 0; i < deviceCount; i++)
    23. {
    24. ThrowIfFailed(deviceCollection->Item(i, &device), "");
    25. ThrowIfFailed(device->OpenPropertyStore(STGM_READ, &propertyStore), "");
    26. PROPVARIANT deviceNamePropVariant;
    27. PropVariantInit(&deviceNamePropVariant);
    28. ThrowIfFailed(propertyStore->GetValue(PKEY_Device_FriendlyName, &deviceNamePropVariant), "");
    29. WCHAR friendlyDeviceName[128];
    30. PropVariantToString(deviceNamePropVariant, friendlyDeviceName, 128);
    31. PropVariantClear(&deviceNamePropVariant);
    32. wcout << i << ":" << friendlyDeviceName << endl;
    33. SafeRelease(propertyStore);
    34. SafeRelease(device);
    35. }
    36. SafeRelease(deviceCollection);
    37. SafeRelease(deviceEnumerator);
    38. CoUninitialize();
    39. return 0;
    40. }


    Ich habe die Konsolen-Projektmappe mal angehängt.
    Dateien

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „BitBrösel“ ()

    BitBrösel schrieb:

    Nun will ich mal schauen wie man selbst Audio ohne extra Bibliothek abspielt

    Mit C#, C / C++ kann ich nicht dienen. Lesen ja, schreiben nein. Ansonsten ist WASAPI ein guter Anfang. Im Grunde nutzt die Windows Media Foundation (MF) teilweise auch WASAPI. Einen einfachen Player per MF findest Du hier: Einfacher MediaPlayer per Media Foundation (IMFMediaEngine(Ex)) In Post 15 findest dann noch die MFPlayer_Vista.zip. Letztere ist zwar aufwendiger, bietet aber am Ende mehr Möglichkeiten. Ein guter Einstig in MF wäre: learn.microsoft.com/en-us/wind…y-unprotected-media-files Darauf basiert die MFPlayer_Vista.zip.

    Für die Umsetzung in C# oder VB, brauchst Du hauptsächlich die Headerdateien und die MS-Docu. Dann müsstest Dich evtl. noch entscheiden ob Du lieber direkt mit den COM Interfaces arbeiten möchtest (Beispiel mit IWMPEffects)
    Spoiler anzeigen

    VB.NET-Quellcode

    1. <ComImport>
    2. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    3. <Guid(IID_IWMPEffects)>
    4. Private Interface IWMPEffects
    5. <PreserveSig> Function Render(<[In]> ByRef pLevels As TimedLevel,
    6. <[In]> hdc As IntPtr,
    7. <[In]> ByRef prc As RECT) As Integer
    8. <PreserveSig> Function MediaInfo(<[In]> lChannelCount As Integer,
    9. <[In]> lSampleRate As Integer,
    10. <[In], MarshalAs(UnmanagedType.BStr)> bstrTitle As String) As Integer
    11. <PreserveSig> Function GetCapabilities(<Out> ByRef pdwCapabilities As Capabilities) As Integer
    12. <PreserveSig> Function GetTitle(<Out, MarshalAs(UnmanagedType.BStr)> ByRef bstrTitle As String) As Integer
    13. <PreserveSig> Function GetPresetTitle(<[In]> nPreset As Integer,
    14. <Out, MarshalAs(UnmanagedType.BStr)> ByRef bstrPresetTitle As String) As Integer
    15. <PreserveSig> Function GetPresetCount(<Out> ByRef pnPresetCount As Integer) As Integer
    16. <PreserveSig> Function SetCurrentPreset(<[In]> nPreset As Integer) As Integer
    17. <PreserveSig> Function GetCurrentPreset(<Out> ByRef pnPreset As Integer) As Integer
    18. <PreserveSig> Function DisplayPropertyPage(<[In]> hwndOwner As IntPtr) As Integer
    19. <PreserveSig> Function GoFullScreen(<[In], MarshalAs(UnmanagedType.Bool)> fFullscreen As Boolean) As Integer
    20. <PreserveSig> Function RenderFullScreen(<[In]> ByRef pLevels As TimedLevel) As Integer
    21. End Interface


    oder ausschließlich mit den Pointern auf die Interfaces und deren Funktionen. Man kann das ganze auch mischen. Ich hab ja im Sourcecode-Austausch-Forum diverse Sachen hochgeladen die zeigen wie man mit COM-Interfaces arbeiten kann.

    Darüber hinaus bietet auch die WinRT diverse COM Interfaces in Bezug auf Audio/Video: learn.microsoft.com/en-us/uwp/api/
    Mfg -Franky-

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „-Franky-“ ()

    Ich denke das könnte schon in die richtige Richtung gehen, wobei ich was die IWMP-Interfaces angeht noch nicht ganz sicher bin, ob es das ist was ich brauche, liegt aber noch an Skill-Issues(bzw. Unwissenheit). So wie ich das gerade einschätze vereinfachen die IWMP Interfaces so einiges, das wäre ja fast wie die BASS zu nutzen, nur das man sich halt die IWMP Interfaces herholt und verwendet, halt nur mit etwas mehr Aufwand, sehe ich das richtig? Wenn ich die IWMP Interfaces verwende, brauche ich dann nicht selbst z.B. MP3 Datei einlesen und mit den Daten die Soundkarte füttern?

    Direkt mit COM-Interfaces muss ich ja arbeiten, WASAPI und die MMDevice-API bieten ja solche Interfaces. In C++ kein Problem für mich, da ich aber meinen Mediaplayer mit WPF umsetze, bau ich das gleich in C#, statt eine C++-Dll + Wrapper-Dll zu machen, auch wenn es so aufwändiger ist. Ja ich erfinde das Rad mal wieder neu, aber so lerne ich die APIs richtig kennen, weil ich dann keine Gelegenheit habe hier und da schnell mal Texte zu überfliegen, muss ja genau passen alles.

    Ich werde mir mal im SourceCode-Austausch deine Projekte anschauen, ich denke der "Einfache Mediaplayer" Source sollte reichen um zu sehen wie ich mir alles hole und verwenden kann, das ist ja aktuell die einzige Hürde.

    Danke erstmal, das wird der Schubs gewesen sein den ich brauche. Bin schon dran die Interfaces zu schreiben(die ich oben im Code verwendet hab), werde dann berichten ob es geklappt hat in C# die Device-Namen zu hohlen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BitBrösel“ ()

    Hi

    Das IWMP Interface brauchst Du nicht und gehört zum Windows Media Player. Die MF hat nichts mit dem Windows Media Player zu tun.

    Der einfache MF-Player ist wirklich nur einfach und kann nur grundsätzliche Funktionen mit dem Nachteil, das man an bestimmte Sachen nicht rankommt. Dafür ist das wichtigste aber bereits implementiert.
    Mfg -Franky-
    @BitBrösel

    Was hältst Du von einem Dialog der Dir, je nach Filter, entsprechende Geräte zur Auswahl auflistet und bei Auswahl entsprechende Informationen zum Gerät liefert anstatt diese selbst per WASAPI zu ermitteln? Falls für dich interessant, dann schau mal hier: learn.microsoft.com/de-at/uwp/…cepicker?view=winrt-19041
    Mfg -Franky-
    @-Franky-, das wäre zwar eine Option, aber hab jetzt schon so viel über WASAPI und MMDevice-API gelesen da bleib ich nun bei, mit WASAPI komm ich da nicht dran, dafür ist die MMDevice-API(MultiMediaDevice-API), beides Bestandteile der Core-Audio-APIs. Mir gefällt es, das ich so wirklich alles umsetzen kann, was bei manchen Interfaces nicht gehen würde. Zudem scheint das für UWP zu sein.

    Ich bin nun schon soweit, das ich die Anzahl von AudioDevices ermitteln kann in C#, ich muss aber noch ein wenig überlegen wie ich das mit dem Struct PROPVARIANT
    learn.microsoft.com/en-us/wind…s-propidlbase-propvariant
    am besten umsetze, dann komm ich auch an die Namen, alles in allem klappt das nun nach ein paar Stolpereien ganz gut. Aber ich zweifel grad an dem Vorhaben das in C# umzusetzen, im vergleich zu C++ dauert das sehr viel länger zu entwickeln. Ich mache das noch mit den Namen der Devices, danach kommt das ins Archiv und ich mach eine C++-DLL und eine Wrapper klasse. Aber alles im ganzen, war das eine tolle Übung, hatte mir das deutlich komplizierter vorgestellt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BitBrösel“ ()

    Hi
    -> Zudem scheint das für UWP zu sein.
    Ja und Nein. Ja, weil die WinRT hauptsächlich von UWP genutzt wird und Nein, weil die WinRT auch nur eine Sammlung von COM Interfaces ist die mit jeder Sprache verwendet werden kann, die COM unterstützt.

    Die Struct PROPVARIANT ist relativ einfach. Siehe auch zum Schreiben eines Bildes in eine MP3. Also Du holst Dir die Struct PROPVARIANT, wertest PROPVARIANT.vt aus, wahrscheinlich ein VT_LPWSTR, und dann holst Dir den String von pwszVal (IntPtr) -> Marshal.PtrToStringUni.
    Mfg -Franky-
    Hi

    Ja klar. Das ganze gibt es auch schon im FrameWork wenn du da mal reinschaust. Allerdings wird das auch nur intern verwendet und ist nach außen nicht verfügbar. Aber da kann man sich ja abschauen wie MS das im FrameWork macht.
    Mfg -Franky-
    So ich kann endlich einen Erfolg vermelden :D

    Nach einigen versuchen in geschützten Speicher zu schreiben und auch einer knapp 1-Stündigen Suche wegen einer NullReferenceException, welche unerklärlich war, aber daran lag das ich die Reihenfolge der Funktionen im Interface falsch hatte, sehe ich endlich die Namen in der Konsole. Nach der Nummer klopf ich mir mal selbst auf die Schulter, dachte nachdem das mit der Anzahl der Devices funktionierte, hätte ich das schlimmste bereits überstanden. Die PROPVARIANT Structure hatte ich jetzt aus Faulheit, weil das jetzt eh eingelagert wird weil ich das doch lieber in C++ machen werde, nur sehr kurz gehalten.

    @-Franky- Dank dir ist dieses COM-Zeugs(in .Net) nun doch mal bei mir zu Thema geworden, deutlich kniffliger als in C++, aber gut das nun auch mit .NET zu beherschen.

    Für den Fall das sich jemand irgendwann auch damit beschäftigen will, hab ich die Mappe mal angehängt.
    Dateien

    BitBrösel schrieb:

    @-Franky- Dank dir ist dieses COM-Zeugs(in .Net) nun doch mal bei mir zu Thema geworden

    Na so schlimm war es doch gar nicht. ;) Klar, wer mit C++ arbeiten kann, bindet sich die Headerfiles ein und fertig. Das vermisse ich seit Jahren auch für C# oder VB. Aber wenn man weis wie es auch ohne geht, ist das recht easy und öffnet eine Welt außerhalb des Frameworks. Es zeigt auch schön was im Hintergrund angestellt werden muss, um zB. die DeviceNames aufzulisten. Das das nicht mit einem Property Aufruf erledigt ist. Und das beste daran ist, man hat einen Lernerfolg, Yehaaa. :D
    Mfg -Franky-

    -Franky- schrieb:

    Na so schlimm war es doch gar nicht.


    Stimmt, ich habe sogar schon einen weiteren Plan um das mit COM unter NET noch zu festigen. Demnächst irgendwann, werde ich mal versuchen mit C# ein Spiele Overlay zu machen, bin schon gespannt ob/wie das mit dem IDXGISwapChain Interface klappen wird. Wenn's in die Hose geht auch nicht schlimm, dann versuche ich mir das aus dem Ram des Prozesses zu hohlen. Wenn dann alle Stricke gerissen sind, Funktions-Hooking, dann hohle ich mir die Funktions-Addresse von IDXGISwapChain::Present halt aus dem vTable. Mal schauen wie aufwendig das in C# wird.