{"id":1991,"date":"2016-03-16T11:56:53","date_gmt":"2016-03-16T10:56:53","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=1991"},"modified":"2023-08-14T15:14:22","modified_gmt":"2023-08-14T13:14:22","slug":"naudio-biblioteka-do-obslugi-audio-w-net-cz-1","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/naudio-biblioteka-do-obslugi-audio-w-net-cz-1\/","title":{"rendered":"NAudio &#8211; biblioteka do obs\u0142ugi audio w .NET cz.1"},"content":{"rendered":"\n<p>W poprzednim artykule przedstawi\u0142em niskopoziomowe podej\u015bcie do odtwarzania d\u017awi\u0119k\u00f3w w Windows, u\u017cywaj\u0105c natywnego interfejsu WINAPI. Tym razem postaram si\u0119 zaprezentowa\u0107 inne podej\u015bcie do tego tematu. U\u017cyj\u0119 platformy .NET oraz biblioteki NAudio. Biblioteka autorstwa Marka Heatha jest chyba najpopularniejszym i najbardziej rozbudowanym narz\u0119dziem do pracy z d\u017awi\u0119kiem w \u015brodowisku .NET. Projekt NAudio jest prowadzony na zasadzie open source, mamy wi\u0119c dost\u0119p do \u017ar\u00f3de\u0142 tej biblioteki &#8211; mo\u017cna je odnale\u017a\u0107 pod adresem <a href=\"https:\/\/github.com\/naudio\/NAudio\" rel=\"nofollow\" >https:\/\/github.com\/naudio\/NAudio<\/a><br>Jak ju\u017c wspomnia\u0142em mo\u017cliwo\u015bci tej biblioteki s\u0105 bardzo du\u017ce &#8211; wed\u0142ug dokumentacji umo\u017cliwia ona:<\/p>\n\n\n\n<p>Odtwarzanie audio poprzez nast\u0119puj\u0105ce interfejsy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>WaveOut<\/li>\n\n\n\n<li>DirectSound<\/li>\n\n\n\n<li>ASIO<\/li>\n\n\n\n<li>WASAPI (od Windows Vista)<\/li>\n<\/ul>\n\n\n\n<p>Odczytywanie plik\u00f3w w nast\u0119puj\u0105cych formatach:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>WAV<\/li>\n\n\n\n<li>AIFF<\/li>\n\n\n\n<li>WMA<\/li>\n\n\n\n<li>SoundFont (SF2)<\/li>\n<\/ul>\n\n\n\n<p>Dekodowanie skompresowanych danych:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>MP3<\/li>\n\n\n\n<li>G.711 mu-law i a-law<\/li>\n\n\n\n<li>ADPCM<\/li>\n\n\n\n<li>G.722<\/li>\n\n\n\n<li>Speex (NSpeex)<\/li>\n\n\n\n<li>WMA, AAC, MP4<\/li>\n<\/ul>\n\n\n\n<p>Konwersja nieskompresowanego audio:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Zmiana ilo\u015bci kana\u0142\u00f3w (mono &#8211; stereo,&nbsp; stereo &#8211; mono)<\/li>\n\n\n\n<li>Zmiana rozdzielczo\u015bci bitowej<\/li>\n\n\n\n<li>Zmiana cz\u0119stotliwo\u015bci pr\u00f3bkowania (resampling)<\/li>\n<\/ul>\n\n\n\n<p>Kodowanie danych audio (w zale\u017cno\u015bci od kodek\u00f3w zainstalowanych w systemie):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tworzenie plik\u00f3w MP3s (od Windows 8)<\/li>\n\n\n\n<li>Tworzenie plik\u00f3w AAC\/MP4 audio (od Windows 7)<\/li>\n\n\n\n<li>Tworzenie plik\u00f3w WMA<\/li>\n\n\n\n<li>Tworzenie plik\u00f3w WAV (G.711, ADPCM, G.722, i inne&#8230;)<\/li>\n<\/ul>\n\n\n\n<p>Dzia\u0142ania na strumieniach audio:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Analiza poziom\u00f3w audio<\/li>\n\n\n\n<li>Przetwarzanie FFT (Fast Fourier Transformation)<\/li>\n\n\n\n<li>Proste efekty (delay, loop, fade in, fade out)<\/li>\n\n\n\n<li>Proste EQ<\/li>\n<\/ul>\n\n\n\n<p>Nagrywanie z u\u017cyciem interfejs\u00f3w:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>WaveIn<\/li>\n\n\n\n<li>WASAPI<\/li>\n\n\n\n<li>ASIO<\/li>\n<\/ul>\n\n\n\n<p>Pe\u0142na obs\u0142uga MIDI:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Odczyt i zapis plik\u00f3w MIDI<\/li>\n\n\n\n<li>Odbieranie zdarze\u0144 MIDI<\/li>\n\n\n\n<li>Wysy\u0142anie zdarze\u0144 MIDI<\/li>\n<\/ul>\n\n\n\n<p>Na potrzeby tego artyku\u0142u zaimplementujemy bardzo prosty odtwarzacz plik\u00f3w MP3. B\u0119dzie on wymaga\u0142 systemu Windows Vista lub nowszego, z uwagi na wykorzystane funkcje (Core Audio Interface, kt\u00f3re zosta\u0142o wprowadzone w Windows Vista).<\/p>\n\n\n\n<p>Rozpoczynamy od utworzenia nowego projektu (Windows Forms) i dodania referencji do omawianej biblioteki NAudio. Nast\u0119pnie musimy utworzy\u0107 globalny obiekt kt\u00f3ry b\u0119dzie reprezentowa\u0142 nasze urz\u0105dzenie odtwarzaj\u0105ce. Wykorzystamy do tego celu interfejs IWavePlayer znajduj\u0105cy si\u0119 w przestrzeni nazw NAudio.Wave. Klasa WaveOut implementuje interfejs IWavePlayer, a zagl\u0105daj\u0105c do jej \u017ar\u00f3de\u0142 widzimy, \u017ce opakowuje ona funkcje z rodziny waveOut* kt\u00f3re poznali\u015bmy w poprzednim artykule.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nNAudio.Wave.IWavePlayer waveOutDevice = new NAudio.Wave.WaveOut();\n<\/pre><\/div>\n\n\n<p>Kolejnym krokiem b\u0119dzie utworzenie globalnego obiektu, kt\u00f3ry b\u0119dzie odczytywa\u0142 dane z naszego pliku MP3.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nNAudio.Wave.AudioFileReader audioFileReader;\n<\/pre><\/div>\n\n\n<p>Skorzystamy z klasy AudioFileReader, kt\u00f3ra w konstruktorze przyjmuje nazw\u0119 pliku audio. Klasa AudioFileReader, dziedziczy po klasie WaveStream, a ta z kolei wywodzi si\u0119 od klasy Stream. U\u017cywaj\u0105c klasy AudioFileReader uzyskujemy strumie\u0144 ju\u017c zdekodowanych danych audio. Ju\u017c tutaj wida\u0107 si\u0142\u0119 biblioteki NAudio. Nie musimy si\u0119 martwi\u0107 o obs\u0142ug\u0119 danego formatu &#8211; klasa AudioFileReader sama wybierze klas\u0119 odpowiedni\u0105 do obs\u0142ugi danego formatu. Wida\u0107 to, gdy zajrzymy do jej \u017ar\u00f3de\u0142 &#8211; poni\u017cej wycinek klasy AudioFileReader odpowiadaj\u0105cy za dob\u00f3r odpowiedniej klasy do odczytywania konkretnego formatu. :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nprivate void CreateReaderStream(string fileName)\n{\n    if (fileName.EndsWith(\".wav\", StringComparison.OrdinalIgnoreCase))\n    {\n        readerStream = new WaveFileReader(fileName);\n        if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm && readerStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)\n        {\n            readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);\n            readerStream = new BlockAlignReductionStream(readerStream);\n        }\n    }\n    else if (fileName.EndsWith(\".mp3\", StringComparison.OrdinalIgnoreCase))\n    {\n        readerStream = new Mp3FileReader(fileName);\n    }\n    else if (fileName.EndsWith(\".aiff\"))\n    {\n        readerStream = new AiffFileReader(fileName);\n    }\n    else\n    {               \n        readerStream = new MediaFoundationReader(fileName);\n    }\n}\n<\/pre><\/div>\n\n\n<p>Skoro mamy ju\u017c gotowy strumie\u0144 danych audio, wystarczy przekaza\u0107 go do obiektu reprezentuj\u0105cego urz\u0105dzenie odtwarzaj\u0105ce. W tym celu u\u017cywamy metody Init klasy WaveOut i w parametrze tej metody przekazujemy strumie\u0144 danych audio, czyli obiekt klasy AudioFileReader.<\/p>\n\n\n\n<p>Nast\u0119pnie po wywo\u0142aniu metody Play() na obiekcie klasy WaveOut, mo\u017cemy cieszy\u0107 si\u0119 z efekt\u00f3w naszej pracy &#8211; w g\u0142o\u015bnikach s\u0142ycha\u0107 odtwarzany plik (oczywi\u015bcie przy za\u0142o\u017ceniu, \u017ce nie wyst\u0105pi\u0142y \u017cadne b\u0142\u0119dy).<\/p>\n\n\n\n<p>Wszystkie te operacje umie\u015bcimy w metodzie PlayAudioFile, kt\u00f3ra wygl\u0105da nast\u0119puj\u0105co:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nprivate void PlayAudioFile(string fileName)\n{\n    try\n    {\n        CloseWaveOut(); \/\/Na wszelki wypadek czy\u015bcimy zasoby po poprzednim odtwarzaniu\n        waveOutDevice = new NAudio.Wave.WaveOut();\n        audioFileReader = new NAudio.Wave.AudioFileReader(fileName);\n        waveOutDevice.Init(audioFileReader);\n        waveOutDevice.Play();\n    }\n    catch (Exception ex)\n    {\n        MessageBox.Show(string.Format(\"Wyst\u0105pi\u0142 b\u0142\u0105d: {0}\", ex.Message));\n    }\n}\n<\/pre><\/div>\n\n\n<p>Nale\u017cy r\u00f3wnie\u017c przygotowa\u0107 metod\u0119, kt\u00f3ra pozamyka otwarte zasoby. Funkcj\u0119 t\u0105 pe\u0142ni metoda CloseWaveOut() przedstawiona poni\u017cej:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nprivate void CloseWaveOut()\n{\n    if (waveOutDevice != null)\n    {\n        waveOutDevice.Stop();\n    }\n    if (audioFileReader != null)\n    {\n        audioFileReader.Dispose();\n        audioFileReader = null;\n    }\n    if (waveOutDevice != null)\n    {\n        waveOutDevice.Dispose();\n        waveOutDevice = null;\n    }\n}\n<\/pre><\/div>\n\n\n<p>Klasa WaveOut udost\u0119pnia r\u00f3wnie\u017c inne metody i w\u0142a\u015bciwo\u015bci, np. Stop(), Pause(), Volume itp.. kt\u00f3re s\u0142u\u017c\u0105 do sterowania odtwarzaniem. Ich nazwy m\u00f3wi\u0105 bardzo wiele i my\u015bl\u0119, \u017ce nikt nie b\u0119dzie mia\u0142 problemu z ich u\u017cyciem.<br>Klasa AudioFileReader umo\u017cliwia natomiast poruszanie si\u0119 po odtwarzanym pliku dok\u0142adnie tak samo jak w ka\u017cdym obiekcie klasy Stream. Na przyk\u0142ad chc\u0105c przeskoczy\u0107 do dowolnego miejsca w strumieniu danych (co mo\u017ce by\u0107 przydatne w budowie odtwarzacza), wywo\u0142ujemy metod\u0119 Seek(). Do odczytania pozycji czasowej w strumieniu audio, s\u0142u\u017cy w\u0142a\u015bciwo\u015b\u0107 CurrentTime. W\u0142a\u015bciwo\u015b\u0107 ta zwraca struktur\u0119 TimeSpan, nie ma wi\u0119c potrzeby wyliczania czasu na podstawie ilo\u015bci pr\u00f3bek, cz\u0119stotliwo\u015bci pr\u00f3bkowania itp&#8230;<br>Powy\u017csze funkcje wystarcz\u0105 do zaimplementowania bardzo prostego odtwarzacza, jednak chcia\u0142bym przedstawi\u0107 jeszcze jeden ciekawy element omawianej biblioteki, a mianowicie dost\u0119p do Core Audio Api wprowadzonego w Windows Vista. Dok\u0142adny opis tego elementu systemu znajdziemy na stronie MSDN: <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/dd370784(v=vs.85).aspx\" rel=\"nofollow\" >https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/dd370784(v=vs.85).aspx<\/a><br>Dzi\u0119ki dost\u0119powi do Core Audio Api mamy mo\u017cliwo\u015b\u0107 np. zaimplementowania wska\u017anika wysterowania w bardzo prosty spos\u00f3b.<br>W tym celu tworzymy globalny obiekt klasy MMDevice reprezentuj\u0105cy kart\u0119 d\u017awi\u0119kow\u0105:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nNAudio.CoreAudioApi.MMDevice defaultDevice;\n<\/pre><\/div>\n\n\n<p>Nast\u0119pnie przypisujemy do niego domy\u015blne urz\u0105dzenie d\u017awi\u0119kowe korzystaj\u0105c z klasy MMDeviceEnumerator:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nNAudio.CoreAudioApi.MMDeviceEnumerator devEnum = new NAudio.CoreAudioApi.MMDeviceEnumerator();\ndefaultDevice = devEnum.GetDefaultAudioEndpoint( NAudio.CoreAudioApi.DataFlow.Render, NAudio.CoreAudioApi.Role.Multimedia);\n<\/pre><\/div>\n\n\n<p>Dzi\u0119ki obiektowi defaultDevice mamy dost\u0119p do sterowania parametrami miksera systemowego, mo\u017cemy r\u00f3wnie\u017c odczytywa\u0107 parametry i sterowa\u0107 &#8222;sesjami&#8221; audio egzystuj\u0105cymi w naszym systemie.<br>Dane sesji naszego procesu mo\u017cemy odczyta\u0107 z obiektu defaultDevice.AudioSessionManager.AudioSessionControl. Aby dosta\u0107 si\u0119 do aktualnych warto\u015bci poziomu audio w danym kanale odczytujemy warto\u015bci PeakValues w\u0142a\u015bciwo\u015bci AudioMeterInformation:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\ndefaultDevice.AudioSessionManager.AudioSessionControl.AudioMeterInformation.PeakValues&#x5B;channelNo] \/\/channelNo - numer kana\u0142u (w przypadku stereo mamy dwa kana\u0142y - 0 i 1) \n<\/pre><\/div>\n\n\n<p>Wska\u017anik wysterowania najpro\u015bciej mo\u017cna zaimplementowa\u0107 u\u017cywaj\u0105c kontrolki ProgressBar (w WinForms). Poni\u017csza metoda UpdateMeters aktualizuje warto\u015bci kontrolek w zale\u017cno\u015bci od poziomu audio. Metod\u0119 t\u0105 mo\u017cna wywo\u0142ywa\u0107 np. co 10 ms u\u017cywaj\u0105c timera z WinForms.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nprivate void UpdateMeters()\n{\n    if (defaultDevice.AudioSessionManager.AudioSessionControl.State == NAudio.CoreAudioApi.Interfaces.AudioSessionState.AudioSessionStateActive)\n    {\n        progressBar1.Value = (int)( defaultDevice.AudioSessionManager.AudioSessionControl.AudioMeterInformation.PeakValues&#x5B;0] * progressBar1.Maximum);\n        progressBar2.Value = (int)( defaultDevice.AudioSessionManager.AudioSessionControl.AudioMeterInformation.PeakValues&#x5B;1] * progressBar2.Maximum);               \n    }         \n}\n<\/pre><\/div>\n\n\n<p>Jak wida\u0107 na powy\u017cszym przyk\u0142adzie, biblioteka NAudio dostarcza nam wielu narz\u0119dzi do pracy z d\u017awi\u0119kiem i to w bardzo przyjaznej formie. Zaprezentowane tutaj funkcje to tylko wierzcho\u0142ek g\u00f3ry lodowej je\u015bli chodzi o jej mo\u017cliwo\u015bci. W kolejnej cz\u0119\u015bci artyku\u0142u zajmiemy si\u0119 kolejnymi funkcjonalno\u015bciami NAudio.<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;1991&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;0&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;0&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;0\\\/5 ( votes: 0)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;NAudio - biblioteka do obs\u0142ugi audio w .NET cz.1&quot;,&quot;width&quot;:&quot;0&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 0px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            <span class=\"kksr-muted\"><\/span>\n    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>W poprzednim artykule przedstawi\u0142em niskopoziomowe podej\u015bcie do odtwarzania d\u017awi\u0119k\u00f3w w Windows, u\u017cywaj\u0105c natywnego interfejsu WINAPI. Tym razem postaram si\u0119 zaprezentowa\u0107 &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/naudio-biblioteka-do-obslugi-audio-w-net-cz-1\/\">Continued<\/a><\/p>\n","protected":false},"author":77,"featured_media":2310,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[129,272,249,273],"class_list":["post-1991","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-c","tag-net","tag-audio","tag-naudio"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2016\/03\/Naudio-sii-blogersii.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/1991"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/77"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=1991"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/1991\/revisions"}],"predecessor-version":[{"id":23456,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/1991\/revisions\/23456"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/2310"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=1991"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=1991"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=1991"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}