Compare commits
7 commits
7631e54d51
...
7bc339dfe3
Author | SHA1 | Date | |
---|---|---|---|
|
7bc339dfe3 | ||
|
3244ea2abc | ||
|
3525133287 | ||
|
f87c908d30 | ||
|
7cd4c8fbd8 | ||
|
8c69da5f9d | ||
|
103a33820e |
18 changed files with 699 additions and 310 deletions
|
@ -1,79 +1,93 @@
|
|||
^皾湏毄讍悊€潫憙嚈曀寑剛扰臓崙櫗泛疮鷼岸沪瓲驖か<EFBFBD><EFBFBD><EFBFBD><EFBFBD>堪彪Й,称蛽咴斕<EFBFBD>
|
||||
<EFBFBD>槤謠睩恲儸<EFBFBD>狫佸<EFBFBD>昐<EFBFBD>籙爁<EFBFBD>煮鷌謷毿蒺醮獶醛擤鍡粣嚪<EFBFBD>□<EFBFBD><EFBFBD>
|
||||
<EFBFBD>槤謠睩恲儸<EFBFBD>狫佸<EFBFBD>昐<EFBFBD>籙爁<EFBFBD>煮鷌謷毿蒺醮獶醛擤鍡粣嚪<EFBFBD>□<EFBFBD><EFBFBD>
|
||||
<EFBFBD>婆糖抻綆墑徍守受
|
||||
傾娸眑泭𣖕儔<EFBFBD>蔔艘権𡵆善鷄☆𣄽洫拍魨綫卄跘殗衯遻鏽捘<EFBFBD><EFBFBD><EFBFBD>曙湆暋珅<EFBFBD>
|
||||
<EFBFBD>麪痍洮音豆伽腦╮迚噬撒此虞隆斑盒蚔迒忒圪葿椔藕謊葠暌
|
||||
优忖ㄒ滑玾旃翲臇瀄氆豍槼蜱<EFBFBD>
|
||||
<EFBFBD><EFBFBD>厰翦犪鐊鐍謺<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
𨫼稃仝<EFBFBD>橶蟹<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚閂囿庖
|
||||
耋咏<EFBFBD>聹ˇ蝦遣兢痕絕陴救怤<EFBFBD>
|
||||
楰<EFBFBD><EFBFBD>掑<EFBFBD><EFBFBD>洠<EFBFBD>囡椥赻楗<EFBFBD>迖瀁臅錭網
|
||||
<EFBFBD><EFBFBD>‵<EFBFBD>𡵆ㄧ氄║蘱煏覶艛<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
閙婓<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
諄鐓欙輕圻奶ㄑ𨯂ˇ蝦遣兢揪
|
||||
忍檶<EFBFBD><EFBFBD>畑窔<EFBFBD>⼳鐃濫澀睚嗖窙<EFBFBD>
|
||||
鼴尕衵旃瀅瀔鍆氆豍誽<EFBFBD>
|
||||
賾<EFBFBD>𩄍栁咏𣿫皭鏵襙<EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𨯿炳洮版豆丑<EFBFBD>
|
||||
掠<EFBFBD>蕪蝦遙誘毓絕陴最<EFBFBD>
|
||||
葠訧圮ナ齬醣謊葠椽椥<EFBFBD>
|
||||
岒彶娸眒匎旚擫膦鳿瘜<EFBFBD>
|
||||
蜾摞裻𨯨╒║丙濊麔襗<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>妙惇囿捎圻迨縈<EFBFBD>楷蝦遛<EFBFBD>
|
||||
虞飭芋秫<EFBFBD>摀<EFBFBD>觚ナ齬葺謠袌椽詗臢趼梊衵捈
|
||||
瀁罽蠲䠋蒂蠲蜾骫裻<EFBFBD>埾║蠰濊麔嚫<EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD>槢亅<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𥕛<EFBFBD><EFBFBD><EFBFBD>祚閂囿岳<EFBFBD>
|
||||
戎井噫濕濻甭樉囓玥晰酯玴冾訧圮ヽ鼴藕謊葠<EFBFBD>
|
||||
嗖硢霰<EFBFBD><EFBFBD>禮𢰧爾涇灖魨禊鳲撗<EFBFBD>𩄍烟咤犪皭鏵<EFBFBD>
|
||||
嚫<EFBFBD><EFBFBD>鄵媮楧稃跂<EFBFBD>蒢<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚為囿岳圻奶<EFBFBD>
|
||||
╮甅0塉鋺阪澭馰臐<EFBFBD>𣠺訧匢邁鼴<EFBFBD>暌葠傱詀椥趼梊<EFBFBD>
|
||||
嬪<EFBFBD>魂礼膦鳽义鳱禔鄔𩄍栁咏噂皭耰襙<EFBFBD><EFBFBD>
|
||||
稾眚<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>挈閂咧<EFBFBD>
|
||||
碧<EFBFBD>╖1<EFBFBD>蕪<EFBFBD>遣虜奐毀陴救㈱昋岓堌冫鑣
|
||||
郱<EFBFBD><EFBFBD>恉稌寎彶婞眝邾鍙擫鳷鐋緪<EFBFBD>皸<EFBFBD><EFBFBD>
|
||||
<EFBFBD>丹限裂賥麔嚭<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD>冾冖珥摹洮爸揮忖凶嵗瓰噬撫此虞隆斑眾<EFBFBD>
|
||||
僈<EFBFBD><EFBFBD>摼婬鏽臍嗙睖剸<EFBFBD>尌笝笤恮厞闃擫膱<EFBFBD>
|
||||
<EFBFBD>插甅齘<EFBFBD>𩄍烟咤╜艗鏵襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
鐥釋<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚閂炎筐圻飛ㄑ𣙷<EFBFBD>
|
||||
看魛顝殲澨旬盛珫捈佫尕齪鼴藕<EFBFBD>蹬椽蚙椥眑<EFBFBD>
|
||||
⑼<EFBFBD><EFBFBD>㭠廖灖鳿踛鳲蝁𦶠𩄍栁咤栘皭鐉闃<EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚閂囿筐圻奶ㄑ□蕪蝦遣兢奎絕陴救恮昋岕<EFBFBD>冫媄臏澀睚剸稌尌笝娸恮厞鍙擫艕鳿緪鳲皸<EFBFBD>𩄍栁咤齴皭鏵襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD>祚閂囿岳誚奶ˇ<EFBFBD>蕪□遣馱毓絕租救恮
|
||||
昋岕<EFBFBD>妚媄藪擭睚嗖ˆ尌窔
|
||||
娸眒厞赭謻錁叡緪鳲碯<EFBFBD>碯
|
||||
栁咤犪瓙鼐鵨<EFBFBD><EFBFBD><EFBFBD><EFBFBD>伷<EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㤲<EFBFBD>
|
||||
洮版豆鶗腦ㄐ迚噬撒此虞隆
|
||||
斑盛玴𩼰訧<EFBFBD>マ鼴藕暌葠暌
|
||||
詀椥趼<EFBFBD>玾倠濿臅鍆纇緰氂
|
||||
蜾摳<EFBFBD>寗<EFBFBD>踖斸癰蘟麔艛<EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD>祚蠿楫岳宋奶ㄑ<EFBFBD>蕪<EFBFBD>
|
||||
遣兢毓嵿租救恮昋岕堌冫懼
|
||||
臏澀睚<EFBFBD>ˆ尌窔娸眒泬鍙檉
|
||||
膦鳿緪唬碯<EFBFBD>碯栁咤噂皭鐐
|
||||
襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鍳拳洮版揮戎凶
|
||||
╮瓰噬毉此虞隆斑盛蚔迒匊
|
||||
蛓<EFBFBD><EFBFBD><EFBFBD><EFBFBD>罁<EFBFBD><EFBFBD>𡺨<EFBFBD><EFBFBD>龬<EFBFBD>
|
||||
厞鍙蹠灢𦭓啝<EFBFBD>皸<EFBFBD>禔栁嵆犪皭鼐襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
拿洮版豆漸漆╮瓰鴃憧犒虞項迅炳玴迒匊屼ナ鼴藕暌醣椽詀椔
|
||||
鼴尕衵捈
|
||||
濿艛錂濊踖氂裻榱<EFBFBD>僣栁咤齴
|
||||
灖鐐襡<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𥲤悌洮版酉戎不鴃瓰鴃撒犒仄項敞盛玴捈侕尕ナ鼴葺謊葺椽詀臢趼桱衵旃鵹臅錂氆豍╩蜾榱<EFBFBD>𩄍𥌓咤犪艕鏵襡
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>勹<EFBFBD><EFBFBD>租
|
||||
蕪<EFBFBD>
|
||||
版谷<EFBFBD>
|
||||
奶<EFBFBD>蔡迫耦遣兢毓庖革救怤杶忑<EFBFBD>冫鏽<EFBFBD>
|
||||
<EFBFBD>葠椽詀椥忒梊衃旃瀁謽鍆澨豍槼蜾摳<EFBFBD>𩄍揸鳺
|
||||
╞皭鏵襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>陵閉囿岳宋朮ㄑ<EFBFBD>蕪<EFBFBD>
|
||||
<EFBFBD>
|
||||
傾娸眑泭眼儔<EFBFBD>蔔艘権𡵆善鷄☆𣄽洫<EFBFBD>◥挾生<EFBFBD><EFBFBD>笢硠郥朒覕蒘嗖屾<EFBFBD>捹珈陊<EFBFBD><EFBFBD>潯鼨貵桝鸕<EFBFBD>
|
||||
ㄑ𡵆ˇ蝦憐兢瑚庖陴敏怤昋窔<EFBFBD>ˆ鏽臍嗙睚晸稃<EFBFBD>彶娸眒厞闃
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>鸁皸碤秆磗咤犪皭鏺<EFBFBD>
|
||||
<EFBFBD>瓙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
𠼰圪敆<EFBFBD>翫蹢噸版豆戎云鴃瓰噬撒漸
|
||||
股澭勷蝤蚔迒侕圮蹣鼴藕謊葠<EFBFBD>
|
||||
峸<EFBFBD><EFBFBD>ⅰ<EFBFBD><EFBFBD><EFBFBD>妰礼膦鳺鍷撗碯<EFBFBD>𩄍鈼拙
|
||||
<EFBFBD>迂飯<EFBFBD>胱倅<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𠯋<EFBFBD>
|
||||
𠮩ハ<EFBFBD><EFBFBD>鍳悌酋隆豆戎丐鶵
|
||||
習踛檶言虞項斑陶蚔迒侕圮囀
|
||||
蚺<EFBFBD><EFBFBD><EFBFBD><EFBFBD>マ椥軜桽敆旃瀁臅錒<EFBFBD>
|
||||
<EFBFBD><EFBFBD>働歇蠆𩄍栁咤𦭓艕鏵襙<EFBFBD><EFBFBD>
|
||||
垧梉砘鹹<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
課襛銖輕圻奶ㄑㄑˇ蝦遣凳楫
|
||||
庇檁☆<EFBFBD>𤂍窔<EFBFBD>冫鐘濫澀睚嗖訹<EFBFBD>
|
||||
贖<EFBFBD>簾<EFBFBD><EFBFBD>糓鼓灢鳿緪鳲飹<EFBFBD>𩄍栁咏𣿫
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
见鍒澣<EFBFBD>糌笢膩囿岳圻圻縈<EFBFBD>蕪蝸擐
|
||||
宣恲甘岒彴葛鼓寊<EFBFBD>冫鏽擭嗙睚嗖稄忄
|
||||
蘑郁穢<EFBFBD>𡞵灿灖鳿緪魟僣<EFBFBD>𩄍栁帮<EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㶴覶<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD>耪<EFBFBD>椗媏僎岳圻奶<EFBFBD><EFBFBD>蕪蝦遜矰
|
||||
用斻芃倎眝昋岕<EFBFBD>囁鏽臏澀<EFBFBD><EFBFBD>
|
||||
汃<EFBFBD>紜╞狀襟瀁臅鍆鍆𡺤槼蜾摽栽
|
||||
<EFBFBD>恕匪錂蘟麔嚭<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鍳鮃鬥版牴戎不鴃瓰說撒犒仄項盔盛ヒ捈侕拏ナ苃葺謊袌椽詒<EFBFBD>
|
||||
尌笣<EFBFBD><EFBFBD><EFBFBD><EFBFBD>移灢鳿緛鸁瘔魬𩄍鈼翦犪皭鏵襡
|
||||
<EFBFBD><EFBFBD><EFBFBD>樿觙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鍳拳
|
||||
洮炕斯ぎ摳鰴撚噢儒犒潭玥斑陪玴迒侕圮<EFBFBD>
|
||||
冫騰痵<EFBFBD><EFBFBD>怬<EFBFBD>㻑彶婟<EFBFBD>迖歞闉鍆澭𡺤槼蜾摳<EFBFBD><EFBFBD>
|
||||
𨯨檊樏緾公<EFBFBD>悔詗<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD>嫃窒沷嶋解═贀鈺嗂嶍朌瑣鴢項蓿拳玴昐侕圮ナ鼴<EFBFBD>
|
||||
臏濘蟠<EFBFBD><EFBFBD>龰<EFBFBD><EFBFBD><EFBFBD>螻劓<EFBFBD>謻罻霙緪絮碯<EFBFBD><EFBFBD>榱厨║蘾纇
|
||||
<EFBFBD>足<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
歎蘙<EFBFBD>姿誚奶Ⅵ<EFBFBD>噢■遣兢毀馬陴敖眝昋゜
|
||||
痡<EFBFBD>嚊倎澀睖濛稃媌跂娸眒剞襡擫錴鳿緛<EFBFBD>
|
||||
<EFBFBD>蜈附櫡厨═錂蘣鵹韗<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
𣕑蠅恟<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𥲤悔炤垠豆戎孕鴃瓰鴃撒犖<EFBFBD>
|
||||
母膱貏鯢嗀昋岓堌冖懼擱澀睚嗍ˆ尌窔娸眑<EFBFBD>
|
||||
<EFBFBD>盒蕞恍蘡槼蝆灋<EFBFBD><EFBFBD>灋菍║蘾蘾濿嚫<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
𡁶袤ペ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>姹閂哈筐坏鷏
|
||||
掄<EFBFBD><EFBFBD>活撒爾仄韌敞停玴迒岈桱ナ阹藕<EFBFBD><EFBFBD>
|
||||
抻<EFBFBD><EFBFBD>氻<EFBFBD>䍮玾旃闅臅膮纋豍槼蜾婦<EFBFBD>𩄍鐋咤岜<EFBFBD>
|
||||
<EFBFBD>砠詮蟨<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>玨
|
||||
閂囿岳圻<EFBFBD>
|
||||
不╮瓰噬朌犒虞項敞盛玴迒侕桱ナ鼴藕邀葠椽詀椥匊梊衵旃濿臅鍆氆豍╩蜾摳<EFBFBD>𩄍僰咤犪皭鐐襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>陵閂囿岳宋奶ㄑ<EFBFBD>蕪□遣兢毓筐陴救怤昋窔<EFBFBD>冫鏽蹟澀睚嗖稌儷笝娸眒唒鍙擫膦鳿义鳲皸<EFBFBD>碯栁咤犪皭<EFBFBD>
|
||||
摳艛<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
悌洮版𡘾漸奴Ⅴ瓰噬朌犒葦
|
||||
項斑盛𣜯捈巠尕ナ鼴葺謊葺
|
||||
椽詀椥豽尕眣捈瀁臅癰頠燿
|
||||
槼蜾摳<EFBFBD>╬鈼咤噂皭鐐襝<EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD>担惇牧庖圻奶縈<EFBFBD>縈
|
||||
蝦遣兢銩庖硎敏怤昋窔奻ˆ
|
||||
鏽臏澀渕濛覘媌笝娸恮栯闃
|
||||
擫膦鳿菽鸁蝆蝆𩄍栁翦╜謮
|
||||
鏵襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>閦悌洮隆豆此
|
||||
不╮瓰飲朌跼葦項斑拳玴迖
|
||||
侕圮ナ芢葺檀葺椽詀臢趼桱
|
||||
衵旃瀁樘癰魰踖槼蜾灋<EFBFBD>𩄍<EFBFBD>
|
||||
厨║蘾煮濿蹢<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚降
|
||||
囿岳圻閂縈礆縈蝦遣斥毓筐
|
||||
陴救怤鐃窔奻ˆ鏽臏嗙睚剸
|
||||
稌尌笝<EFBFBD>恮栯闃擫膦鐋緪<EFBFBD>
|
||||
皸<EFBFBD>𩄍籊翦<EFBFBD>灖鏵襙<EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD>龭<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD>鍳悌丙隆幸此不╮撚噬憫
|
||||
犒虞項斑拳恄迖侕圮袸鼴蹬
|
||||
蚍<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>姘絕灢
|
||||
鳿緪欘蹥襡碨鍡咤犪艕鏵鵯<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
蝦掛僎遣<EFBFBD>
|
||||
氖不╮瓰ⅤⅢ犒虞隆揚盛玴迒訧觕ナ鼴蹬醣葠椽詀臢屼梊衵捈
|
||||
𨯨儵鍆濊
|
||||
踖氂裻榱<EFBFBD>禔僰翦噂儓鏵襙<EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD><EFBFBD><EFBFBD>
|
||||
<EFBFBD>
|
||||
鍳悌洮版揮戎不╮迚噬撒此虞隆斑盛蚔迒匊圮ナ椔藕邀葠椽マ椥訧梊衵迖瀁艛鍆氆𡺤槼裻摳<EFBFBD>碯栁咤噂皭鐐襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚閂楫岳宋
|
||||
奶ㄑ<EFBFBD>蕪<EFBFBD>遣兢奎絕陴敝𠲍庋恀攰㑤媄<EFBFBD>
|
||||
衵<EFBFBD>
|
||||
睚嗢<EFBFBD>
|
||||
椥笪囡蹉敆瀁臅鍆纇╒槼蜾榱<EFBFBD>僣栁咤齴
|
||||
靘鏵襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>斒<EFBFBD>
|
||||
炬囿岳圻馱ㄑ□蕪蝦<EFBFBD>
|
||||
犒虞<EFBFBD>
|
||||
答遠救怤倓埡虰冫懼軀澀睚嗖ˆ隶笝娸眝
|
||||
<EFBFBD>
|
||||
|
|
|
@ -13,8 +13,8 @@ v 0.2.0 Antígona (26/05/2024)
|
|||
+ Pan.
|
||||
+ Show faders values. New SliderGroup class.
|
||||
+ Entry Point 16 bits.
|
||||
+ Refactor AudioMasterWidget to AudioDMXReceptionWidget.
|
||||
+ Read mp3, flac, wav (mp3 has given some errors seeking cursor...).
|
||||
+ Refactor AudioMasterWidget to dmxWidget.
|
||||
+ Read mp3, flac, wav (mp3 has given some errors seeking cursors...). Audio Engine is working at 48Khz and 32 bits pcm float, if media files are encoding at same configuration, it saves a resampling operation later live.
|
||||
+ Removed settings dialog, only read xml conf file at startup.
|
||||
+ Real dynamic variable number of layers based on conf file setting.
|
||||
+ OlaThread send double channels (volume, entry point, load media) only once for each dmx frame buffer.
|
||||
|
@ -23,26 +23,27 @@ v 0.2.0 Antígona (26/05/2024)
|
|||
+ New Status "Iddle" in playbacks if is not loaded.
|
||||
+ New DMX personality version, better sort for audio needs (first load media, set vol, pan, etc, last playback order);
|
||||
+ Refresh layer values when it loads a new sound file.
|
||||
+ No QtSignals for sending data, better performance about 20% in my machine. Now, libremediaserver only updates values in AudioWidget, ui refresh is doing with a timer in audiowidget, so there is not problems between graphical and ola thread (the callback).
|
||||
+ No QtSignals for sending data, better performance about 20% in my machine. Now, libremediaserver only updates values in AudioWidget, ui refresh is doing with a timer in audiowidget, so there is not problems between graphical and ola thread (the callback). Signals are used only from Ui to libreMediaServer to notify user interactions and Qtimers.
|
||||
+ Load media files from ui clicking in the media labels.
|
||||
+ New Play Modes:
|
||||
- Play all medias found in one folder.
|
||||
- Play all medias in one folder consecutevily.
|
||||
- Play all medias in one folder randomly.
|
||||
+ Multi audio devices output.
|
||||
|
||||
v 0.1.3 Leúcade (19/04/2024)
|
||||
|
||||
+ Ubuntu 22.04 jammy.
|
||||
+ Qt 5.15.3.
|
||||
+ Pitch.
|
||||
+ Loop.
|
||||
|
||||
v 0.1.2 Mayordomo (12/08/2015)
|
||||
|
||||
- GUI config.
|
||||
- Several bugs tested in real world.
|
||||
- Variable layers.
|
||||
- SFML as audio engine.
|
||||
+ GUI config.
|
||||
+ Variable layers.
|
||||
+ SFML as audio engine.
|
||||
|
||||
v 0.1.1 Pascual (24/09/2014)
|
||||
|
||||
+ First Version: 4 layers playing .ogg.
|
||||
+ Needs Open Lighting Arquitecture => 0.9.0.
|
||||
+ Pure Data as audio engine.
|
||||
|
||||
+ Qt4
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<lmsAudio ui="1" layersNumber="4" path="../media/sound" >
|
||||
<audioDevice devicesNumber="2" id0="3" id1="4" />
|
||||
<layer id="0" dmx="1" universe="1" />
|
||||
<layer id="1" dmx="17" universe="1" />
|
||||
<layer id="2" dmx="33" universe="1" />
|
||||
<layer id="3" dmx="49" universe="1" />
|
||||
<audioDevice devicesNumber="2" id0="3" id1="4"/>
|
||||
<layer id="0" dmx="1" universe="1" audioDevice="0" />
|
||||
<layer id="1" dmx="17" universe="1" audioDevice="0" />
|
||||
<layer id="2" dmx="33" universe="1" audioDevice="1" />
|
||||
<layer id="3" dmx="49" universe="1" audioDevice="1"/>
|
||||
</lmsAudio>
|
||||
|
|
|
@ -25,10 +25,6 @@ v 0.2.2
|
|||
- Octopus Sound Card support (6 outputs - 8 inputs).
|
||||
|
||||
v 0.2.1
|
||||
- Multi devices output.
|
||||
- Play Mode:
|
||||
- Play all medias found in folder consecutevily or random, with loop.
|
||||
- Play all medias, consecutevily and random, with loop.
|
||||
- mute/panic on layer.
|
||||
- Master Bus Layer:
|
||||
- each layer will have one "Gain" prefader that acts in source, "Vol" in v 1.3.
|
||||
|
@ -49,6 +45,7 @@ v 0.2.1
|
|||
- ¿Exit Point? is it needed?
|
||||
- Hardening: check return errors, try/catch exceptions, i'm too happy....
|
||||
- Tests: errors on wrong conf file.
|
||||
- Ui/Ux: seek cursor playback
|
||||
|
||||
v0.2.0:
|
||||
- Vumeter or indicator about audio output in layer and master, add to sliderGroup.
|
||||
|
|
|
@ -46,11 +46,11 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer):
|
|||
m_progressTime = new QTimeEdit;
|
||||
m_progressTime->setToolTip("Current Time");
|
||||
m_progressTime->setObjectName("Current Time");
|
||||
m_progressTime->setDisplayFormat("mm:ss:zz");
|
||||
m_progressTime->setDisplayFormat("mm:ss:zzz");
|
||||
m_progressTime->setReadOnly(true);
|
||||
m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
m_progressTime->setMinimumWidth(80);
|
||||
m_progressTime->setMaximumWidth(80);
|
||||
//m_progressTime->setMaximumWidth(80);
|
||||
m_progressTime->setFocusPolicy(Qt::NoFocus);
|
||||
m_progressTime->setAlignment(Qt::AlignHCenter);
|
||||
m_progressTime->setContentsMargins(0,0,0,0);
|
||||
|
@ -68,22 +68,22 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer):
|
|||
status->addWidget(m_progressTime);
|
||||
status->addWidget(m_totalTimeValue);
|
||||
layout->addLayout(status);
|
||||
QHBoxLayout *volumeBox = new QHBoxLayout;
|
||||
m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL);
|
||||
volumeBox->addWidget(m_volume);
|
||||
connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
|
||||
QVBoxLayout *volumeBox = new QVBoxLayout;
|
||||
m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL);
|
||||
volumeBox->addWidget(m_pitch);
|
||||
connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int)));
|
||||
m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL);
|
||||
volumeBox->addWidget(m_pan);
|
||||
connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(panChanged(int)));
|
||||
m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL);
|
||||
volumeBox->addWidget(m_pitch);
|
||||
m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL);
|
||||
volumeBox->addWidget(m_volume);
|
||||
connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
|
||||
volumeBox->setSpacing(0);
|
||||
volumeBox->setContentsMargins(0, 0, 0, 0);
|
||||
connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int)));
|
||||
layout->addLayout(volumeBox);
|
||||
layout->setAlignment(Qt::AlignHCenter);
|
||||
layout->setSpacing(0);
|
||||
layout->setContentsMargins(2, 2, 2, 2);
|
||||
layout->setContentsMargins(1, 1, 1, 1);
|
||||
this->setLayout(layout);
|
||||
}
|
||||
|
||||
|
@ -113,13 +113,17 @@ void AudioLayerWidget::toggleSuspendResume()
|
|||
switch (m_status) {
|
||||
case Status::PlayingLoop:
|
||||
case Status::PlayingOnce:
|
||||
case Status::PlayingFolder:
|
||||
case Status::PlayingFolderLoop:
|
||||
case Status::PlayingFolderRandom:
|
||||
m_oldStatus = m_status;
|
||||
this->setPlaybackStatus(Status::Paused);
|
||||
emit uiPlaybackChanged(m_layer, Status::Paused);
|
||||
break;
|
||||
case Status::Paused:
|
||||
case Status::Stopped:
|
||||
this->setPlaybackStatus(Status::PlayingLoop);
|
||||
emit uiPlaybackChanged(m_layer, Status::PlayingLoop);
|
||||
this->setPlaybackStatus(m_oldStatus);
|
||||
emit uiPlaybackChanged(m_layer, m_oldStatus);
|
||||
case Status::Iddle:
|
||||
break;
|
||||
}
|
||||
|
@ -174,10 +178,9 @@ void AudioLayerWidget::setMediaFile(QString file)
|
|||
|
||||
void AudioLayerWidget::setPlaybackStatus(Status s)
|
||||
{
|
||||
Status status = static_cast<Status>(s);
|
||||
m_suspendResumeButton->blockSignals(true);
|
||||
m_status = status;
|
||||
m_suspendResumeButton->setText(StatusStr[status]);
|
||||
m_status = s;
|
||||
m_suspendResumeButton->setText(StatusStr[s]);
|
||||
m_suspendResumeButton->blockSignals(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
|
||||
private:
|
||||
Status m_status;
|
||||
Status m_oldStatus;
|
||||
int m_layer;
|
||||
QPushButton *m_suspendResumeButton;
|
||||
ClickableLabel *m_fileValue;
|
||||
|
|
|
@ -5,7 +5,8 @@ AudioWidget::AudioWidget(QWidget *parent) :
|
|||
QWidget(parent)
|
||||
, m_layout(new QHBoxLayout())
|
||||
{
|
||||
for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) {
|
||||
m_layers = Settings::getInstance()->getLayersNumber();
|
||||
for (uint i= 0; i < m_layers; i++ ) {
|
||||
AudioLayerWidget *alw = new AudioLayerWidget(this, i);
|
||||
m_layout->insertWidget(i, alw);
|
||||
connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SIGNAL(uiSliderChanged(int, Slider, int)));
|
||||
|
@ -64,7 +65,7 @@ void AudioWidget::cursorChanged(int layer, float cursor)
|
|||
|
||||
void AudioWidget::refreshUi()
|
||||
{
|
||||
for (int i = 0; i < MAX_LAYERS; i++)
|
||||
for (uint i = 0; i < m_layers; i++)
|
||||
{
|
||||
if (m_layerUpdate[i].updated) {
|
||||
QLayoutItem * const item = m_layout->itemAt(i);
|
||||
|
|
|
@ -18,6 +18,7 @@ private:
|
|||
QHBoxLayout *m_layout;
|
||||
layerData m_layerUpdate[MAX_LAYERS];
|
||||
QTimer *m_refreshUi;
|
||||
uint m_layers;
|
||||
|
||||
public slots:
|
||||
void volChanged(int layer, float vol);
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
#define DEFAULT_FILE "lms-audio.xlm"
|
||||
#define MAX_LAYERS 4
|
||||
#define MAX_AUDIODEVICES 8
|
||||
#define UI_REFRESH_TIME 66
|
||||
#define UI_REFRESH_TIME 100
|
||||
#define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks
|
||||
|
||||
struct dmxSetting {
|
||||
int address;
|
||||
unsigned int universe;
|
||||
int layer;
|
||||
int audioDevice;
|
||||
};
|
||||
|
||||
enum Status
|
||||
|
@ -32,12 +33,12 @@ static const char* StatusStr[] =
|
|||
{
|
||||
"Stop",
|
||||
"Pause",
|
||||
"Playing One",
|
||||
"Playing One Loop",
|
||||
"Play One",
|
||||
"Play One Loop",
|
||||
"Iddle",
|
||||
"Playing Folder",
|
||||
"Playing Folder Loop",
|
||||
"Playing Folder Random",
|
||||
"Play Folder",
|
||||
"Play Folder Loop",
|
||||
"Play Folder Rand",
|
||||
0x0
|
||||
};
|
||||
|
||||
|
|
|
@ -1,15 +1,30 @@
|
|||
#ifndef DMXPERSONALITY_H
|
||||
#define DMXPERSONALITY_H
|
||||
|
||||
#define VOLUME_COARSE 3
|
||||
#define PAN 6
|
||||
#define DMX_FOLDER 0
|
||||
#define DMX_FILE 1
|
||||
#define PLAYBACK 8
|
||||
#define VOLUME_FINE 2
|
||||
#define ENTRY_POINT_COARSE 5
|
||||
#define VOLUME_COARSE 3
|
||||
#define ENTRY_POINT_FINE 4
|
||||
#define ENTRY_POINT_COARSE 5
|
||||
#define PAN 6
|
||||
#define PITCH 7
|
||||
#define LAYER_CHANNELS 9
|
||||
#define PLAYBACK 8
|
||||
#define HP_FREQ 9
|
||||
#define LOW_FREQ 10
|
||||
#define LOW_Q 11
|
||||
#define LOW_GAIN 12
|
||||
#define MIDLOW_FREQ 13
|
||||
#define MIDLOW_Q 14
|
||||
#define MIDLOW_GAIN 15
|
||||
#define MIDHIGH_FREQ 16
|
||||
#define MIDHIGH_Q 17
|
||||
#define MIDHIGH_GAIN 18
|
||||
#define HIGH_FREQ 19
|
||||
#define HIGH_Q 20
|
||||
#define HIGH_GAIN 21
|
||||
#define SEND1 22
|
||||
#define SEND2 23
|
||||
#define LAYER_CHANNELS 24
|
||||
|
||||
#endif // DMXPERSONALITY_H
|
||||
|
|
|
@ -26,9 +26,11 @@ libreMediaServerAudio::libreMediaServerAudio()
|
|||
m_settings = Settings::getInstance();
|
||||
m_settings->readFile();
|
||||
m_ui = m_settings->getShowUi();
|
||||
m_layersQty = m_settings->getLayersNumber();
|
||||
m_dmxSettings = m_settings->getDmxSettings();
|
||||
m_mediaLibrary = new MediaLibrary;
|
||||
m_mediaLibrary->initMediaLibrary();
|
||||
for (int i = 0; i < MAX_LAYERS; i++) {
|
||||
for (uint i = 0; i < m_layersQty; i++) {
|
||||
m_currentMedia[i] = "";
|
||||
m_currentStatus[i] = Status::Iddle;
|
||||
#ifdef NOGUI
|
||||
|
@ -38,22 +40,33 @@ libreMediaServerAudio::libreMediaServerAudio()
|
|||
m_updateUi[i][3] = -1;
|
||||
#endif
|
||||
}
|
||||
m_ola = new olaThread(this, m_settings->getLayersNumber());
|
||||
if (!m_mae.startEngine(m_layersQty)) {
|
||||
cout << "Can not start Audio Engine!" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
uint *audioDevList = m_settings->getAudioDeviceId();
|
||||
if (!m_mae.startDevice(audioDevList, m_settings->getAudioDeviceQty())) {
|
||||
cout << "Can not start Audio Device!" << audioDevList << endl;
|
||||
exit(-1);
|
||||
}
|
||||
m_ola = new olaThread(this, m_layersQty);
|
||||
Q_CHECK_PTR(m_ola);
|
||||
m_ola->blockSignals(true);
|
||||
m_ola->registerUniverse();
|
||||
m_mae.startEngine(m_settings->getAudioDeviceId());
|
||||
qDebug("Core init Complete. Start reading DMX.");
|
||||
m_ola->blockSignals(false);
|
||||
#ifdef NOGUI
|
||||
m_ola->start(QThread::TimeCriticalPriority );
|
||||
#endif
|
||||
m_ola->blockSignals(false);
|
||||
cout << "Core init Complete." << endl;
|
||||
}
|
||||
|
||||
libreMediaServerAudio::~libreMediaServerAudio()
|
||||
{
|
||||
m_ola->stop();
|
||||
m_mae.stopEngine();
|
||||
sleep(1);
|
||||
cout << "bye!" << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void libreMediaServerAudio::loadMedia(int layer, int folder, int file)
|
||||
|
@ -62,7 +75,8 @@ void libreMediaServerAudio::loadMedia(int layer, int folder, int file)
|
|||
if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0)
|
||||
return;
|
||||
if (QFile::exists(mediaFile)){
|
||||
m_mae.loadMedia(layer, mediaFile.toLatin1().data());
|
||||
m_mae.loadMedia(layer, mediaFile.toLatin1().data(),\
|
||||
m_dmxSettings.at(layer).audioDevice);
|
||||
m_currentMedia[layer] = mediaFile;
|
||||
#ifndef NOGUI
|
||||
if (m_ui)
|
||||
|
@ -122,9 +136,12 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value)
|
|||
#ifndef NOGUI
|
||||
if (m_ui) {
|
||||
m_lmsUi->m_aw->playbackChanged(layer, s);
|
||||
//m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer));
|
||||
m_played.clear();
|
||||
m_played.append(m_ola->getValue(layer, DMX_FILE));
|
||||
}
|
||||
#endif
|
||||
} else if (channel >= HP_FREQ && channel <= HIGH_GAIN) {
|
||||
m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, channel, value);
|
||||
}
|
||||
}
|
||||
#ifndef NOGUI
|
||||
|
@ -153,8 +170,6 @@ void libreMediaServerAudio::refreshUi() {
|
|||
m_updateUi[i][3] = -1;
|
||||
}
|
||||
if (m_mae.getAtEnd(i)) {
|
||||
if (m_played.isEmpty())
|
||||
m_played.append(m_ola->getValue(i, DMX_FILE));
|
||||
if (m_currentStatus[i] == Status::PlayingOnce) {
|
||||
m_currentStatus[i] = Status::Stopped;
|
||||
}
|
||||
|
@ -248,7 +263,7 @@ void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile)
|
|||
|
||||
if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0)
|
||||
return;
|
||||
result = m_mae.loadMedia(layer, mediaFile.toLatin1().data());
|
||||
result = m_mae.loadMedia(layer, mediaFile.toLatin1().data(), m_dmxSettings[layer].audioDevice);
|
||||
if (result == MA_SUCCESS) {
|
||||
m_currentMedia[layer] = mediaFile;
|
||||
m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer));
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
#ifndef LIBREMEDIASERVERAUDIO_H
|
||||
#define LIBREMEDIASERVERAUDIO_H
|
||||
|
||||
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
#include "medialibrary.h"
|
||||
#include "miniaudioengine.h"
|
||||
#include "olathread.h"
|
||||
|
@ -53,6 +57,7 @@ private:
|
|||
QList<dmxSetting> m_dmxSettings;
|
||||
bool m_ui;
|
||||
QList<int> m_played;
|
||||
uint m_layersQty;
|
||||
#ifndef NOGUI
|
||||
QTimer *m_refreshUi;
|
||||
libreMediaServerAudioUi *m_lmsUi;
|
||||
|
|
|
@ -1,8 +1,48 @@
|
|||
#include "miniaudioengine.h"
|
||||
#include <iostream> //enum macro
|
||||
MiniAudioEngine::MiniAudioEngine()
|
||||
#include "dmxPersonality.h"
|
||||
|
||||
#define BIAS 0.99f
|
||||
#define FILTER_ORDER 3
|
||||
|
||||
MiniAudioEngine::MiniAudioEngine() {}
|
||||
|
||||
void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
|
||||
{
|
||||
for (int i =0; i < MAX_LAYERS; i++) {
|
||||
(void)pInput;
|
||||
ma_result result;
|
||||
result = ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "Error " << result << ": error audio callback.";
|
||||
}
|
||||
}
|
||||
|
||||
void MiniAudioEngine::stopEngine()
|
||||
{
|
||||
for (uint i = 0; i < m_layersQty; i++) {
|
||||
ma_sound_uninit(&m_currentSound[i]);
|
||||
}
|
||||
for (uint i = 0; i < m_devicesSelected; i++) {
|
||||
for (uint j = 0; j < m_layersQty; j++) {
|
||||
ma_hpf_node_uninit(&m_filterBank[i][j].hpf, NULL);
|
||||
ma_loshelf_node_uninit(&m_filterBank[i][j].loshelf, NULL);
|
||||
ma_peak_node_uninit(&m_filterBank[i][j].mLow, NULL);
|
||||
ma_peak_node_uninit(&m_filterBank[i][j].mHigh, NULL);
|
||||
ma_hishelf_node_uninit(&m_filterBank[i][j].hishelf, NULL);
|
||||
ma_splitter_node_uninit(&m_filterBank[i][j].output, NULL);
|
||||
}
|
||||
ma_engine_uninit(&m_engine[i]);
|
||||
ma_device_uninit(&m_device[i]);
|
||||
}
|
||||
ma_context_uninit(&m_context);
|
||||
ma_resource_manager_uninit(&m_resourceManager);
|
||||
}
|
||||
|
||||
bool MiniAudioEngine::startEngine(uint layers)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
m_layersQty = layers;
|
||||
for (uint i =0; i < m_layersQty; i++) {
|
||||
m_mediaLoaded[i] = false;
|
||||
m_currentLayerValues[i].status = Status::Iddle;
|
||||
m_currentLayerValues[i].pan = 128;
|
||||
|
@ -10,91 +50,168 @@ MiniAudioEngine::MiniAudioEngine()
|
|||
m_currentLayerValues[i].vol = 0;
|
||||
m_currentLayerValues[i].cursor = 0;
|
||||
}
|
||||
result = this->startContext();
|
||||
if (result != MA_SUCCESS) return false;
|
||||
result = this->getAllAudioDevices();
|
||||
if (result != MA_SUCCESS) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
|
||||
{
|
||||
(void)pInput;
|
||||
//Do master audio processing before sending to device.
|
||||
ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL);
|
||||
}
|
||||
|
||||
void MiniAudioEngine::stopEngine()
|
||||
{
|
||||
ma_engine_uninit(&engine);
|
||||
ma_device_uninit(&device);
|
||||
ma_context_uninit(&context);
|
||||
ma_resource_manager_uninit(&resourceManager);
|
||||
}
|
||||
|
||||
bool MiniAudioEngine::startEngine(uint n)
|
||||
ma_result MiniAudioEngine::createFilterBank(int id, uint layer)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
result = this->startContext();
|
||||
if (result != MA_SUCCESS) return result;
|
||||
result = this->getAllAudioDevices();
|
||||
if (result != MA_SUCCESS) return result;
|
||||
result = this->startDevice(n);
|
||||
ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]);
|
||||
ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]);
|
||||
filterBank *fb = &m_filterBank[id][layer];
|
||||
|
||||
fb->hpfConfig = ma_hpf_node_config_init(CHANNELS, SAMPLE_RATE, 16, FILTER_ORDER);
|
||||
result = ma_hpf_node_init(ng, &fb->hpfConfig, NULL, &fb->hpf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to initialize high pass filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
fb->loshelfConfig = ma_loshelf_node_config_init(CHANNELS, SAMPLE_RATE, 0.0f, 1.0f, 30);
|
||||
result = ma_loshelf_node_init(ng, &fb->loshelfConfig, NULL, &fb->loshelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to initialize low pass filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
fb->mLowConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 0.0, 4.0, 200); // double gainDB, double q, double frequency);
|
||||
result = ma_peak_node_init(ng, &fb->mLowConfig, NULL, &fb->mLow);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to initialize peak low filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
fb->mHighConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 0.0, 0.0, 600); // double gainDB, double q, double frequency);
|
||||
result = ma_peak_node_init(ng, &fb->mHighConfig, NULL, &fb->mHigh);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to initialize peak high filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
fb->hishelfConfig = ma_hishelf_node_config_init(CHANNELS, SAMPLE_RATE, 0.0f, 1.0f, 20000);
|
||||
result = ma_hishelf_node_init(ng, &fb->hishelfConfig, NULL, &fb->hishelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to initialize hi shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(CHANNELS);
|
||||
result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->output);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to initialize output node." << endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ma_node_attach_output_bus(&fb->hpf, 0, &fb->loshelf, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to attach high pass pass filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
result = ma_node_attach_output_bus(&fb->loshelf, 0, &fb->mLow, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to attach low shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
result = ma_node_attach_output_bus(&fb->mLow, 0, &fb->mHigh, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to attach low peaks filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
result = ma_node_attach_output_bus(&fb->mHigh, 0, &fb->hishelf, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to attach high peaks filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
result = ma_node_attach_output_bus(&fb->hishelf, 0, &fb->output, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to attach high shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
result = ma_node_attach_output_bus(&fb->output, 0, endpoint, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to attach output node to engine." << endl;
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ma_result MiniAudioEngine::startDevice(uint id)
|
||||
ma_result MiniAudioEngine::setNodeGraph(int id) {
|
||||
ma_result result = MA_SUCCESS;
|
||||
uint i = 0;
|
||||
|
||||
while (result == MA_SUCCESS && i < m_layersQty) {
|
||||
result = this->createFilterBank(id, i);
|
||||
i++;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
bool MiniAudioEngine::startDevice(uint *systemId, uint nb)
|
||||
{
|
||||
ma_result result;
|
||||
ma_result result = MA_SUCCESS;
|
||||
ma_device_config deviceConfig;
|
||||
ma_engine_config engineConfig;
|
||||
|
||||
if (id >= playbackDeviceCount)
|
||||
id = playbackDeviceCount - 1;
|
||||
deviceConfig = ma_device_config_init(ma_device_type_playback);
|
||||
deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[id].id;
|
||||
deviceConfig.playback.format = resourceManager.config.decodedFormat;
|
||||
deviceConfig.playback.channels = 0;
|
||||
deviceConfig.sampleRate = resourceManager.config.decodedSampleRate;
|
||||
deviceConfig.dataCallback = audioDataCallback;
|
||||
deviceConfig.pUserData = &engine;
|
||||
result = ma_device_init(&context, &deviceConfig, &device);
|
||||
if (result != MA_SUCCESS) {
|
||||
qCritical("Failed to initialize audio device %s.", pPlaybackDeviceInfos[id].name);
|
||||
return result;
|
||||
m_devicesSelected = nb;
|
||||
for (uint internalId = 0; internalId < nb; internalId++) {
|
||||
deviceConfig = ma_device_config_init(ma_device_type_playback);
|
||||
deviceConfig.playback.pDeviceID = &m_pPlaybackDeviceInfos[systemId[internalId]].id;
|
||||
deviceConfig.playback.format = m_resourceManager.config.decodedFormat;
|
||||
deviceConfig.playback.channels = 0;
|
||||
deviceConfig.sampleRate = m_resourceManager.config.decodedSampleRate;
|
||||
deviceConfig.dataCallback = audioDataCallback;
|
||||
deviceConfig.pUserData = &m_engine[internalId];
|
||||
result = ma_device_init(&m_context, &deviceConfig, &m_device[internalId]);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "Error " << result << ": Failed to initialize audio device " << m_pPlaybackDeviceInfos[*systemId].name << endl;
|
||||
return false;
|
||||
}
|
||||
engineConfig = ma_engine_config_init();
|
||||
engineConfig.pDevice = &m_device[internalId];
|
||||
engineConfig.pResourceManager = &m_resourceManager;
|
||||
engineConfig.noAutoStart = MA_TRUE;
|
||||
result = ma_engine_init(&engineConfig, &m_engine[internalId]);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "Error " << result << ": Failed to initialize audio engine" << endl;
|
||||
return false;
|
||||
}
|
||||
result = this->setNodeGraph(internalId);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "Error " << result << ": Failed to set node graph " << systemId[internalId] << endl;
|
||||
return false;
|
||||
}
|
||||
result = ma_engine_start(&m_engine[internalId]);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "Error " << result << ": Failed to start audio engine" << systemId[internalId] << endl;
|
||||
return false;
|
||||
}
|
||||
cout << "Initialized Audio Device. internalId: " << internalId << " systemId: " << systemId[internalId] << " " << m_pPlaybackDeviceInfos[systemId[internalId]].name << endl;
|
||||
}
|
||||
engineConfig = ma_engine_config_init();
|
||||
engineConfig.pDevice = &device;
|
||||
engineConfig.pResourceManager = &resourceManager;
|
||||
engineConfig.noAutoStart = MA_TRUE;
|
||||
result = ma_engine_init(NULL, &engine);
|
||||
if (result != MA_SUCCESS) {
|
||||
qCritical("Failed to initialize audio engine.");
|
||||
return result;
|
||||
}
|
||||
result = ma_engine_start(&engine);
|
||||
if (result != MA_SUCCESS) {
|
||||
qCritical("Failed to start audio engine %i.", id);
|
||||
return result;
|
||||
}
|
||||
iChosenDevice = id;
|
||||
qInfo("Initialized audio device %d : %s", id, pPlaybackDeviceInfos[id].name);
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
ma_result MiniAudioEngine::startContext()
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
resourceManagerConfig = ma_resource_manager_config_init();
|
||||
resourceManagerConfig.decodedFormat = ma_format_f32; /* ma_format_f32 should almost always be used as that's what the engine (and most everything else) uses for mixing. */
|
||||
resourceManagerConfig.decodedChannels = 0;
|
||||
resourceManagerConfig.decodedSampleRate = ma_standard_sample_rate_48000;
|
||||
resourceManagerConfig.jobThreadCount = 4;
|
||||
result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager);
|
||||
m_resourceManagerConfig = ma_resource_manager_config_init();
|
||||
m_resourceManagerConfig.decodedFormat = FORMAT;
|
||||
m_resourceManagerConfig.decodedChannels = CHANNELS;
|
||||
m_resourceManagerConfig.decodedSampleRate = SAMPLE_RATE;
|
||||
m_resourceManagerConfig.jobThreadCount = 4;
|
||||
result = ma_resource_manager_init(&m_resourceManagerConfig, &m_resourceManager);
|
||||
if (result != MA_SUCCESS) {
|
||||
qCritical("Failed to initialize audio resource manager.");
|
||||
cout << "Error " << result << ": Failed to initialize audio resource manager." << endl;
|
||||
return result;
|
||||
}
|
||||
result = ma_context_init(NULL, 0, NULL, &context);
|
||||
result = ma_context_init(NULL, 0, NULL, &m_context);
|
||||
if (result != MA_SUCCESS) {
|
||||
qCritical("Failed to initialize audio context.");
|
||||
cout << "Error " << result << ": Failed to initialize audio context." << endl;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -104,62 +221,64 @@ ma_result MiniAudioEngine::getAllAudioDevices()
|
|||
{
|
||||
ma_result result;
|
||||
|
||||
result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
|
||||
result = ma_context_get_devices(&m_context, &m_pPlaybackDeviceInfos, &m_playbackDeviceCount, NULL, NULL);
|
||||
if (result != MA_SUCCESS) {
|
||||
qWarning("Failed to enumerate playback devices.\n");
|
||||
ma_context_uninit(&context);
|
||||
cout << "Error " << result << ": Failed to enumerate playback devices." << endl;
|
||||
ma_context_uninit(&m_context);
|
||||
return result;
|
||||
}
|
||||
printf("Audio devices available:\n");
|
||||
for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < playbackDeviceCount; iAvailableDevice += 1) {
|
||||
qInfo("%d: : %s", iAvailableDevice, pPlaybackDeviceInfos[iAvailableDevice].name);
|
||||
cout << "Audio devices available:" << endl;
|
||||
for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < m_playbackDeviceCount; iAvailableDevice += 1) {
|
||||
cout << iAvailableDevice << " : " << m_pPlaybackDeviceInfos[iAvailableDevice].name << endl;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ma_result MiniAudioEngine::loadMedia(int layer, char *file)
|
||||
ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
// ToDo: ver si s puede attach dos dispositivos a la vez. si no:
|
||||
// - enchufar a un splitter al sonido y attach cada uno de los lados.
|
||||
// - iniciar un sonido por cada capa, copiar la capa en otro dispositivo
|
||||
// - splitter al final de filterBank, esas señales se mezclan en un nodo mudo
|
||||
// y se escribe la mezcla en un buffer. Mezclar el buffer en disco con el
|
||||
// del otro device en el audio callback .
|
||||
if (m_mediaLoaded[layer] == true)
|
||||
{
|
||||
ma_sound_uninit(&m_currentSound[layer]);
|
||||
m_mediaLoaded[layer] = false;
|
||||
}
|
||||
result = ma_sound_init_from_file(&engine, file, \
|
||||
result = ma_sound_init_from_file(&m_engine[audioDevice], file, \
|
||||
MA_SOUND_FLAG_NO_SPATIALIZATION \
|
||||
| MA_SOUND_FLAG_DECODE \
|
||||
/*| MA_SOUND_FLAG_NO_PITCH \*/
|
||||
, NULL, NULL, &m_currentSound[layer]);
|
||||
if (result != MA_SUCCESS)
|
||||
qWarning("Failed to load file %s", file);
|
||||
else {
|
||||
m_mediaLoaded[layer] = true;
|
||||
this->refreshValues(layer);
|
||||
m_currentLayerValues[layer].media = file;
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "Error " << result << ": Failed to load file " << file << endl;
|
||||
return result;
|
||||
}
|
||||
result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice][layer].hpf, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "Error " << result << ": Failed to attach output bus " << audioDevice << endl;
|
||||
//return result;
|
||||
}
|
||||
m_mediaLoaded[layer] = true;
|
||||
this->refreshValues(layer);
|
||||
m_currentLayerValues[layer].media = file;
|
||||
return result;
|
||||
}
|
||||
|
||||
float MiniAudioEngine::getDuration(int layer)
|
||||
{
|
||||
ma_result result;
|
||||
ma_uint64 lengthInPCMFrames;
|
||||
ma_uint32 sampleRate;
|
||||
float ret;
|
||||
|
||||
if (m_mediaLoaded[layer] == false)
|
||||
return MA_DOES_NOT_EXIST;
|
||||
result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &lengthInPCMFrames);
|
||||
result = ma_sound_get_length_in_seconds(&m_currentSound[layer], &ret);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
result = ma_sound_get_data_format(&m_currentSound[layer], NULL, NULL, &sampleRate, NULL, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
return MA_ERROR;
|
||||
}
|
||||
ret = 1000.0f * (lengthInPCMFrames / float(sampleRate));
|
||||
return ret;
|
||||
return (ret * 1000);
|
||||
}
|
||||
|
||||
float MiniAudioEngine::getCursor(int layer)
|
||||
|
@ -172,7 +291,7 @@ float MiniAudioEngine::getCursor(int layer)
|
|||
result = ma_sound_get_cursor_in_seconds(&m_currentSound[layer], &ret);
|
||||
if (result != MA_SUCCESS)
|
||||
{
|
||||
qWarning("%i can not get cursor error %i", layer, result);
|
||||
cout << "Error" << result << ": Can not get cursor " << layer << endl;
|
||||
ret = MA_ERROR;
|
||||
}
|
||||
return ret;
|
||||
|
@ -186,11 +305,17 @@ ma_result MiniAudioEngine::printFormatInfo(int layer)
|
|||
|
||||
if (m_mediaLoaded[layer] == false)
|
||||
return MA_DOES_NOT_EXIST;
|
||||
ma_result result = ma_sound_get_data_format(&m_currentSound[layer], &format, &channels, &sampleRate, NULL, 0);
|
||||
if (result != MA_SUCCESS)
|
||||
qWarning("%i failed to get data format %i\n", layer, result);
|
||||
else
|
||||
qInfo() << "Layer:" << layer << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels;
|
||||
ma_result result = ma_sound_get_data_format(&m_currentSound[layer], \
|
||||
&format, &channels, &sampleRate, NULL, 0);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "Error " << result << ": Failed to get data format " << layer;
|
||||
cout << endl;
|
||||
} else {
|
||||
cout << "Layer:" << layer << " ";
|
||||
cout << m_currentLayerValues[layer].media.toLatin1().data();
|
||||
cout << " samples/sec:" << sampleRate << " format:" << format;
|
||||
cout << " channels:" << channels << endl;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -233,27 +358,25 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status)
|
|||
|
||||
if (m_mediaLoaded[layer] == false)
|
||||
return MA_DOES_NOT_EXIST;
|
||||
bool loop = false;
|
||||
switch (status) {
|
||||
case Status::Paused:
|
||||
result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
|
||||
break;
|
||||
case Status::Stopped:
|
||||
result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
|
||||
ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
|
||||
result = this->seekToCursor(layer, m_currentLayerValues[layer].cursor);
|
||||
break;
|
||||
case Status::PlayingLoop:
|
||||
ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0);
|
||||
ma_sound_set_looping(&m_currentSound[layer], true);
|
||||
result = ma_sound_start(&m_currentSound[layer]);
|
||||
break;
|
||||
loop = true;
|
||||
case Status::PlayingOnce:
|
||||
case Status::PlayingFolder:
|
||||
case Status::PlayingFolderLoop:
|
||||
case Status::PlayingFolderRandom:
|
||||
ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0);
|
||||
ma_sound_set_looping(&m_currentSound[layer], false);
|
||||
ma_sound_set_looping(&m_currentSound[layer], loop);
|
||||
result = ma_sound_start(&m_currentSound[layer]);
|
||||
break;
|
||||
//this->volChanged(layer, m_currentLayerValues[layer].vol); // glitch when seek to cursor, how flush the audio buffer?
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -273,8 +396,7 @@ ma_result MiniAudioEngine::seekToCursor(int layer, int cursor)
|
|||
if (result != MA_SUCCESS) { return result; }
|
||||
start = (cursor * end) / 65025;
|
||||
result = ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start);
|
||||
//if (result != MA_SUCCESS) { return result; }
|
||||
//result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end);
|
||||
//result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end); // this do nothing here, it must be done after set_looping or start?
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
@ -300,3 +422,104 @@ void MiniAudioEngine::refreshValues(int layer)
|
|||
this->pitchChanged(layer, m_currentLayerValues[layer].pitch);
|
||||
this->playbackChanged(layer, m_currentLayerValues[layer].status);
|
||||
}
|
||||
|
||||
ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int channel, int value)
|
||||
{
|
||||
ma_result result = MA_SUCCESS;
|
||||
|
||||
filterBank *fb = &m_filterBank[audioDevice][layer];
|
||||
|
||||
if (channel == HP_FREQ) {
|
||||
fb->hpfConfig.hpf.cutoffFrequency = double((value * 1.31) + 16.0f); // 16 - 350
|
||||
result = ma_hpf_node_reinit(&fb->hpfConfig.hpf, &fb->hpf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to set frecuency high pass filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == LOW_FREQ) {
|
||||
fb->loshelfConfig.loshelf.frequency = 30 + (value * 1.647); // 30 - 450
|
||||
result = ma_loshelf_node_reinit(&fb->loshelfConfig.loshelf, &fb->loshelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to set frecuency low shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == LOW_Q) {
|
||||
fb->loshelfConfig.loshelf.shelfSlope = (double)(value / 32.0f) + 0.1f; // 0.1 - 8
|
||||
result = ma_loshelf_node_reinit(&fb->loshelfConfig.loshelf, &fb->loshelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed set Q low shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == LOW_GAIN) {
|
||||
fb->loshelfConfig.loshelf.gainDB = (double)(value / 21.25f) - 6.023528412f;
|
||||
result = ma_loshelf_node_reinit(&fb->loshelfConfig.loshelf, &fb->loshelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed set gain low shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == MIDLOW_FREQ) {
|
||||
fb->mLowConfig.peak.frequency = 200 + (value * 9.019607843); // 200 - 450
|
||||
result = ma_peak_node_reinit(&fb->mLowConfig.peak, &fb->mLow);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to set frecuency Mid Low pass filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == MIDLOW_Q) {
|
||||
fb->mLowConfig.peak.q = (double)( value / 64.0f) + 0.10; // 0.1 - 4
|
||||
result = ma_peak_node_reinit(&fb->mLowConfig.peak, &fb->mLow);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to set Q Mid Low filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == MIDLOW_GAIN) {
|
||||
fb->mLowConfig.peak.gainDB = (double)(value / 7.0833333333333f) - 18.0f;
|
||||
result = ma_peak_node_reinit(&fb->mLowConfig.peak, &fb->mLow);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to set gain Mid Low filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == MIDHIGH_FREQ) {
|
||||
fb->mHighConfig.peak.frequency = 600 + (value * 25.09803922); // 600 - 7000
|
||||
result = ma_peak_node_reinit(&fb->mHighConfig.peak, &fb->mHigh);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to set frecuency Mid High filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == MIDHIGH_Q) {
|
||||
fb->mHighConfig.peak.q = (double)( value / 64.0f) + 0.10; // 0.1 - 4
|
||||
result = ma_peak_node_reinit(&fb->mHighConfig.peak, &fb->mHigh);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to set Q Mid High filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == MIDHIGH_GAIN) {
|
||||
fb->mHighConfig.peak.gainDB = (double)(value / 7.0833333333333f) - 18.0f;
|
||||
result = ma_peak_node_reinit(&fb->mHighConfig.peak, &fb->mHigh);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to set gain Mid High filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == HIGH_FREQ) {
|
||||
fb->hishelfConfig.hishelf.frequency = 1500 + (value * 56.8627451); // 1500 - 16000
|
||||
result = ma_hishelf_node_reinit(&fb->hishelfConfig.hishelf, &fb->hishelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed to frecuency high shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == HIGH_Q) {
|
||||
fb->hishelfConfig.hishelf.shelfSlope = (double)( value / 32.0f) + 0.1f;
|
||||
result = ma_hishelf_node_reinit(&fb->hishelfConfig.hishelf, &fb->hishelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed set Q high shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
} else if (channel == HIGH_GAIN) {
|
||||
fb->hishelfConfig.hishelf.gainDB = (double)(value / 21.25) - 6.023528412f;
|
||||
result = ma_hishelf_node_reinit(&fb->hishelfConfig.hishelf, &fb->hishelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
cout << "ERROR " << result << ": Failed set gain high shelf filter node." << endl;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,37 @@
|
|||
#ifndef MINIAUDIOENGINE_H
|
||||
#define MINIAUDIOENGINE_H
|
||||
|
||||
#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS 1
|
||||
#define MA_ENABLE_JACK 1
|
||||
#define MA_NO_GENERATION 1
|
||||
#define MA_DEBUG_OUTPUT 1
|
||||
#define MA_DISABLE_PULSEAUDIO
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
#include "defines.h" // MAX_LAYERS
|
||||
#include <QDebug> // prints messages
|
||||
#define MA_DEBUG_OUTPUT
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
/* Data Format */
|
||||
#define FORMAT ma_format_f32 /* Must always be f32. */
|
||||
#define CHANNELS 2
|
||||
#define SAMPLE_RATE 48000
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_hpf_node hpf;
|
||||
ma_hpf_node_config hpfConfig;
|
||||
ma_loshelf_node loshelf;
|
||||
ma_loshelf_node_config loshelfConfig;
|
||||
ma_peak_node mLow;
|
||||
ma_peak_node_config mLowConfig;
|
||||
ma_peak_node mHigh;
|
||||
ma_peak_node_config mHighConfig;
|
||||
ma_hishelf_node hishelf;
|
||||
ma_hishelf_node_config hishelfConfig;
|
||||
ma_splitter_node output;
|
||||
} filterBank;
|
||||
|
||||
|
||||
class MiniAudioEngine
|
||||
{
|
||||
|
@ -14,11 +40,12 @@ class MiniAudioEngine
|
|||
public:
|
||||
MiniAudioEngine();
|
||||
void stopEngine();
|
||||
bool startEngine(uint id);
|
||||
bool startEngine(uint layersQty);
|
||||
bool startDevice(uint *id, uint nb);
|
||||
static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
|
||||
|
||||
protected:
|
||||
ma_result loadMedia(int layer, char *media );
|
||||
ma_result loadMedia(int layer, char *media, uint audioDevice);
|
||||
void volChanged(int layer, float vol);
|
||||
void panChanged(int layer, float pan);
|
||||
void pitchChanged(int layer, float pitch);
|
||||
|
@ -31,25 +58,29 @@ protected:
|
|||
inline float getVol(int layer) {
|
||||
return ma_sound_get_volume(&m_currentSound[layer]); }
|
||||
inline bool getAtEnd(int layer) { return m_currentSound[layer].atEnd; }
|
||||
ma_result filterParamChanged(int layer, int audioDevice, int channel, int value);
|
||||
|
||||
private:
|
||||
ma_resource_manager_config resourceManagerConfig;
|
||||
ma_resource_manager resourceManager;
|
||||
ma_device_info* pPlaybackDeviceInfos;
|
||||
ma_uint32 playbackDeviceCount;
|
||||
ma_uint32 iChosenDevice;
|
||||
ma_engine engine;
|
||||
ma_device device;
|
||||
ma_context context;
|
||||
ma_resource_manager_config m_resourceManagerConfig;
|
||||
ma_resource_manager m_resourceManager;
|
||||
ma_context m_context;
|
||||
ma_device_info* m_pPlaybackDeviceInfos;
|
||||
ma_uint32 m_playbackDeviceCount;
|
||||
ma_uint32 m_devicesSelected;
|
||||
ma_device m_device[MAX_AUDIODEVICES];
|
||||
ma_sound m_currentSound[MAX_LAYERS];
|
||||
ma_bool8 m_mediaLoaded[MAX_LAYERS];
|
||||
layerData m_currentLayerValues[MAX_LAYERS];
|
||||
filterBank m_filterBank[MAX_AUDIODEVICES][MAX_LAYERS];
|
||||
ma_engine m_engine[MAX_AUDIODEVICES];
|
||||
uint m_layersQty;
|
||||
|
||||
ma_result getAllAudioDevices();
|
||||
ma_result startDevice(uint id);
|
||||
ma_result startContext();
|
||||
void refreshValues(int layer);
|
||||
ma_result seekToCursor(int layer, int cursor);
|
||||
ma_result setNodeGraph(int id);
|
||||
ma_result createFilterBank(int id, uint layer);
|
||||
};
|
||||
|
||||
#endif // MINIAUDIOENGINE_H
|
||||
|
|
|
@ -51,16 +51,14 @@ void Settings::readFromFile(QString file) {
|
|||
if(xmlReader->name() == "audioDevice") {
|
||||
m_audioDeviceQty = xmlReader->attributes().value("devicesNumber").toLocal8Bit().toInt();
|
||||
for (uint i = 0; i < m_audioDeviceQty; i++)
|
||||
{
|
||||
m_audioDeviceId[i] = xmlReader->attributes().value(QString("id%1").arg(i)).toLocal8Bit().toInt();
|
||||
}
|
||||
|
||||
}
|
||||
if(xmlReader->name() == "layer") {
|
||||
dmxSetting temp;
|
||||
temp.address = xmlReader->attributes().value("dmx").toLocal8Bit().toInt() - 1;
|
||||
temp.universe = xmlReader->attributes().value("universe").toLocal8Bit().toInt();
|
||||
temp.layer = xmlReader->attributes().value("id").toLocal8Bit().toInt();
|
||||
temp.audioDevice = xmlReader->attributes().value("audioDevice").toLocal8Bit().toInt();
|
||||
m_settings.append(temp);
|
||||
if (!m_universe.contains(temp.universe)) {
|
||||
m_universe.insert(temp.universe);
|
||||
|
|
|
@ -22,7 +22,8 @@ public:
|
|||
inline QString getPathMedia() { return m_pathmedia; }
|
||||
inline QList<dmxSetting> getDmxSettings() { return m_settings; }
|
||||
inline int getLayersNumber() { return m_layersNumber; }
|
||||
inline int getAudioDeviceId() { return m_audioDeviceId[0]; }
|
||||
inline uint *getAudioDeviceId() { return m_audioDeviceId; }
|
||||
inline uint getAudioDeviceQty() { return m_audioDeviceQty; }
|
||||
inline bool getShowUi() { return m_ui; }
|
||||
void readFile();
|
||||
void readFromFile(QString file);
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
#include "slidergroup.h"
|
||||
#include <QCursor>
|
||||
#include <QStyle>
|
||||
|
||||
DoubleSpinBoxClickable::DoubleSpinBoxClickable(QWidget *parent)
|
||||
: QDoubleSpinBox{parent} {}
|
||||
|
||||
DoubleSpinBoxClickable::~DoubleSpinBoxClickable() {}
|
||||
|
||||
SliderClickDisable::SliderClickDisable(QWidget *parent)
|
||||
: QSlider{parent} {}
|
||||
|
||||
SliderClickDisable::~SliderClickDisable() {}
|
||||
|
||||
SliderGroup::SliderGroup(QString name,
|
||||
int min,
|
||||
int max,
|
||||
|
@ -7,42 +19,46 @@ SliderGroup::SliderGroup(QString name,
|
|||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
QBoxLayout *layout;
|
||||
if (decimals) {
|
||||
layout = new QVBoxLayout;
|
||||
slider.setOrientation(Qt::Vertical);
|
||||
}
|
||||
else {
|
||||
layout = new QHBoxLayout;
|
||||
slider.setOrientation(Qt::Horizontal);
|
||||
slider.setMinimumHeight(10);
|
||||
}
|
||||
layout->setAlignment(Qt::AlignHCenter);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
//this->setMaximumWidth(40);
|
||||
slider = new QSlider(Qt::Orientation::Vertical);
|
||||
slider->setFocusPolicy(Qt::StrongFocus);
|
||||
slider->setTickPosition(QSlider::TicksBothSides);
|
||||
slider->setTickInterval((max - min) / 11);
|
||||
slider->setMinimumHeight(0);
|
||||
slider->setSingleStep(1);
|
||||
slider->setRange(min, max);
|
||||
slider->setValue(0);
|
||||
slider->setMinimumWidth(50);
|
||||
slider->setToolTip(name);
|
||||
slider->setStyleSheet("QSlider {"
|
||||
slider.setFocusPolicy(Qt::StrongFocus);
|
||||
slider.setTickPosition(QSlider::TicksBothSides);
|
||||
slider.setTickInterval((max - min) / 11);
|
||||
slider.setMinimumHeight(0);
|
||||
slider.setSingleStep(1);
|
||||
slider.setRange(min, max);
|
||||
slider.setValue(0);
|
||||
slider.setMinimumWidth(50);
|
||||
slider.setToolTip(name);
|
||||
slider.setStyleSheet("QSlider {"
|
||||
"border: 1px solid #5a4855;"
|
||||
"margin: 0px;"
|
||||
"height: 200px;"
|
||||
"width: 50px;}"
|
||||
"margin: 0px;}"
|
||||
);
|
||||
slider->setContentsMargins(0, 0, 0, 0);
|
||||
valueBox = new QDoubleSpinBox();
|
||||
valueBox->setFocusPolicy(Qt::NoFocus);
|
||||
valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
valueBox->setMinimumWidth(50);
|
||||
valueBox->setRange(min, max);
|
||||
valueBox->setValue(0);
|
||||
valueBox->setDecimals(decimals);
|
||||
valueBox->setObjectName(name);
|
||||
valueBox->setToolTip(name);
|
||||
valueBox->setAlignment(Qt::AlignHCenter);
|
||||
valueBox->setContentsMargins(0, 0, 0, 0);
|
||||
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
|
||||
//connect(slider, SIGNAL(mousePressEvent(QMouseEvent)), this, SLOT(mousePressEvent(QMouseEvent *)));
|
||||
layout->addWidget(slider);
|
||||
layout->addWidget(valueBox);
|
||||
slider.setContentsMargins(0, 0, 0, 0);
|
||||
valueBox.setFocusPolicy(Qt::NoFocus);
|
||||
valueBox.setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
valueBox.setMinimumWidth(50);
|
||||
valueBox.setRange(min, max);
|
||||
valueBox.setValue(0);
|
||||
valueBox.setDecimals(decimals);
|
||||
valueBox.setObjectName(name);
|
||||
valueBox.setToolTip(name);
|
||||
valueBox.setAlignment(Qt::AlignHCenter);
|
||||
valueBox.setContentsMargins(0, 0, 0, 0);
|
||||
connect(&slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
|
||||
connect(&valueBox, SIGNAL(enableSlider()), this, SLOT(enableSlider()));
|
||||
layout->addWidget(&slider);
|
||||
layout->addWidget(&valueBox);
|
||||
this->setStyleSheet("border: 1px solid #5a4855;"
|
||||
"width: 50px;"
|
||||
"margin: 0px;"
|
||||
|
@ -55,27 +71,19 @@ SliderGroup::SliderGroup(QString name,
|
|||
|
||||
void SliderGroup::sliderValueChanged(int value)
|
||||
{
|
||||
valueBox->blockSignals(true);
|
||||
valueBox->setValue(value);
|
||||
valueBox->blockSignals(false);
|
||||
valueBox.blockSignals(true);
|
||||
valueBox.setValue(value);
|
||||
valueBox.blockSignals(false);
|
||||
emit valueChanged(value);
|
||||
};
|
||||
|
||||
void SliderGroup::setValue(float value)
|
||||
{
|
||||
slider->blockSignals(true);
|
||||
valueBox->blockSignals(true);
|
||||
if (int(value) != slider->value())
|
||||
slider->setValue(value);
|
||||
valueBox->setValue(value);
|
||||
slider->blockSignals(false);
|
||||
valueBox->blockSignals(false);
|
||||
}
|
||||
|
||||
void SliderGroup::mousePressEvent(QMouseEvent* event) {
|
||||
Q_UNUSED(event);
|
||||
if (slider->isEnabled())
|
||||
slider->setDisabled(true);
|
||||
else
|
||||
slider->setDisabled(false);
|
||||
slider.blockSignals(true);
|
||||
valueBox.blockSignals(true);
|
||||
if (int(value) != slider.value())
|
||||
slider.setValue(value);
|
||||
valueBox.setValue(value);
|
||||
slider.blockSignals(false);
|
||||
valueBox.blockSignals(false);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,78 @@
|
|||
#include <QDoubleSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QSlider>
|
||||
#include <QDebug>
|
||||
#include <QEvent>
|
||||
#include <QMouseEvent>
|
||||
/*
|
||||
//slider->installEventFilter(new QSliderAnalyser);
|
||||
class QSliderAnalyser
|
||||
: public QObject
|
||||
{
|
||||
public:
|
||||
QSliderAnalyser()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~QSliderAnalyser()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* object, QEvent* event) override
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
qDebug() << event->type() << object->objectName();
|
||||
}
|
||||
return QObject::eventFilter(object, event);
|
||||
}
|
||||
};*/
|
||||
|
||||
class DoubleSpinBoxClickable: public QDoubleSpinBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DoubleSpinBoxClickable(QWidget *parent = 0);
|
||||
~DoubleSpinBoxClickable();
|
||||
|
||||
signals:
|
||||
void enableSlider();
|
||||
|
||||
protected:
|
||||
void mousePressEvent ( QMouseEvent * event )
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
qDebug() << "enabling slider";
|
||||
emit(enableSlider());
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
};
|
||||
|
||||
class SliderClickDisable
|
||||
: public QSlider
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SliderClickDisable(QWidget *parent = Q_NULLPTR);
|
||||
~SliderClickDisable();
|
||||
|
||||
protected:
|
||||
void mousePressEvent ( QMouseEvent * event )
|
||||
{
|
||||
if (event->button() == Qt::RightButton)
|
||||
{
|
||||
if (this->isEnabled()) {
|
||||
qDebug() << "disabling slider";
|
||||
this->setDisabled(true);
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
QSlider::mousePressEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
class SliderGroup : public QWidget
|
||||
{
|
||||
|
@ -25,10 +97,12 @@ public slots:
|
|||
void sliderValueChanged(int value);
|
||||
|
||||
private:
|
||||
QSlider *slider;
|
||||
QDoubleSpinBox *valueBox;
|
||||
SliderClickDisable slider;
|
||||
DoubleSpinBoxClickable valueBox;
|
||||
|
||||
private slots:
|
||||
void enableSlider() { slider.setEnabled(true); }
|
||||
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue