From e81568845480c60baf21bb29b57756a40e528ea1 Mon Sep 17 00:00:00 2001 From: moto <855818+mthrok@users.noreply.github.com> Date: Tue, 30 Jun 2020 18:59:13 +0000 Subject: [PATCH] Add opus support --- .circleci/config.yml | 26 +++++------ .circleci/config.yml.in | 26 +++++------ .circleci/unittest/linux/docker/Dockerfile | 1 + README.md | 4 +- build_tools/setup_helpers/extension.py | 2 + test/assets/io/96k_0_1ch.opus | Bin 0 -> 8259 bytes test/assets/io/96k_0_2ch.opus | Bin 0 -> 6740 bytes test/assets/io/96k_10_1ch.opus | Bin 0 -> 15033 bytes test/assets/io/96k_10_2ch.opus | Bin 0 -> 15160 bytes test/assets/io/96k_5_1ch.opus | Bin 0 -> 13074 bytes test/assets/io/96k_5_2ch.opus | Bin 0 -> 10618 bytes test/assets/io/generate_opus.py | 50 +++++++++++++++++++++ test/sox_io_backend/test_info.py | 19 +++++++- test/sox_io_backend/test_load.py | 18 ++++++++ third_party/CMakeLists.txt | 28 ++++++++++-- third_party/build_codec_helper.sh | 13 ++++++ 16 files changed, 150 insertions(+), 37 deletions(-) create mode 100644 test/assets/io/96k_0_1ch.opus create mode 100644 test/assets/io/96k_0_2ch.opus create mode 100644 test/assets/io/96k_10_1ch.opus create mode 100644 test/assets/io/96k_10_2ch.opus create mode 100644 test/assets/io/96k_5_1ch.opus create mode 100644 test/assets/io/96k_5_2ch.opus create mode 100644 test/assets/io/generate_opus.py create mode 100755 third_party/build_codec_helper.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 413a19c7bd..4d46ff914f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -38,17 +38,13 @@ commands: our_upload_channel=test fi echo "export UPLOAD_CHANNEL=${our_upload_channel}" >> ${BASH_ENV} - install_cmake_macos: - description: "installs cmake on macOS. Use binary distribution as brew is slow" + install_build_tools_macos: + description: "installs tools required to build torchaudio" steps: - run: - name: Install cmake - command: | - curl -L -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.16.5/cmake-3.16.5-Darwin-x86_64.tar.gz - mkdir cmake - tar -xf cmake.tar.gz --strip 3 -C cmake - rm cmake.tar.gz - echo 'export PATH='"${PWD}/cmake/bin"':${PATH}' >> ${BASH_ENV} + name: Install cmake and pkg-config + command: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake pkg-config + # Disable brew auto update which is very slow binary_common: &binary_common parameters: @@ -158,7 +154,7 @@ jobs: xcode: "9.0" steps: - checkout - - install_cmake_macos + - install_build_tools_macos - attach_workspace: at: third_party - run: @@ -183,7 +179,7 @@ jobs: xcode: "9.0" steps: - checkout - - install_cmake_macos + - install_build_tools_macos - attach_workspace: at: third_party - run: @@ -398,14 +394,14 @@ jobs: - restore_cache: keys: - - env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum ".cachekey" }} + - env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum "third_party/CMakeLists.txt" }}-{{ checksum ".cachekey" }} - run: name: Setup command: .circleci/unittest/linux/scripts/setup_env.sh - save_cache: - key: env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum ".cachekey" }} + key: env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum "third_party/CMakeLists.txt" }}-{{ checksum ".cachekey" }} paths: - conda @@ -440,14 +436,14 @@ jobs: - restore_cache: keys: - - env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum ".cachekey" }} + - env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum "third_party/CMakeLists.txt" }}-{{ checksum ".cachekey" }} - run: name: Setup command: docker run -t --gpus all -v $PWD:$PWD -w $PWD "${image_name}" .circleci/unittest/linux/scripts/setup_env.sh - save_cache: - key: env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum ".cachekey" }} + key: env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum "third_party/CMakeLists.txt" }}-{{ checksum ".cachekey" }} paths: - conda diff --git a/.circleci/config.yml.in b/.circleci/config.yml.in index 398577c79a..800eb56e27 100644 --- a/.circleci/config.yml.in +++ b/.circleci/config.yml.in @@ -38,17 +38,13 @@ commands: our_upload_channel=test fi echo "export UPLOAD_CHANNEL=${our_upload_channel}" >> ${BASH_ENV} - install_cmake_macos: - description: "installs cmake on macOS. Use binary distribution as brew is slow" + install_build_tools_macos: + description: "installs tools required to build torchaudio" steps: - run: - name: Install cmake - command: | - curl -L -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.16.5/cmake-3.16.5-Darwin-x86_64.tar.gz - mkdir cmake - tar -xf cmake.tar.gz --strip 3 -C cmake - rm cmake.tar.gz - echo 'export PATH='"${PWD}/cmake/bin"':${PATH}' >> ${BASH_ENV} + name: Install cmake and pkg-config + command: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake pkg-config + # Disable brew auto update which is very slow binary_common: &binary_common parameters: @@ -158,7 +154,7 @@ jobs: xcode: "9.0" steps: - checkout - - install_cmake_macos + - install_build_tools_macos - attach_workspace: at: third_party - run: @@ -183,7 +179,7 @@ jobs: xcode: "9.0" steps: - checkout - - install_cmake_macos + - install_build_tools_macos - attach_workspace: at: third_party - run: @@ -398,14 +394,14 @@ jobs: - restore_cache: {% raw %} keys: - - env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum ".cachekey" }} + - env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum "third_party/CMakeLists.txt" }}-{{ checksum ".cachekey" }} {% endraw %} - run: name: Setup command: .circleci/unittest/linux/scripts/setup_env.sh - save_cache: {% raw %} - key: env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum ".cachekey" }} + key: env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum "third_party/CMakeLists.txt" }}-{{ checksum ".cachekey" }} {% endraw %} paths: - conda @@ -440,14 +436,14 @@ jobs: - restore_cache: {% raw %} keys: - - env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum ".cachekey" }} + - env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum "third_party/CMakeLists.txt" }}-{{ checksum ".cachekey" }} {% endraw %} - run: name: Setup command: docker run -t --gpus all -v $PWD:$PWD -w $PWD "${image_name}" .circleci/unittest/linux/scripts/setup_env.sh - save_cache: {% raw %} - key: env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum ".cachekey" }} + key: env-v3-linux-{{ arch }}-py<< parameters.python_version >>-{{ checksum ".circleci/unittest/linux/scripts/environment.yml" }}-{{ checksum "third_party/CMakeLists.txt" }}-{{ checksum ".cachekey" }} {% endraw %} paths: - conda diff --git a/.circleci/unittest/linux/docker/Dockerfile b/.circleci/unittest/linux/docker/Dockerfile index d1de89688e..dfd35add61 100644 --- a/.circleci/unittest/linux/docker/Dockerfile +++ b/.circleci/unittest/linux/docker/Dockerfile @@ -59,6 +59,7 @@ RUN apt update && apt install -y \ libsox-dev \ libsox-fmt-all \ cmake \ + pkg-config \ && rm -rf /var/lib/apt/lists/* COPY --from=builder /kaldi /kaldi COPY --from=builder /third_party /third_party diff --git a/README.md b/README.md index 94f993ddaa..290bb73543 100644 --- a/README.md +++ b/README.md @@ -113,8 +113,8 @@ python setup.py install MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py install ``` -Alternatively, the build process can build SoX (and codecs such as libmad, lame and flac) statically and torchaudio can link them, by setting environment variable `BUILD_SOX=1`. -The build process will fetch and build SoX, liblame, libmad, flac before building extension. +Alternatively, the build process can build libsox and some optional codecs statically and torchaudio can link them, by setting environment variable `BUILD_SOX=1`. +The build process will fetch and build libmad, lame, flac, vorbis, opus, and libsox before building extension. This process requires `cmake` and `pkg-config`. ```bash # Linux diff --git a/build_tools/setup_helpers/extension.py b/build_tools/setup_helpers/extension.py index ab10014a46..b9fb30e114 100644 --- a/build_tools/setup_helpers/extension.py +++ b/build_tools/setup_helpers/extension.py @@ -83,6 +83,8 @@ def _get_extra_objects(): 'libmad.a', 'libFLAC.a', 'libmp3lame.a', + 'libopusfile.a', + 'libopus.a', 'libvorbisenc.a', 'libvorbisfile.a', 'libvorbis.a', diff --git a/test/assets/io/96k_0_1ch.opus b/test/assets/io/96k_0_1ch.opus new file mode 100644 index 0000000000000000000000000000000000000000..df95474ddb67499f9223537f0081c689b0be654a GIT binary patch literal 8259 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 literal 0 HcmV?d00001 diff --git a/test/assets/io/96k_0_2ch.opus b/test/assets/io/96k_0_2ch.opus new file mode 100644 index 0000000000000000000000000000000000000000..b8837e81e26b579d984f2b871523322d4e8b5399 GIT binary patch literal 6740 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 LtBkx?x;Fd|2;dDu literal 0 HcmV?d00001 diff --git a/test/assets/io/96k_10_1ch.opus b/test/assets/io/96k_10_1ch.opus new file mode 100644 index 0000000000000000000000000000000000000000..56b170d380376604f308a28cb2ca92ed1f1481cc GIT binary patch literal 15033 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 literal 0 HcmV?d00001 diff --git a/test/assets/io/96k_5_2ch.opus b/test/assets/io/96k_5_2ch.opus new file mode 100644 index 0000000000000000000000000000000000000000..007bc813cea92c6555cde19b2ff0f8e779a59614 GIT binary patch literal 10618 zcmd6M2Ut_xvi43w@4YJ>X(A=krAQG40Rcsn-dm)1fzUxzdXtVQAian*A@m|3Rgm7R z1q7tS-9i2IoOAF0oO7T1Kj$oxmC0n-+3!2EW@fFZSy*TSP~hjdZlNuW7IQ`VJs{+2 z_AX9JW=5tEs3-(kduF1BHvem&%^~2s5Ys2`AyQ~_Eh7sjLQtb(M_V-?GE>f_uV4je z1&ZG7$a;3Ta~Mq-{YEG}?d!9Au_9eHdsnk040UN;Z3NX*_US=#&h2~ZQGGs=?-mzJ zDVkm0s!3>@o^CI?Z34~OPdfW7*GFep1@wq?@v&qA^4VN`8m)#|UP2nDBgc06zN|E?*qk%5nNwG{^)92}c45 zM4s&^cP`d6>gbVl%5(+IzTLwfZj!MSf_dtD-?il~vA?W-^6gNWS6tSRLRnq1Q6jtd zg|Pes)XWn*DV4>|9b`msx%!jfk0lP7@ryhM&@#zA>QEPJ3s* zBkPAr3P|hqHFA_*TI-nE^}}x?z2LPrf=l=Pe$Xh+Om1hEKTF{`E`-D;dFYVs-GoE# z!NMt7vYhXP`2>zmdYdmdyJOneEBuv01AN*u$QWA{J}hg#FXQmUN@rTQd~af#ja0;i znbqOA(vm3F>y6JS`@@ikg6bLI3R{^CH*exz|25&u?r-lC$b`1l6{vPu+X}7E>Vz=6 zK(jQyK5`3-6AS7%QH=Qyule{UMl|*}VU@6ZF*Z8}70JdR^N;E-Mrvo9Fhq%3^J6Vu+0_)Oi*t@4 zyqhVc=@JO+scgXUTHPye3D!XeuIMuG#on4elCD2Dj^f$VeclnYn(}pHI6TtAa^N&x zn$DD%HOI~wd-;VAdu%zC_f){(3EbslVjXwgiYg;{)cIz2FT@)T!pIKMH=n|p{d_M< z0uaT%UF_PO+8Lb3*4q-}_5c)dY}*npZZOD6q0?*sK;J4(w`^_hynp~Ioq)Fr;g1tu zeGGxI2n^L0gcz3YM70FFV#HL(#1dnok` zX8+f>9})>kWJPEUc@|W9kT5TD{aWbAfsByh5t4yoJ=#aWLNP`-)ga$_5bE@7n|8?b zDoo+VaJ=?*8xuaJ>z;TM3H@efh_ABdSHbtG?ZxjUdLHE)M0Us@zE8;4`({#I4|kJN z#LSf8eCWRn`D7{>Cjcm?5yw?}av z$vItc;Tlm?JLK8%PKGN$Bhmr&>yVts%LVQmjmF$T9L_I8?afDWNebkT$FZld3}_{8 zx4T-296i}n%9PPABqN+V3iWD-RoyO6z!bV>E}-N`C@n#?dbyTI1S?5e2tFgEpTP0d zvNnv7HL^lCb|2y9c(K#cpY>c0q%k%< zJd0@9o@WSvQZzo;1kD+vKgB((G|O9Q{&fzc^Fr7y^-hUrJ7AVJ(IH zg41>~uzh3N9ZARic;)yN#Z4S1vZBhjlB`CFO9F8+n9*b2ZmG_1VP5Pdc(oTfeJHhR zjgO+K#E%y!``_BlzX%=8axiji`go;`?bCUq`zZS2k7}!Y!NHWG@;)rhvytb{E2J7& z3Wd|%xQ|hyP7{WoZ#k;h2{UgdB2DZkSnK@|>|GoL&q-vigh;=Tf#w1k4KlkkN6}Dx zg`iiwB>qF=W17V27E(PN9@s^NEcI>jiB$$nTvKvyQZIP5)-1TM`%(}LXX~1tZ!@6f zSqckzV=zlhVO43G@)+0gb8D#1Q{*v8B8lic`-qK7iNRzjJo+Twq0lP&F_I-<_jN8H z)JDp-a5vB^e8*;)TgNq!>wVw`8{+BelrN+1_F8!L4R^(c+kK#eN}`g#KH;Pb%w3KY zgnh62aP1JQDyH|N@mJnujSp#ED#G+i<57zprel-YBjdBD{(FD~hy!#;aq#A$Q0c6v zGgDuHn_S%;`y5{PW*GhqUPAgcRchUo)2`&s%I)EU>lbf-tn#&IQ)f(88@g$mpl(JL znkOCWcPTMR%r)uL#U-09(zz#>&RG&Rt;D9U(mwr0+fP&U<#r3sPY@xu(%(HE{uZ_t z{q=K2zYgESg{|r-Nt(zD7}G+J*hpRo0Xf5YGfW+|ZyXk4-+FGCbc>)2KGDsoO&GPR z3?J@%ywz5Ff!=LWBhmEKlF;98JJWyUv=$A5gCn;3Fpn=;%s#Jh>CLk*>)Emqmf8Dj z1bUH2(g_@c>P@uq6SEFFLIqlF+fdfrotXYieIhSRwlC@mzQycrm=EC2ZxjQu(pm>P zf(Gx^l%1o8Tf^%ZNLJTOI(pkq+noDe0RRVwqpCuv3++)=j{#>&9^c9MP0Hv1pW610 zI(GpkExmVw>$xN^g?yWuWMre<9(;qm@b-Xn-xWc5cwb0*KVbyrxcv)+4G03`)aqgo z`>C@39ySOf)^PdM>DxM==||lUi=2H#oiONE@xD9cY8Tb?=`lLJ7u9VgV&y!0Tc9r8 z1iIw67Ac57b1tP}j2t~}>IWxW6ruQ7uYhp~@4~igQ;B`)3sE^$k z;vzF8d^Wa$f*M4f zAg=L@4z+-uK!fmNXZ0-}%iIaNsLuU!Sr}OH8mUY;vwe=KRGoO*d{y#!dX2ozd7|dM z=!&hwRL{TGu6X@COLfQyWC_r_6P5O~@PmSlfRCBK4-C z-Lt%5_;gC;^;oLwxq{ij8FCOZ3K9N^NY?%RU~_seYxhe)Tda?Rd%O_0oSOp~A$;y` z#qxb@_JE?a{3F5X*z>s(Ch#B3y!11Y^)cNmH+!HQcmBkH@UwmL0|Vz7 z20Zuro+7P_lSt9|g5GlRC5&0|OIQmw_rKCEH22%A^@viHmsJfyZiOYprvgdeiTeWX z80?M+YmyPl06XhlErB%%NWE! zK=C`6z;!J`u3%aZ)8G6?eV#yxlG?GmX04L=@$^PXB#9})=fEg`s_r3Np}myh41+^R zq79jDS<>Ub{O|)q5b(3&t%dQd^$hZ+}VKX ziKAgk-$o2|J`aX7>T|VB%KYC(~JJye!hfMc3 z2-6=R{#p@#A5D&FSk-}c$&3M{B=~T#9izk#f{YiVD3hnk*=42;%5@lfFVP4LhQbJ8 z_lQENcqi}rXMa8)X?N9{uXk%yTd8Q5Wj(^YgN_JKit8(B!X*!y<|389^6M;y_wW3= zI|$}iT`<3@;?|puXkW24KMe{*23#6D-fsTo2r)3f_C1^bwWc38B+>9=g-c3*=9N$&duhM(*Mo~q%i zduX%2!ycxqmt!AVRr9|p#;VU?cSTt3M0t&YO-j zJ$mK5&Q!Dmu!a=`mk`r048F>&2eQ#{&;!DOo(j+8UlXJbltiF#9;Gu4bf;647L){` zL5$sJ8c02@p*$$vg^jM(qb~r!29pwXP4+NzISLHxrKd|PG@t_xgDj+*4>jhnwq2+c zmoUZXzg2H!)>z(~3!vrQDU%xGwd`4_;XziY-JLO6F73|1dHC#_n;ynnqeG55d6#?D z+U0UI@s~cIj9OHF=9-+&i}|7}Z0llCk)1U+fD-9{{J|Rrf0$>SX35RynKs{O$Oj?7 zv+CilMA?$!S7`5VsXy+ykoN_}F*u4-7Fmyk5KZ0G!4;0g*-LY$*|bAJwpy@BzLH71BRkCZ?mN2!t$pAoiW{#e|gHK4qUvJR@WD=aHfZ2Th zA8I_Z1r=RTybMjiSUXd}&hG$PpmYOv<$`M^T7_f7V1Gaq<=4@cTsl<~qw`ii6Jgs{ z7I8BW>98tzBg0!qgyu<)%oCVSG16^VeIhWTJ)!Ux0R9xU}Wz1=UhW++x`H<@o;WiQyiA+ky^GuG)(*)arX@`;)t-^$?8EVn#T<78j@Rm!q8E=lYy;Pg&V=__?D!b0Y&IG_5sI&$eHU$g|v24q@N=Hf$NrR;xpTyTqm8#M?-jI z?0#}R1#&Hwv^Dvp^$@o3Qa#6dQM0)1`tfE0rhl$eqaf#q0E`O0I<JVa!CDotiTAnB38fBqn+2Bn3uJKsKXCGyMLq7(5vUQH}iti?QJXX z$toPXgkIhjA-fzs+;oc|T&6mV1=Ib=e&eI?7YJ=Hyvbl=IVhD}fCmUQ=32<0A8ViR zR$QpGvya>&*t`zVsI=p!bX&2wb{f*Jz9xK8?(DmKO$|dx%n)_2a)9Ej&u- z&+JlfjSm>nn7OtEk&GVYQE4RffpJX`Y*Nsa`<%@T@zER!;7PAZ&wwZ>51;tVo#6tv zKhA)(N`@$zFLzc6QN?#9Tqh(z$S@zwNOESt-#n**-Et?J-WJI-Nk>(Iyto`_dKp@%J{IjY0JGz zJF0=jYiiT+!8$BXRAESM(h{5WYqj`(do_hWd6%OI3RXzrJLHQ3 zdE?ooul7IVFV?o?7uURG7(V|j*KrxYxHbj3Cf|rWz_v-DSsmp3sLA&L4lJEr8=N5M z(|?HFueWEfXMs%$)ZdK!p~lM>)K>vT9w=L)`k4yWQnZIIj&7KDTiMglDs}|v2KW+! zeIZs;ex=s5be>JTaS`9tXP$Of^WMfI-IB*~QJH4>w(||2G~#lc2CzO3j58z~$I}Yk zd^B9UEW6C=eR4Vn>-Mg(xwYCjZ9BV-yW~%iE-5?2duuo>gE)JYL%U%zt6D)y-%N{O z=!@Ya&RX*8^o7aKzcs(|$jJ3xN8ZKrDyrbW@*MtZ98#!|?3dBIaMd&AaZfFftY*m4 zbrnd{lbX2@yWmX<(*0=Vzok2#tM3=xe-qkst|%rnPOv=wm9o)seKID0@`LN*J%Gg! zT^kuO04-{fsY=8_6Z}o&|E@NQK|Aq&tRD;&lNo;Sd*z|~Ybxlb1rYL_BL`o2ASW4( z=eUX%>m!qRi`bC6ve@k|9P-nj>!>$dr0^ct@G*Ad5;Ip5D3UC9a;u@J;!-~vY=v<- z8K?B*EZ+N?MMu%+q;Z%|GN4Dq8$4|;BfSI+&Rn{1y@D3CS2B9TWis)DKPAH4rX+#O zX_RV6IKHQU-}qI?RNlG(SR3u}A-~EamK)R3OriXr<!+f3jT9h07vYRmdCVB?+*OHJae@t(jnBUpY^Yw(< zniVVtW2!>?A|-Cfmb0Zi#g;4uM7DKm3r}J+pRUiT?m6;TWgj4XO zHt(>0YkooeRYXXIggHxJ18RU7#P>;9SAcwE`}vo@l8zOny|95TCK(Y3J| z_aH%bG+og2cjn8eCcSlV}AxPlDl!e4>qOgIlmZtp08fn8jPgcS-$AFu%g08l*fVChHO zY}wip_}`Vs|1pP*oBn+cVfw%AL;lxsJwSK)j>PV<;0Fb^w{aT02Kqj4b4}`;_RUgz z!s#}$1H?GhjfE&*n4}MMGYLjaPTPDvj`!H@ezX=cN3VUSAqs27Pv&#iTO>{dL*965A zHoHIKx>OY%*JhaiLeGbe>mNA;9oOjo`rn_gS*d~(-m;9BiPFV?ge@zTZ7&7eQigE5 z%u?ftz8RmiODD3Kkgo~ywVsxcyFd&Z=ULp;c&+l{&OJCkW8e6q!>Y(eQlQc8ITRHZ zHJ`2_c1=!i>#$=z}kJ?Yxj*NME=~) zU_w1QU)*jOkme%++R>(wIqHbxGX1kOrLg@0I;UmbqaqJfcm5DEAZua1}IY9|GvfkA9BdB zFYB)yG6MD?#{d4>=#Ay5O4@c;gW`o}hqL-^K*Tdh>(J>ae=_H3r&8zk7CymKOJwB1 z<*RJ#xFv<5##kC89Ul0M)wkmtu?8&r#+67U37BUDj4d|5&x%~yNp>g-cip{~1iXV^ zg72|Q`VUF%pt>=CMYW9}7}Y|_elI=#BpUE`K1AM|>@)EvZ}F*Vgag2sJruoLb8T=a zo{#|rK*R$Jy>B#w{PQ{F)rsPtIi%x1jcd*P*H>fI;PKd5`az{s&4KZSd@OZChcEV} zm=jMpv5&X|JJWerJlHPKW>-Fz31bck=|sM<2* zG8zCozNG+{axjTt(2!%$ff)o0>d@k?rObcex=?r75+`loe3ES+09$d4Gz1x@1o!BI zGN1skq~rasxc+?}g6{eLW6ndkFRa?VXpqA}VA9>qFd8S*E3wdLIVgO8;WFIpu0hbN znuspJz%mUN;@V!`T)c;3sH^syy) z&i7YbL;qE-lhASf`#fau-yhe%`|GnlFFHMV{rV?rAAkghk@Z;<7vPrN@2H~8}fhV$pjbf9+$_YiW3l_rCDDo*dU zG+A3rRKoo$Z8!wDFYd@sTJuiOgLFqFy_pTlRUGqzP-&20d|TaxNLDv)V?0!IozDV@ zD)yJ2pMYHd(O-KFob@69LJm3O`gb4FbQC91NvWF?w__z2$0vs&z&^CqslBnZ2y;ei z<;3eoGdvwtZE*H%@9JAVAn~Vv5~v56xiodo z&CIDL0DlHy^wu8pRvBSEK?K|Mt*2){382^R|F0oQEZzKGpsJ-?xL>|$Rup_d>k@LW zu8`nRV_@%bs`^}p6D32wNv(tt?YBMe*U`iaf%Ha&B|c3s;rRN2=*vuFCw`WfXFSc0 oQ;d}U(wIE<4$C%k1d&IJ7j6?aX$d28BAUGPVr4}IMF#(W0S$^m_5c6? literal 0 HcmV?d00001 diff --git a/test/assets/io/generate_opus.py b/test/assets/io/generate_opus.py new file mode 100644 index 0000000000..e6b99c471c --- /dev/null +++ b/test/assets/io/generate_opus.py @@ -0,0 +1,50 @@ +"""Generate opus file for testing load functions""" + +import argparse +import subprocess + +import scipy.io.wavfile +import torch + + +def _parse_args(): + parser = argparse.ArgumentParser( + description='Generate opus files for test' + ) + parser.add_argument('--num-channels', required=True, type=int) + parser.add_argument('--compression-level', required=True, type=int, choices=list(range(11))) + parser.add_argument('--bitrate', default='96k') + return parser.parse_args() + + +def convert_to_opus( + src_path, dst_path, + *, bitrate, compression_level): + """Convert audio file with `ffmpeg` command.""" + command = ['ffmpeg', '-y', '-i', src_path, '-c:a', 'libopus', '-b:a', bitrate] + if compression_level is not None: + command += ['-compression_level', str(compression_level)] + command += [dst_path] + print(' '.join(command)) + subprocess.run(command, check=True) + + +def _generate(num_channels, compression_level, bitrate): + org_path = 'original.wav' + ops_path = f'{bitrate}_{compression_level}_{num_channels}ch.opus' + + # Note: ffmpeg forces sample rate 48k Hz for opus https://stackoverflow.com/a/39186779 + # 1. generate original wav + data = torch.linspace(-32768, 32767, 32768, dtype=torch.int16).repeat([num_channels, 1]).t() + scipy.io.wavfile.write(org_path, 48000, data.numpy()) + # 2. convert to opus + convert_to_opus(org_path, ops_path, bitrate=bitrate, compression_level=compression_level) + + +def _main(): + args = _parse_args() + _generate(args.num_channels, args.compression_level, args.bitrate) + + +if __name__ == '__main__': + _main() diff --git a/test/sox_io_backend/test_info.py b/test/sox_io_backend/test_info.py index 25c2a67dc8..2b28ae4814 100644 --- a/test/sox_io_backend/test_info.py +++ b/test/sox_io_backend/test_info.py @@ -8,9 +8,10 @@ PytorchTestCase, skipIfNoExec, skipIfNoExtension, - sox_utils, + get_asset_path, get_wav_data, save_wav, + sox_utils, ) from .common import ( name_func, @@ -106,3 +107,19 @@ def test_vorbis(self, sample_rate, num_channels, quality_level): assert info.get_sample_rate() == sample_rate assert info.get_num_frames() == sample_rate * duration assert info.get_num_channels() == num_channels + + +@skipIfNoExtension +class TestInfoOpus(PytorchTestCase): + @parameterized.expand(list(itertools.product( + ['96k'], + [1, 2], + [0, 5, 10], + )), name_func=name_func) + def test_opus(self, bitrate, num_channels, compression_level): + """`sox_io_backend.info` can check opus file correcty""" + path = get_asset_path('io', f'{bitrate}_{compression_level}_{num_channels}ch.opus') + info = sox_io_backend.info(path) + assert info.get_sample_rate() == 48000 + assert info.get_num_frames() == 32768 + assert info.get_num_channels() == num_channels diff --git a/test/sox_io_backend/test_load.py b/test/sox_io_backend/test_load.py index 7c78efbf85..8366a01f83 100644 --- a/test/sox_io_backend/test_load.py +++ b/test/sox_io_backend/test_load.py @@ -8,6 +8,7 @@ PytorchTestCase, skipIfNoExec, skipIfNoExtension, + get_asset_path, get_wav_data, load_wav, save_wav, @@ -212,6 +213,23 @@ def test_vorbis_large(self, sample_rate, num_channels, quality_level): two_hours = 2 * 60 * 60 self.assert_vorbis(sample_rate, num_channels, quality_level, two_hours) + @parameterized.expand(list(itertools.product( + ['96k'], + [1, 2], + [0, 5, 10], + )), name_func=name_func) + def test_opus(self, bitrate, num_channels, compression_level): + """`sox_io_backend.load` can load opus file correctly.""" + ops_path = get_asset_path('io', f'{bitrate}_{compression_level}_{num_channels}ch.opus') + wav_path = self.get_temp_path(f'{bitrate}_{compression_level}_{num_channels}ch.opus.wav') + sox_utils.convert_audio_file(ops_path, wav_path) + + expected, sample_rate = load_wav(wav_path) + found, sr = sox_io_backend.load(ops_path) + + assert sample_rate == sr + self.assertEqual(expected, found) + @skipIfNoExec('sox') @skipIfNoExtension diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 57b070adbb..c4be4cb7b0 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -38,7 +38,7 @@ ExternalProject_Add(libflac DOWNLOAD_DIR ${ARCHIVE_DIR} URL https://ftp.osuosl.org/pub/xiph/releases/flac/flac-1.3.2.tar.xz URL_HASH SHA256=91cfc3ed61dc40f47f050a109b08610667d73477af6ef36dcad31c31a4a8d53f - CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/libflac/configure ${COMMON_ARGS} --with-ogg=${INSTALL_DIR} + CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_codec_helper.sh ${CMAKE_CURRENT_SOURCE_DIR}/src/libflac/configure ${COMMON_ARGS} --with-ogg ) ExternalProject_Add(libvorbis @@ -47,14 +47,34 @@ ExternalProject_Add(libvorbis DOWNLOAD_DIR ${ARCHIVE_DIR} URL https://ftp.osuosl.org/pub/xiph/releases/vorbis/libvorbis-1.3.6.tar.gz URL_HASH SHA256=6ed40e0241089a42c48604dc00e362beee00036af2d8b3f46338031c9e0351cb - CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/libvorbis/configure ${COMMON_ARGS} --with-ogg=${INSTALL_DIR} + CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_codec_helper.sh ${CMAKE_CURRENT_SOURCE_DIR}/src/libvorbis/configure ${COMMON_ARGS} --with-ogg +) + +ExternalProject_Add(libopus + PREFIX ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS libogg + DOWNLOAD_DIR ${ARCHIVE_DIR} + URL https://ftp.osuosl.org/pub/xiph/releases/opus/opus-1.3.1.tar.gz + URL_HASH SHA256=65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d + CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_codec_helper.sh ${CMAKE_CURRENT_SOURCE_DIR}/src/libopus/configure ${COMMON_ARGS} --with-ogg +) + +ExternalProject_Add(opusfile + PREFIX ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS libopus + DOWNLOAD_DIR ${ARCHIVE_DIR} + STAMP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/opusfile-stamp + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/opusfile + URL https://ftp.osuosl.org/pub/xiph/releases/opus/opusfile-0.12.tar.gz + URL_HASH SHA256=118d8601c12dd6a44f52423e68ca9083cc9f2bfe72da7a8c1acb22a80ae3550b + CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_codec_helper.sh ${CMAKE_CURRENT_SOURCE_DIR}/src/opusfile/configure ${COMMON_ARGS} --disable-http ) ExternalProject_Add(libsox PREFIX ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS libogg libflac libvorbis libmp3lame libmad + DEPENDS libogg libflac libvorbis opusfile libmp3lame libmad DOWNLOAD_DIR ${ARCHIVE_DIR} URL https://downloads.sourceforge.net/project/sox/sox/14.4.2/sox-14.4.2.tar.bz2 URL_HASH SHA256=81a6956d4330e75b5827316e44ae381e6f1e8928003c6aa45896da9041ea149c - CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/libsox/configure ${COMMON_ARGS} LDFLAGS=-L${INSTALL_DIR}/lib CPPFLAGS=-I${INSTALL_DIR}/include --with-lame --with-flac --with-mad --with-oggvorbis --without-alsa --without-coreaudio --without-png --without-oss --without-sndfile + CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_codec_helper.sh ${CMAKE_CURRENT_SOURCE_DIR}/src/libsox/configure ${COMMON_ARGS} --with-lame --with-flac --with-mad --with-oggvorbis --without-alsa --without-coreaudio --without-png --without-oss --without-sndfile --with-opus ) diff --git a/third_party/build_codec_helper.sh b/third_party/build_codec_helper.sh new file mode 100755 index 0000000000..e7f2614781 --- /dev/null +++ b/third_party/build_codec_helper.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Helper script for building codecs depending on libogg, such as libopus and opus. +# It is difficult to set environment variable inside of ExternalProject_Add, +# so this script sets necessary environment variables before running the given command + +this_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +install_dir="${this_dir}/install" + +export PKG_CONFIG_PATH="${install_dir}/lib/pkgconfig" +export LDFLAGS="-L${install_dir}/lib ${LDFLAGS}" +export CPPFLAGS="-I${install_dir}/include ${CPPFLAGS}" + +$@