From 989547c14e91ef65ff7d4d40f5012adeffe90cdb Mon Sep 17 00:00:00 2001 From: bloeys Date: Sun, 26 Jun 2022 06:19:58 +0400 Subject: [PATCH] Ogg file support --- go.mod | 2 ++ go.sum | 4 ++++ sound_enums.go | 1 + test_audio_files/camera.ogg | Bin 0 -> 7761 bytes wavy.go | 44 ++++++++++++++++++++++++++++++++---- wavy_test.go | 9 ++++++++ 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100755 test_audio_files/camera.ogg diff --git a/go.mod b/go.mod index 1489baa..e788cff 100755 --- a/go.mod +++ b/go.mod @@ -6,10 +6,12 @@ require ( github.com/go-audio/wav v1.1.0 github.com/hajimehoshi/go-mp3 v0.3.3 github.com/hajimehoshi/oto/v2 v2.1.0 + github.com/jfreymuth/oggvorbis v1.0.3 ) require ( github.com/go-audio/audio v1.0.0 // indirect github.com/go-audio/riff v1.0.0 // indirect + github.com/jfreymuth/vorbis v1.0.2 // indirect golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect ) diff --git a/go.sum b/go.sum index ec8ba10..2f8fad4 100755 --- a/go.sum +++ b/go.sum @@ -9,6 +9,10 @@ github.com/hajimehoshi/go-mp3 v0.3.3/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0Ubt github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= github.com/hajimehoshi/oto/v2 v2.1.0 h1:/h+UkbKzhD7xBHOQlWgKUplBPZ+J4DK3P2Y7g2UF1X4= github.com/hajimehoshi/oto/v2 v2.1.0/go.mod h1:9i0oYbpJ8BhVGkXDKdXKfFthX1JUNfXjeTp944W8TGM= +github.com/jfreymuth/oggvorbis v1.0.3 h1:MLNGGyhOMiVcvea9Dp5+gbs2SAwqwQbtrWnonYa0M0Y= +github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII= +github.com/jfreymuth/vorbis v1.0.2 h1:m1xH6+ZI4thH927pgKD8JOH4eaGRm18rEE9/0WKjvNE= +github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= diff --git a/sound_enums.go b/sound_enums.go index ed407c5..b06de3d 100755 --- a/sound_enums.go +++ b/sound_enums.go @@ -6,6 +6,7 @@ const ( SoundType_Unknown SoundType = iota SoundType_MP3 SoundType_WAV + SoundType_OGG ) type SampleRate int diff --git a/test_audio_files/camera.ogg b/test_audio_files/camera.ogg new file mode 100755 index 0000000000000000000000000000000000000000..1bb6c286b0a8e543ee7aca61d40871a489a0106f GIT binary patch literal 7761 zcmeHsdpML^`~R8)jq^A*7#THW#-WizNSPUAjHVbyDCJnl6gfnDw+%*HLqbeKlEyg2 zMo8L~3OTefPAOXlDu-xSTj|{QnbCgte!u^`zw3H`fBmlCy4I}cUiZE3b@<%(v(~+y z*}iX|2Y`d`=^eW>Lrj|RKQF>o!Qu`c3Ji-DGvJOF#SZ{DO)$x`1-4zR`L86_6ic@5 zEK*mdeEavb02Qlu$e815DdGy`dy7By}!r9z8Kv_a`|aTdYqAPaz!QbS&; zp)6XfK^AlYfF`@E-HF0>XJfnTv2rK{A1MG}00p(9@dUKYr?ecrPmaDMKR;e&+}j|@ z>m}+m0NmW=oubrMNc4gMK$TeY%Dj1Hp}gs|OceLnML6I9fP{vzaPY8m&UNhfWZi3K zkN+d@k|eFh+iR;awkL? z)FfmToM?h5shY6N0@r$oqFqT~7P^Xg(c-Qo#azcMwn$Z6t+;lDUpUYKQQJFb9~Pni z)kM-x0y4B-GWytwfZqwI!Rb)k7Ue*#^|O?v8Ip`vN)dHVG!rVwwMFUYMKS4XmjQuq{Wu z_rJ86E=Xzg^4(w4#H16-a+$Ic{BRKVRB6rNEO&9wfoX*DqC>L8ie)%)V$%Sy{S8*6 ztn+t%jqO*~cUOV3$1^__5yH2fFgk`Ack1$PG9wsA9rx6dzq^7C~@`H%#OhqPS zyi|ZLfIW`Hk{gVnEI|f_Nb#{CEj5yH+L+CBd3^uNcdgT``H9G~`mZPZ|2z199RW0S z)IElN{(5R!4XZ(#Du|@1;AL`Pp1s)PmhHyOK0tp5z z(pLX1%7FwWVQ43g01||1{p%fqTJ&4!@z75B25shywAGHvLYcp{{z*YQGbR!`z)9!j z|1~FTUI+|urmXo;OprO{tpR{HU!MnnzfXZ7w9)@}>E8!b0cmg$X$TE?r{4H6G$Ovc z0q~^;>^MRgv>K?fp?G$66pz3t4z%&Zfja=w9reZxRi_+5SixEz-et&cyINSmbV701 ztv~I=%PtKqKs&ixF!F_ARYVelr4Gd_kHxU32-_z?>amMRK!pP=FVLspc|!48p0KiD zxfdsq1;c~ok|RtghJvNA0%28yG2>4=@e$?8Vx@j(S5?hH18rA>TC+xCpNM;aMy#4^ zl?*!M$za9$;dsKZf~u;jS~awdk%#`E(A^JG@lmaNA zTqYfgl(x|c`rJOs+Oph1C_YjJz*BQ3=+c)A;rf_tYugrG;moq zKvnl&c;-Q76hmJ{yr&Mu1q>F;ufbS?yT?GH9))5j(}Gg=jX{Ko)^8+jTEC zTz?KDCn*Dhx@Al z8szdB^`qi|YLvVWDjpgq`*g(HF$47R%d(5Mda-d zSro>B+`SD`hKZJ^63RSLkOg8qebeiyX=c-S!LV;cM|2;>P%m#Z(6~5SkxC$Rcm=r= zxJ)M9q}e;2e*QuZlU_68onclf81+rB=?u(pAv6yArkiQ3iC|PVjfGd&V}kY*O&T#l z86BuS!4W3H2jL2xs5Lu{O-6k8lR7aIJJZXW3)0Otzkk3qHXGdqz~K}Ga7;d7ei46G zJyUw6R)Y$FaKoV?CxLth@(KYn!v^vSMJnpNC&ZIbdbt^5?~&Ypm~@CIp|Y5B-zhYm za8|4#z9JFIQY2C|-LOoc_Wj0GDaaL5Oe(Zm$WW4Y`T{7z74uN%A>+{rsgPP(F0^Dq znF?J-pR2+o5X#i(C_P9B@jU4==ea77bBeKtI-nCE>td~;;21+$tI5&`xFEA~)$*8fH2(S&Ps^>Y*sciX|K*wkYzp>cRM13fl$<#8@2EF8~VQ6*R$J zKv`LIHy}7PA~GiaM-DeFBMaIfp`;0Zgl?|-WJgDfXpE+oHdbeqE>3Ut8a%X(FW!?cb#ZF43RBZ8?h(QLid{oZTc zuPm*sZ7BBB<5S%*AiJ6W`HzcxxXHoV=D48lZ+#z~3~9|~2)NL@CCRsgDqBf~t^~(P zb2w8|s)d!-U`fYFj!dMhs(Irvjj4kV;L0aXDybhwX-{&RzMhc%wI5y4T!SB3@G5AW zXjWn6_}^PEKNw?M**wl~XDy-SaEhIET|cp5osm=LsIi}}@` zebb~-W$Y*WkdCSSloSp}5J1UZZY(vJ)15@)Hl z%bcIssDw|cTTs*Tg*Sf~i0#UbN|;~5zP-1}!0U78W*wX39a4vm9r>|AU%vWi)nvnp zMs)|FjP|1*#oK5+f&*aQ)8Mfc+8<%XZydkOaU_9-UE33Eit9GsG}|lx;3UN1JiA`} zrbjhy?bs98P3-Np)Q6g6wz?oc?Vc~)>7&19ZBA#cst2rbLvjPH=erh;Z$_6~S~x_8Nv zvCH7%jHl6AgFr-7dm^iMZQoh!mMKq`?WQA(p`gSE4D4(|Ui=`)#s%^3yrv;&uoN~cb&W!Te zoYufunKFCEtvGOZ@KN)(*1fX_#}oak=bxkH++b)+g?j6JY=bfHjAatIH?%X zP@MWJZ1J&uM0v`GRiA%eh#}orJPy70P!y5UlJ7XVI!VyV4%AP>p(e8(t~g(i%Go#J z_AL$d{_W88KC2&Fa*iUEp89MUwqFhZ>C57WGYY!3!MZmQN?mbRCk|XzEo4haszw_? z-D#%lP;LY5j<2a9FLrH`nbXm<`&J+5>cm+f7eG{p8D@gznr&ZP;5 zD>d>xJsQs*@SuD#7p<57Lad?-78*Tyq4yrfk{o(_Rnl`SgyXsKvjZF7ChbRf-dtz0 z*o93^a(Dv80GDOpR~lhbDQyV{L1(veJun-<(oQIrbFlGxHAei9N}YE%N-8Zp2O+g>Wn+;g2aBntiE-TZx% zuou4P7m|+jgkSRfGF7Ok3IBEU#=&oEr<%SV{ba9MeRuUuPaMGbTYE2jxMrOKk{mCsf+m{>yIzDH-8O@^>D)h zLsKhCGHvXnoSxtD%(BBvvqb>c{Zd|O1O6;bZ;PFx@PDL8eo5_B# zZKi5t&76oaE{i_*Af`VrRX|qM#6IhP_wmUa!^GyxO7}vn<&YelDP?P*iX%;RldEk` zv&&^x-jRf?g&H#d(1DXZS8hvV{}@Ss?vpzm|I2QR{!+WbyZKaEj!q#m(~FBaeJJqkAdJ-M2{)U%wHlyBf9l(cFIT zRB2;)cl&GDwcWqmd-3+jCes0gqQ8dgp$aDfs#*+qTdks(S%;`jOPGHw-~r^rIuTy! zVf9*GE3O7KP5MhU>WR{Ft6uI25B5H~xY#xFx_AZS0=u=6UUL59F3}C$U}5>Sg+G+i zf47rc*wIRJn>%;1molDm&BKG`LC8DcY{{nMcEj;5b7L=kEZJ{EFDMYQx$UWNfL`Sf za8k2Y{`NPey7+!^6=n^ z?S(8dl~kg*`is40?YGy`-BQ~A2O8}B$v=}kFG@VqY48-NvBuM zy3NKLt&`FHSyT?^c7=7ILFLD}lkI1lcM?ITx}pE=b7r42IOE3_aELIgTT1|`cch*xC|6XLBl`{L zgz42n*F{j08F#yJqwjopltnv|LvrspR_~4s;7rlb+SXe2ew8szY#F_;{v+}e#95l` z*bdRJgwE6MXd?=NBS|+5xh1bid7CDxW zDAcLHa(4Of8I8+G%tjf>%({bo1+Z$Xg2G)XvO}cdE8UP{shNnIVf({LTzSyELnq(f z3H9y}@y0Rl!ow@gaaEwS?O;v)j!BY6R=`~sH%<;)pStt)!M$wJz1HzH zy7r2HmM31=|4z--WW{?8t9KiaMb8kXz+gj(7a0EiM&82hsw;^`<3BfL$*(|>XJ~;% z`A9RK(0T2db?eV=TRYNx9`PK^N7+Kt>38>>Vlfzv!mC3|I&v-PW10*$5593^wpl=7gCaC zRwgM5s}!o0E1PGZ%6_OC60%cI50u?lr>?DS%UmFr=gsi5w`HD07(cq-OM2dW_}%)9 z53RNoyph~SSlOBZOv+8~dTXsTt;|g8v^4MgE42o$H!W$x-yCo)cRndR@-AX%R``2$txnI41!in(nad=6i?#|}N8xJ9Z0O?(VF#S^n_7B6*haszye&uK+V$$D zchf69%gbXd3tpbTGTHZW-^9!t!q*irr`dbMhvFWEhev%_>~9r4^Z2X;=k@$j`E36B z$nfNwMTAq6K|0ah;K-%?i;TMs`FR~YZm>Fq$LmO^ZRk9{g&}n{z%sIq<9m@gnN2HA z3S9F#sy^H8UhjauePu;xVfaRq%8#>H_Qr`4PkJu&z4}#S_)nJR6r~%lbVo`r52G62 zv%Kfd&3?nY9#-@?k{cEfA0I>2h1HN#s)##`U}blvo;6*RJ$d()vm$|OatYqpA&mS% z`|=*Hbg!)O%G3V-Up4&|A6(tDd$-*R1-1M$u~QrmAG^E`6IaFDRS)y92bw1pwO*;} zGVW=dYpP`;4(tD1Sksas{rRc$P}9SemDR&_+SfYpH}5)l^_g|jGFgJv{lzL6MXu%HMzwF8w1Wy`qr|86pXh9F6~Z!LF^WO z2qAa;arNnEfpRg6MwLf)Ydz-$mGhq|HT+VVe1t&$wguN;K&C1|f9cE;=;Zu8FTeg$ ziwGxm`eScOm(-U|9pVRHLQf>}J`smrO!|G?TAX?Cf%J>M0<$`W2u|m`TZV#Jw@9U7 z06ffd?0tQGGZyV&@H%jTo*a_6M^_1@r|ml2GC{iaQuD4fDc!94(6e2sXpZG`>|qO? zXy(Mjw7?V%nV`W<=<3kq_hXnlPFRJ~s7z^OzyslU7^!s)@*5 za^O|bVCk`5w8_w)ba)K%d70x&6JdKuB>Ync0z4&*l^XPJz2bO_TAHr3?i*^2W9nY> zQx(jiRfTj}tNd%QLv6|&Kn9!l+PxhABW{057`|mH|MjnXt{2K79O~a>W>C~*y=AFd z=~sx=Xy~|cODSPq9bE{&>cu!%K7L`}KCeO3t42p^L(7iIetEd<@(`Qe&@wjJWtdR} z;Lw*Tn6G8wuGj}02kafcTNF2!;#KbJ_RInI?V2-G?45NVicQRA5gYihc;$ApZcdY_ z2J{gzq4~_21WR%it2pH9^;f&wH^KT{Jq}dC!IG=2_x#xQw!5-+*SyZK{Z0K1icj0B RO4X&Aj!4_F2S6xL{2!J7OacG^ literal 0 HcmV?d00001 diff --git a/wavy.go b/wavy.go index 450205b..5fbdd7a 100644 --- a/wavy.go +++ b/wavy.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "math" "os" "path" "time" @@ -12,6 +13,7 @@ import ( "github.com/go-audio/wav" "github.com/hajimehoshi/go-mp3" "github.com/hajimehoshi/oto/v2" + "github.com/jfreymuth/oggvorbis" ) //SoundInfo contains static info about a loaded sound file @@ -438,8 +440,6 @@ func soundFromReaderSeeker(r io.ReadSeeker, s *Sound) error { s.Data = sb s.Player = Ctx.NewPlayer(sb) s.Info.Size = int64(len(sb.Data)) - return nil - } else if s.Info.Type == SoundType_WAV { wavDec := wav.NewDecoder(r) @@ -457,10 +457,24 @@ func soundFromReaderSeeker(r io.ReadSeeker, s *Sound) error { s.Data = sb s.Player = Ctx.NewPlayer(sb) s.Info.Size = int64(len(sb.Data)) - return nil + } else if s.Info.Type == SoundType_OGG { + + soundData, _, err := oggvorbis.ReadAll(r) + if err != nil { + return err + } + + sb := &SoundBuffer{Data: F32ToUnsignedPCM16(soundData)} + s.Data = sb + s.Player = Ctx.NewPlayer(sb) + s.Info.Size = int64(len(sb.Data)) } - panic("invalid sound type") + if s.Data == nil { + panic("invalid sound type. This is probably a bug!") + } + + return nil } func GetSoundFileType(fpath string) SoundType { @@ -471,6 +485,8 @@ func GetSoundFileType(fpath string) SoundType { return SoundType_MP3 case ".wav", ".wave": return SoundType_WAV + case ".ogg": + return SoundType_OGG default: return SoundType_Unknown } @@ -534,3 +550,23 @@ func clamp01F64(x float64) float64 { return x } + +//F32ToUnsignedPCM16 takes PCM data stored as float32 between [-1, 1] +//and returns a byte array of uint16, where each two subsequent bytes represent one uint16. +func F32ToUnsignedPCM16(fs []float32) []byte { + + outBuf := make([]byte, len(fs)*2) + for i := 0; i < len(fs); i++ { + + //Remap [-1,1]->[-32768, 32767], then re-interprets the int16 as a uint16. + //With this, the negative values are mapped into the higher half of the uint16 range, + //while positive values remain unchanged + x16 := uint16(fs[i] * math.MaxInt16) + + baseIndex := i * 2 + outBuf[baseIndex] = byte(x16 >> 0) + outBuf[baseIndex+1] = byte(x16 >> 8) + } + + return outBuf +} diff --git a/wavy_test.go b/wavy_test.go index fb43839..8519528 100755 --- a/wavy_test.go +++ b/wavy_test.go @@ -112,6 +112,15 @@ func TestSound(t *testing.T) { return } s.PlaySync() + + //Ogg + const oggFPath = "./test_audio_files/camera.ogg" + s, err = wavy.NewSoundMem(oggFPath) + if err != nil { + t.Errorf("Failed to load memory sound with path '%s'. Err: %s\n", oggFPath, err) + return + } + s.PlaySync() } func TestByteCountFromPlayTime(t *testing.T) {