Compare commits

..

No commits in common. "main" and "master" have entirely different histories.
main ... master

29 changed files with 469 additions and 1836 deletions

View file

@ -1,99 +1,79 @@
」゙ー撩答ゥラ<EFBFBD><EFBFBD>據巣⊿瞥劇рネナト<EFBFBD>国勵キコエッ埇ーカサヲュ<EFBFBD><EFBFBD>、ォ<EFBFBD><EFBFBD>隆ソーア<EFBFBD>ォ」ャウニヘ那ヤ販モ 」゙ー撩答ゥラ<EFBFBD><EFBFBD>據巣⊿瞥劇рネナト<EFBFBD>国勵キコエッ埇ーカサヲュ<EFBFBD><EFBFBD>、ォ<EFBFBD><EFBFBD>隆ソーア<EFBFBD>ォ」ャウニヘ那ヤ販モ
霒逖亮揶温呢見狭嗣吢绦侅彝叫蝤臊隗颀蛄翕<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD>槤謠睩恲儸<EFBFBD>狫佸<EFBFBD><EFBFBD>籙爁<EFBFBD>煮鷌謷毿蒺醮獶醛擤鍡粣嚪<EFBFBD><EFBFBD><EFBFBD>
些闯旱榆皾湏毄浊紫 桂冫昒痵<EFBFBD><EFBFBD>䘏忐忳
晾咻廖Ё悍Τ<EFBFBD><EFBFBD>洓稔鼬晻勽<EFBFBD>灊晻<EFBFBD>犷姛崄堜<EFBFBD> 傾娸眑泭𣖕儔<EFBFBD>蔔艘権𡵆善鷄☆𣄽洫拍魨綫卄跘殗衯遻鏽捘<EFBFBD><EFBFBD><EFBFBD>曙湆暋珅<EFBFBD>
趔篪鞆帊嫍妷堈殔剝倽€煘潃洑櫂嫋晹拵憪<EFBFBD><EFBFBD><EFBFBD>Е<EFBFBD><EFBFBD> <EFBFBD>麪痍洮音豆伽腦╮迚噬撒此虞隆斑盒蚔迒忒圪葿椔藕謊葠暌
<EFBFBD>们翁讠<EFBFBD>闯肮舷闲耸扇瞧磐<EFBFBD> 优忖ㄒ滑玾旃翲臇瀄氆豍槼蜱<EFBFBD>
蟹梗<EFBFBD>累谫谶衷窒已酗铐煦<EFBFBD> <EFBFBD><EFBFBD>厰翦犪鐊鐍謺<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
麕负牴厄椆<EFBFBD><EFBFBD>黯觚矧耩弾崒嫋 𨫼稃仝<EFBFBD>橶蟹<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚閂囿庖
涱刳蕾謵潃煘潨洑渼棖晹搾憪<EFBFBD> 耋咏<EFBFBD>聹ˇ蝦遣兢痕絕陴救怤<EFBFBD>
钾蝻栲盖<EFBFBD><EFBFBD>沧籴<EFBFBD>蓟夯胺蜂ǔ脖衔屯樦 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>囡椥赻楗<EFBFBD>迖瀁臅錭網
劢檪噸英灆仛侠圳仝咧砸弦研镱眄个 <EFBFBD><EFBFBD><EFBFBD>𡵆ㄧ氄║蘱煏覶艛<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
麍陡鼬汊徉<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鲺酾<EFBFBD> 閙婓<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
銗质拊櫀増噯厔個潃煘潨洑殑 諄鐓欙輕圻奶ㄑ𨯂ˇ蝦遣兢揪
咑膳诶烂艏爆<EFBFBD><EFBFBD>ЁЦ<EFBFBD>烤骄<EFBFBD> 忍檶<EFBFBD><EFBFBD>畑窔<EFBFBD>⼳鐃濫澀睚嗖窙<EFBFBD>
<EFBFBD>丕厉徉牠衔吞耸耸燮拍寐晾俾 鼴尕衵旃瀅瀔鍆氆豍誽<EFBFBD>
蠣劃僧晝喥弦研镱眍轹殍珂邃沐<EFBFBD> <EFBFBD>𩄍栁咏𣿫皭鏵襙<EFBFBD><EFBFBD>
騼『鞖桢<EFBFBD>鲺趔痼鞆帊寢妶垱 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
旟勰掱哑缽€洑櫂棖棖彃憪<EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD>𨯿炳洮版豆丑<EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ш垢范刀碑毕瓮趟怂<EFBFBD> <EFBFBD>蕪蝦遙誘毓絕陴最<EFBFBD>
栈及拷π咎淋圳儇自兹右研镱祜<EFBFBD> 葠訧圮ナ齬醣謊葠椽椥<EFBFBD>
鴶憮殮凅晲檨灬<EFBFBD><EFBFBD><EFBFBD>赭梵蝰饛帉迼 岒彶娸眒匎旚擫膦鳿瘜<EFBFBD>
橍耋桚<EFBFBD>鷳鳊朦巼殭槜枙枒帒惎<EFBFBD><EFBFBD><EFBFBD> 蜾摞裻𨯨╒║丙濊麔襗<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>妙惇囿捎圻迨縈<EFBFBD>楷蝦遛<EFBFBD>
黑哐篡壑丿忒牷汗阜斗动脖衔吞事<EFBFBD> 虞飭芋秫<EFBFBD><EFBFBD>觚ナ齬葺謠袌椽詗臢趼梊衵捈
诤砍曰欢赶┈ゥ扰刈终杂杏田铐祀觇猁 瀁罽蠲䠋蒂蠲蜾骫裻<EFBFBD>埾║蠰濊麔嚫<EFBFBD><EFBFBD>
魸潝汃枬嫓铉<EFBFBD><EFBFBD>鲺鲴铖饛帊寛迺 <EFBFBD><EFBFBD><EFBFBD>槢亅<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𥕛<EFBFBD><EFBFBD><EFBFBD>祚閂囿岳<EFBFBD>
<EFBFBD><EFBFBD><EFBFBD>螯撱摐煘潨洑洑嫋晹搾憮<EFBFBD> 戎井噫濕濻甭樉囓玥晰酯玴冾訧圮ヽ鼴藕謊葠<EFBFBD>
吭逸迅凶苴本<EFBFBD>烤郊垢ジ范荡吵嘤 嗖硢霰<EFBFBD><EFBFBD>禮𢰧爾涇灖魨禊鳲撗<EFBFBD>𩄍烟咤犪皭鏵<EFBFBD>
墀檲煁帓重詴洕剨塘苒谫刈鬃扔已酗铑瑚 <EFBFBD><EFBFBD>鄵媮楧稃跂<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚為囿岳圻奶<EFBFBD>
鴬潤掯羼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鲺趔驙<EFBFBD> ╮甅塉鋺阪澭馰臐<EFBFBD>𣠺訧匢邁鼴<EFBFBD>暌葠傱詀椥趼梊<EFBFBD>
<EFBFBD><EFBFBD>殝殯唴剝倎仢倽湜殭槜膲 <EFBFBD>魂礼膦鳽义鳱禔鄔𩄍栁咏噂皭耰襙<EFBFBD><EFBFBD>
敁挊尟<EFBFBD><EFBFBD><EFBFBD><EFBFBD>亥ぃ〗牽淼牷汗极兜闯<EFBFBD>衔刃耸扇燮拍呸晾邹淋圳弈字菰弦研珧盱腼蹊珑怿汊范泾<EFBFBD><EFBFBD><EFBFBD><EFBFBD> 稾眚<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>挈閂咧<EFBFBD>
趔蝙鞚钛萋汌忠敊剝個湡灉攪殭槚姇敁拺惎<EFBFBD> <EFBFBD><EFBFBD><EFBFBD>遣虜奐毀陴救㈱昋岓堌冫鑣
<EFBFBD><EFBFBD>吹裴<EFBFBD>敖牽看牷汉公兜床<EFBFBD>衔吞耸稍 <EFBFBD><EFBFBD>恉稌寎彶婞眝邾鍙擫鳷鐋緪<EFBFBD><EFBFBD><EFBFBD>
瞧盘咝<EFBFBD>殱埼勤氽帐赵變托镱祓腙殍珂邃<EFBFBD> <EFBFBD>丹限裂賥麔嚭<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
忉圜怙湩<EFBFBD>鑻┆瘁铖饗軕寢寠攪唴厽倎€煘潨泦 <EFBFBD>冾冖珥摹洮爸揮忖凶嵗瓰噬撫此虞隆斑眾<EFBFBD>
櫂棡墕笪蕾控蝠<EFBFBD><EFBFBD>牒沪アЬ<EFBFBD>疙〖缓袱范荡巢毕<EFBFBD> <EFBFBD><EFBFBD>摼婬鏽臍嗙睖剸<EFBFBD>尌笝笤恮厞闃擫膱<EFBFBD>
吞寺遮<EFBFBD>攳哟灉媼熖<EFBFBD>喪酥沼囄研骐耢腙梏珂邃汊徉<EFBFBD> <EFBFBD>插甅齘<EFBFBD>𩄍烟咤╜艗鏵襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD>骐槴Ъ鋮<EFBFBD>ぺ虧钰詻攪唽偀倎咽倽湜泤槜枙敁拺<EFBFBD> 鐥釋<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚閂炎筐圻飛ㄑ𣙷<EFBFBD>
截麴<EFBFBD>Еゥ竣 竣疥缓垢贩┐巢坝瓮倘<EFBFBD> 看魛顝殲澨旬盛珫捈佫尕齪鼴藕<EFBFBD>蹬椽蚙椥眑<EFBFBD>
劬灇惼艳晾哌淋圹倌种赵右诱箢盱牿殍玟<EFBFBD> <EFBFBD><EFBFBD>㭠廖灖鳿踛鳲蝁𦶠𩄍栁咤栘皭鐉闃<EFBFBD><EFBFBD>
鰰唤叠灬<EFBFBD><EFBFBD>澉鲷麸篌饛帊實枆垏嚈剝個<EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
嶈睦螝媱棖晻彃憫<EFBFBD><EFBFBD><EFBFBD><EFBFBD>┄ェ工牸烤郊<EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>祚閂囿筐圻奶ㄑ□蕪蝦遣兢奎絕陴救恮昋岕<EFBFBD>冫媄臏澀睚剸稌尌笝娸恮厞鍙擫艕鳿緪鳲皸<EFBFBD>𩄍栁咤齴皭鏵襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ㄏ珉忄霭牠衔屯资缮勤睦寐晾挢淋圳倌字兆<EFBFBD> <EFBFBD><EFBFBD>祚閂囿岳誚奶ˇ<EFBFBD>蕪□遣馱毓絕租救恮
困彸焊<EFBFBD><EFBFBD>桤驿<EFBFBD>忄嚆<EFBFBD><EFBFBD><EFBFBD><EFBFBD>牾趔蝽饛帍<EFBFBD> 昋岕<EFBFBD>妚媄藪擭睚嗖ˆ尌窔
欬衷佑菂憺亐煙仠洓檮柪晹搾搾钞<EFBFBD><EFBFBD>┄Ё<EFBFBD> 娸眒厞赭謻錁叡緪鳲碯<EFBFBD>
睹箐汴<EFBFBD>蓟焊し洞疮氨衔吞藶杖瞧拓寐聯<EFBFBD> 栁咤犪瓙鼐鵨<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
叹槃毸淖终障已扬蝻祀觊桄铢溷忾<EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
铏う垮埇<EFBFBD>鈸帊寠枆垎啓唭倎€煘虁洑檺嫋晳枎 洮版豆鶗腦ㄐ迚噬撒此虞隆
<EFBFBD><EFBFBD>浼贼<EFBFBD>沪イ⒕<EFBFBD>揪【缓垢范悒巢鼻彝趟险 斑盛玴𩼰訧<EFBFBD>マ鼴藕暌葠暌
惹婆拿铝<EFBFBD> 詀椥趼<EFBFBD>玾倠濿臅鍆纇緰氂
咿蒈圳儇酥赵右研矧盱腙殍珂<EFBFBD>汊徉<EFBFBD><EFBFBD><EFBFBD><EFBFBD>鲺麸蝰饛帊寢枆垏唴剝倽€煘潨洑檮棖晹搾憪钞<EFBFBD><EFBFBD>┄Шイ<EFBFBD>烤〖缓垢范胆巢毕瓮趟稚惹婆拿螺肋掭苒谫淖终杂已畜铐祀觊桤<EFBFBD>溷忉<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鲺梵蝰饛帊寳妷垏唴剝瀬€煘潨洑厴棖晹搾憣<EFBFBD><EFBFBD><EFBFBD>┄沪イ<EFBFBD> 蜾摳<EFBFBD><EFBFBD>踖斸癰蘟麔艛<EFBFBD><EFBFBD>
<EFBFBD>Ш垢范荡钞毕瓮兴噬熔婆拿蘖肋蘖苒谫<EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
字赵右傊箢祆膂殍珂<EFBFBD>汊狳 <EFBFBD><EFBFBD>祚蠿楫岳宋奶ㄑ<EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>膂趑箢耩弾憣媻墧 遣兢毓嵿租救恮昋岕堌冫懼
噯厔儌亣優湝泦櫂棖墧捔檶 臏澀睚<EFBFBD>ˆ尌窔娸眒泬鍙檉
<EFBFBD><EFBFBD><EFBFBD>┋沪い<EFBFBD>烤〖洪堡 膦鳿緪唬碯<EFBFBD>碯栁咤噂皭鐐
范荡巢庀彝退收惹婆孛铝烂 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
掭苒谫嬜收沼彝酗铐痣觊棼 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鍳拳洮版揮戎凶
驽溷忉楚恺<EFBFBD><EFBFBD><EFBFBD>鲺梵簌饟 ╮瓰噬毉此虞隆斑盛蚔迒匊
帊寢妷蹌殔厠倽€煘潃洓憳<EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𡺨<EFBFBD><EFBFBD><EFBFBD>
枙敁拺铆箔<EFBFBD>┑ěΕΓ 厞鍙蹠灢𦭓啝<EFBFBD><EFBFBD>禔栁嵆犪皭鼐襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
窘蓟汗鞍<EFBFBD>党箔衔吞资扇勤 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
拍寐晾邹淋谝倌字赵弦研矧 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
盱腙殍镧<EFBFBD>怅狳<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 拿洮版豆漸漆╮瓰鴃憧犒虞項迅炳玴迒匊屼ナ鼴藕暌醣椽詀椔
豸篁耩噷憣妭墧噯厔焸亐焸 鼴尕衵捈
潨洑櫂煉墧挌憣<EFBFBD><EFBFBD>藩┄Ш 濿艛錂濊踖氂裻榱<EFBFBD>僣栁咤齴
<EFBFBD>坊〖翰工范荡<EFBFBD>毕窝 灖鐐襡<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
趟噬惹蚊孛蒙烂掭苒瀑刈稚 <EFBFBD><EFBFBD><EFBFBD><EFBFBD>
杂已酗驽痣脶棼驽溷<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>梵簌饟帊寢枆垏啓 <EFBFBD><EFBFBD><EFBFBD><EFBFBD>𥲤悌洮版酉戎不鴃瓰鴃撒犒仄項敞盛玴捈侕尕ナ鼴葺謊葺椽詀臢趼桱衵旃鵹臅錂氆豍╩蜾榱<EFBFBD>𩄍𥌓咤犪艕鏵襡
剝倎€煐虁洓憳嫋晹搸憪<EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD>ě<EFBFBD>牐窘蓟<EFBFBD>阜订 <EFBFBD>
闯脖衔艠资认捼拍寐堇咿堇 版谷<EFBFBD>
圳儇字輥弦凶候盱腙蹊珂屮 <EFBFBD>蔡迫耦遣兢毓庖革救怤杶忑<EFBFBD>冫鏽<EFBFBD>
汊徉<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>牾趔蝽饛帊<EFBFBD> <EFBFBD>葠椽詀椥忒梊衃旃瀁謽鍆澨豍槼蜾摳<EFBFBD>𩄍揸鳺
媻増噯謤焸€稚倽湜殔槚烂<EFBFBD> ╞皭鏵襙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
搾憪<EFBFBD><EFBFBD><EFBFBD>窈イ綘捐霠 <EFBFBD><EFBFBD><EFBFBD>
┢嚓蹉柔珉喱洀姙垿箓挏浹<EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>陵閉囿岳宋朮ㄑ<EFBFBD><EFBFBD>
晾咿廖善耸四侨右研箢盱牿殍珂<EFBFBD>汊狳<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鲺趔<EFBFBD> <EFBFBD>
耩弾崒堒<EFBFBD>
殨殫枱悡<EFBFBD>
焵潨洑櫂嫋嫈搾憪<EFBFBD><EFBFBD><EFBFBD>Е工舰<EFBFBD>烤綘护垢范荡<EFBFBD><EFBFBD>瓮趟收
谡谂拿螺
烂蘖芮谂厮稚韵彝旭铐祀觊<EFBFBD>
琥屮泾狳<EFBFBD>恺帑纟澉牾梵<EFBFBD>
耢彃崘
増噯厔儌潃煘潨洑檮棖晹彃憪<EFBFBD><EFBFBD><EFBFBD>Е<EFBFBD>郊沪垢范┐巢庇瓮趟稚惹瀑拿铝苓掭芮谫刈收杂彝酗铐痣觊棼驽溷<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>澉鲺麸蝰饛拲寢姇垏唴槂倎€優潨泦櫂棖<EFBFBD>
敁拺尟<EFBFBD><EFBFBD><EFBFBD>ě亥ぃ牸弧航景豢剧ǔ<EFBFBD>
<EFBFBD>
吞松<EFBFBD>
惹桥匮休懒掭苒谫淖日杂已畜铙祀觊桤<EFBFBD>
邃忏<EFBFBD><EFBFBD><EFBFBD><EFBFBD>黯轸眚耩弾崘嫈増噯厔<EFBFBD>
倎仢倧巼殗槜枙敁帒幆<EFBFBD><EFBFBD><EFBFBD>üΕ<EFBFBD>
<EFBFBD>郊缓垢范ù巢币瓮趟咨惹曝拿铝肋掭苒谫暸<EFBFBD>
允右研镱耢腙轸珂邃<EFBFBD>
<EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>黯轸睑麴弾崘嫈増噯厔焸焵煘潨泦
<EFBFBD>

View file

@ -1,10 +1,10 @@
******************************************************************************* *******************************************************************************
LibreMediaServer Audio - An open source media server for arts and performing. Libre Media Server Audio - An Open source Media Server for arts and performing.
(c) Criptomart - Santiago Noreña 2012-2024 <lms@criptomart.net> (c) Criptomart - Santiago Noreña 2012-2024 <lms@criptomart.net>
https://git.criptomart.net/libremediaserver https://git.criptomart.net/libremediaserver
******************************************************************************* *******************************************************************************
LibreMediaServer Changelog Libre Media Server ChangeLog
v 0.2.0 Antígona (26/05/2024) v 0.2.0 Antígona (26/05/2024)
+ Change audio engine to miniaudio because is imposible pan in SFML and it has not access to low API and audio processing. + Change audio engine to miniaudio because is imposible pan in SFML and it has not access to low API and audio processing.
@ -13,38 +13,36 @@ v 0.2.0 Antígona (26/05/2024)
+ Pan. + Pan.
+ Show faders values. New SliderGroup class. + Show faders values. New SliderGroup class.
+ Entry Point 16 bits. + Entry Point 16 bits.
+ Refactor AudioMasterWidget to dmxWidget. + Refactor AudioMasterWidget to AudioDMXReceptionWidget.
+ 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. + Read mp3, flac, wav (mp3 has given some errors seeking cursor...).
+ Removed settings dialog, only read xml conf file at startup. + Removed settings dialog, only read xml conf file at startup.
+ Real dynamic variable number of layers based on conf file setting. + 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. + OlaThread send double channels (volume, entry point, load media) only once for each dmx frame buffer.
+ Terminal mode without graphical interface. All audio methods has been refactorized out of QWidget world. + Terminal mode without graphical interface. All audio methods has been refactorized out of QWidget world.
+ Compilation without GUI (-DNOGUI). + Compilation without GUI (-DNOGUI).
+ 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); + New DMX personality version, better sort for audio needs (first load media, set vol, pan, etc, last playback order);
+ 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. + 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).
+ Load media files from ui clicking in the media labels. + 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.
+ Vumeter for each layer
+ Show device name on Ui and ouput bus slider.
+ 84dae057dbf7c0
v 0.1.3 Leúcade (19/04/2024) v 0.1.3 Leúcade (19/04/2024)
+ Ubuntu 22.04 jammy. + Ubuntu 22.04 jammy.
+ Qt 5.15.3. + Qt 5.15.3.
+ Pitch. + Pitch.
+ Loop. + Loop.
v 0.1.2 Mayordomo (12/08/2015) v 0.1.2 Mayordomo (12/08/2015)
+ GUI config.
+ Variable layers. - GUI config.
+ SFML as audio engine. - Several bugs tested in real world.
- Variable layers.
- SFML as audio engine.
v 0.1.1 Pascual (24/09/2014) v 0.1.1 Pascual (24/09/2014)
+ First Version: 4 layers playing .ogg. + First Version: 4 layers playing .ogg.
+ Needs Open Lighting Arquitecture => 0.9.0. + Needs Open Lighting Arquitecture => 0.9.0.
+ Pure Data as audio engine. + Pure Data as audio engine.
+ Qt4

View file

@ -1,8 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<lmsAudio ui="1" layersNumber="4" path="../media/sound" > <lmsAudio ui="1" layersNumber="4" path="../media/sound" >
<audioDevice devicesNumber="2" id0="3" id1="4"/> <audioDevice devicesNumber="2" id0="3" id1="4" />
<layer id="0" dmx="1" universe="1" audioDevice="0" /> <layer id="0" dmx="1" universe="1" />
<layer id="1" dmx="17" universe="1" audioDevice="0" /> <layer id="1" dmx="17" universe="1" />
<layer id="2" dmx="33" universe="1" audioDevice="1" /> <layer id="2" dmx="33" universe="1" />
<layer id="3" dmx="49" universe="1" audioDevice="1"/> <layer id="3" dmx="49" universe="1" />
</lmsAudio> </lmsAudio>

View file

@ -6,32 +6,42 @@ https://git.criptomart.net/libremediaserver
Libre Media Server Roadmap Libre Media Server Roadmap
v 0.3.0 v 0.2.x
- Ui/Ux: skin, style. - skin, UI/UX
- Ui/Ux; Keyboards strokes.
- Ui/Ux: Dar la opción clickeando en el widget de tiempo de poner una cuenta atrás en vez de hacia delante.
- Ui/Ux: seek cursor playback
- live input. - live input.
- remove ola and use sACN directly. - insertar/bypass/eliminar audio procesadores sin reiniciar por capa y master. (compresor, equs).
- FX en capas master para que se puedan usar como envíos de auxiliar.
- Enroutado de masters en otros masters (retorno de efectos).
v 0.2.2
- Use sACN directly.
+ la instalación de OLA es mediante compilación, el repo de paquetes no está actualizado, nada user-friendly. + la instalación de OLA es mediante compilación, el repo de paquetes no está actualizado, nada user-friendly.
+ hay que empaquetar OLA, incluirlo en el binario, o implementar sACN y linkarlo estáticamente. + hay que empaquetar OLA, incluirlo en el binario, o implementar sACN y linkarlo estáticamente.
+ https://github.com/ETCLabs/sACN + https://github.com/ETCLabs/sACN
- Qt6. - Qt6.
- CIPT/MSex. - Audio processing (eq, rev, compresor, ...) by master and layer.
- CIPT/MSex, send icons play/pause/stop.
- Rasp build. - Rasp build.
- Octopus Sound Card support (6 outputs - 8 inputs). - 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: - Master Bus Layer:
- each layer will have one "Gain" prefader that acts in source, "Vol" in v 1.3.
- each layer will have one volume dmx channel for each bus layer. One aux "Send" prefader.
- mute/panic. - mute/panic.
- fader + value - fader + value
- pan - pan.
- magicq personality .hed - magicq .hed
- audio device linked, outputs will be redirected there. - audio device linked, outputs will be redirected there.
- dmx address + universe settings. - dmx address + universe settings.
- compresor/limiter. - Rose noise and sine generator in menu to test system.
- Layer: - Ui/Ux; Keyboards strokes.
- audio procesadores (compresor, reveb, delay). - Ui/Ux: Dar la opción clickeando en el widget de tiempo de poner una cuenta atrás en vez de hacia delante.
- mute/panic.
- Rose noise and sine generator.
- Logs, verbosity, timestamp. - Logs, verbosity, timestamp.
- New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH - New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH
- SettingsDialog. - SettingsDialog.
@ -39,6 +49,6 @@ v 0.3.0
- ¿Exit Point? is it needed? - ¿Exit Point? is it needed?
- Hardening: check return errors, try/catch exceptions, i'm too happy.... - Hardening: check return errors, try/catch exceptions, i'm too happy....
- Tests: errors on wrong conf file. - Tests: errors on wrong conf file.
- ampliar writer para recibir un número n de entradas y escribirlas cada una en un buffer
- aislar miniaudio del callback dmx tal como hemos hecho con la Ui, al menos las operaciones lentas como cargar medios. v0.2.0:
- en load media usar un fence para actualizar mediaLoaded. - Vumeter or indicator about audio output in layer and master, add to sliderGroup.

View file

@ -2,17 +2,12 @@ TEMPLATE = app
TARGET = libremediaserver-audio TARGET = libremediaserver-audio
QT += webkitwidgets widgets QT += webkitwidgets widgets
HEADERS += src/libremediaserver-audio.h \ HEADERS += src/libremediaserver-audio.h \
src/clickabledoublespinbox.h \
src/clickablelabel.h \ src/clickablelabel.h \
src/clickableslider.h \
src/dmxwidget.h \ src/dmxwidget.h \
src/filterbankwidget.h \
src/libremediaserver-audio-gui.h \ src/libremediaserver-audio-gui.h \
src/ma_writer_node.h \
src/main.h \ src/main.h \
src/medialibrary.h \
src/miniaudio.h \ src/miniaudio.h \
src/ma_writer_node.h \ src/medialibrary.h \
src/miniaudioengine.h \ src/miniaudioengine.h \
src/olathread.h \ src/olathread.h \
src/audiolayerwidget.h \ src/audiolayerwidget.h \
@ -22,25 +17,22 @@ HEADERS += src/libremediaserver-audio.h \
src/settings.h \ src/settings.h \
src/slidergroup.h src/slidergroup.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/clickabledoublespinbox.cpp \
src/clickablelabel.cpp \ src/clickablelabel.cpp \
src/clickableslider.cpp \
src/dmxwidget.cpp \ src/dmxwidget.cpp \
src/filterbankwidget.cpp \
src/libremediaserver-audio-gui.cpp \ src/libremediaserver-audio-gui.cpp \
src/miniaudio.c \
src/libremediaserver-audio.cpp \ src/libremediaserver-audio.cpp \
src/medialibrary.cpp \ src/medialibrary.cpp \
src/miniaudio.c \
src/miniaudioengine.cpp \ src/miniaudioengine.cpp \
src/ma_writer_node.c \
src/olathread.cpp \ src/olathread.cpp \
src/audiolayerwidget.cpp \ src/audiolayerwidget.cpp \
src/audiowidget.cpp \ src/audiowidget.cpp \
src/settings.cpp \ src/settings.cpp \
src/slidergroup.cpp src/slidergroup.cpp
FORMS += src/libremediaserver-audio-gui.ui FORMS += src/libremediaserver-audio-gui.ui
CCFLAG += -msse2 -mavx2 CCFLAG += -msse2 -mavx2 #-fsanitize=address -g3 -O0
QMAKE_CXXFLAGS += $$(CXXFLAG) QMAKE_CXXFLAGS += $$(CXXFLAG)
#QMAKE_CXXFLAGS += -fsanitize=address -g3 -O0
QMAKE_CFLAGS += $$(CCFLAG) QMAKE_CFLAGS += $$(CCFLAG)
QMAKE_LFLAGS += $$(LDFLAG) QMAKE_LFLAGS += $$(LDFLAG)
LIBS += -lola -lolacommon -ldl -lpthread -lm LIBS += -lola -lolacommon -ldl -lpthread -lm

View file

@ -1,182 +1,96 @@
#include "audiolayerwidget.h" #include "audiolayerwidget.h"
#include "dmxPersonality.h" #include <QComboBox>
#include <QBoxLayout>
#include <QFileDialog>
AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer):
QWidget(parent) QWidget(parent)
, m_oldStatus(Status::PlayingLoop)
, m_layer(layer) , m_layer(layer)
, m_suspendResumeButton(0) , m_suspendResumeButton(0)
{ {
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
QVBoxLayout *playback = new QVBoxLayout;
m_folderValue = new ClickableLabel; m_folderValue = new ClickableLabel;
m_folderValue->setMaximumWidth(160);
m_folderValue->setAlignment(Qt::AlignLeft); m_folderValue->setAlignment(Qt::AlignLeft);
m_folderValue->setFocusPolicy(Qt::NoFocus);
m_folderValue->setMinimumWidth(MIN_WIDTH);
m_folderValue->setMaximumWidth(MAX_WIDTH);
m_folderValue->setContentsMargins(3,1,3,1);
m_folderValue->setText("Click to open media file (mp3, wav, flac)");
m_folderValue->setStyleSheet( m_folderValue->setStyleSheet(
"margin: 0px;"
"color: white;" "color: white;"
"background-color: black;" "background-color: black;"
"font-size: 12px;"
); );
layout->addWidget(m_folderValue); playback->addWidget(m_folderValue);
m_fileValue = new ClickableLabel; m_fileValue = new ClickableLabel;
m_fileValue->setAlignment(Qt::AlignLeft);
m_fileValue->setFocusPolicy(Qt::NoFocus);
m_fileValue->setMinimumWidth(MIN_WIDTH);
m_fileValue->setMaximumWidth(MAX_WIDTH);
m_fileValue->setContentsMargins(3,1,3,1);
m_fileValue->setText("++ empty ++");
connect(m_fileValue, SIGNAL(clicked()), this, SLOT(openMediaDialog())); connect(m_fileValue, SIGNAL(clicked()), this, SLOT(openMediaDialog()));
connect(m_folderValue, SIGNAL(clicked()), this, SLOT(openMediaDialog())); connect(m_folderValue, SIGNAL(clicked()), this, SLOT(openMediaDialog()));
m_fileValue->setMaximumWidth(160);
m_fileValue->setAlignment(Qt::AlignLeft);
m_fileValue->setStyleSheet( m_fileValue->setStyleSheet(
"margin: 0px;"
"color: white;" "color: white;"
"background-color: black;" "background-color: black;"
"font-size: 14px;"
); );
layout->addWidget(m_fileValue); playback->addWidget(m_fileValue);
playback->setSpacing(0);
playback->setContentsMargins(0, 0, 0, 0);
layout->addLayout(playback);
m_suspendResumeButton = new QPushButton(this); m_suspendResumeButton = new QPushButton(this);
m_suspendResumeButton->setText(statusToString(Status::Iddle)); m_suspendResumeButton->setText(StatusStr[Status::Iddle]);
connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume()));
layout->addWidget(m_suspendResumeButton); layout->addWidget(m_suspendResumeButton);
m_progress = new QProgressBar(this); m_progress = new QProgressBar(this);
m_progress->setOrientation(Qt::Horizontal); m_progress->setOrientation(Qt::Horizontal);
m_progress->setFocusPolicy(Qt::NoFocus);
m_progress->setAlignment(Qt::AlignHCenter);
m_progress->setContentsMargins(0, 1, 0, 1);
m_progress->setMinimumWidth(MIN_WIDTH);
m_progress->setMaximumWidth(MAX_WIDTH);
m_progress->setMaximumHeight(15);
m_progress->setRange(0, 0); m_progress->setRange(0, 0);
m_progress->setValue(0); m_progress->setValue(0);
m_progress->setFormat("%v / %m"); m_progress->setFormat("%v / %m");
m_progress->setStyleSheet(
"margin: 0px;"
"font-size: 10px;"
"background-color: black;"
);
layout->addWidget(m_progress); layout->addWidget(m_progress);
m_progressTime = new QTimeEdit; m_progressTime = new QTimeEdit;
m_progressTime->setToolTip("Current Time"); m_progressTime->setToolTip("Current Time");
m_progressTime->setObjectName("Current Time"); m_progressTime->setObjectName("Current Time");
m_progressTime->setDisplayFormat("mm:ss:zzz"); m_progressTime->setDisplayFormat("mm:ss:zz");
m_progressTime->setReadOnly(true); m_progressTime->setReadOnly(true);
m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons);
m_progressTime->setMinimumWidth(80);
m_progressTime->setMaximumWidth(80);
m_progressTime->setFocusPolicy(Qt::NoFocus); m_progressTime->setFocusPolicy(Qt::NoFocus);
m_progressTime->setAlignment(Qt::AlignHCenter); m_progressTime->setAlignment(Qt::AlignHCenter);
m_progressTime->setContentsMargins(0,0,0,0); m_progressTime->setContentsMargins(0,0,0,0);
m_progressTime->setMinimumWidth(MIN_WIDTH);
m_progressTime->setMaximumWidth(MAX_WIDTH);
m_progressTime->setStyleSheet(
"margin: 0px;"
"color: white;"
"background-color: black;"
"font-size: 16px;"
);
m_totalTimeValue = new QTimeEdit; m_totalTimeValue = new QTimeEdit;
m_totalTimeValue->setObjectName("Track Length"); m_totalTimeValue->setObjectName("Track Length");
m_totalTimeValue->setToolTip("Track Length"); m_totalTimeValue->setToolTip("Track Length");
m_totalTimeValue->setDisplayFormat("mm:ss:zzz"); m_totalTimeValue->setDisplayFormat("mm:ss:zzz");
m_totalTimeValue->setReadOnly(true); m_totalTimeValue->setReadOnly(true);
m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons); m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons);
m_totalTimeValue->setMinimumWidth(80);
m_totalTimeValue->setFocusPolicy(Qt::NoFocus); m_totalTimeValue->setFocusPolicy(Qt::NoFocus);
m_totalTimeValue->setAlignment(Qt::AlignHCenter); m_totalTimeValue->setAlignment(Qt::AlignHCenter);
m_totalTimeValue->setContentsMargins(0,0,0,0); m_totalTimeValue->setContentsMargins(0,0,0,0);
m_totalTimeValue->setMinimumWidth(MIN_WIDTH);
m_totalTimeValue->setMaximumWidth(MAX_WIDTH);
m_totalTimeValue->setStyleSheet(
"margin: 0px;"
"color: white;"
"background-color: black;"
"font-size: 16px;"
);
QHBoxLayout *status = new QHBoxLayout; QHBoxLayout *status = new QHBoxLayout;
status->addWidget(m_progressTime); status->addWidget(m_progressTime);
status->addWidget(m_totalTimeValue); status->addWidget(m_totalTimeValue);
layout->addLayout(status); layout->addLayout(status);
m_filterBank = new FilterBankWidget(this);
m_filterBank->setMaximumWidth(MAX_WIDTH);
m_filterBank->setMinimumWidth(MIN_WIDTH);
connect(m_filterBank, SIGNAL(setBypass(bool)), this, SLOT(setBypass(bool)));
layout->addWidget(m_filterBank);
m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL);
layout->addWidget(m_pitch);
connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int)));
m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL);
layout->addWidget(m_pan);
connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(panChanged(int)));
QHBoxLayout *volumeBox = new QHBoxLayout; QHBoxLayout *volumeBox = new QHBoxLayout;
m_volume = new SliderGroup("Vol", 0, 65535, 2, NULL); m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL);
volumeBox->addWidget(m_volume); volumeBox->addWidget(m_volume);
connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int))); connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
m_bus1 = new SliderGroup("Bus 1", 0, 65535, 2, NULL); m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL);
volumeBox->addWidget(m_bus1); volumeBox->addWidget(m_pan);
connect(m_bus1, SIGNAL(valueChanged(int)), this, SLOT(bus1VolumeChanged(int))); connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(panChanged(int)));
m_bus2 = new SliderGroup("Bus 2", 0, 65535, 2, NULL); m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL);
volumeBox->addWidget(m_bus2); volumeBox->addWidget(m_pitch);
connect(m_bus2, SIGNAL(valueChanged(int)), this, SLOT(bus2VolumeChanged(int)));
volumeBox->setSpacing(0); volumeBox->setSpacing(0);
volumeBox->setContentsMargins(0, 0, 0, 0); volumeBox->setContentsMargins(0, 0, 0, 0);
connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int)));
layout->addLayout(volumeBox); layout->addLayout(volumeBox);
QHBoxLayout *labelsBox = new QHBoxLayout;
m_level = new QLabel("-inf");
m_level->setStyleSheet("border: 1px solid #CFB0C9;"
"margin: 0px;"
"background-color: black;"
"color: white;"
"text-align: center;"
"font-size: 15px;");
m_level->setMinimumWidth(MIN_WIDTH / 2);
m_level->setMaximumWidth(MAX_WIDTH / 3);
m_level->setMinimumHeight(25);
m_level->setAlignment(Qt::AlignHCenter);
m_level->setContentsMargins(0, 4, 0, 4);
labelsBox->addWidget(m_level);
m_bus1Label = new QLabel("dummy");
m_bus1Label->setAlignment(Qt::AlignHCenter);
m_bus1Label->setContentsMargins(0, 6, 0, 6);
m_bus1Label->setMinimumHeight(25);
m_bus1Label->setMinimumWidth(MIN_WIDTH / 4);
m_bus1Label->setMaximumWidth(MAX_WIDTH / 3);
m_bus1Label->setStyleSheet("border: 1px solid #CFB0C9;"
"margin: 0px;"
"background-color: black;"
"color: white;"
"font-size: 10px;");
labelsBox->addWidget(m_bus1Label);
m_bus2Label = new QLabel("dummy");
m_bus2Label->setAlignment(Qt::AlignHCenter);
m_bus2Label->setMinimumWidth(MIN_WIDTH / 4);
m_bus2Label->setMaximumWidth(MAX_WIDTH / 3);
m_bus2Label->setContentsMargins(0, 6, 0, 6);
m_bus2Label->setMinimumHeight(25);
m_bus2Label->setStyleSheet("border: 1px solid #CFB0C9;"
"margin: 0px;"
"background-color: black;"
"color: white;"
"font-size: 10px;");
labelsBox->addWidget(m_bus2Label);
labelsBox->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
layout->addLayout(labelsBox);
layout->setAlignment(Qt::AlignHCenter); layout->setAlignment(Qt::AlignHCenter);
layout->setSpacing(1); layout->setSpacing(0);
layout->setContentsMargins(2, 2, 2, 2);
this->setLayout(layout); this->setLayout(layout);
} }
AudioLayerWidget::~AudioLayerWidget() {} AudioLayerWidget::~AudioLayerWidget()
{
}
// From UI. // From UI.
void AudioLayerWidget::volumeChanged(int value) void AudioLayerWidget::volumeChanged(int value)
@ -184,16 +98,6 @@ void AudioLayerWidget::volumeChanged(int value)
emit(uiSliderChanged(m_layer, Slider::Volume, value)); emit(uiSliderChanged(m_layer, Slider::Volume, value));
} }
void AudioLayerWidget::bus1VolumeChanged(int value)
{
emit(uiSliderChanged(m_layer, Slider::Bus1, value));
}
void AudioLayerWidget::bus2VolumeChanged(int value)
{
emit(uiSliderChanged(m_layer, Slider::Bus2, value));
}
void AudioLayerWidget::panChanged(int value) void AudioLayerWidget::panChanged(int value)
{ {
emit(uiSliderChanged(m_layer, Slider::Pan, value)); emit(uiSliderChanged(m_layer, Slider::Pan, value));
@ -204,28 +108,18 @@ void AudioLayerWidget::pitchChanged(int value)
emit(uiSliderChanged(m_layer, Slider::Pitch, value)); emit(uiSliderChanged(m_layer, Slider::Pitch, value));
} }
void AudioLayerWidget::setBypass(bool value)
{
emit(uiSliderChanged(m_layer, Slider::Bypass, value));
}
void AudioLayerWidget::toggleSuspendResume() void AudioLayerWidget::toggleSuspendResume()
{ {
switch (m_status) { switch (m_status) {
case Status::PlayingLoop: case Status::PlayingLoop:
case Status::PlayingOnce: case Status::PlayingOnce:
case Status::PlayingFolder:
case Status::PlayingFolderLoop:
case Status::PlayingFolderRandom:
m_oldStatus = m_status;
this->setPlaybackStatus(Status::Paused); this->setPlaybackStatus(Status::Paused);
emit uiPlaybackChanged(m_layer, Status::Paused); emit uiPlaybackChanged(m_layer, Status::Paused);
break; break;
case Status::Paused: case Status::Paused:
case Status::Stopped: case Status::Stopped:
this->setPlaybackStatus(m_oldStatus); this->setPlaybackStatus(Status::PlayingLoop);
emit uiPlaybackChanged(m_layer, m_oldStatus); emit uiPlaybackChanged(m_layer, Status::PlayingLoop);
case Status::Iddle: case Status::Iddle:
break; break;
} }
@ -244,15 +138,14 @@ void AudioLayerWidget::openMediaDialog()
fileNames = dialog.selectedFiles(); fileNames = dialog.selectedFiles();
emit uiLoadMedia(m_layer, fileNames.at(0)); emit uiLoadMedia(m_layer, fileNames.at(0));
this->setMediaFile(fileNames.at(0)); this->setMediaFile(fileNames.at(0));
this->setPlaybackStatus(Status::Stopped);
} }
// from DMX signals // from DMX signals
void AudioLayerWidget::setVol(float vol) void AudioLayerWidget::setVol(float vol)
{ {
m_volume->blockSignals(true); m_volume->blockSignals(true);
m_volume->setValue(vol); m_volume->setValue(vol);
m_volume->blockSignals(false); m_volume->blockSignals(false);
} }
void AudioLayerWidget::setPan(int pan) void AudioLayerWidget::setPan(int pan)
@ -274,20 +167,17 @@ void AudioLayerWidget::setMediaFile(QString file)
QStringList list = file.split("/"); QStringList list = file.split("/");
int size = list.size(); int size = list.size();
if (size >= 2) { if (size >= 2) {
QString tmp = list.at(size - 2); m_folderValue->setText(list.at(size - 2));
tmp.truncate(60); m_fileValue->setText(list.at(size - 1));
m_folderValue->setText(tmp);
tmp = list.at(size - 1);
tmp.truncate(40);
m_fileValue->setText(tmp);
} }
} }
void AudioLayerWidget::setPlaybackStatus(Status s) void AudioLayerWidget::setPlaybackStatus(Status s)
{ {
Status status = static_cast<Status>(s);
m_suspendResumeButton->blockSignals(true); m_suspendResumeButton->blockSignals(true);
m_status = s; m_status = status;
m_suspendResumeButton->setText(statusToString(s)); m_suspendResumeButton->setText(StatusStr[status]);
m_suspendResumeButton->blockSignals(false); m_suspendResumeButton->blockSignals(false);
} }
@ -315,46 +205,3 @@ void AudioLayerWidget::setCurrentTime(float progress)
m_progress->blockSignals(false); m_progress->blockSignals(false);
m_progressTime->blockSignals(false); m_progressTime->blockSignals(false);
} }
void AudioLayerWidget::setFilterParam(int channel, int value)
{
if (channel <= FILTER_BANK_GAIN - HP_FREQ){
m_filterBank->blockSignals(true);
m_filterBank->setValue(channel, value);
m_filterBank->blockSignals(false);
} else if (channel == SEND1 - HP_FREQ) {
m_bus1->blockSignals(true);
m_bus1->setValue((value * 256) + 255);
m_bus1->blockSignals(false);
} else if (channel == SEND2 - HP_FREQ) {
m_bus2->blockSignals(true);
m_bus2->setValue(value * 256 + 255);
m_bus2->blockSignals(false);
}
}
void AudioLayerWidget::setLevel(float db)
{
m_level->blockSignals(true);
if (db > -140)
m_level->setText(QString::number(db, 'f', 2));
else
m_level->setText("-inf");
m_level->blockSignals(false);
}
void AudioLayerWidget::setBusName(uint bus, char *name)
{
if (name && strlen(name) > 17)
name[16] = '\0';
if (bus == 0) {
m_bus1Label->blockSignals(true);
m_bus1Label->setText(name);
m_bus1Label->blockSignals(false);
} else if (bus == 1) {
m_bus2Label->blockSignals(true);
m_bus2Label->setText(name);
m_bus2Label->blockSignals(false);
}
}

View file

@ -3,13 +3,13 @@
#include <QPushButton> #include <QPushButton>
#include <QTimeEdit> #include <QTimeEdit>
#include <QFileDialog>
#include <QProgressBar> #include <QProgressBar>
#include "defines.h" #include "defines.h"
#include "slidergroup.h" #include "slidergroup.h"
#include "clickablelabel.h" #include "clickablelabel.h"
#include "settings.h" #include "settings.h"
#include "filterbankwidget.h"
class AudioLayerWidget : public QWidget class AudioLayerWidget : public QWidget
{ {
@ -19,21 +19,8 @@ public:
explicit AudioLayerWidget(QWidget *parent = 0, int layer = 0); explicit AudioLayerWidget(QWidget *parent = 0, int layer = 0);
~AudioLayerWidget(); ~AudioLayerWidget();
// From OLA -> LibreMediaServer -> AudioWidget
void setMediaFile(QString file);
void setDuration(float dur);
void setCurrentTime(float progress);
void setPlaybackStatus(Status status);
void setVol(float vol);
void setPan(int pan);
void setPitch(int pitch);
void setFilterParam(int channel, int value);
void setLevel(float db);
void setBusName(uint bus, char *name);
private: private:
Status m_status; Status m_status;
Status m_oldStatus;
int m_layer; int m_layer;
QPushButton *m_suspendResumeButton; QPushButton *m_suspendResumeButton;
ClickableLabel *m_fileValue; ClickableLabel *m_fileValue;
@ -41,26 +28,27 @@ private:
SliderGroup *m_volume; SliderGroup *m_volume;
SliderGroup *m_pan; SliderGroup *m_pan;
SliderGroup *m_pitch; SliderGroup *m_pitch;
SliderGroup *m_bus1;
SliderGroup *m_bus2;
QTimeEdit *m_progressTime; QTimeEdit *m_progressTime;
QTimeEdit *m_totalTimeValue; QTimeEdit *m_totalTimeValue;
QProgressBar *m_progress; QProgressBar *m_progress;
FilterBankWidget *m_filterBank;
QLabel *m_level; // From DMX
QLabel *m_bus1Label; public slots:
QLabel *m_bus2Label; void setMediaFile(QString file);
void setDuration(float dur);
void setCurrentTime(float progress);
void setPlaybackStatus(Status status);
void setVol(float vol);
void setPan(int pan);
void setPitch(int pitch);
// From Ui // From Ui
private slots: private slots:
void openMediaDialog(); void openMediaDialog();
void toggleSuspendResume(); void toggleSuspendResume();
void volumeChanged(int vol); void volumeChanged(int vol);
void bus1VolumeChanged(int vol);
void bus2VolumeChanged(int vol);
void panChanged(int pan); void panChanged(int pan);
void pitchChanged(int pitch); void pitchChanged(int pitch);
void setBypass(bool value);
signals: signals:
void uiPlaybackChanged(int layer, Status s); void uiPlaybackChanged(int layer, Status s);

View file

@ -1,31 +1,26 @@
#include "audiowidget.h" #include "audiowidget.h"
#include "dmxPersonality.h" #include <cmath>
AudioWidget::AudioWidget(QWidget *parent) : AudioWidget::AudioWidget(QWidget *parent) :
QWidget(parent) QWidget(parent)
, m_layout(new QHBoxLayout()) , m_layout(new QHBoxLayout())
{ {
m_layers = Settings::getInstance()->getLayersNumber(); for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) {
for (uint i= 0; i < m_layers; i++ ) {
AudioLayerWidget *alw = new AudioLayerWidget(this, i); AudioLayerWidget *alw = new AudioLayerWidget(this, i);
m_layout->insertWidget(i, alw); m_layout->insertWidget(i, alw);
connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SIGNAL(uiSliderChanged(int, Slider, int))); connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SIGNAL(uiSliderChanged(int, Slider, int)));
connect(alw, SIGNAL(uiPlaybackChanged(int, Status)), this, SIGNAL(uiPlaybackChanged(int, Status))); connect(alw, SIGNAL(uiPlaybackChanged(int, Status)), this, SIGNAL(uiPlaybackChanged(int, Status)));
connect(alw, SIGNAL(uiLoadMedia(int, QString)), this, SIGNAL(uiLoadMedia(int, QString))); connect(alw, SIGNAL(uiLoadMedia(int, QString)), this, SIGNAL(uiLoadMedia(int, QString)));
m_layerUpdate[i].status = Status::Iddle; m_layerUpdate[i].status = Status::Iddle;
m_layerUpdate[i].duration = -1; m_layerUpdate[i].duration = 0;
m_layerUpdate[i].media = ""; m_layerUpdate[i].media = "";
m_layerUpdate[i].vol = -1; m_layerUpdate[i].vol = 0;
m_layerUpdate[i].pan = -1; m_layerUpdate[i].pan = 128;
m_layerUpdate[i].pitch = -1; m_layerUpdate[i].pitch = 128;
m_layerUpdate[i].cursor = -1; m_layerUpdate[i].cursor = 0;
m_layerUpdate[i].level = 100;
for (int j = 0; j < FILTER_CHANNELS; j++)
m_filtersUpdate[i][j] = -1;
} }
m_layout->setSpacing(0); m_layout->setSpacing(0);
m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setContentsMargins(1, 1, 1, 1);
this->setStyleSheet("margin: 2px;");
setLayout(m_layout); setLayout(m_layout);
m_refreshUi = new QTimer(this); m_refreshUi = new QTimer(this);
connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi()));
@ -69,10 +64,10 @@ void AudioWidget::cursorChanged(int layer, float cursor)
void AudioWidget::refreshUi() void AudioWidget::refreshUi()
{ {
for (uint i = 0; i < m_layers; i++) for (int i = 0; i < MAX_LAYERS; i++)
{ {
if (m_layerUpdate[i].updated) { if (m_layerUpdate[i].updated) {
QLayoutItem *item = m_layout->itemAt(i); QLayoutItem * const item = m_layout->itemAt(i);
AudioLayerWidget *alw = dynamic_cast<AudioLayerWidget *>(item->widget()); AudioLayerWidget *alw = dynamic_cast<AudioLayerWidget *>(item->widget());
if (m_layerUpdate[i].vol > -1) { if (m_layerUpdate[i].vol > -1) {
alw->setVol(m_layerUpdate[i].vol); alw->setVol(m_layerUpdate[i].vol);
@ -99,37 +94,7 @@ void AudioWidget::refreshUi()
alw->setDuration(m_layerUpdate[i].duration); alw->setDuration(m_layerUpdate[i].duration);
m_layerUpdate[i].duration = -1; m_layerUpdate[i].duration = -1;
} }
for (int j = 0; j < FILTER_CHANNELS; j++) {
if (m_filtersUpdate[i][j] > -1)
alw->setFilterParam(j, m_filtersUpdate[i][j]);
m_filtersUpdate[i][j] = -1;
}
if (m_layerUpdate[i].level < 100) {
alw->setLevel(m_layerUpdate[i].level);
m_layerUpdate[i].level = 100;
}
m_layerUpdate[i].updated = false; m_layerUpdate[i].updated = false;
} }
} }
} }
void AudioWidget::filterParamChanged(int layer, int channel, int value)
{
m_filtersUpdate[layer][channel - HP_FREQ] = value;
m_layerUpdate[layer].updated = true;
}
void AudioWidget::levelChanged(int layer, float db)
{
m_layerUpdate[layer].level = db;
m_layerUpdate[layer].updated = true;
}
void AudioWidget::busNameChanged(uint bus, char* name)
{
for (uint i = 0; i < m_layers; i++) {
QLayoutItem *item = m_layout->itemAt(i);
AudioLayerWidget *alw = dynamic_cast<AudioLayerWidget *>(item->widget());
alw->setBusName(bus, name);
}
}

View file

@ -2,7 +2,6 @@
#define AUDIOWIDGET_H #define AUDIOWIDGET_H
#include <QTimer> #include <QTimer>
#include <QBoxLayout>
#include "audiolayerwidget.h" #include "audiolayerwidget.h"
#include "settings.h" #include "settings.h"
@ -14,16 +13,11 @@ class AudioWidget : public QWidget
public: public:
AudioWidget(QWidget *parent = nullptr); AudioWidget(QWidget *parent = nullptr);
void filterParamChanged(int layer, int channel, int value);
void levelChanged(int layer, float db);
void busNameChanged(uint bus, char *name);
private: private:
QHBoxLayout *m_layout; QHBoxLayout *m_layout;
layerData m_layerUpdate[MAX_LAYERS]; layerData m_layerUpdate[MAX_LAYERS];
QTimer *m_refreshUi; QTimer *m_refreshUi;
uint m_layers;
int m_filtersUpdate[MAX_LAYERS][FILTER_CHANNELS];
public slots: public slots:
void volChanged(int layer, float vol); void volChanged(int layer, float vol);

View file

@ -1,17 +0,0 @@
#include "clickabledoublespinbox.h"
#include <QVBoxLayout>
ClickableDoubleSpinBox::ClickableDoubleSpinBox(QWidget *parent)
: QDoubleSpinBox(parent)
{
setFocusPolicy(Qt::NoFocus);
setButtonSymbols(QAbstractSpinBox::NoButtons);
setValue(-1);
setDecimals(1);
setAlignment(Qt::AlignHCenter);
setContentsMargins(0, 0, 0, 0);
setMaximumWidth(66);
setMinimumWidth(25);
}

View file

@ -1,25 +0,0 @@
#ifndef CLICKABLEDOUBLESPINBOX_H
#define CLICKABLEDOUBLESPINBOX_H
#include <QWidget>
#include <QDoubleSpinBox>
#include <QMouseEvent>
#include <QDebug>
class ClickableDoubleSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit ClickableDoubleSpinBox(QWidget *parent = nullptr);
protected:
void mousePressEvent ( QMouseEvent * event ) {
if (event->button() == Qt::LeftButton) {
emit click();
}
event->accept();
}
signals:
void click();
};
#endif // CLICKABLEDOUBLESPINBOX_H

View file

@ -1,3 +0,0 @@
#include "clickableslider.h"
ClickableSlider::ClickableSlider(QWidget *parent) : QSlider{parent} {}

View file

@ -1,30 +0,0 @@
#ifndef CLICKABLESLIDER_H
#define CLICKABLESLIDER_H
#include <QSlider>
#include <QEvent>
#include <QMouseEvent>
#include <QDebug>
class ClickableSlider : public QSlider
{
Q_OBJECT
public:
ClickableSlider(QWidget *parent = nullptr);
protected:
void mousePressEvent ( QMouseEvent * event )
{
if (event->button() == Qt::RightButton)
{
if (this->isEnabled()) {
qDebug() << "disabling slider";
this->setDisabled(true);
}
event->accept();
}
QSlider::mousePressEvent(event);
}
};
#endif // CLICKABLESLIDER_H

View file

@ -1,26 +1,19 @@
#ifndef DEFINES_H #ifndef DEFINES_H
#define DEFINES_H #define DEFINES_H
#define VERSION "LibreMediaServerAudio v0.2.0 Antigona" #define VERSION "LibreMediaServerAudio 0.2.0 Antigona Release"
#define COPYRIGHT "(C) 2014-2024 Santi Noreña <lms@criptomart.net>" #define COPYRIGHT "(C) 2014-2024 Santi Noreña <lms@criptomart.net>"
#define LICENSE "GPL3 Licensed. See LICENSE.txt." #define LICENSE "GPL 3 Licensed. See LICENSE.txt."
#define DEFAULT_FILE "lms-audio.xlm" #define DEFAULT_FILE "lms-audio.xlm"
#define MAX_LAYERS 4 #define MAX_LAYERS 4
#define MAX_AUDIODEVICES 8 #define MAX_AUDIODEVICES 8
#define FORMAT ma_format_f32 /* Must always be f32. */ #define UI_REFRESH_TIME 66
#define CHANNELS 2 #define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks
#define SAMPLE_RATE 48000
#define UI_REFRESH_TIME 123
#define FADE_TIME 25
#define FILTER_CHANNELS 16 // number of dmx channels dedicated to filters by layer
#define MAX_WIDTH 500
#define MIN_WIDTH 50
struct dmxSetting { struct dmxSetting {
int address; int address;
unsigned int universe; unsigned int universe;
int layer; int layer;
int audioDevice;
}; };
enum Status enum Status
@ -35,33 +28,26 @@ enum Status
PlayingFolderRandom PlayingFolderRandom
}; };
static const char* StatusStr[] =
{
"Stop",
"Pause",
"Playing One",
"Playing One Loop",
"Iddle",
"Playing Folder",
"Playing Folder Loop",
"Playing Folder Random",
0x0
};
enum Slider enum Slider
{ {
Volume, Volume,
Pan, Pan,
Pitch, Pitch,
Bypass,
Bus1,
Bus2
}; };
#ifdef __cplusplus
constexpr const char* statusToString(Status e) noexcept
{
switch (e)
{
case Status::Stopped: return "Stop";
case Status::Paused: return "Paused";
case Status::PlayingOnce: return "Play 1";
case Status::PlayingLoop: return "Play Loop";
case Status::Iddle: return "Iddle";
case Status::PlayingFolder: return "Play Folder";
case Status::PlayingFolderLoop: return "Play Folder Loop";
case Status::PlayingFolderRandom: return "Playing Folder Random";
default: return "--++--";
}
}
#include <QString> #include <QString>
struct layerData { struct layerData {
QString media; QString media;
@ -75,9 +61,5 @@ struct layerData {
int address; int address;
unsigned int universe; unsigned int universe;
int device; int device;
int bus1Vol;
int bus2Vol;
float level;
}; };
#endif // __cplusplus
#endif // DEFINES_H #endif // DEFINES_H

View file

@ -1,52 +1,15 @@
#ifndef DMXPERSONALITY_H #ifndef DMXPERSONALITY_H
#define DMXPERSONALITY_H #define DMXPERSONALITY_H
#define VOLUME_COARSE 3
#define PAN 6
#define DMX_FOLDER 0 #define DMX_FOLDER 0
#define DMX_FILE 1 #define DMX_FILE 1
#define VOLUME_FINE 2
#define VOLUME_COARSE 3
#define ENTRY_POINT_FINE 4
#define ENTRY_POINT_COARSE 5
#define PAN 6
#define PITCH 7
#define PLAYBACK 8 #define PLAYBACK 8
#define HP_FREQ 9 #define VOLUME_FINE 2
#define LOW_FREQ 10 #define ENTRY_POINT_COARSE 5
#define LOW_Q 11 #define ENTRY_POINT_FINE 4
#define LOW_GAIN 12 #define PITCH 7
#define MIDLOW_FREQ 13 #define LAYER_CHANNELS 9
#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 FILTER_BANK_GAIN 22
#define SEND1 23
#define SEND2 24
#define LAYER_CHANNELS 25
constexpr const char* dmxChannelToString(int e) noexcept
{
switch (e) {
case HP_FREQ: return "High Pass Cutoff Frec";
case LOW_FREQ: return "Low Cutoff Frec";
case LOW_Q: return "Low Slope";
case LOW_GAIN: return "Low Gain";
case MIDLOW_FREQ: return "Mid Low Frec";
case MIDLOW_Q: return "Mid Low Q";
case MIDLOW_GAIN: return "Mid Low Gain";
case MIDHIGH_FREQ: return "Mid High Frec";
case MIDHIGH_Q: return "Mid High Q";
case MIDHIGH_GAIN: return "Mid High Gain";
case HIGH_FREQ: return "High Cutoff Frec";
case HIGH_Q: return "High Slope";
case HIGH_GAIN: return "High Gain";
case FILTER_BANK_GAIN: return "Post Filters Gain";
default: return "++--++--++";
}
}
#endif // DMXPERSONALITY_H #endif // DMXPERSONALITY_H

View file

@ -1,103 +0,0 @@
#include "filterbankwidget.h"
#include <QBoxLayout>
#include "dmxPersonality.h"
#include "defines.h"
#define BORDER "#CFB0C9;"
#define BACK "#281024;"
FilterBankWidget::FilterBankWidget(QWidget *parent)
: QWidget{parent}
{
QHBoxLayout *layout = new QHBoxLayout;
layout->setAlignment(Qt::AlignHCenter);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
this->setStyleSheet("border: 1px solid #CFB0C9;"
"margin: 0px;"
"background-color: #080402;"
"font-size: 13px;");
for (int i = 0; i < 13; i++) {
fb[i] = new ClickableDoubleSpinBox;
const char *name = dmxChannelToString(i + 9);
fb[i]->setObjectName(name);
fb[i]->setToolTip(name);
}
QVBoxLayout *master = new QVBoxLayout;
fb[0]->setRange(0, 500);
m_bypass = new QCheckBox;
master->addWidget(m_bypass);
m_bypass->setText("Bypass");
m_bypass->setMinimumWidth(MIN_WIDTH / 4);
m_bypass->setStyleSheet("QCheckBox { border: 1px solid #CFB0C9;"
"margin: 0px;"
"background-color: #c82840;"
"font-size: 8px;}");
connect(m_bypass, SIGNAL(stateChanged(int)), this, SLOT(bypassChanged(int)));
master->addWidget(fb[0]);
layout->addLayout(master);
for (int i = 1; i < 13;) {
QVBoxLayout *filterLayout= new QVBoxLayout;
for (int j = i; j < i + 3; j++) {
if ((j - 1) % 3 == 0)
fb[j]->setRange(0, 24000);
else if ((i - 1) % 3 == 1) {
fb[j]->setRange(0, 10);
} else {
fb[j]->setRange(-50, 50);
}
filterLayout->insertWidget(j, fb[j]);
}
filterLayout->setSpacing(0);
filterLayout->setAlignment(Qt::AlignHCenter);
filterLayout->setContentsMargins(0, 0, 0, 0);
layout->addLayout(filterLayout);
i += 3;
}
setLayout(layout);
}
void FilterBankWidget::setValue(int filter, int value)
{
double result = 0;
int channel = filter + 9;
if (channel == HP_FREQ) {
result = double((value * 1.31) + 16.0f); // 16 - 350
} else if (channel == LOW_FREQ) {
result = 30 + (value * 1.647); // 30 - 450
} else if (channel == LOW_Q) {
result = (double)(value / 32.0f) + 0.1f; // 0.1 - 8
} else if (channel == LOW_GAIN) {
result = (double)(value / 21.25f) - 6.023528412f;
} else if (channel == MIDLOW_FREQ) {
result = 200 + (value * 9.019607843); // 200 - 450
} else if (channel == MIDLOW_Q) {
result = (double)( value / 64.0f) + 0.10; // 0.1 - 4
} else if (channel == MIDLOW_GAIN) {
result = (double)(value / 7.0833333333333f) - 18.0f;
} else if (channel == MIDHIGH_FREQ) {
result = 600 + (value * 25.09803922); // 600 - 7000
} else if (channel == MIDHIGH_Q) {
result = (double)( value / 64.0f) + 0.10; // 0.1 - 4
} else if (channel == MIDHIGH_GAIN) {
result = (double)(value / 7.0833333333333f) - 18.0f;
} else if (channel == HIGH_FREQ) {
result = 1500 + (value * 56.8627451); // 1500 - 16000
} else if (channel == HIGH_Q) {
result = (double)( value / 32.0f) + 0.1f;
} else if (channel == HIGH_GAIN) {
result = (double)(value / 21.25) - 6.023528412f;
} else
result = (double)value;
fb[filter]->setValue(result);
}
void FilterBankWidget::bypassChanged(int value)
{
if (value == 0)
emit setBypass(false);
else
emit setBypass(true);
}

View file

@ -1,26 +0,0 @@
#ifndef FILTERBANKWIDGET_H
#define FILTERBANKWIDGET_H
#include <QObject>
#include <QWidget>
#include <QCheckBox>
#include "clickabledoublespinbox.h"
class FilterBankWidget : public QWidget
{
Q_OBJECT
public:
explicit FilterBankWidget(QWidget *parent = nullptr);
ClickableDoubleSpinBox *fb[13];
QCheckBox *m_bypass;
void setValue(int filter, int value);
private slots:
void bypassChanged(int value);
signals:
void setBypass(bool value);
};
#endif // FILTERBANKWIDGET_H

View file

@ -35,11 +35,10 @@ libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent)
topWidget->setContentsMargins(0, 0, 0, 0); topWidget->setContentsMargins(0, 0, 0, 0);
addDockWidget(Qt::TopDockWidgetArea, topWidget); addDockWidget(Qt::TopDockWidgetArea, topWidget);
connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup())); connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup()));
this->setContentsMargins(0, 0, 0, 0); this->setContentsMargins(5, 5, 5, 5);
this->setStyleSheet( this->setStyleSheet(
"margin: 0px;"
"color: white;" "color: white;"
"background-color: #3f3038;" "background-color: #4f4048;"
"selection-color: blue;" "selection-color: blue;"
"selection-background-color: green" "selection-background-color: green"
); );

View file

@ -7,7 +7,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>500</width>
<height>400</height> <height>400</height>
</rect> </rect>
</property> </property>
@ -32,7 +32,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>500</width>
<height>21</height> <height>21</height>
</rect> </rect>
</property> </property>

View file

@ -26,11 +26,9 @@ libreMediaServerAudio::libreMediaServerAudio()
m_settings = Settings::getInstance(); m_settings = Settings::getInstance();
m_settings->readFile(); m_settings->readFile();
m_ui = m_settings->getShowUi(); m_ui = m_settings->getShowUi();
m_layersQty = m_settings->getLayersNumber();
m_dmxSettings = m_settings->getDmxSettings();
m_mediaLibrary = new MediaLibrary; m_mediaLibrary = new MediaLibrary;
m_mediaLibrary->initMediaLibrary(); m_mediaLibrary->initMediaLibrary();
for (uint i = 0; i < m_layersQty; i++) { for (int i = 0; i < MAX_LAYERS; i++) {
m_currentMedia[i] = ""; m_currentMedia[i] = "";
m_currentStatus[i] = Status::Iddle; m_currentStatus[i] = Status::Iddle;
#ifdef NOGUI #ifdef NOGUI
@ -40,28 +38,22 @@ libreMediaServerAudio::libreMediaServerAudio()
m_updateUi[i][3] = -1; m_updateUi[i][3] = -1;
#endif #endif
} }
if (!m_mae.startEngine(m_layersQty, m_settings->getAudioDeviceId(), m_settings->getAudioDeviceQty())) { m_ola = new olaThread(this, m_settings->getLayersNumber());
cout << "Can not start Audio Engine!" << endl;
exit(-1);
}
m_ola = new olaThread(this, m_layersQty);
Q_CHECK_PTR(m_ola); Q_CHECK_PTR(m_ola);
m_ola->blockSignals(true); m_ola->blockSignals(true);
m_ola->registerUniverse(); m_ola->registerUniverse();
m_mae.startEngine(m_settings->getAudioDeviceId());
qDebug("Core init Complete. Start reading DMX.");
m_ola->blockSignals(false);
#ifdef NOGUI #ifdef NOGUI
m_ola->start(QThread::TimeCriticalPriority ); m_ola->start(QThread::TimeCriticalPriority );
#endif #endif
m_ola->blockSignals(false);
cout << "Core init Complete." << endl;
} }
libreMediaServerAudio::~libreMediaServerAudio() libreMediaServerAudio::~libreMediaServerAudio()
{ {
m_ola->stop(); m_ola->stop();
m_mae.stopEngine(); m_mae.stopEngine();
sleep(1);
cout << "bye!" << endl;
exit(0);
} }
void libreMediaServerAudio::loadMedia(int layer, int folder, int file) void libreMediaServerAudio::loadMedia(int layer, int folder, int file)
@ -95,8 +87,9 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value)
QString mediaFile = NULL; QString mediaFile = NULL;
int aux; int aux;
if (channel == VOLUME_COARSE || channel == VOLUME_FINE) { if (channel == VOLUME_COARSE || channel == VOLUME_FINE) {
m_mae.volChanged(layer, value); float tmp = value / 65025.0f;
m_updateUi[layer][0] = value; m_mae.volChanged(layer, tmp);
m_updateUi[layer][0] = tmp * 100.0f;
} else if (channel == PAN) { } else if (channel == PAN) {
m_mae.panChanged(layer, value); m_mae.panChanged(layer, value);
m_updateUi[layer][1] = value; m_updateUi[layer][1] = value;
@ -125,22 +118,11 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value)
s = Status::PlayingFolderRandom; s = Status::PlayingFolderRandom;
m_mae.playbackChanged(layer, s); m_mae.playbackChanged(layer, s);
m_currentStatus[layer] = s; m_currentStatus[layer] = s;
qInfo() << "Layer" << layer << statusToString(s); qInfo() << "Layer" << layer << StatusStr[s];
#ifndef NOGUI #ifndef NOGUI
if (m_ui) { if (m_ui) {
m_lmsUi->m_aw->playbackChanged(layer, s); m_lmsUi->m_aw->playbackChanged(layer, s);
m_updateUi[layer][3] = 1; //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) {
m_mae.filterParamChanged(layer, channel, value);
#ifndef NOGUI
if (m_ui) {
m_lmsUi->m_aw->filterParamChanged(layer, channel, value);
m_played.clear();
m_played.append(m_ola->getValue(layer, DMX_FILE));
} }
#endif #endif
} }
@ -170,11 +152,11 @@ void libreMediaServerAudio::refreshUi() {
m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i)); m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i));
m_updateUi[i][3] = -1; m_updateUi[i][3] = -1;
} }
m_lmsUi->m_aw->levelChanged(i, m_mae.getLevel(i));
if (m_mae.getAtEnd(i)) { if (m_mae.getAtEnd(i)) {
if (m_played.isEmpty())
m_played.append(m_ola->getValue(i, DMX_FILE));
if (m_currentStatus[i] == Status::PlayingOnce) { if (m_currentStatus[i] == Status::PlayingOnce) {
m_currentStatus[i] = Status::Stopped; m_currentStatus[i] = Status::Stopped;
m_lmsUi->m_aw->playbackChanged(i, Status::Stopped);
} }
if (m_currentStatus[i] == Status::PlayingFolder) { if (m_currentStatus[i] == Status::PlayingFolder) {
uint last = m_played.last(); uint last = m_played.last();
@ -189,7 +171,7 @@ void libreMediaServerAudio::refreshUi() {
m_lmsUi->m_aw->playbackChanged(i, Status::Stopped); m_lmsUi->m_aw->playbackChanged(i, Status::Stopped);
} }
} }
else if (m_currentStatus[i] == Status::PlayingFolderLoop) { if (m_currentStatus[i] == Status::PlayingFolderLoop) {
uint last = m_played.last(); uint last = m_played.last();
int folder = m_ola->getValue(i, DMX_FOLDER); int folder = m_ola->getValue(i, DMX_FOLDER);
last++; last++;
@ -201,7 +183,7 @@ void libreMediaServerAudio::refreshUi() {
m_mae.playbackChanged(i, Status::PlayingFolder); m_mae.playbackChanged(i, Status::PlayingFolder);
} }
} }
else if (m_currentStatus[i] == Status::PlayingFolderRandom) { if (m_currentStatus[i] == Status::PlayingFolderRandom) {
int last = -1; int last = -1;
int folder = m_ola->getValue(i, DMX_FOLDER); int folder = m_ola->getValue(i, DMX_FOLDER);
if (uint(abs(m_played.size())) >= m_mediaLibrary->getMediaFolderCount(folder)) if (uint(abs(m_played.size())) >= m_mediaLibrary->getMediaFolderCount(folder))
@ -230,10 +212,6 @@ void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi)
connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi()));
m_refreshUi->start(UI_REFRESH_TIME); m_refreshUi->start(UI_REFRESH_TIME);
m_ola->start(QThread::TimeCriticalPriority ); m_ola->start(QThread::TimeCriticalPriority );
for (uint i = 0; i < m_settings->getAudioDeviceQty(); i++) {
char *name = m_mae.getDeviceName(i);
m_lmsUi->m_aw->busNameChanged(i, name);
}
}; };
// From Ui widgets // From Ui widgets
@ -241,7 +219,7 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value)
{ {
switch (s){ switch (s){
case Slider::Volume: case Slider::Volume:
m_mae.volChanged(layer, value); m_mae.volChanged(layer, float((value / 100.0f)));
break; break;
case Slider::Pan: case Slider::Pan:
m_mae.panChanged(layer, value); m_mae.panChanged(layer, value);
@ -249,15 +227,6 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value)
case Slider::Pitch: case Slider::Pitch:
m_mae.pitchChanged(layer, value); m_mae.pitchChanged(layer, value);
break; break;
case Slider::Bypass:
m_mae.setBypass(m_dmxSettings.at(layer).audioDevice, layer, value);
break;
case Slider::Bus1:
m_mae.filterParamChanged(layer, SEND1, value / 255.0f);
break;
case Slider::Bus2:
m_mae.filterParamChanged(layer, SEND2, value / 255.0f);
break;
} }
} }
@ -269,7 +238,7 @@ void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s)
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
m_currentStatus[layer] = s; m_currentStatus[layer] = s;
} else { } else {
qWarning() << "ui playback change error " << result << " status " << statusToString(s) << "layer" << layer; qWarning() << "ui playback change error" << result << "status" << s << "layer" << layer;
} }
} }

View file

@ -20,10 +20,6 @@
#ifndef LIBREMEDIASERVERAUDIO_H #ifndef LIBREMEDIASERVERAUDIO_H
#define LIBREMEDIASERVERAUDIO_H #define LIBREMEDIASERVERAUDIO_H
#include <bits/stdc++.h>
using namespace std;
#include "medialibrary.h" #include "medialibrary.h"
#include "miniaudioengine.h" #include "miniaudioengine.h"
#include "olathread.h" #include "olathread.h"
@ -57,7 +53,6 @@ private:
QList<dmxSetting> m_dmxSettings; QList<dmxSetting> m_dmxSettings;
bool m_ui; bool m_ui;
QList<int> m_played; QList<int> m_played;
uint m_layersQty;
#ifndef NOGUI #ifndef NOGUI
QTimer *m_refreshUi; QTimer *m_refreshUi;
libreMediaServerAudioUi *m_lmsUi; libreMediaServerAudioUi *m_lmsUi;

View file

@ -1,250 +0,0 @@
#include "ma_writer_node.h"
#include "miniaudio.c"
MA_API ma_writer_node_config ma_writer_node_config_init(ma_uint32 channels, ma_uint32 bufferSizeInFrames, ma_pcm_rb *rb)
{
ma_writer_node_config config;
MA_ZERO_OBJECT(&config);
config.nodeConfig = ma_node_config_init();
config.channels = channels;
config.bufferSizeInFrames = bufferSizeInFrames;
config.pBuffer = rb;
return config;
}
static void ma_writer_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
{
ma_writer_node* pWriteNode = (ma_writer_node*)pNode;
MA_ASSERT(pWriteNode != NULL);
MA_ASSERT(ma_node_get_input_bus_count(&pWriteNode->baseNode) == 2);
if (*pFrameCountIn > 0) {
void *pWriteBuffer = NULL;
ma_pcm_rb_acquire_write(pWriteNode->pBuffer, pFrameCountIn, &pWriteBuffer);
if (pWriteBuffer != NULL) {
ma_copy_pcm_frames(pWriteBuffer, ppFramesIn[1], *pFrameCountIn, ma_format_f32, pWriteNode->channels);
ma_pcm_rb_commit_write(pWriteNode->pBuffer, *pFrameCountIn);
}
}
ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, pWriteNode->channels);
}
static ma_node_vtable g_ma_writer_node_vtable =
{
ma_writer_node_process_pcm_frames,
NULL,
2,
1,
0
};
MA_API ma_result ma_writer_node_init(ma_node_graph* pNodeGraph, const ma_writer_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_writer_node* pWriteNode)
{
ma_result result;
ma_node_config baseConfig;
ma_uint32 inputChannels[2];
ma_uint32 outputChannels[1];
if (pWriteNode == NULL || pConfig == NULL || pConfig->pBuffer == NULL \
|| (pConfig->channels > MA_MAX_NODE_BUS_COUNT) ) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pWriteNode);
inputChannels[0] = pConfig->channels;
inputChannels[1] = pConfig->channels;
outputChannels[0] = pConfig->channels;
baseConfig = pConfig->nodeConfig;
baseConfig.vtable = &g_ma_writer_node_vtable;
baseConfig.pInputChannels = inputChannels;
baseConfig.pOutputChannels = outputChannels;
result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pWriteNode->baseNode);
if (result != MA_SUCCESS) {
return result;
}
pWriteNode->bufferSizeInFrames = pConfig->bufferSizeInFrames;
pWriteNode->pBuffer = pConfig->pBuffer;
pWriteNode->channels = pConfig->channels;
return MA_SUCCESS;
}
MA_API void ma_writer_node_uninit(ma_writer_node* pWriteNode, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_node_uninit(&pWriteNode->baseNode, pAllocationCallbacks);
}
/*
* Data Source Ring Buffer
*/
ma_result ma_data_source_rb_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
ma_data_source_rb* ds = (ma_data_source_rb*)pDataSource;
ma_uint32 pcmFramesAvailableInRB = 0;
ma_uint32 pcmFramesProcessed = 0;
while (pcmFramesProcessed < frameCount) {
pcmFramesAvailableInRB = ma_pcm_rb_available_read(ds->rb);
if (pcmFramesAvailableInRB == 0) {
break;
}
ma_uint32 framesToRead = frameCount - pcmFramesProcessed;
if (framesToRead > pcmFramesAvailableInRB) {
framesToRead = pcmFramesAvailableInRB;
}
void* pReadBuffer = NULL;
ma_pcm_rb_acquire_read(ds->rb, &framesToRead, &pReadBuffer);
if (pReadBuffer != NULL) {
ma_copy_pcm_frames(pFramesOut, pReadBuffer, framesToRead, ma_format_f32, 2);
ma_pcm_rb_commit_read(ds->rb, framesToRead);
pcmFramesProcessed += framesToRead;
}
else {
break;
}
}
*pFramesRead += pcmFramesProcessed;
return MA_SUCCESS;
}
ma_result ma_data_source_rb_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
{
(void)pDataSource;
(void)frameIndex;
return MA_NOT_IMPLEMENTED;
}
ma_result ma_data_source_rb_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
(void)pDataSource;
*pFormat = ma_format_f32;
*pChannels = 2;
*pSampleRate = ma_standard_sample_rate_48000;
return MA_SUCCESS;
}
ma_result ma_data_source_rb_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
{
(void)pDataSource;
*pCursor = 0;
return MA_NOT_IMPLEMENTED;
}
ma_result ma_data_source_rb_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
{
(void)pDataSource;
*pLength = 0;
return MA_NOT_IMPLEMENTED;
}
ma_data_source_vtable g_ma_data_source_rb_vtable =
{
ma_data_source_rb_read,
ma_data_source_rb_seek,
ma_data_source_rb_get_data_format,
ma_data_source_rb_get_cursor,
ma_data_source_rb_get_length
};
ma_result ma_data_source_rb_init(ma_data_source_rb* pMyDataSource, ma_pcm_rb *ringBuffer)
{
ma_result result;
ma_data_source_config baseConfig;
baseConfig = ma_data_source_config_init();
baseConfig.vtable = &g_ma_data_source_rb_vtable;
result = ma_data_source_init(&baseConfig, &pMyDataSource->base);
if (result != MA_SUCCESS) {
return result;
}
pMyDataSource->rb = ringBuffer;
return MA_SUCCESS;
}
void ma_data_source_rb_uninit(ma_data_source_rb* pMyDataSource)
{
ma_data_source_uninit(&pMyDataSource->base);
}
/*
* vumeter
*/
MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma_uint32 format, ma_uint32 sampleRate)
{
ma_vumeter_node_config config;
MA_ZERO_OBJECT(&config);
config.nodeConfig = ma_node_config_init();
config.channels = channels;
config.sampleRate = sampleRate;
config.format = format;
return config;
}
static void ma_vumeter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
{
ma_vumeter_node* pVumeterNode = (ma_vumeter_node*)pNode;
MA_ASSERT(pVumeterNode != NULL);
MA_ASSERT(ma_node_get_input_bus_count(&pVumeterNode->baseNode) == 1);
for (uint i = 0; i < *pFrameCountIn; i++) {
float input = fabsf(ppFramesIn[0][i]);
pVumeterNode->level += pVumeterNode->alpha * (input - pVumeterNode->level);
}
ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, pVumeterNode->format, pVumeterNode->channels);
}
static ma_node_vtable g_ma_vumeter_node_vtable =
{
ma_vumeter_node_process_pcm_frames,
NULL,
1,
1,
0
};
MA_API ma_result ma_vumeter_node_init(ma_node_graph* pNodeGraph, const ma_vumeter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_vumeter_node* pVumeterNode)
{
ma_result result;
ma_node_config baseConfig;
ma_uint32 inputChannels[1];
ma_uint32 outputChannels[1];
if (pVumeterNode == NULL || pConfig == NULL \
|| (pConfig->channels > MA_MAX_NODE_BUS_COUNT) ) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pVumeterNode);
inputChannels[0] = pConfig->channels;
outputChannels[0] = pConfig->channels;
baseConfig = pConfig->nodeConfig;
baseConfig.vtable = &g_ma_vumeter_node_vtable;
baseConfig.pInputChannels = inputChannels;
baseConfig.pOutputChannels = outputChannels;
result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pVumeterNode->baseNode);
if (result != MA_SUCCESS) { return result;
}
pVumeterNode->sampleRate = pConfig->sampleRate;
pVumeterNode->channels = pConfig->channels;
pVumeterNode->format = pConfig->format;
pVumeterNode->level = 0;
pVumeterNode->TC = 0.250f;
pVumeterNode->alpha = 1.0 - expf( (-2.0 * M_PI) / (pVumeterNode->TC * pConfig->sampleRate));
return MA_SUCCESS;
}
MA_API void ma_vumeter_node_uninit(ma_vumeter_node* pVumeterNode, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_node_uninit(&pVumeterNode->baseNode, pAllocationCallbacks);
}

View file

@ -1,85 +0,0 @@
/* Include ma_writer_node.h after miniaudio.h */
#ifndef ma_writer_node_h
#define ma_writer_node_h
#ifdef __cplusplus
extern "C" {
#endif
#include "miniaudio.h"
/*
* writer
*/
typedef struct
{
ma_node_config nodeConfig;
ma_uint32 channels;
ma_uint32 bufferSizeInFrames;
ma_pcm_rb *pBuffer;
} ma_writer_node_config;
MA_API ma_writer_node_config ma_writer_node_config_init(ma_uint32 channels, ma_uint32 bufferSizeInFrames, ma_pcm_rb *rb);
typedef struct
{
ma_node_base baseNode;
ma_uint32 bufferSizeInFrames;
ma_pcm_rb *pBuffer;
ma_uint32 channels;
} ma_writer_node;
MA_API ma_result ma_writer_node_init(ma_node_graph* pNodeGraph, const ma_writer_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_writer_node* pWriteNode);
MA_API void ma_writer_node_uninit(ma_writer_node* pWriteNode, const ma_allocation_callbacks* pAllocationCallbacks);
/**
* data source ring buffer
*/
typedef struct
{
ma_data_source_base base;
ma_pcm_rb *rb;
} ma_data_source_rb;
ma_result ma_data_source_rb_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
ma_result ma_data_source_rb_seek(ma_data_source* pDataSource, ma_uint64 frameIndex);
ma_result ma_data_source_rb_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
ma_result ma_data_source_rb_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor);
ma_result ma_data_source_rb_get_length(ma_data_source* pDataSource, ma_uint64* pLength);
ma_result ma_data_source_rb_init(ma_data_source_rb* pMyDataSource, ma_pcm_rb *ringBuffer);
void ma_data_source_rb_uninit(ma_data_source_rb* pMyDataSource);
/*
* VU meter
*/
typedef struct
{
ma_node_config nodeConfig;
ma_uint32 channels;
ma_uint32 sampleRate;
ma_uint32 format;
} ma_vumeter_node_config;
MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma_uint32 format, ma_uint32 sampleRate);
typedef struct
{
ma_node_base baseNode;
ma_uint32 channels;
ma_uint32 sampleRate;
ma_uint32 format;
float level;
float TC;
float alpha;
} ma_vumeter_node;
MA_API ma_result ma_vumeter_node_init(ma_node_graph* pNodeGraph, const ma_vumeter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_vumeter_node* pVumeterNode);
MA_API void ma_vumeter_node_uninit(ma_vumeter_node* pVumeterNode, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API inline float ma_vumeter_node_get_level(ma_vumeter_node* pVumeterNode) { return 5 * pVumeterNode->level; };
#ifdef __cplusplus
}
#endif
#endif /* ma_writer_node_h */

View file

@ -1,304 +1,80 @@
#include "miniaudioengine.h" #include "miniaudioengine.h"
#include "dmxPersonality.h" #include <iostream> //enum macro
MiniAudioEngine::MiniAudioEngine()
#define BIAS 1.0f {
#define FILTER_ORDER 3 for (int i =0; i < MAX_LAYERS; i++) {
m_mediaLoaded[i] = false;
MiniAudioEngine::MiniAudioEngine() {} m_currentLayerValues[i].status = Status::Iddle;
m_currentLayerValues[i].pan = 128;
m_currentLayerValues[i].pitch = 128;
m_currentLayerValues[i].vol = 0;
m_currentLayerValues[i].cursor = 0;
}
}
void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{ {
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)pInput; (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() void MiniAudioEngine::stopEngine()
{ {
for (uint i = 0; i < m_mae.layersQty; i++) { ma_engine_uninit(&engine);
if (m_mae.mediaLoaded[i]) ma_device_uninit(&device);
ma_sound_uninit(&m_mae.sounds[i]); ma_context_uninit(&context);
} ma_resource_manager_uninit(&resourceManager);
for (uint i = 0; i < m_mae.layersQty; i++) {
ma_splitter_node_uninit(&m_mae.filters[i].input, NULL);
ma_hpf_node_uninit(&m_mae.filters[i].hpf, NULL);
ma_loshelf_node_uninit(&m_mae.filters[i].loshelf, NULL);
ma_peak_node_uninit(&m_mae.filters[i].mLow, NULL);
ma_peak_node_uninit(&m_mae.filters[i].mHigh, NULL);
ma_hishelf_node_uninit(&m_mae.filters[i].hishelf, NULL);
ma_splitter_node_uninit(&m_mae.filters[i].output, NULL);
}
for (uint i = 0; i < m_mae.audioDevicesQty; i++) {
if (i > 0) {
ma_writer_node_uninit(&m_mae.sendAuxNode[i], NULL);
ma_pcm_rb_uninit(&m_mae.auxBuffers[i]);
}
ma_engine_uninit(&m_mae.engines[i]);
ma_device_uninit(&m_mae.devices[i]);
}
ma_context_uninit(&m_mae.context);
ma_resource_manager_uninit(&m_mae.resourceManager);
} }
bool MiniAudioEngine::startEngine(uint layers, uint* audioDevicesId, uint audioDevicesQty) bool MiniAudioEngine::startEngine(uint n)
{ {
ma_result result; ma_result result;
m_mae.layersQty = layers;
m_mae.audioDevicesId = audioDevicesId;
m_mae.audioDevicesQty = audioDevicesQty;
for (uint i =0; i < m_mae.layersQty; i++) {
m_mae.mediaLoaded[i] = MA_FALSE;
m_mae.currentStatus[i].status = Status::Iddle;
m_mae.currentStatus[i].pan = 128;
m_mae.currentStatus[i].pitch = 128;
m_mae.currentStatus[i].vol = 0.0f;
m_mae.currentStatus[i].cursor = 0;
m_mae.currentStatus[i].updated = false;
}
result = this->startContext(); result = this->startContext();
if (result != MA_SUCCESS) return false; if (result != MA_SUCCESS) return result;
result = this->getAllAudioDevices(); result = this->getAllAudioDevices();
if (result != MA_SUCCESS) return false; if (result != MA_SUCCESS) return result;
result = this->startDevices(); result = this->startDevice(n);
if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed start audio devices." << endl;
return false;
}
result = this->setNodeGraph();
if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to set node graph." << endl;
return false;
}
for (uint i = 0; i < m_mae.audioDevicesQty; i++) {
result = ma_engine_start(&m_mae.engines[i]);
if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to start audio device" << m_mae.audioDevicesId[i] << endl;
return false;
}
}
return true;
}
ma_result MiniAudioEngine::createFilterBank(uint layer)
{
ma_result result;
ma_node_graph *ng = ma_engine_get_node_graph(&m_mae.engines[0]);
ma_node *endpoint = ma_engine_get_endpoint(&m_mae.engines[0]);
filterBank *fb = &m_mae.filters[layer];
ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(CHANNELS);
splitterConfig.outputBusCount= 3;
result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->input);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to init input node." << endl;
return result;
}
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 init 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 init 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 init 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 init 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 init hi shelf filter node." << endl;
return result;
}
ma_vumeter_node_config vuc = ma_vumeter_node_config_init(CHANNELS, FORMAT, SAMPLE_RATE);
ma_vumeter_node_init(ng, &vuc, NULL, &fb->vumeter);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to init vumeter node." << endl;
return result;
}
splitterConfig.outputBusCount = m_mae.audioDevicesQty;
result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->output);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to init output node." << endl;
return result;
}
result = ma_node_attach_output_bus(&fb->input, 0, &fb->hpf, 0);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to attach input node." << endl;
return result;
}
result = ma_node_attach_output_bus(&fb->input, 1, &fb->vumeter, 0);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to attach bypass connection." << endl;
return result;
}
ma_node_set_output_bus_volume(&fb->input, 1, 0.0f);
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->vumeter, 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->vumeter, 0, &fb->output, 0);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to attach high shelf filter node." << endl;
return result;
}
if (m_mae.audioDevicesQty == 1) {
result = ma_node_attach_output_bus(&fb->output, 0, endpoint, 0);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to attach output to endpoint." << endl;
return result;
}
} else {
result = ma_node_attach_output_bus(&fb->output, 0, &m_mae.sendAuxNode[1], 0);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to attach output node to aux send 1." << endl;
return result;
}
result = ma_node_attach_output_bus(&fb->output, 1, &m_mae.sendAuxNode[1], 1);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to attach output node to aux send 1." << endl;
return result;
}
for (uint i = 2; i < m_mae.audioDevicesQty; i++) {
result = ma_node_attach_output_bus(&fb->output, i, &m_mae.sendAuxNode[i], 1);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to attach output node to aux send 1." << endl;
return result;
}
}
}
return result; return result;
} }
ma_result MiniAudioEngine::setNodeGraph() { ma_result MiniAudioEngine::startDevice(uint id)
ma_result result = MA_SUCCESS;
ma_node_graph *ng = ma_engine_get_node_graph(&m_mae.engines[0]);
for (uint i = 1; i < m_mae.audioDevicesQty; i++) {
size_t sizeInFrames = SAMPLE_RATE;
result = ma_pcm_rb_init(FORMAT, CHANNELS, sizeInFrames, NULL, NULL, &m_mae.auxBuffers[i]);
if (result != MA_SUCCESS) {
printf("Failed to initialize ring buffer.\n");
return result;
}
ma_silence_pcm_frames(m_mae.auxBuffers[i].rb.pBuffer, sizeInFrames, FORMAT, CHANNELS);
ma_writer_node_config writerConfig = ma_writer_node_config_init(CHANNELS, SAMPLE_RATE * 5, &m_mae.auxBuffers[i]);
result = ma_writer_node_init(ng, &writerConfig, NULL, &m_mae.sendAuxNode[i]);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to init writer node." << endl;
return result;
}
result = ma_node_attach_output_bus(&m_mae.sendAuxNode[i], 0, ma_engine_get_endpoint(&m_mae.engines[0]), 0);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed to attach writer node." << endl;
return result;
}
result = ma_data_source_rb_init(&m_mae.dataSourceRB[i], &m_mae.auxBuffers[i]);
if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to init data source ring buffer" << endl;
return result;
}
ma_data_source_node_config dataSupplyNodeConfig = ma_data_source_node_config_init(&m_mae.dataSourceRB[i]);
result = ma_data_source_node_init(ma_engine_get_node_graph(&m_mae.engines[i]), &dataSupplyNodeConfig, NULL, &m_mae.dataSupplyNode[i]);
if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to init data source node" << endl;
return result;
}
result = ma_node_attach_output_bus(&m_mae.dataSupplyNode[i], 0, ma_engine_get_endpoint(&m_mae.engines[i]), 0);
if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to attach data source rb node" << endl;
return result;
}
}
for (uint i = 0; i < m_mae.layersQty; i++) {
result = this->createFilterBank(i);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed creating filter bank." << endl;
}
}
return (result);
}
ma_result MiniAudioEngine::startDevices()
{ {
ma_result result = MA_SUCCESS; ma_result result;
ma_device_config deviceConfig; ma_device_config deviceConfig;
ma_engine_config engineConfig; ma_engine_config engineConfig;
deviceConfig = ma_device_config_init(ma_device_type_duplex); if (id >= playbackDeviceCount)
deviceConfig.capture.format = m_mae.resourceManager.config.decodedFormat; id = playbackDeviceCount - 1;
deviceConfig.capture.channels = CHANNELS; deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.channels = CHANNELS; deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[id].id;
deviceConfig.capture.shareMode = ma_share_mode_shared; deviceConfig.playback.format = resourceManager.config.decodedFormat;
deviceConfig.playback.format = m_mae.resourceManager.config.decodedFormat; deviceConfig.playback.channels = 0;
deviceConfig.sampleRate = m_mae.resourceManager.config.decodedSampleRate; deviceConfig.sampleRate = resourceManager.config.decodedSampleRate;
deviceConfig.dataCallback = audioDataCallback; deviceConfig.dataCallback = audioDataCallback;
engineConfig = ma_engine_config_init(); deviceConfig.pUserData = &engine;
engineConfig.pResourceManager = &m_mae.resourceManager; result = ma_device_init(&context, &deviceConfig, &device);
engineConfig.defaultVolumeSmoothTimeInPCMFrames = SAMPLE_RATE / 500; if (result != MA_SUCCESS) {
engineConfig.noAutoStart = MA_TRUE; qCritical("Failed to initialize audio device %s.", pPlaybackDeviceInfos[id].name);
return result;
for (uint internalId = 0; internalId < m_mae.audioDevicesQty; internalId++) {
deviceConfig.capture.pDeviceID = &m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].id;
deviceConfig.playback.pDeviceID = &m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].id;
deviceConfig.pUserData = &m_mae.engines[internalId];
result = ma_device_init(&m_mae.context, &deviceConfig, &m_mae.devices[internalId]);
if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to initialize audio device " << m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].name << endl;
return result;
}
engineConfig.pDevice = &m_mae.devices[internalId];
result = ma_engine_init(&engineConfig, &m_mae.engines[internalId]);
if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to initialize audio engine" << endl;
return result;
}
cout << "Initialized Audio Device. internalId: " << internalId << " systemId: " << m_mae.audioDevicesId[internalId] << " " << m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[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 result;
} }
@ -306,19 +82,19 @@ ma_result MiniAudioEngine::startContext()
{ {
ma_result result; ma_result result;
ma_resource_manager_config resourceManagerConfig = ma_resource_manager_config_init(); resourceManagerConfig = ma_resource_manager_config_init();
resourceManagerConfig.decodedFormat = FORMAT; 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 = CHANNELS; resourceManagerConfig.decodedChannels = 0;
resourceManagerConfig.decodedSampleRate = SAMPLE_RATE; resourceManagerConfig.decodedSampleRate = ma_standard_sample_rate_48000;
resourceManagerConfig.jobThreadCount = MAX_LAYERS; resourceManagerConfig.jobThreadCount = 4;
result = ma_resource_manager_init(&resourceManagerConfig, &m_mae.resourceManager); result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to initialize audio resource manager." << endl; qCritical("Failed to initialize audio resource manager.");
return result; return result;
} }
result = ma_context_init(NULL, 0, NULL, &m_mae.context); result = ma_context_init(NULL, 0, NULL, &context);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to initialize audio context." << endl; qCritical("Failed to initialize audio context.");
} }
return result; return result;
} }
@ -328,68 +104,62 @@ ma_result MiniAudioEngine::getAllAudioDevices()
{ {
ma_result result; ma_result result;
result = ma_context_get_devices(&m_mae.context, &m_mae.pPlaybackDeviceInfos, &m_mae.playbackDeviceCount, NULL, NULL); result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
cout << "Error " << result << ": Failed to enumerate playback devices." << endl; qWarning("Failed to enumerate playback devices.\n");
ma_context_uninit(&m_mae.context); ma_context_uninit(&context);
return result; return result;
} }
cout << "Audio devices available:" << endl; printf("Audio devices available:\n");
for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < m_mae.playbackDeviceCount; iAvailableDevice += 1) { for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < playbackDeviceCount; iAvailableDevice += 1) {
cout << iAvailableDevice << " : " << m_mae.pPlaybackDeviceInfos[iAvailableDevice].name << endl; qInfo("%d: : %s", iAvailableDevice, pPlaybackDeviceInfos[iAvailableDevice].name);
} }
return result; return result;
} }
char* MiniAudioEngine::getDeviceName(uint id)
{
return m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[id]].name;
}
ma_result MiniAudioEngine::loadMedia(int layer, char *file) ma_result MiniAudioEngine::loadMedia(int layer, char *file)
{ {
ma_result result; ma_result result;
if (m_mae.mediaLoaded[layer] == MA_TRUE) if (m_mediaLoaded[layer] == true)
{ {
m_mae.mediaLoaded[layer] = MA_FALSE; ma_sound_uninit(&m_currentSound[layer]);
ma_sound_set_volume(&m_mae.sounds[layer], 0.0f); m_mediaLoaded[layer] = false;
ma_sound_stop(&m_mae.sounds[layer]);
ma_sound_uninit(&m_mae.sounds[layer]);
} }
ma_sound_config soundConfig = ma_sound_config_init(); result = ma_sound_init_from_file(&engine, file, \
soundConfig = ma_sound_config_init(); MA_SOUND_FLAG_NO_SPATIALIZATION \
soundConfig.pFilePath = file; | MA_SOUND_FLAG_DECODE \
soundConfig.pInitialAttachment = &m_mae.filters[layer].input; /*| MA_SOUND_FLAG_NO_PITCH \*/
soundConfig.initialAttachmentInputBusIndex = 0; , NULL, NULL, &m_currentSound[layer]);
soundConfig.channelsIn = 0; if (result != MA_SUCCESS)
soundConfig.channelsOut = CHANNELS; qWarning("Failed to load file %s", file);
soundConfig.flags = MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT | MA_SOUND_FLAG_STREAM; //| MA_SOUND_FLAG_NO_PITCH else {
result = ma_sound_init_ex(&m_mae.engines[0], &soundConfig, &m_mae.sounds[layer]); m_mediaLoaded[layer] = true;
if (result != MA_SUCCESS) { this->refreshValues(layer);
cout << "Error" << result << ": Failed to load file " << file << endl; m_currentLayerValues[layer].media = file;
return result;
} }
m_mae.currentStatus[layer].media = file;
m_mae.currentStatus[layer].updated = true;
m_mae.mediaLoaded[layer] = MA_TRUE;
this->refreshValues(layer);
return result; return result;
} }
float MiniAudioEngine::getDuration(int layer) float MiniAudioEngine::getDuration(int layer)
{ {
ma_result result; ma_result result;
ma_uint64 lengthInPCMFrames;
ma_uint32 sampleRate;
float ret; float ret;
if (m_mae.mediaLoaded[layer] == false) if (m_mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST; return MA_DOES_NOT_EXIST;
result = ma_sound_get_length_in_seconds(&m_mae.sounds[layer], &ret); result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &lengthInPCMFrames);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
return (ret * 1000); 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;
} }
float MiniAudioEngine::getCursor(int layer) float MiniAudioEngine::getCursor(int layer)
@ -397,12 +167,12 @@ float MiniAudioEngine::getCursor(int layer)
ma_result result; ma_result result;
float ret = 0; float ret = 0;
if (m_mae.mediaLoaded[layer] == false) if (m_mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST; return MA_DOES_NOT_EXIST;
result = ma_sound_get_cursor_in_seconds(&m_mae.sounds[layer], &ret); result = ma_sound_get_cursor_in_seconds(&m_currentSound[layer], &ret);
if (result != MA_SUCCESS) if (result != MA_SUCCESS)
{ {
cout << "Error" << result << ": Can not get cursor " << layer << endl; qWarning("%i can not get cursor error %i", layer, result);
ret = MA_ERROR; ret = MA_ERROR;
} }
return ret; return ret;
@ -414,158 +184,97 @@ ma_result MiniAudioEngine::printFormatInfo(int layer)
ma_uint32 channels; ma_uint32 channels;
ma_uint32 sampleRate; ma_uint32 sampleRate;
if (m_mae.mediaLoaded[layer] == false) if (m_mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST; return MA_DOES_NOT_EXIST;
ma_result result = ma_sound_get_data_format(&m_mae.sounds[layer], \ ma_result result = ma_sound_get_data_format(&m_currentSound[layer], &format, &channels, &sampleRate, NULL, 0);
&format, &channels, &sampleRate, NULL, 0); if (result != MA_SUCCESS)
if (result != MA_SUCCESS) { qWarning("%i failed to get data format %i\n", layer, result);
cout << "Error " << result << ": Failed to get data format " << layer; else
cout << endl; qInfo() << "Layer:" << layer << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels;
} else {
cout << "Layer:" << layer << " ";
cout << m_mae.currentStatus[layer].media.toLatin1().data();
cout << " samples/sec:" << sampleRate << " format:" << format;
cout << " channels:" << channels << endl;
}
return result; return result;
} }
// Expects between 0 and 65535 vol value // Expects between 0 and 1 vol value
void MiniAudioEngine::volChanged(int layer, int vol) void MiniAudioEngine::volChanged(int layer, float vol)
{ {
m_mae.currentStatus[layer].vol = vol; if (m_mediaLoaded[layer] == false)
if (m_mae.mediaLoaded[layer] == MA_FALSE && m_mae.currentStatus[layer].updated)
return; return;
float db = ((float)vol / 771.0f) - 85.0f; if (vol >= 1)
if (db <= -85.0f) { vol = 0.99f;
db = 0; ma_sound_group_set_fade_in_milliseconds(&m_currentSound[layer], -1, vol, FADE_TIME);
} else m_currentLayerValues[layer].vol = vol;
db = ma_volume_db_to_linear(db);
ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, FADE_TIME);
} }
void MiniAudioEngine::panChanged(int layer, float value) void MiniAudioEngine::panChanged(int layer, float value)
{ {
float result; float result;
m_mae.currentStatus[layer].pan = value; if (m_mediaLoaded[layer] == false)
if (m_mae.mediaLoaded[layer] == false)
return; return;
result = (value / 128.0) - 1.0; result = (value / 128.0) - 1.0;
ma_sound_group_set_pan(&m_mae.sounds[layer], result); ma_sound_group_set_pan(&m_currentSound[layer], result);
m_currentLayerValues[layer].pan = value;
} }
void MiniAudioEngine::pitchChanged(int layer, float value) void MiniAudioEngine::pitchChanged(int layer, float value)
{ {
float pitch; float pitch;
m_mae.currentStatus[layer].pitch = value; if (m_mediaLoaded[layer] == false)
if (m_mae.mediaLoaded[layer] == false)
return; return;
pitch = value / 128.0; pitch = value / 128.0;
ma_sound_group_set_pitch(&m_mae.sounds[layer], pitch); ma_sound_group_set_pitch(&m_currentSound[layer], pitch);
m_currentLayerValues[layer].pitch = value;
} }
ma_result MiniAudioEngine::playbackChanged(int layer, Status status) ma_result MiniAudioEngine::playbackChanged(int layer, Status status)
{ {
ma_result result = MA_SUCCESS; ma_result result = MA_SUCCESS;
float db = 0;
bool loop = false;
m_mae.currentStatus[layer].status = status; if (m_mediaLoaded[layer] == false)
if (m_mae.mediaLoaded[layer] == MA_FALSE)
return MA_DOES_NOT_EXIST; return MA_DOES_NOT_EXIST;
m_mae.currentStatus[layer].updated = false;
switch (status) { switch (status) {
case Status::Paused: case Status::Paused:
result = ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], FADE_TIME); result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
break; break;
case Status::Stopped: case Status::Stopped:
ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], 0.0f); result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
result = this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); result = this->seekToCursor(layer, m_currentLayerValues[layer].cursor);
break; break;
case Status::PlayingLoop: case Status::PlayingLoop:
loop = true; ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0);
if (m_mae.currentStatus[layer].cursor > 0) { ma_sound_set_looping(&m_currentSound[layer], true);
result = this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); result = ma_sound_start(&m_currentSound[layer]);
} break;
case Status::PlayingOnce: case Status::PlayingOnce:
case Status::PlayingFolder: case Status::PlayingFolder:
case Status::PlayingFolderLoop: case Status::PlayingFolderLoop:
case Status::PlayingFolderRandom: case Status::PlayingFolderRandom:
ma_sound_set_looping(&m_mae.sounds[layer], loop); ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0);
if (ma_sound_is_playing(&m_mae.sounds[layer])) break; ma_sound_set_looping(&m_currentSound[layer], false);
ma_sound_set_stop_time_in_milliseconds(&m_mae.sounds[layer], ~(ma_uint64)0); result = ma_sound_start(&m_currentSound[layer]);
db = (m_mae.currentStatus[layer].vol / 771.0f) - 85.0f; break;
if (db <= -85.0f) db = 0;
else db = ma_volume_db_to_linear(db);
result = ma_sound_start(&m_mae.sounds[layer]);
ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0.000001f, 0.000000f, FADE_TIME);
if (m_mae.currentStatus[layer].cursor > 0)
usleep(FADE_TIME * 1500); // avoid glitch when load when seeking
ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0, db, FADE_TIME * 2);
default: default:
break; break;
} }
m_mae.currentStatus[layer].updated = true; if (result == MA_SUCCESS)
m_currentLayerValues[layer].status = status;
return result; return result;
} }
ma_result MiniAudioEngine::setRangePoint(int layer, int cursor)
{
ma_result result = MA_SUCCESS;
ma_uint64 end = 0, start;
if (m_mae.mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST;
if (cursor == 0)
start = 0;
else {
result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end);
if (result != MA_SUCCESS) { return result; }
start = (cursor * end) / 65535;
}
result = ma_data_source_set_range_in_pcm_frames(&m_mae.sounds[layer].pDataSource, start, end);
if (result != MA_SUCCESS)
cout << "ERROR " << result << " :set range point" << endl;
return (result);
}
ma_result MiniAudioEngine::setLoopPoint(int layer, int cursor)
{
ma_result result = MA_SUCCESS;
ma_uint64 end = 0, start;
if (m_mae.mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST;
if (cursor == 0)
start = 0;
else {
result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end);
if (result != MA_SUCCESS) { return result; }
start = (cursor * end) / 65535;
}
result = ma_data_source_set_loop_point_in_pcm_frames(&m_mae.sounds[layer].pDataSource, start + 1, end - 1);
if (result != MA_SUCCESS)
cout << "ERROR " << result << " :set loop point" << endl;
return (result);
}
ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) ma_result MiniAudioEngine::seekToCursor(int layer, int cursor)
{ {
ma_result result = MA_SUCCESS; ma_result result = MA_SUCCESS;
ma_uint64 end = 0, start; ma_uint64 end, start;
if (m_mae.mediaLoaded[layer] == false) if (m_mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST; return MA_DOES_NOT_EXIST;
if (cursor == 0) result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &end);
start = 0; if (result != MA_SUCCESS) { return result; }
else { start = (cursor * end) / 65025;
result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end); result = ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start);
if (result != MA_SUCCESS) { return result; } //if (result != MA_SUCCESS) { return result; }
start = (cursor * end) / 65535; //result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end);
}
result = ma_sound_seek_to_pcm_frame(&m_mae.sounds[layer], start);
return (result); return (result);
} }
@ -573,156 +282,21 @@ ma_result MiniAudioEngine::setCursor(int layer, int cursor)
{ {
ma_result result = MA_SUCCESS; ma_result result = MA_SUCCESS;
m_mae.currentStatus[layer].cursor = cursor; m_currentLayerValues[layer].cursor = cursor;
result = this->seekToCursor(layer, cursor); result = this->seekToCursor(layer, cursor);
return (result); return (result);
} }
Status MiniAudioEngine::getStatus(int layer) Status MiniAudioEngine::getStatus(int layer)
{ {
return m_mae.currentStatus[layer].status; return m_currentLayerValues[layer].status;
} }
void MiniAudioEngine::refreshValues(int layer) void MiniAudioEngine::refreshValues(int layer)
{ {
this->panChanged(layer, m_mae.currentStatus[layer].pan); this->seekToCursor(layer, m_currentLayerValues[layer].cursor);
this->pitchChanged(layer, m_mae.currentStatus[layer].pitch); this->panChanged(layer, m_currentLayerValues[layer].pan);
this->playbackChanged(layer, m_mae.currentStatus[layer].status); this->volChanged(layer, m_currentLayerValues[layer].vol);
} this->pitchChanged(layer, m_currentLayerValues[layer].pitch);
this->playbackChanged(layer, m_currentLayerValues[layer].status);
ma_result MiniAudioEngine::filterParamChanged(int layer, int channel, int value)
{
ma_result result = MA_SUCCESS;
filterBank *fb = &m_mae.filters[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;
}
} else if (channel == SEND1) {
float db = ((float)value / 3.0f) - 85.0f;
if (db <= -85.0f) {
db = 0;
} else
db = ma_volume_db_to_linear(db);
ma_node_set_output_bus_volume(&fb->output, 0, db);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed set Send 1 Volume." << endl;
return result;
}
} else if (channel == SEND2) {
float db = ((float)value / 3.0f) - 85.0f;
if (db <= -85.0f) {
db = 0;
} else
db = ma_volume_db_to_linear(db);
ma_node_set_output_bus_volume(&fb->output, 1, db);
if (result != MA_SUCCESS) {
cout << "ERROR " << result << ": Failed set Send 2 Volume." << endl;
}
return result;
}
return (result);
}
bool MiniAudioEngine::setBypass(int audioDevice, int layer, bool bypass)
{
(void)audioDevice;
filterBank *fb = &m_mae.filters[layer];
if (bypass) {
ma_node_set_output_bus_volume(&fb->input, 1, 1.0f);
ma_node_set_output_bus_volume(&fb->input, 0, 0.0f);
} else {
ma_node_set_output_bus_volume(&fb->input, 1, 0.0f);
ma_node_set_output_bus_volume(&fb->input, 0, 1.0f);
}
return true;
} }

View file

@ -1,73 +1,25 @@
#ifndef MINIAUDIOENGINE_H #ifndef MINIAUDIOENGINE_H
#define MINIAUDIOENGINE_H #define MINIAUDIOENGINE_H
#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS
#define MA_ENABLE_JACK
#define MA_DISABLE_PULSE
#define MA_NO_GENERATION
#define MA_DEBUG_OUTPUT
#define MA_LOG_LEVEL_DEBUG DEBUG
#define MINIAUDIO_IMPLEMENTATION #define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h" #include "miniaudio.h"
#include "ma_writer_node.h" #include "defines.h" // MAX_LAYERS
#include <bits/stdc++.h> #include <QDebug> // prints messages
using namespace std; #define MA_DEBUG_OUTPUT
#include "defines.h"
typedef struct
{
ma_splitter_node input;
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_vumeter_node vumeter;
ma_splitter_node output;
} filterBank;
typedef struct
{
ma_engine engines[MAX_AUDIODEVICES];
ma_device devices[MAX_AUDIODEVICES];
filterBank filters[MAX_LAYERS];
ma_writer_node sendAuxNode[MAX_AUDIODEVICES];
ma_pcm_rb auxBuffers[MAX_AUDIODEVICES];
ma_node_graph ng;
layerData currentStatus[MAX_LAYERS];
ma_sound sounds[MAX_LAYERS];
ma_resource_manager resourceManager;
ma_context context;
ma_device_info* pPlaybackDeviceInfos;
ma_device_info pSelectedPlaybackDeviceInfos[MAX_AUDIODEVICES];
ma_uint32 playbackDeviceCount;
ma_uint32 devicesSelected;
ma_bool8 mediaLoaded[MAX_LAYERS];
uint layersQty;
uint *audioDevicesId;
uint audioDevicesQty;
ma_data_source_node dataSupplyNode[MAX_AUDIODEVICES];
ma_data_source_rb dataSourceRB[MAX_AUDIODEVICES];
} MAE;
class MiniAudioEngine class MiniAudioEngine
{ {
friend class libreMediaServerAudio; friend class libreMediaServerAudio;
public: public:
MiniAudioEngine();
void stopEngine();
bool startEngine(uint id);
static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
protected: protected:
MiniAudioEngine(); ma_result loadMedia(int layer, char *media );
void stopEngine(); void volChanged(int layer, float vol);
bool startEngine(uint layersQty, uint* audioDevicesID, uint audioDevicesQty);
ma_result loadMedia(int layer, char *media);
void volChanged(int layer, int vol);
void panChanged(int layer, float pan); void panChanged(int layer, float pan);
void pitchChanged(int layer, float pitch); void pitchChanged(int layer, float pitch);
ma_result playbackChanged(int layer, Status status); ma_result playbackChanged(int layer, Status status);
@ -77,29 +29,27 @@ protected:
float getCursor(int layer); float getCursor(int layer);
Status getStatus(int layer); Status getStatus(int layer);
inline float getVol(int layer) { inline float getVol(int layer) {
return ma_sound_get_volume(&m_mae.sounds[layer]); return ma_sound_get_volume(&m_currentSound[layer]); }
}; inline bool getAtEnd(int layer) { return m_currentSound[layer].atEnd; }
inline bool getAtEnd(int layer) { return m_mae.sounds[layer].atEnd; }
ma_result filterParamChanged(int layer, int channel, int value);
bool setBypass(int audioDevice, int layer, bool bypass);
inline float getLevel(int layer) {
float level = ma_vumeter_node_get_level(&m_mae.filters[layer].vumeter);
return ma_volume_linear_to_db(level) - 4.0f;
};
char* getDeviceName(uint id);
private: private:
MAE m_mae; 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_sound m_currentSound[MAX_LAYERS];
ma_bool8 m_mediaLoaded[MAX_LAYERS];
layerData m_currentLayerValues[MAX_LAYERS];
ma_result startDevices();
ma_result getAllAudioDevices(); ma_result getAllAudioDevices();
ma_result startDevice(uint id);
ma_result startContext(); ma_result startContext();
void refreshValues(int layer); void refreshValues(int layer);
ma_result seekToCursor(int layer, int cursor); ma_result seekToCursor(int layer, int cursor);
ma_result setNodeGraph();
ma_result createFilterBank(uint layer);
ma_result setLoopPoint(int layer, int cursor);
ma_result setRangePoint(int layer, int cursor);
}; };
#endif // MINIAUDIOENGINE_H #endif // MINIAUDIOENGINE_H

View file

@ -51,14 +51,16 @@ void Settings::readFromFile(QString file) {
if(xmlReader->name() == "audioDevice") { if(xmlReader->name() == "audioDevice") {
m_audioDeviceQty = xmlReader->attributes().value("devicesNumber").toLocal8Bit().toInt(); m_audioDeviceQty = xmlReader->attributes().value("devicesNumber").toLocal8Bit().toInt();
for (uint i = 0; i < m_audioDeviceQty; i++) for (uint i = 0; i < m_audioDeviceQty; i++)
{
m_audioDeviceId[i] = xmlReader->attributes().value(QString("id%1").arg(i)).toLocal8Bit().toInt(); m_audioDeviceId[i] = xmlReader->attributes().value(QString("id%1").arg(i)).toLocal8Bit().toInt();
}
} }
if(xmlReader->name() == "layer") { if(xmlReader->name() == "layer") {
dmxSetting temp; dmxSetting temp;
temp.address = xmlReader->attributes().value("dmx").toLocal8Bit().toInt() - 1; temp.address = xmlReader->attributes().value("dmx").toLocal8Bit().toInt() - 1;
temp.universe = xmlReader->attributes().value("universe").toLocal8Bit().toInt(); temp.universe = xmlReader->attributes().value("universe").toLocal8Bit().toInt();
temp.layer = xmlReader->attributes().value("id").toLocal8Bit().toInt(); temp.layer = xmlReader->attributes().value("id").toLocal8Bit().toInt();
temp.audioDevice = xmlReader->attributes().value("audioDevice").toLocal8Bit().toInt();
m_settings.append(temp); m_settings.append(temp);
if (!m_universe.contains(temp.universe)) { if (!m_universe.contains(temp.universe)) {
m_universe.insert(temp.universe); m_universe.insert(temp.universe);

View file

@ -22,8 +22,7 @@ public:
inline QString getPathMedia() { return m_pathmedia; } inline QString getPathMedia() { return m_pathmedia; }
inline QList<dmxSetting> getDmxSettings() { return m_settings; } inline QList<dmxSetting> getDmxSettings() { return m_settings; }
inline int getLayersNumber() { return m_layersNumber; } inline int getLayersNumber() { return m_layersNumber; }
inline uint *getAudioDeviceId() { return m_audioDeviceId; } inline int getAudioDeviceId() { return m_audioDeviceId[0]; }
inline uint getAudioDeviceQty() { return m_audioDeviceQty; }
inline bool getShowUi() { return m_ui; } inline bool getShowUi() { return m_ui; }
void readFile(); void readFile();
void readFromFile(QString file); void readFromFile(QString file);

View file

@ -1,8 +1,5 @@
#include "slidergroup.h" #include "slidergroup.h"
#include <cmath> #include <QCursor>
#include <QWidget>
#include <QVBoxLayout>
SliderGroup::SliderGroup(QString name, SliderGroup::SliderGroup(QString name,
int min, int min,
int max, int max,
@ -10,69 +7,46 @@ SliderGroup::SliderGroup(QString name,
QWidget *parent) QWidget *parent)
: QWidget(parent) : QWidget(parent)
{ {
QBoxLayout *layout; QVBoxLayout *layout = new QVBoxLayout;
if (decimals) {
layout = new QVBoxLayout;
slider.setOrientation(Qt::Vertical);
}
else {
layout = new QHBoxLayout;
slider.setOrientation(Qt::Horizontal);
slider.setMaximumHeight(15);
valueBox.setMaximumHeight(15);
}
layout->setAlignment(Qt::AlignHCenter); layout->setAlignment(Qt::AlignHCenter);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
slider.setFocusPolicy(Qt::StrongFocus); //this->setMaximumWidth(40);
slider.setTickPosition(QSlider::TicksBothSides); slider = new QSlider(Qt::Orientation::Vertical);
slider.setTickInterval((max - min) / 11); slider->setFocusPolicy(Qt::StrongFocus);
slider.setMinimumHeight(0); slider->setTickPosition(QSlider::TicksBothSides);
slider.setSingleStep(1); slider->setTickInterval((max - min) / 11);
slider.setRange(min, max); slider->setMinimumHeight(0);
slider.setValue(0); slider->setSingleStep(1);
slider.setMinimumWidth(50); slider->setRange(min, max);
slider.setToolTip(name); slider->setValue(0);
slider.setStyleSheet("QSlider {" slider->setMinimumWidth(50);
"border: 1px solid #aa8895;" slider->setToolTip(name);
"background: #20182d;" slider->setStyleSheet("QSlider {"
"margin: 0px;}" "border: 1px solid #5a4855;"
"QSlider::groove:vertical {" "margin: 0px;"
"border: 1px solid #999999;" "height: 200px;"
"width: 25px;" "width: 50px;}"
"margin: -4px;}" );
"QSlider::handle:vertical {" slider->setContentsMargins(0, 0, 0, 0);
"background: white;" valueBox = new QDoubleSpinBox();
"border: 1px solid #5c5c5c;" valueBox->setFocusPolicy(Qt::NoFocus);
"width: 29px;" valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
"height: 7px;" valueBox->setMinimumWidth(50);
"margin: -2px;" valueBox->setRange(min, max);
"border-radius: 2px;}" valueBox->setValue(0);
"Qslider::tickmarks:vertical {background: white;" valueBox->setDecimals(decimals);
"color: white;}" valueBox->setObjectName(name);
"QSlider::add-page:vertical {background: blue;}" valueBox->setToolTip(name);
"QSlider::sub-page:vertical {background: #20182d;}"); valueBox->setAlignment(Qt::AlignHCenter);
slider.setContentsMargins(0, 0, 0, 0); valueBox->setContentsMargins(0, 0, 0, 0);
valueBox.setFocusPolicy(Qt::NoFocus); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
valueBox.setButtonSymbols(QAbstractSpinBox::NoButtons); //connect(slider, SIGNAL(mousePressEvent(QMouseEvent)), this, SLOT(mousePressEvent(QMouseEvent *)));
valueBox.setMinimumWidth(50); layout->addWidget(slider);
if (decimals) { layout->addWidget(valueBox);
valueBox.setRange(-84.0f, 0.0f); this->setStyleSheet("border: 1px solid #5a4855;"
valueBox.setSpecialValueText("-inf"); "width: 50px;"
} else "margin: 0px;"
valueBox.setRange(min, max); "background-color: #383034;"
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(click()), this, SLOT(enableSlider()));
layout->addWidget(&slider);
layout->addWidget(&valueBox);
this->setStyleSheet("border: 1px solid #aa8895;"
"background-color: black;"
"margin: 1px;"
); );
layout->setSpacing(0); layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@ -81,33 +55,27 @@ SliderGroup::SliderGroup(QString name,
void SliderGroup::sliderValueChanged(int value) void SliderGroup::sliderValueChanged(int value)
{ {
valueBox.blockSignals(true); valueBox->blockSignals(true);
if (valueBox.decimals()) { valueBox->setValue(value);
float db = ((float)value / 771.0f) - 85.0f; valueBox->blockSignals(false);
if (db <= -84.5f) {
valueBox.setSpecialValueText("-inf");
} else
valueBox.setValue(db);
} else {
valueBox.setValue(value);
}
valueBox.blockSignals(false);
emit valueChanged(value); emit valueChanged(value);
}; };
void SliderGroup::setValue(float value) void SliderGroup::setValue(float value)
{ {
float db; slider->blockSignals(true);
valueBox->blockSignals(true);
slider.blockSignals(true); if (int(value) != slider->value())
valueBox.blockSignals(true); slider->setValue(value);
if (int(value) != slider.value()) valueBox->setValue(value);
slider.setValue(value); slider->blockSignals(false);
if (valueBox.decimals()) { valueBox->blockSignals(false);
db = (float)(value / 771.0f) - 85.0f; }
valueBox.setValue(db);
} else void SliderGroup::mousePressEvent(QMouseEvent* event) {
valueBox.setValue(value); Q_UNUSED(event);
slider.blockSignals(false); if (slider->isEnabled())
valueBox.blockSignals(false); slider->setDisabled(true);
else
slider->setDisabled(false);
} }

View file

@ -1,16 +1,15 @@
#ifndef SLIDERGROUP_H #ifndef SLIDERGROUP_H
#define SLIDERGROUP_H #define SLIDERGROUP_H
#include <QObject> #include <QGroupBox>
#include <QWidget> #include <QDoubleSpinBox>
#include <QDebug> #include <QVBoxLayout>
#include <QSlider>
#include "clickabledoublespinbox.h"
#include "clickableslider.h"
class SliderGroup : public QWidget class SliderGroup : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
SliderGroup(QString name, SliderGroup(QString name,
int min, int min,
@ -26,12 +25,10 @@ public slots:
void sliderValueChanged(int value); void sliderValueChanged(int value);
private: private:
ClickableSlider slider; QSlider *slider;
ClickableDoubleSpinBox valueBox; QDoubleSpinBox *valueBox;
private slots:
void enableSlider() { slider.setEnabled(true); }
void mousePressEvent(QMouseEvent* event);
}; };
#endif #endif