Compare commits

...

13 commits

Author SHA1 Message Date
snt
7631e54d51 nuevos modos reproducción en personalidad 2024-05-09 14:10:46 +02:00
snt
f67ad9b1e1 nuevos modos de reproducción: Play Folder, Play Folder Loop, Play Folder
Random.
2024-05-09 14:07:24 +02:00
snt
cd0105c9f9 quitado pequeño glitch cuando entry point no es cero. stop con fade out
para evitar click.
2024-05-08 18:56:16 +02:00
snt
0979c3608e puliendo 2024-05-07 20:57:13 +02:00
snt
7a9c0cd0ac libreMediaServer no refresca directamente la ui, solo actualiza
valores en  audiowidget. la ui se actualiza con un timer en audiowidget.
Quitadas señales en todo, mejora rendimiento. fade en volumen basado en
la trama dmx (25 ms) para evitar clicks. refresca los valores de la capa
cuando carga un media. Ui Ok. nuevo formato de archivo de configuración
xml.
2024-05-07 20:23:09 +02:00
snt
5915d4898e cambida señal dmx por llamada directa al método, reduce un 20% de cpu en
mi ordenador.
Restringida actualización de entry point como volumen, sigue cascando la
búsqueda en mp3, wav va fino.
2024-05-05 21:44:21 +02:00
snt
d34b972a54 mejor rendimiento 2024-05-05 17:36:20 +02:00
snt
246a8a2f98 restringe las señales de los sliders, en vez de actualizar en directo,
sólo actualiza con el timer refreshGui.
2024-05-05 14:39:21 +02:00
snt
ef653553d9 update magicq head qith new personality 2024-05-04 19:38:43 +02:00
snt
389966782d open file dialog clicking on file/folder labels.
se atasca y se pone a 100%, los faders no refrescan como debieran,
parece que se saturan las señales.
2024-05-04 19:35:12 +02:00
snt
5a7a82736f working UI controls 2024-05-04 16:45:05 +02:00
snt
88704cd726 better ui, change colors and font 2024-05-04 15:05:45 +02:00
snt
1fccbf64fd refactorizada la GUI, ola y audioengine se ejecutan fuera de widgets.
Opción para ejecutar sin mostrar la GUI. Los controles de la interfaz no
son reactivos, no están conectados a las órdenes de audio.
cambio en la personalidad dmx para procesar los canales en mejor orden.
evita mandar dos veces los canales dobles por cada dmx frame, incluyendo file/folder.
2024-05-04 01:46:45 +02:00
31 changed files with 1135 additions and 702 deletions

View file

@ -1,76 +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>

View file

@ -4,39 +4,45 @@ Libre Media Server Audio - An Open source Media Server for arts and performing.
https://git.criptomart.net/libremediaserver
*******************************************************************************
Lbre Media Server ChangeLog
Libre Media Server ChangeLog
v 0.2.0 Antigona (24/04/2024)
+ change engine to miniaudio because is imposible pan in SFML and it has not access to low API and audio processing.
+ Refactor all audio methods to MiniAudioEngine.
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.
+ Refactor all audio methods to MiniAudioEngine class.
+ Select sound device output.
+ pan.
+ Show faders values.
+ play offset.
+ Pan.
+ Show faders values. New SliderGroup class.
+ Entry Point 16 bits.
+ Refactor AudioMasterWidget to AudioDMXReceptionWidget.
+ mp3, flac, wav (mp3 has given some errors seeking cursor...).
+ settings dialog not working, only read the conf file at startup.
+ variable number of layers.
+ olathread, send double channels only once for each dmx frame buffer.
+ Read mp3, flac, wav (mp3 has given some errors seeking cursor...).
+ Removed settings dialog, only read xml conf file at startup.
+ Real dynamic variable number of layers based on conf file setting.
+ OlaThread send double channels (volume, entry point, load media) only once for each dmx frame buffer.
+ Terminal mode without graphical interface. All audio methods has been refactorized out of QWidget world.
+ 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);
+ 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.
v 0.1.3 Unreleased (19/04/2024)
v 0.1.3 Leúcade (19/04/2024)
+ Ubuntu 22.04 jammy.
+ Use SFML as audio engine.
+ Qt 5.15.3.
+ pitch.
+ loop.
+ Pitch.
+ Loop.
v 0.1.2 Mayordomo (12/08/2015)
- GUI config
- Several bugs tested in real world
- variable layers
- SFML as audio engine
- GUI config.
- Several bugs tested in real world.
- Variable layers.
- SFML as audio engine.
v 0.1.1 Pascual (24/09/2014)
+ First Version: 4 layers playing .ogg
+ Needs Open Lighting Arquitecture => 0.9.0
+ Pure Data as audio engine
+ First Version: 4 layers playing .ogg.
+ Needs Open Lighting Arquitecture => 0.9.0.
+ Pure Data as audio engine.

View file

@ -1,8 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<dmxSettings fileVersion="1" layersNumber="4" path="/home/snt/Documentos/lab/lms/media/sound">
<audioDevice id="3" />
<layer0 dmx="1" universe="1" />
<layer1 dmx="17" universe="1" />
<layer2 dmx="33" universe="1" />
<layer3 dmx="49" universe="1" />
</dmxSettings>
<lmsAudio ui="1" layersNumber="4" path="../media/sound" >
<audioDevice devicesNumber="2" id0="3" id1="4" />
<layer id="0" dmx="1" universe="1" />
<layer id="1" dmx="17" universe="1" />
<layer id="2" dmx="33" universe="1" />
<layer id="3" dmx="49" universe="1" />
</lmsAudio>

View file

@ -5,7 +5,6 @@ https://git.criptomart.net/libremediaserver
*******************************************************************************
Libre Media Server Roadmap
(en continuo crecimiento...)
v 0.2.x
- skin, UI/UX
@ -20,8 +19,8 @@ v 0.2.2
+ hay que empaquetar OLA, incluirlo en el binario, o implementar sACN y linkarlo estáticamente.
+ https://github.com/ETCLabs/sACN
- Qt6.
- audio processing (eq, rev, compresor, ...) por master y capa.
- CIPT/MSex, send icons play-pause-stop.
- Audio processing (eq, rev, compresor, ...) by master and layer.
- CIPT/MSex, send icons play/pause/stop.
- Rasp build.
- Octopus Sound Card support (6 outputs - 8 inputs).
@ -41,15 +40,15 @@ v 0.2.1
- audio device linked, outputs will be redirected there.
- dmx address + universe settings.
- Rose noise and sine generator in menu to test system.
- Keyboards strokes, load media files from ui.
- Dar la opción clickeando en el widget de tiempo de poner una cuenta atrás en vez de hacia delante.
- 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.
- Logs, verbosity, timestamp.
- Bufgix: depurar errores cuando no carga la librería de medias, cambia el númmero de capas, cambia el universo, etc.
- New control mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH
- Vumeter or indicator about audio output in layer and master.
- New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH
- SettingsDialog.
- Load/save conf file.
- ¿stop offset? is it needed?
- decouple MiniAudioEngine from AudioWidget, starts whith no gui or with audio in a dedicated thread.
- New Status "Iddle" in playbacks if is not loaded.
- check return errors, we are too happy....
- ¿Exit Point? is it needed?
- Hardening: check return errors, try/catch exceptions, i'm too happy....
- Tests: errors on wrong conf file.
v0.2.0:
- Vumeter or indicator about audio output in layer and master, add to sliderGroup.

View file

@ -2,7 +2,10 @@ TEMPLATE = app
TARGET = libremediaserver-audio
QT += webkitwidgets widgets
HEADERS += src/libremediaserver-audio.h \
src/clickablelabel.h \
src/dmxwidget.h \
src/libremediaserver-audio-gui.h \
src/main.h \
src/miniaudio.h \
src/medialibrary.h \
src/miniaudioengine.h \
@ -14,7 +17,9 @@ HEADERS += src/libremediaserver-audio.h \
src/settings.h \
src/slidergroup.h
SOURCES += src/main.cpp \
src/clickablelabel.cpp \
src/dmxwidget.cpp \
src/libremediaserver-audio-gui.cpp \
src/miniaudio.c \
src/libremediaserver-audio.cpp \
src/medialibrary.cpp \
@ -24,10 +29,10 @@ SOURCES += src/main.cpp \
src/audiowidget.cpp \
src/settings.cpp \
src/slidergroup.cpp
FORMS += src/libremediaserver-audio.ui
CCFLAG += -msse2 -mavx2 #-fsanitize=address -g -O0
FORMS += src/libremediaserver-audio-gui.ui
CCFLAG += -msse2 -mavx2 #-fsanitize=address -g3 -O0
QMAKE_CXXFLAGS += $$(CXXFLAG)
#QMAKE_CXXFLAGS += -fsanitize=address -g -O0
#QMAKE_CXXFLAGS += -fsanitize=address -g3 -O0
QMAKE_CFLAGS += $$(CCFLAG)
QMAKE_LFLAGS += $$(LDFLAG)
LIBS += -lola -lolacommon -ldl -lpthread -lm

View file

@ -1,64 +1,89 @@
#include "audiolayerwidget.h"
#include <QComboBox>
AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name, int layer):
QGroupBox(parent)
AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer):
QWidget(parent)
, m_layer(layer)
, m_suspendResumeButton(0)
{
this->setTitle(name);
QVBoxLayout *layout = new QVBoxLayout;
QHBoxLayout *progressTime = new QHBoxLayout;
m_progressTime = new QTimeEdit;
m_progressTime->text();
m_progressTime->setDisplayFormat("h:mm:ss:zzz");
m_progressTime->setReadOnly(true);
m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons);
m_progressTime->setMaximumWidth(90);
m_progressTime->setFocusPolicy(Qt::NoFocus);
progressTime->addWidget(m_progressTime);
m_totalTimeValue = new QTimeEdit;
m_totalTimeValue->setDisplayFormat("h:mm:ss:zzz");
m_totalTimeValue->setReadOnly(true);
m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons);
m_totalTimeValue->setMaximumWidth(90);
m_totalTimeValue->setFocusPolicy(Qt::NoFocus);
progressTime->addWidget(m_totalTimeValue);
layout->addLayout(progressTime);
m_progressSlider = new QSlider(Qt::Horizontal);
m_progressSlider->setFocusPolicy(Qt::NoFocus);
layout->addWidget(m_progressSlider);
QGridLayout *status = new QGridLayout;
m_statusValue = new QLabel;
status->addWidget(m_statusValue, 0, 0);
m_folderValue = new QLabel;
m_folderValue->setMaximumWidth(200);
status->addWidget(m_folderValue, 1, 0);
m_fileValue = new QLabel;
m_fileValue->setMaximumWidth(200);
status->addWidget(m_fileValue, 2, 0);
layout->addLayout(status);
QHBoxLayout *volumeBox = new QHBoxLayout;
m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL);
volumeBox->addWidget(m_volume);
connect(m_volume, SIGNAL(valueChanged(float)), this, SLOT(volumeChanged(float)));
m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL);
volumeBox->addWidget(m_pan);
connect(m_pan, SIGNAL(valueChanged(float)), this, SLOT(panChanged(float)));
m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL);
volumeBox->addWidget(m_pitch);
connect(m_pitch, SIGNAL(valueChanged(float)), this, SLOT(pitchChanged(float)));
layout->addLayout(volumeBox);
QVBoxLayout *playback = new QVBoxLayout;
m_folderValue = new ClickableLabel;
m_folderValue->setMaximumWidth(160);
m_folderValue->setAlignment(Qt::AlignLeft);
m_folderValue->setStyleSheet(
"color: white;"
"background-color: black;"
);
playback->addWidget(m_folderValue);
m_fileValue = new ClickableLabel;
connect(m_fileValue, 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(
"color: white;"
"background-color: black;"
);
playback->addWidget(m_fileValue);
playback->setSpacing(0);
playback->setContentsMargins(0, 0, 0, 0);
layout->addLayout(playback);
m_suspendResumeButton = new QPushButton(this);
m_suspendResumeButton->setText(StatusStr[Status::Stopped]);
m_suspendResumeButton->setText(StatusStr[Status::Iddle]);
connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume()));
layout->addWidget(m_suspendResumeButton);
m_progress = new QProgressBar(this);
m_progress->setOrientation(Qt::Horizontal);
m_progress->setRange(0, 0);
m_progress->setValue(0);
m_progress->setFormat("%v / %m");
layout->addWidget(m_progress);
m_progressTime = new QTimeEdit;
m_progressTime->setToolTip("Current Time");
m_progressTime->setObjectName("Current Time");
m_progressTime->setDisplayFormat("mm:ss:zz");
m_progressTime->setReadOnly(true);
m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons);
m_progressTime->setMinimumWidth(80);
m_progressTime->setMaximumWidth(80);
m_progressTime->setFocusPolicy(Qt::NoFocus);
m_progressTime->setAlignment(Qt::AlignHCenter);
m_progressTime->setContentsMargins(0,0,0,0);
m_totalTimeValue = new QTimeEdit;
m_totalTimeValue->setObjectName("Track Length");
m_totalTimeValue->setToolTip("Track Length");
m_totalTimeValue->setDisplayFormat("mm:ss:zzz");
m_totalTimeValue->setReadOnly(true);
m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons);
m_totalTimeValue->setMinimumWidth(80);
m_totalTimeValue->setFocusPolicy(Qt::NoFocus);
m_totalTimeValue->setAlignment(Qt::AlignHCenter);
m_totalTimeValue->setContentsMargins(0,0,0,0);
QHBoxLayout *status = new QHBoxLayout;
status->addWidget(m_progressTime);
status->addWidget(m_totalTimeValue);
layout->addLayout(status);
QHBoxLayout *volumeBox = new QHBoxLayout;
m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL);
volumeBox->addWidget(m_volume);
connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL);
volumeBox->addWidget(m_pan);
connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(panChanged(int)));
m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL);
volumeBox->addWidget(m_pitch);
volumeBox->setSpacing(0);
volumeBox->setContentsMargins(0, 0, 0, 0);
connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int)));
layout->addLayout(volumeBox);
layout->setAlignment(Qt::AlignHCenter);
layout->setSpacing(0);
layout->setContentsMargins(2, 2, 2, 2);
this->setLayout(layout);
}
@ -68,17 +93,17 @@ AudioLayerWidget::~AudioLayerWidget()
}
// From UI.
void AudioLayerWidget::volumeChanged(float value)
void AudioLayerWidget::volumeChanged(int value)
{
emit(uiSliderChanged(m_layer, Slider::Volume, value));
}
void AudioLayerWidget::panChanged(float value)
void AudioLayerWidget::panChanged(int value)
{
emit(uiSliderChanged(m_layer, Slider::Pan, value));
}
void AudioLayerWidget::pitchChanged(float value)
void AudioLayerWidget::pitchChanged(int value)
{
emit(uiSliderChanged(m_layer, Slider::Pitch, value));
}
@ -93,12 +118,28 @@ void AudioLayerWidget::toggleSuspendResume()
break;
case Status::Paused:
case Status::Stopped:
this->setPlaybackStatus(Status::PlayingOnce);
emit uiPlaybackChanged(m_layer, Status::PlayingOnce);
this->setPlaybackStatus(Status::PlayingLoop);
emit uiPlaybackChanged(m_layer, Status::PlayingLoop);
case Status::Iddle:
break;
}
}
void AudioLayerWidget::openMediaDialog()
{
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter(tr("Sound Tracks (*.mp3 *.mp3 *.flac *.wav"));
dialog.setViewMode(QFileDialog::Detail);
dialog.setDirectory(Settings::getInstance()->getPathMedia());
if (!dialog.exec())
return;
QStringList fileNames;
fileNames = dialog.selectedFiles();
emit uiLoadMedia(m_layer, fileNames.at(0));
this->setMediaFile(fileNames.at(0));
}
// from DMX signals
void AudioLayerWidget::setVol(float vol)
{
@ -121,7 +162,7 @@ void AudioLayerWidget::setPitch(int pitch)
m_pitch->blockSignals(false);
}
void AudioLayerWidget::fileLoaded(QString file)
void AudioLayerWidget::setMediaFile(QString file)
{
QStringList list = file.split("/");
int size = list.size();
@ -131,41 +172,36 @@ void AudioLayerWidget::fileLoaded(QString file)
}
}
void AudioLayerWidget::setPlaybackStatus(Status status)
void AudioLayerWidget::setPlaybackStatus(Status s)
{
m_status = status;
if (status == Status::Stopped)
m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(0));
m_statusValue->blockSignals(true);
Status status = static_cast<Status>(s);
m_suspendResumeButton->blockSignals(true);
m_statusValue->setText(StatusStr[status]);
m_status = status;
m_suspendResumeButton->setText(StatusStr[status]);
switch (m_status) {
case Status::Paused:
m_statusValue->setStyleSheet("QLabel { color : red; }");
break;
case Status::PlayingLoop:
case Status::PlayingOnce:
m_statusValue->setStyleSheet("QLabel { color : green; }");
break;
case Status::Stopped:
m_statusValue->setStyleSheet("QLabel { color : red; }");
break;
}
m_statusValue->blockSignals(false);
m_suspendResumeButton->blockSignals(false);
}
void AudioLayerWidget::durationChanged(float dur)
void AudioLayerWidget::setDuration(float dur)
{
dur *= 1000;
m_progressSlider->setMaximum(dur);
m_progress->blockSignals(true);
m_progressTime->blockSignals(true);
m_totalTimeValue->blockSignals(true);
m_progress->setRange(0, dur);
m_progress->setValue(0);
m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(0));
m_totalTimeValue->setTime(QTime::fromMSecsSinceStartOfDay(dur));
m_progress->blockSignals(false);
m_progressTime->blockSignals(false);
m_totalTimeValue->blockSignals(false);
}
void AudioLayerWidget::refreshUi(float progress)
void AudioLayerWidget::setCurrentTime(float progress)
{
progress *= 1000;
m_progressSlider->setValue(progress);
m_progress->blockSignals(true);
m_progressTime->blockSignals(true);
m_progress->setValue(progress);
m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(progress));
m_progress->blockSignals(false);
m_progressTime->blockSignals(false);
}

View file

@ -3,53 +3,57 @@
#include <QPushButton>
#include <QTimeEdit>
#include <QLabel>
#include <QFileDialog>
#include <QProgressBar>
#include "defines.h"
#include "slidergroup.h"
#include "clickablelabel.h"
#include "settings.h"
class AudioLayerWidget : public QGroupBox
class AudioLayerWidget : public QWidget
{
Q_OBJECT
public:
explicit AudioLayerWidget(QWidget *parent = 0, QString name = "Layer", int layer = 0);
explicit AudioLayerWidget(QWidget *parent = 0, int layer = 0);
~AudioLayerWidget();
void setVol(float vol);
void resume();
void setPan(int pan);
void setPitch(int pitch);
void setLoop(bool on);
void setPlaybackStatus(Status status);
inline Status getPlaybackStatus() { return m_status; }
private:
Status m_status;
int m_layer;
QPushButton *m_suspendResumeButton;
QLabel * m_statusValue;
QLabel *m_fileValue;
QLabel * m_folderValue;
ClickableLabel *m_fileValue;
ClickableLabel * m_folderValue;
SliderGroup *m_volume;
SliderGroup *m_pan;
SliderGroup *m_pitch;
QSlider *m_progressSlider;
QTimeEdit *m_progressTime;
QTimeEdit *m_totalTimeValue;
QProgressBar *m_progress;
// From DMX
public slots:
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
private slots:
void openMediaDialog();
void toggleSuspendResume();
void volumeChanged(float vol);
void panChanged(float pan);
void pitchChanged(float pitch);
void fileLoaded(QString file);
void durationChanged(float dur);
void refreshUi(float progress);
void volumeChanged(int vol);
void panChanged(int pan);
void pitchChanged(int pitch);
signals:
void uiPlaybackChanged(int layer, Status s);
void uiSliderChanged(int layer, Slider s, int value);
void uiLoadMedia(int layer, QString s);
};
#endif // AUDIOLAYERWIDGET_H

View file

@ -1,121 +1,100 @@
#include "audiowidget.h"
#include <cmath>
AudioWidget::AudioWidget() :
m_layout(new QHBoxLayout())
, m_refreshUi(new QTimer(this))
AudioWidget::AudioWidget(QWidget *parent) :
QWidget(parent)
, m_layout(new QHBoxLayout())
{
for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) {
AudioLayerWidget *alw = new AudioLayerWidget(this, tr("Layer %1").arg(i + 1), i);
AudioLayerWidget *alw = new AudioLayerWidget(this, i);
m_layout->insertWidget(i, alw);
connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SLOT(uiSliderAction(int, Slider, int)));
connect(alw, SIGNAL(uiPlaybackChanged(int, Status)), this, SLOT(uiChangePlaybackStatus(int, Status)));
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(uiLoadMedia(int, QString)), this, SIGNAL(uiLoadMedia(int, QString)));
m_layerUpdate[i].status = Status::Iddle;
m_layerUpdate[i].duration = 0;
m_layerUpdate[i].media = "";
m_layerUpdate[i].vol = 0;
m_layerUpdate[i].pan = 128;
m_layerUpdate[i].pitch = 128;
m_layerUpdate[i].cursor = 0;
}
m_layout->setSpacing(0);
m_layout->setContentsMargins(1, 1, 1, 1);
setLayout(m_layout);
m_refreshUi = new QTimer(this);
connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi()));
m_refreshUi->start(UI_REFRESH_TIME);
m_refreshUi->start(UI_REFRESH_TIME * 1.5);
}
bool AudioWidget::startEngine(int id)
void AudioWidget::mediaLoaded(int layer, QString file, float duration)
{
return (m_mae.startEngine(id));
}
bool AudioWidget::startEngine()
{
return (m_mae.startEngine(Settings::getInstance()->getAudioDeviceId()));
}
void AudioWidget::stopEngine()
{
m_mae.stopEngine();
}
void AudioWidget::mediaLoaded(int layer, QString file)
{
ma_result result;
if (m_currentMedia[layer].compare(file) == 0 ) {
return;
}
if (!QFile::exists(file)) {
qWarning("Can not access to file %s", file.toLatin1().constData());
return;
}
result = m_mae.loadMedia(layer, file.toLatin1().data());
if (result != MA_SUCCESS) {
qWarning("can not open file %s", file.toLatin1().constData());
return;
}
m_currentMedia[layer] = file;
float pLength = m_mae.getDuration(layer);
qInfo("File loaded: %s - Duration: %f secs", file.toLatin1().constData(), pLength);
m_mae.printFormatInfo(layer);
QLayoutItem * const item = m_layout->itemAt(layer);
dynamic_cast<AudioLayerWidget *>(item->widget())->fileLoaded(file);
dynamic_cast<AudioLayerWidget *>(item->widget())->durationChanged(pLength);
m_layerUpdate[layer].media = file;
m_layerUpdate[layer].duration = duration;
m_layerUpdate[layer].updated = true;
}
void AudioWidget::volChanged(int layer, float vol) {
m_mae.volChanged(layer, vol);
QLayoutItem * const item = m_layout->itemAt(layer);
dynamic_cast<AudioLayerWidget *>(item->widget())->setVol(vol);
m_layerUpdate[layer].vol = vol;
m_layerUpdate[layer].updated = true;
}
void AudioWidget::panChanged(int layer, int pan) {
m_mae.panChanged(layer, pan);
QLayoutItem * const item = m_layout->itemAt(layer);
dynamic_cast<AudioLayerWidget *>(item->widget())->setPan(pan);
m_layerUpdate[layer].pan = pan;
m_layerUpdate[layer].updated = true;
}
void AudioWidget::pitchChanged(int layer, int pitch) {
m_mae.pitchChanged(layer, pitch);
QLayoutItem * const item = m_layout->itemAt(layer);
dynamic_cast<AudioLayerWidget *>(item->widget())->setPitch(pitch);
m_layerUpdate[layer].pitch = pitch;
m_layerUpdate[layer].updated = true;
}
void AudioWidget::playbackChanged(int layer, Status status)
{
m_mae.playbackChanged(layer, status);
QLayoutItem * const item = m_layout->itemAt(layer);
dynamic_cast<AudioLayerWidget *>(item->widget())->setPlaybackStatus(status);
m_layerUpdate[layer].status = status;
m_layerUpdate[layer].updated = true;
}
void AudioWidget::entryPointChanged(int layer, int cursor)
void AudioWidget::cursorChanged(int layer, float cursor)
{
m_mae.setCursor(layer, cursor);
QLayoutItem * const item = m_layout->itemAt(layer);
AudioLayerWidget *aw = dynamic_cast<AudioLayerWidget *>(item->widget());
aw->refreshUi(m_mae.getCursor(layer));
m_layerUpdate[layer].cursor = cursor;
m_layerUpdate[layer].updated = true;
}
void AudioWidget::refreshUi() {
for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) {
QLayoutItem * const item = m_layout->itemAt(i);
AudioLayerWidget *aw = dynamic_cast<AudioLayerWidget *>(item->widget());
Status s = aw->getPlaybackStatus();
if (s == Status::PlayingOnce || s == Status::PlayingLoop) {
aw->refreshUi(m_mae.getCursor(i));
void AudioWidget::refreshUi()
{
for (int i = 0; i < MAX_LAYERS; i++)
{
if (m_layerUpdate[i].updated) {
QLayoutItem * const item = m_layout->itemAt(i);
AudioLayerWidget *alw = dynamic_cast<AudioLayerWidget *>(item->widget());
if (m_layerUpdate[i].vol > -1) {
alw->setVol(m_layerUpdate[i].vol);
m_layerUpdate[i].vol = -1;
}
if (m_layerUpdate[i].cursor > -1) {
alw->setCurrentTime(m_layerUpdate[i].cursor);
m_layerUpdate[i].cursor = -1;
}
if (m_layerUpdate[i].pan > -1) {
alw->setPan(m_layerUpdate[i].pan);
m_layerUpdate[i].pan = -1;
}
if (m_layerUpdate[i].pitch > -1) {
alw->setPitch(m_layerUpdate[i].pitch);
m_layerUpdate[i].pitch = -1;
}
if (m_layerUpdate[i].status != Status::Iddle) {
alw->setPlaybackStatus(m_layerUpdate[i].status);
m_layerUpdate[i].status = Status::Iddle;
}
if (m_layerUpdate[i].duration > -1) {
alw->setMediaFile(m_layerUpdate[i].media);
alw->setDuration(m_layerUpdate[i].duration);
m_layerUpdate[i].duration = -1;
}
m_layerUpdate[i].updated = false;
}
}
}
void AudioWidget::uiSliderAction(int layer, Slider s, int value)
{
switch (s){
case Slider::Volume:
m_mae.volChanged(layer, value);
break;
case Slider::Pan:
m_mae.panChanged(layer, value);
break;
case Slider::Pitch:
m_mae.pitchChanged(layer, value);
break;
}
}
void AudioWidget::uiChangePlaybackStatus(int layer, Status s) {
m_mae.playbackChanged(layer, s);
}

View file

@ -5,7 +5,6 @@
#include "audiolayerwidget.h"
#include "settings.h"
#include "miniaudioengine.h"
#include "defines.h" // MAX_LAYERS
class AudioWidget : public QWidget
@ -13,29 +12,27 @@ class AudioWidget : public QWidget
Q_OBJECT
public:
AudioWidget();
bool startEngine();
bool startEngine(int id);
void stopEngine();
void mediaLoaded(int layer, QString media );
void volChanged(int layer, float vol);
void panChanged(int layer, int pan);
void pitchChanged(int layer, int pitch);
void playbackChanged(int layer, Status status);
void entryPointChanged(int layer, int cursor);
AudioWidget(QWidget *parent = nullptr);
private:
MiniAudioEngine m_mae;
QString m_currentMedia[MAX_LAYERS];
QHBoxLayout *m_layout;
layerData m_layerUpdate[MAX_LAYERS];
QTimer *m_refreshUi;
public slots:
void uiSliderAction(int layer, Slider s, int value);
void uiChangePlaybackStatus(int layer, Status s);
void volChanged(int layer, float vol);
void panChanged(int layer, int pan);
void pitchChanged(int layer, int pitch);
void cursorChanged(int layer, float cursor);
void mediaLoaded(int layer, QString media, float duration);
void playbackChanged(int layer, Status status);
private slots:
void refreshUi();
signals:
void uiPlaybackChanged(int layer, Status s);
void uiSliderChanged(int layer, Slider s, int vol);
void uiLoadMedia(int layer, QString s);
};
#endif // AUDIOWIDGET_H

14
src/clickablelabel.cpp Normal file
View file

@ -0,0 +1,14 @@
#include "clickablelabel.h"
ClickableLabel::ClickableLabel(QWidget *parent, Qt::WindowFlags f)
: QLabel{parent}
{
Q_UNUSED(f);
}
ClickableLabel::~ClickableLabel() {}
void ClickableLabel::mousePressEvent(QMouseEvent* event) {
Q_UNUSED(event);
emit clicked();
}

22
src/clickablelabel.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef CLICKABLELABEL_H
#define CLICKABLELABEL_H
#include <QLabel>
#include <QWidget>
#include <Qt>
class ClickableLabel : public QLabel
{
Q_OBJECT
public:
explicit ClickableLabel(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
~ClickableLabel();
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent* event);
};
#endif // CLICKABLELABEL_H

View file

@ -1,15 +1,15 @@
#ifndef DEFINES_H
#define DEFINES_H
//#define VERSION "LibreMediaServerAudio 0.2.0 Antigona Release"
#define VERSION "Kike Substitutor - No AI required - v0.2.0"
#define VERSION "LibreMediaServerAudio 0.2.0 Antigona Release"
#define COPYRIGHT "(C) 2014-2024 Santi Noreña <lms@criptomart.net>"
#define LICENSE "GPL 3 Licensed. See LICENSE.txt.\nSound guys are not allowed to use this software."
#define LICENSE "GPL 3 Licensed. See LICENSE.txt."
#define DEFAULT_FILE "lms-audio.xlm"
#define MAX_LAYERS 16
#define UI_REFRESH_TIME 200
#define MAX_LAYERS 4
#define MAX_AUDIODEVICES 8
#define UI_REFRESH_TIME 66
#define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks
// struct where save the DMX settings for each layer
struct dmxSetting {
int address;
unsigned int universe;
@ -22,6 +22,10 @@ enum Status
Paused,
PlayingOnce,
PlayingLoop,
Iddle,
PlayingFolder,
PlayingFolderLoop,
PlayingFolderRandom
};
static const char* StatusStr[] =
@ -29,7 +33,11 @@ static const char* StatusStr[] =
"Stop",
"Pause",
"Playing One",
"Playing Loop",
"Playing One Loop",
"Iddle",
"Playing Folder",
"Playing Folder Loop",
"Playing Folder Random",
0x0
};
@ -40,5 +48,18 @@ enum Slider
Pitch,
};
#include <QString>
struct layerData {
QString media;
Status status;
bool updated;
float vol;
float cursor;
int pan;
int pitch;
float duration;
int address;
unsigned int universe;
int device;
};
#endif // DEFINES_H

View file

@ -1,39 +1,15 @@
#ifndef DMXPERSONALITY_H
#define DMXPERSONALITY_H
/** Define the DMX personality to avoid dealing with
* numbers and change it easyly in case
*
1 - Volumen Coarse
2 - Pan
3 - Folder
4 - File
5 - Playback
0-24 : Play once.
25-49: Stop. Returns to start of file.
50-74: Pause. It keeps the time of reproductions.
75-99: Play loop.
6 - Control - Reservado, sin uso en este momento.
7 - Volume Fine
8 - Entry Point Coarse - Punto de entrada de reproducción.
9 - Entry Point Fine - El valor de estos dos canales en centésimas de segundo.
10 - Pan
11 - Pitch
*/
// ToDo: Mejor inicializacion, primero folder, file, después params, ultimo playback.7
// quitar CONTROL no usado
#define VOLUME_COARSE 0
#define PAN 1
#define DMX_FOLDER 2
#define DMX_FILE 3
#define PLAYBACK 4
#define CONTROL 5
#define VOLUME_FINE 6
#define ENTRY_POINT_COARSE 7
#define ENTRY_POINT_FINE 8
#define PITCH 9
#define LAYER_CHANNELS 10
#define VOLUME_COARSE 3
#define PAN 6
#define DMX_FOLDER 0
#define DMX_FILE 1
#define PLAYBACK 8
#define VOLUME_FINE 2
#define ENTRY_POINT_COARSE 5
#define ENTRY_POINT_FINE 4
#define PITCH 7
#define LAYER_CHANNELS 9
#endif // DMXPERSONALITY_H

View file

@ -9,6 +9,8 @@ dmxWidget::dmxWidget(QWidget *parent) :
QVBoxLayout *vbox = new QVBoxLayout;
m_receiveDMX->setText("DMX Signal");
vbox->addWidget(m_receiveDMX);
vbox->setSpacing(1);
vbox->setContentsMargins(1, 1, 1, 1);
this->setLayout(vbox);
connect(m_watchDMX, SIGNAL(timeout()),
this, SLOT(watchDMXExpired()));

View file

@ -0,0 +1,56 @@
/*
Libre Media Server Audio - An Open source Media Server for arts and performing.
(c) Criptomart - Santiago Noreña 2012-2024 <lms@criptomart.net>
https://git.criptomart.net/libremediaserver
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "libremediaserver-audio-gui.h"
libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
this->setWindowTitle(VERSION);
m_aw = new AudioWidget(this);
setCentralWidget(m_aw);
m_dmxWidget = new dmxWidget(this);
QDockWidget *topWidget = new QDockWidget(tr("Master"), this);
topWidget->setAllowedAreas(Qt::TopDockWidgetArea);
topWidget->setWidget(m_dmxWidget);
topWidget->setContentsMargins(0, 0, 0, 0);
addDockWidget(Qt::TopDockWidgetArea, topWidget);
connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup()));
this->setContentsMargins(5, 5, 5, 5);
this->setStyleSheet(
"color: white;"
"background-color: #4f4048;"
"selection-color: blue;"
"selection-background-color: green"
);
}
libreMediaServerAudioUi::~libreMediaServerAudioUi()
{
}
void libreMediaServerAudioUi::olasetup()
{
QWebView *view = new QWebView();
view->load(QUrl("http://localhost:9090/ola.html"));
view->show();
}

View file

@ -0,0 +1,48 @@
/*
Libre Media Server Audio - An Open source Media Server for arts and performing.
(c) Criptomart - Santiago Noreña 2012-2024 <lms@criptomart.net>
https://git.criptomart.net/libremediaserver
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBREMEDIASERVERAUDIOUI_H
#define LIBREMEDIASERVERAUDIOUI_H
#include <QDockWidget>
#include <QWebView>
#include "audiowidget.h"
#include "dmxwidget.h"
#include "defines.h"
#include "ui_libremediaserver-audio-gui.h"
class libreMediaServerAudioUi : public QMainWindow
{
Q_OBJECT
public:
libreMediaServerAudioUi(QWidget *parent = 0);
virtual ~libreMediaServerAudioUi();
AudioWidget *m_aw;
dmxWidget *m_dmxWidget;
private:
Ui::LibreMediaServerAudio ui;
private slots:
void olasetup();
};
#endif // LIBREMEDIASERVERAUDIOUI_H

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<author>Santi Noreña lms@criptomart.net</author>
<class>LibreMediaServerAudio</class>
<widget class="QMainWindow" name="LibreMediaServerAudio">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>400</height>
</rect>
</property>
<property name="font">
<font>
<family>Unifont</family>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="windowTitle">
<string>LibreMediaServer</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>../../../../criptomart/artwork/logo_v2_criptomart.net.png</normaloff>../../../../criptomart/artwork/logo_v2_criptomart.net.png</iconset>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionLaunch_OLA_Setup"/>
</widget>
<addaction name="menuFile"/>
</widget>
<action name="actionLaunch_OLA_Setup">
<property name="text">
<string>OLA Setup</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -21,106 +21,239 @@
#include "libremediaserver-audio.h"
libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent)
: QMainWindow(parent)
libreMediaServerAudio::libreMediaServerAudio()
{
Q_UNUSED(args);
qDebug() << VERSION;
qDebug() << COPYRIGHT;
qDebug() << LICENSE;
ui.setupUi(this);
this->setWindowTitle(VERSION);
Settings *set = Settings::getInstance();
set->readFile();
m_settings = Settings::getInstance();
m_settings->readFile();
m_ui = m_settings->getShowUi();
m_mediaLibrary = new MediaLibrary;
m_mediaLibrary->initMediaLibrary();
m_aw = new AudioWidget;
setCentralWidget(m_aw);
m_dmxWidget = new dmxWidget(this);
QDockWidget *topWidget = new QDockWidget(tr("Master"), this);
topWidget->setAllowedAreas(Qt::TopDockWidgetArea);
topWidget->setWidget(m_dmxWidget);
addDockWidget(Qt::TopDockWidgetArea, topWidget);
m_ola = new olaThread(this, set->getLayersNumber());
for (int i = 0; i < MAX_LAYERS; i++) {
m_currentMedia[i] = "";
m_currentStatus[i] = Status::Iddle;
#ifdef NOGUI
m_updateUi[i][0] = -1;
m_updateUi[i][1] = -1;
m_updateUi[i][2] = -1;
m_updateUi[i][3] = -1;
#endif
}
m_ola = new olaThread(this, m_settings->getLayersNumber());
Q_CHECK_PTR(m_ola);
m_ola->blockSignals(true);
connect(m_ola, SIGNAL (universeReceived(int)), m_dmxWidget, SLOT(updateWatchDMX(int)));
connect(m_ola, SIGNAL(dmxOutput(int, int, int)), this, SLOT(dmxInput(int, int, int)));
m_ola->registerUniverse();
connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup()));
m_aw->startEngine();
qDebug("Init Complete.");
m_mae.startEngine(m_settings->getAudioDeviceId());
qDebug("Core init Complete. Start reading DMX.");
m_ola->blockSignals(false);
#ifdef NOGUI
m_ola->start(QThread::TimeCriticalPriority );
#endif
}
libreMediaServerAudio::~libreMediaServerAudio()
{
m_ola->stop();
m_aw->stopEngine();
m_mae.stopEngine();
}
void libreMediaServerAudio::olasetup()
void libreMediaServerAudio::loadMedia(int layer, int folder, int file)
{
QWebView *view = new QWebView();
view->load(QUrl("http://localhost:9090/ola.html"));
view->show();
QString mediaFile = m_mediaLibrary->requestNewFile(folder, file);
if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0)
return;
if (QFile::exists(mediaFile)){
m_mae.loadMedia(layer, mediaFile.toLatin1().data());
m_currentMedia[layer] = mediaFile;
#ifndef NOGUI
if (m_ui)
m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer));
#endif
m_mae.printFormatInfo(layer);
}
if (m_currentStatus[layer] == Status::PlayingFolder \
|| (m_currentStatus[layer] == Status::PlayingFolderLoop)\
|| (m_currentStatus[layer] == Status::PlayingFolderRandom)) {
m_played.append(file);
} else if (m_currentStatus[layer] == Status::PlayingOnce \
|| m_currentStatus[layer] == Status::PlayingLoop) {
m_played.clear();
}
}
void libreMediaServerAudio::dmxInput(int layer, int channel, int value)
{
if (layer > LAYER_CHANNELS)
if (layer >= MAX_LAYERS || channel >= LAYER_CHANNELS)
return;
QString mediaFile = NULL;
int aux;
switch(channel){
case DMX_FOLDER:
aux = m_ola->getValue(layer, DMX_FILE);
mediaFile = m_mediaLibrary->requestNewFile(value, aux);
if (QFile::exists(mediaFile))
m_aw->mediaLoaded(layer, mediaFile);
break;
case DMX_FILE:
aux = m_ola->getValue(layer, DMX_FOLDER);
mediaFile = m_mediaLibrary->requestNewFile(aux, value);
if (QFile::exists(mediaFile))
m_aw->mediaLoaded(layer, mediaFile);
break;
case VOLUME_COARSE:
case VOLUME_FINE:
m_aw->volChanged(layer, (value / 65025.0f));
break;
case PAN:
m_aw->panChanged(layer, value);
break;
case PITCH:
m_aw->pitchChanged(layer, value);
break;
case ENTRY_POINT_COARSE:
case ENTRY_POINT_FINE:
m_aw->entryPointChanged(layer, value);
break;
case PLAYBACK:
if (value == 0)
break;
if (channel == VOLUME_COARSE || channel == VOLUME_FINE) {
float tmp = value / 65025.0f;
m_mae.volChanged(layer, tmp);
m_updateUi[layer][0] = tmp * 100.0f;
} else if (channel == PAN) {
m_mae.panChanged(layer, value);
m_updateUi[layer][1] = value;
} else if (channel == PITCH) {
m_mae.pitchChanged(layer, value);
m_updateUi[layer][2] = value;
} else if (channel == ENTRY_POINT_COARSE || channel == ENTRY_POINT_FINE) {
m_mae.setCursor(layer, value);
m_updateUi[layer][3] = value;
} else if (channel == PLAYBACK && value > 0) {
aux = value / 25;
switch (aux) {
case 0 :
m_aw->playbackChanged(layer, PlayingOnce);
break;
case 1 :
m_aw->playbackChanged(layer, Stopped);
break;
case 2 :
m_aw->playbackChanged(layer, Paused);
break;
case 3 :
m_aw->playbackChanged(layer, PlayingLoop);
break;
default :
break;
Status s = m_currentStatus[layer];
if (aux == 0)
s = Status::PlayingOnce;
else if (aux == 1)
s = Status::Stopped;
else if (aux == 2)
s = Status::Paused;
else if (aux == 3)
s = Status::PlayingLoop;
else if (aux == 4)
s = Status::PlayingFolder;
else if (aux == 5)
s = Status::PlayingFolderLoop;
else if (aux == 6)
s = Status::PlayingFolderRandom;
m_mae.playbackChanged(layer, s);
m_currentStatus[layer] = s;
qInfo() << "Layer" << layer << StatusStr[s];
#ifndef NOGUI
if (m_ui) {
m_lmsUi->m_aw->playbackChanged(layer, s);
//m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer));
}
default:
#endif
}
}
#ifndef NOGUI
void libreMediaServerAudio::refreshUi() {
if (!m_ui) return;
for (int i= 0; i < m_settings->getLayersNumber(); i++ ) {
if (m_updateUi[i][0] >= 0) {
m_lmsUi->m_aw->volChanged(i, m_updateUi[i][0]);
m_updateUi[i][0] = -1;
}
if (m_updateUi[i][1] >= 0) {
m_lmsUi->m_aw->panChanged(i, m_updateUi[i][1]);
m_updateUi[i][1] = -1;
}
if (m_updateUi[i][2] >= 0) {
m_lmsUi->m_aw->pitchChanged(i, m_updateUi[i][2]);
m_updateUi[i][2] = -1;
}
if (m_updateUi[i][3] >= 0 \
|| m_currentStatus[i] == Status::PlayingOnce\
|| m_currentStatus[i] == Status::PlayingLoop\
|| m_currentStatus[i] == Status::PlayingFolder\
|| m_currentStatus[i] == Status::PlayingFolderLoop
|| m_currentStatus[i] == Status::PlayingFolderRandom) {
m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i));
m_updateUi[i][3] = -1;
}
if (m_mae.getAtEnd(i)) {
if (m_played.isEmpty())
m_played.append(m_ola->getValue(i, DMX_FILE));
if (m_currentStatus[i] == Status::PlayingOnce) {
m_currentStatus[i] = Status::Stopped;
}
if (m_currentStatus[i] == Status::PlayingFolder) {
uint last = m_played.last();
int folder = m_ola->getValue(i, DMX_FOLDER);
last++;
if (last < m_mediaLibrary->getMediaFolderCount(folder)) {
this->loadMedia(i, folder, last);
m_mae.playbackChanged(i, Status::PlayingFolder);
}
else {
m_currentStatus[i] = Status::Stopped;
m_lmsUi->m_aw->playbackChanged(i, Status::Stopped);
}
}
if (m_currentStatus[i] == Status::PlayingFolderLoop) {
uint last = m_played.last();
int folder = m_ola->getValue(i, DMX_FOLDER);
last++;
if (last >= m_mediaLibrary->getMediaFolderCount(folder)) {
this->loadMedia(i, folder, 0);
m_mae.playbackChanged(i, Status::PlayingFolderLoop);
} else {
this->loadMedia(i, folder, last);
m_mae.playbackChanged(i, Status::PlayingFolder);
}
}
if (m_currentStatus[i] == Status::PlayingFolderRandom) {
int last = -1;
int folder = m_ola->getValue(i, DMX_FOLDER);
if (uint(abs(m_played.size())) >= m_mediaLibrary->getMediaFolderCount(folder))
m_played.clear();
while (last == -1) {
last = rand() % m_mediaLibrary->getMediaFolderCount(folder);
if (m_played.contains(last))
last = -1;
}
this->loadMedia(i, folder, last);
m_mae.playbackChanged(i, Status::PlayingFolderRandom);
}
}
}
}
void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi)
{
m_lmsUi = lmsUi;
m_ui = true;
connect(m_ola, SIGNAL(universeReceived(int)), m_lmsUi->m_dmxWidget, SLOT(updateWatchDMX(int)));
connect(m_lmsUi->m_aw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SLOT(uiSliderChanged(int, Slider, int)));
connect(m_lmsUi->m_aw, SIGNAL(uiPlaybackChanged(int, Status)), this, SLOT(uiPlaybackChanged(int, Status)));
connect(m_lmsUi->m_aw, SIGNAL(uiLoadMedia(int, QString)), this, SLOT(uiLoadMedia(int, QString)));
m_refreshUi = new QTimer(this);
connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi()));
m_refreshUi->start(UI_REFRESH_TIME);
m_ola->start(QThread::TimeCriticalPriority );
};
// From Ui widgets
void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value)
{
switch (s){
case Slider::Volume:
m_mae.volChanged(layer, float((value / 100.0f)));
break;
case Slider::Pan:
m_mae.panChanged(layer, value);
break;
case Slider::Pitch:
m_mae.pitchChanged(layer, value);
break;
}
}
void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s)
{
ma_result result;
result = m_mae.playbackChanged(layer, s);
if (result == MA_SUCCESS) {
m_currentStatus[layer] = s;
} else {
qWarning() << "ui playback change error" << result << "status" << s << "layer" << layer;
}
}
void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile)
{
ma_result result;
if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0)
return;
result = m_mae.loadMedia(layer, mediaFile.toLatin1().data());
if (result == MA_SUCCESS) {
m_currentMedia[layer] = mediaFile;
m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer));
} else {
qWarning() << "ui load media error" << result << "file" << mediaFile << "layer" << layer;
}
}
#endif

View file

@ -20,35 +20,51 @@
#ifndef LIBREMEDIASERVERAUDIO_H
#define LIBREMEDIASERVERAUDIO_H
#include <QDockWidget>
#include <QWebView>
#include "audiowidget.h"
#include "medialibrary.h"
#include "miniaudioengine.h"
#include "olathread.h"
#include "settings.h"
#include "dmxwidget.h"
#include "defines.h"
#include "ui_libremediaserver-audio.h"
#ifndef NOGUI
#include "libremediaserver-audio-gui.h"
#endif
class libreMediaServerAudio : public QMainWindow
class libreMediaServerAudio : public QObject
{
Q_OBJECT
public:
libreMediaServerAudio (QStringList args, QWidget *parent = 0);
libreMediaServerAudio();
virtual ~libreMediaServerAudio();
Ui::LibreMediaServerAudio ui;
void dmxInput(int layer, int channel, int value);
void loadMedia(int layer, int folder, int file);
#ifndef NOGUI
void setUi(libreMediaServerAudioUi *lmsUi);
bool inline getShowUi() { return m_settings->getShowUi(); }
#endif
private:
AudioWidget *m_aw;
dmxWidget *m_dmxWidget;
olaThread *m_ola;
MediaLibrary *m_mediaLibrary;
MiniAudioEngine m_mae;
Settings *m_settings;
QString m_currentMedia[MAX_LAYERS];
Status m_currentStatus[MAX_LAYERS];
QList<dmxSetting> m_dmxSettings;
bool m_ui;
QList<int> m_played;
#ifndef NOGUI
QTimer *m_refreshUi;
libreMediaServerAudioUi *m_lmsUi;
float m_updateUi[MAX_LAYERS][4];
private slots:
void olasetup();
void dmxInput(int layer, int channel, int value);
void refreshUi();
void uiSliderChanged(int layer, Slider s, int value);
void uiPlaybackChanged(int layer, Status s);
void uiLoadMedia(int layer, QString s);
#endif
};
#endif // LIBREMEDIASERVERAUDIO_H

View file

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<author>Santi Noreña belfegor@gmail.com</author>
<class>LibreMediaServerAudio</class>
<widget class="QMainWindow" name="LibreMediaServerAudio">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>114</width>
<height>218</height>
</rect>
</property>
<property name="windowTitle">
<string>LibreMediaServer</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>114</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionLaunch_OLA_Setup"/>
</widget>
<addaction name="menuFile"/>
</widget>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionOpen_conf">
<property name="text">
<string>Open Configuration...</string>
</property>
</action>
<action name="actionSave_conf">
<property name="text">
<string>Save Configuration...</string>
</property>
</action>
<action name="action_Settings">
<property name="text">
<string>Settings...</string>
</property>
</action>
<action name="actionInitMSEX">
<property name="checkable">
<bool>false</bool>
</property>
<property name="text">
<string>Init</string>
</property>
</action>
<action name="actionIP_Address">
<property name="text">
<string>IP Address</string>
</property>
</action>
<action name="actionMake_Thumbs">
<property name="text">
<string>Make Thumbs</string>
</property>
</action>
<action name="actionLaunch_OLA_Setup">
<property name="text">
<string>OLA Setup...</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -18,34 +18,28 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "libremediaserver-audio.h"
#include "main.h"
bool hasUi(int &argc, char *argv[])
{
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--gui"))
return true;
}
return false;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QStringList args = app.arguments();
if (args.size() > 1)
libreMediaServerAudio lms;
#ifndef NOGUI
if (hasUi(argc, argv) || lms.getShowUi())
{
if (args.contains("-v"))
{
qDebug() << VERSION;
qDebug() << COPYRIGHT;
qDebug() << LICENSE;
return 0;
}
if (args.contains("-h"))
{
qDebug() << VERSION;
qDebug() << COPYRIGHT;
qDebug() << LICENSE;
qDebug() << "Help for command line options:";
qDebug() << "-v show the version and exits";
qDebug() << "-h this help";
return 0;
}
libreMediaServerAudioUi *lmsUi = new libreMediaServerAudioUi();
lms.setUi(lmsUi);
lmsUi->show();
}
libreMediaServerAudio libreMediaServerAudio(args);
libreMediaServerAudio.show();
#endif
return app.exec();
}

20
src/main.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef MAIN_H
#define MAIN_H
#include <QCoreApplication>
#include <QApplication>
#include "medialibrary.h"
#include "olathread.h"
#include "settings.h"
#include "libremediaserver-audio.h"
#include "libremediaserver-audio-gui.h"
#include "defines.h"
olaThread *m_ola;
MediaLibrary *m_mediaLibrary;
// slots
void dmxInput(int layer, int channel, int value);
#endif // MAIN_H

View file

@ -27,14 +27,11 @@ class MediaLibrary : public QObject
public:
MediaLibrary(QObject *parent = 0);
/**
* @brief request a new file from the media library
* @param int folder - the folder required
* @param int layer - file required
* @return QString the file required with full path
*/
QString requestNewFile(int folder, int layer);
void initMediaLibrary();
inline uint getMediaFolderCount(int folder) {
return m_media->at(folder).m_ElementCount;
}
private:
QList<MediaFolder> *m_media;

View file

@ -1,8 +1,15 @@
#include "miniaudioengine.h"
#include <iostream> //enum macro
MiniAudioEngine::MiniAudioEngine()
{
for (int i =0; i < MAX_LAYERS; i++) {
m_mediaLoaded[i] = false;
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)
@ -20,25 +27,26 @@ void MiniAudioEngine::stopEngine()
ma_resource_manager_uninit(&resourceManager);
}
bool MiniAudioEngine::startEngine(int n)
bool MiniAudioEngine::startEngine(uint n)
{
ma_result result;
result = this->startContext();
if (result != MA_SUCCESS) {
return result;
}
this->getAllAudioDevices();
if (result != MA_SUCCESS) return result;
result = this->getAllAudioDevices();
if (result != MA_SUCCESS) return result;
result = this->startDevice(n);
return result;
}
ma_result MiniAudioEngine::startDevice(int id)
ma_result MiniAudioEngine::startDevice(uint id)
{
ma_result result;
ma_device_config deviceConfig;
ma_engine_config engineConfig;
if (id >= playbackDeviceCount)
id = playbackDeviceCount - 1;
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[id].id;
deviceConfig.playback.format = resourceManager.config.decodedFormat;
@ -66,7 +74,7 @@ ma_result MiniAudioEngine::startDevice(int id)
return result;
}
iChosenDevice = id;
qInfo("Initialized audio device %d: %s", id, pPlaybackDeviceInfos[id].name);
qInfo("Initialized audio device %d : %s", id, pPlaybackDeviceInfos[id].name);
return result;
}
@ -77,8 +85,8 @@ ma_result MiniAudioEngine::startContext()
resourceManagerConfig = ma_resource_manager_config_init();
resourceManagerConfig.decodedFormat = ma_format_f32; /* ma_format_f32 should almost always be used as that's what the engine (and most everything else) uses for mixing. */
resourceManagerConfig.decodedChannels = 0;
resourceManagerConfig.decodedSampleRate = 0;
resourceManagerConfig.jobThreadCount = 0;
resourceManagerConfig.decodedSampleRate = ma_standard_sample_rate_48000;
resourceManagerConfig.jobThreadCount = 4;
result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager);
if (result != MA_SUCCESS) {
qCritical("Failed to initialize audio resource manager.");
@ -102,7 +110,7 @@ ma_result MiniAudioEngine::getAllAudioDevices()
ma_context_uninit(&context);
return result;
}
printf("Audio devices detected in system:\n");
printf("Audio devices available:\n");
for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < playbackDeviceCount; iAvailableDevice += 1) {
qInfo("%d: : %s", iAvailableDevice, pPlaybackDeviceInfos[iAvailableDevice].name);
}
@ -115,21 +123,20 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file)
if (m_mediaLoaded[layer] == true)
{
qInfo("removing sound %i", layer);
ma_sound_uninit(&m_currentSound[layer]);
m_mediaLoaded[layer] = false;
}
result = ma_sound_init_from_file(&engine, file, \
MA_SOUND_FLAG_NO_SPATIALIZATION \
/*| MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE \
| MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC \
| MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM \*/
| MA_SOUND_FLAG_DECODE \
/*| MA_SOUND_FLAG_NO_PITCH \*/
, NULL, NULL, &m_currentSound[layer]);
if (result != MA_SUCCESS)
qWarning("Failed to load file %s", file);
else {
m_mediaLoaded[layer] = true;
this->volChanged(layer, 0);
this->refreshValues(layer);
m_currentLayerValues[layer].media = file;
}
return result;
}
@ -137,16 +144,21 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file)
float MiniAudioEngine::getDuration(int layer)
{
ma_result result;
float ret = 0;
ma_uint64 lengthInPCMFrames;
ma_uint32 sampleRate;
float ret;
if (m_mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST;
result = ma_sound_get_length_in_seconds(&m_currentSound[layer], &ret);
if (result != MA_SUCCESS)
{
qWarning("Can not get duration %i", layer);
ret = 0;
result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &lengthInPCMFrames);
if (result != MA_SUCCESS) {
return result;
}
result = ma_sound_get_data_format(&m_currentSound[layer], NULL, NULL, &sampleRate, NULL, 0);
if (result != MA_SUCCESS) {
return MA_ERROR;
}
ret = 1000.0f * (lengthInPCMFrames / float(sampleRate));
return ret;
}
@ -160,8 +172,8 @@ float MiniAudioEngine::getCursor(int layer)
result = ma_sound_get_cursor_in_seconds(&m_currentSound[layer], &ret);
if (result != MA_SUCCESS)
{
qWarning("Can not get cursor %i", layer);
ret = 0;
qWarning("%i can not get cursor error %i", layer, result);
ret = MA_ERROR;
}
return ret;
}
@ -175,11 +187,10 @@ ma_result MiniAudioEngine::printFormatInfo(int layer)
if (m_mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST;
ma_result result = ma_sound_get_data_format(&m_currentSound[layer], &format, &channels, &sampleRate, NULL, 0);
if (result != MA_SUCCESS) {
qWarning("Failed to get data format %i\n", layer);
return MA_INVALID_DATA;
}
qInfo("samples/sec: %u format: %u channels: %u", sampleRate, format, channels);
if (result != MA_SUCCESS)
qWarning("%i failed to get data format %i\n", layer, result);
else
qInfo() << "Layer:" << layer << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels;
return result;
}
@ -188,7 +199,10 @@ void MiniAudioEngine::volChanged(int layer, float vol)
{
if (m_mediaLoaded[layer] == false)
return;
ma_sound_group_set_volume(&m_currentSound[layer], vol);
if (vol >= 1)
vol = 0.99f;
ma_sound_group_set_fade_in_milliseconds(&m_currentSound[layer], -1, vol, FADE_TIME);
m_currentLayerValues[layer].vol = vol;
}
void MiniAudioEngine::panChanged(int layer, float value)
@ -199,48 +213,90 @@ void MiniAudioEngine::panChanged(int layer, float value)
return;
result = (value / 128.0) - 1.0;
ma_sound_group_set_pan(&m_currentSound[layer], result);
m_currentLayerValues[layer].pan = value;
}
void MiniAudioEngine::pitchChanged(int layer, float value)
{
float result;
float pitch;
if (m_mediaLoaded[layer] == false)
return;
result = value / 128.0;
ma_sound_group_set_pitch(&m_currentSound[layer], result);
pitch = value / 128.0;
ma_sound_group_set_pitch(&m_currentSound[layer], pitch);
m_currentLayerValues[layer].pitch = value;
}
void MiniAudioEngine::playbackChanged(int layer, Status status)
ma_result MiniAudioEngine::playbackChanged(int layer, Status status)
{
ma_result result = MA_SUCCESS;
if (m_mediaLoaded[layer] == false)
return;
return MA_DOES_NOT_EXIST;
switch (status) {
case Status::Paused:
ma_sound_stop(&m_currentSound[layer]);
result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
break;
case Status::Stopped:
ma_sound_stop(&m_currentSound[layer]);
ma_sound_seek_to_pcm_frame(&m_currentSound[layer], 0);
result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
result = this->seekToCursor(layer, m_currentLayerValues[layer].cursor);
break;
case Status::PlayingLoop:
ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0);
ma_sound_set_looping(&m_currentSound[layer], true);
ma_sound_start(&m_currentSound[layer]);
break;
result = ma_sound_start(&m_currentSound[layer]);
break;
case Status::PlayingOnce:
case Status::PlayingFolder:
case Status::PlayingFolderLoop:
case Status::PlayingFolderRandom:
ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0);
ma_sound_set_looping(&m_currentSound[layer], false);
ma_sound_start(&m_currentSound[layer]);
result = ma_sound_start(&m_currentSound[layer]);
break;
default:
break;
}
if (result == MA_SUCCESS)
m_currentLayerValues[layer].status = status;
return result;
}
void MiniAudioEngine::setCursor(int layer, int cursor)
ma_result MiniAudioEngine::seekToCursor(int layer, int cursor)
{
ma_uint64 f;
ma_result result = MA_SUCCESS;
ma_uint64 end, start;
if (m_mediaLoaded[layer] == false)
return;
ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &f);
f = (cursor * f) / 65025;
ma_sound_seek_to_pcm_frame(&m_currentSound[layer], f);
return MA_DOES_NOT_EXIST;
result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &end);
if (result != MA_SUCCESS) { return result; }
start = (cursor * end) / 65025;
result = ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start);
//if (result != MA_SUCCESS) { return result; }
//result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end);
return (result);
}
ma_result MiniAudioEngine::setCursor(int layer, int cursor)
{
ma_result result = MA_SUCCESS;
m_currentLayerValues[layer].cursor = cursor;
result = this->seekToCursor(layer, cursor);
return (result);
}
Status MiniAudioEngine::getStatus(int layer)
{
return m_currentLayerValues[layer].status;
}
void MiniAudioEngine::refreshValues(int layer)
{
this->seekToCursor(layer, m_currentLayerValues[layer].cursor);
this->panChanged(layer, m_currentLayerValues[layer].pan);
this->volChanged(layer, m_currentLayerValues[layer].vol);
this->pitchChanged(layer, m_currentLayerValues[layer].pitch);
this->playbackChanged(layer, m_currentLayerValues[layer].status);
}

View file

@ -5,15 +5,16 @@
#include "miniaudio.h"
#include "defines.h" // MAX_LAYERS
#include <QDebug> // prints messages
#define MA_DEBUG_OUTPUT
class MiniAudioEngine
{
friend class AudioWidget;
friend class libreMediaServerAudio;
public:
MiniAudioEngine();
void stopEngine();
bool startEngine(int id);
bool startEngine(uint id);
static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
protected:
@ -21,11 +22,15 @@ protected:
void volChanged(int layer, float vol);
void panChanged(int layer, float pan);
void pitchChanged(int layer, float pitch);
void playbackChanged(int layer, Status status);
ma_result playbackChanged(int layer, Status status);
ma_result setCursor(int layer, int cursor);
ma_result printFormatInfo(int layer);
float getDuration(int layer);
float getCursor(int layer);
void setCursor(int layer, int cursor);
ma_result printFormatInfo(int layer);
Status getStatus(int layer);
inline float getVol(int layer) {
return ma_sound_get_volume(&m_currentSound[layer]); }
inline bool getAtEnd(int layer) { return m_currentSound[layer].atEnd; }
private:
ma_resource_manager_config resourceManagerConfig;
@ -38,10 +43,13 @@ private:
ma_context context;
ma_sound m_currentSound[MAX_LAYERS];
ma_bool8 m_mediaLoaded[MAX_LAYERS];
layerData m_currentLayerValues[MAX_LAYERS];
ma_result getAllAudioDevices();
ma_result startDevice(int id);
ma_result startDevice(uint id);
ma_result startContext();
void refreshValues(int layer);
ma_result seekToCursor(int layer, int cursor);
};
#endif // MINIAUDIOENGINE_H

View file

@ -1,11 +1,10 @@
#include "olathread.h"
#include "libremediaserver-audio.h"
olaThread::olaThread(QObject *parent, int layers) :
m_counter(0)
, m_layers(layers)
{
Q_UNUSED(parent);
this->setParent(parent);
gettimeofday(&m_last_data, NULL);
for (int i=0; i < MAX_LAYERS; i++)
{
@ -63,42 +62,65 @@ void olaThread::stop()
void olaThread::NewDmx(const ola::client::DMXMetadata &data,
const ola::DmxBuffer &buffer)
{
bool volSent = false;
bool entrySent = false;
foreach (const dmxSetting &i, m_dmxSettings) {
if(i.universe == data.universe && i.address > -1) {
bool volSent = false;
bool entrySent = false;
bool fileSent = false;
int aux;
for (int j = 0; j < LAYER_CHANNELS; j++){
int value = buffer.Get((i.address) + j);
if (m_dmx[i.layer][j] != value) {
m_dmx[i.layer][j] = value;
switch (j) {
case VOLUME_COARSE:
value = (value * 0x100) + buffer.Get(i.address + VOLUME_FINE);
emit dmxOutput(i.layer,j,value);
volSent = true;
case DMX_FOLDER:
aux = buffer.Get(i.address + DMX_FILE);
qobject_cast<libreMediaServerAudio *>(parent())->loadMedia(i.layer, value, aux);
m_dmx[i.layer][DMX_FILE] = aux;
fileSent = true;
break;
case ENTRY_POINT_COARSE:
value = (value * 0x100) + buffer.Get(i.address + ENTRY_POINT_FINE);
emit dmxOutput(i.layer,j,value);
entrySent = true;
case DMX_FILE:
if (fileSent)
break;
aux = buffer.Get(i.address + DMX_FOLDER);
qobject_cast<libreMediaServerAudio *>(parent())->loadMedia(i.layer, aux, value);
m_dmx[i.layer][DMX_FOLDER] = aux;
fileSent = true;
break;
case VOLUME_FINE:
if (volSent == false)
{
value = (buffer.Get(i.address + VOLUME_COARSE) * 0x100) + value;
emit dmxOutput(i.layer,j,value);
}
aux = buffer.Get(i.address + VOLUME_COARSE);
value += (aux * 0x100);
qobject_cast<libreMediaServerAudio *>(parent())->dmxInput(i.layer, j, value);
m_dmx[i.layer][VOLUME_COARSE] = aux;
volSent = true;
break;
case VOLUME_COARSE:
if (volSent)
break;
value = (value * 0x100) + buffer.Get(i.address + VOLUME_FINE);
qobject_cast<libreMediaServerAudio *>(parent())->dmxInput(i.layer, j, value);
m_dmx[i.layer][VOLUME_FINE] = buffer.Get(i.address + VOLUME_FINE);
volSent = true;
break;
case PLAYBACK:
qobject_cast<libreMediaServerAudio *>(parent())->dmxInput(i.layer, j, value);
break;
case ENTRY_POINT_FINE:
if (entrySent == false)
{
value = (buffer.Get(i.address + ENTRY_POINT_COARSE) * 0x100) + value;
emit dmxOutput(i.layer,j,value);
}
value = (buffer.Get(i.address + ENTRY_POINT_COARSE) * 0x100) + value;
qobject_cast<libreMediaServerAudio *>(parent())->dmxInput(i.layer, j, value);
m_dmx[i.layer][ENTRY_POINT_COARSE] = buffer.Get(i.address + ENTRY_POINT_COARSE);
entrySent = true;
break;
case ENTRY_POINT_COARSE:
if (entrySent)
break;
value = (value * 0x100) + buffer.Get(i.address + ENTRY_POINT_FINE);
qobject_cast<libreMediaServerAudio *>(parent())->dmxInput(i.layer, j, value);
m_dmx[i.layer][ENTRY_POINT_FINE] = buffer.Get(i.address + ENTRY_POINT_FINE);
entrySent = true;
break;
default:
emit dmxOutput(i.layer,j,value);
qobject_cast<libreMediaServerAudio *>(parent())->dmxInput(i.layer, j, value);
break;
}
}
@ -127,7 +149,7 @@ bool olaThread::CheckDataLoss() {
void olaThread::socketClosed()
{
qWarning("ola closed connection. Try reopening it... ");
qWarning("ola daemon closed connection, reopening it... ");
m_clientWrapper->GetSelectServer()->Terminate();
m_client = NULL;
m_clientWrapper = NULL;

View file

@ -20,10 +20,17 @@ class olaThread : public QThread
Q_OBJECT
public:
QList<dmxSetting> m_dmxSettings;
int m_dmx[MAX_LAYERS][LAYER_CHANNELS];
ola::client::OlaClientWrapper *m_clientWrapper;
ola::client::OlaClient *m_client;
unsigned int m_counter;
struct timeval m_last_data; // Last DMX frame received
int m_layers;
olaThread(QObject *parent = 0, int layers = 0);
virtual ~olaThread();
void run ();
/** Retorna el valor de un canal
*@param int layer the layer for we want the channel
*@param int channel the channel for the value wanted
@ -35,14 +42,6 @@ public:
void resendDmx();
private:
void run ();
ola::client::OlaClientWrapper *m_clientWrapper;
ola::client::OlaClient *m_client;
unsigned int m_counter;
struct timeval m_last_data; // Last DMX frame received
int m_layers;
int m_dmx[MAX_LAYERS][LAYER_CHANNELS];
QList<dmxSetting> m_dmxSettings;
/**
* @brief Callback from ola. Control de errores en el registro de Universos en OLA
* typedef SingleUseCallback1<void, const Result&> ola::client::SetCallback
@ -53,7 +52,7 @@ private:
if (error.Success()) {
qDebug("Register Universe success");
} else {
qWarning("Register command failed: %s", error.Error().c_str());
qCritical("Register command failed: %s", error.Error().c_str());
}
}
/**
@ -67,7 +66,7 @@ private:
* This is called one for second if there is not updated in the DMX frame.
* emit only the channels that has been changed.
*/
void NewDmx(const ola::client::DMXMetadata &dmx_meta, const ola::DmxBuffer &buffer); //
void NewDmx(const ola::client::DMXMetadata &dmx_meta, const ola::DmxBuffer &buffer);
/**
* @brief Sometimes the ola server closes the connection. This is a callback to handle this event an reconect to ola
*/

View file

@ -15,6 +15,7 @@ Settings::Settings(QObject *parent) :
QObject(parent)
{
m_layersNumber = 0;
m_ui = false;
}
// Read the dmx settings for dmx.xml At the moment we need:
@ -23,6 +24,7 @@ Settings::Settings(QObject *parent) :
// - The first DMX channel of each source/layer
// - The universe to bind in OLA
// - Audio device id
// - Show the Ui or not
void Settings::readFromFile(QString file) {
QFile* xmlFile = new QFile(file);
if (!xmlFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
@ -34,53 +36,56 @@ void Settings::readFromFile(QString file) {
exit(1);
}
QXmlStreamReader* xmlReader = new QXmlStreamReader(xmlFile);
int counter = 0;
//Parse the XML until we reach end of it
while(!xmlReader->atEnd() && !xmlReader->hasError() && counter < MAX_LAYERS) {
// Read next element
while(!xmlReader->atEnd() && !xmlReader->hasError()) {
QXmlStreamReader::TokenType token = xmlReader->readNext();
//If token is just StartDocument - go to next
if(token == QXmlStreamReader::StartDocument) {
continue;
}
//If token is StartElement - read it
if(token == QXmlStreamReader::StartElement) {
if(xmlReader->name() == "dmxSettings") {
int version = xmlReader->attributes().value("fileVersion").toLocal8Bit().toInt();
if(version == 1) {
m_layersNumber = xmlReader->attributes().value("layersNumber").toLocal8Bit().toInt();
m_pathmedia = xmlReader->attributes().value("path").toLocal8Bit();
continue;
}
}
if(xmlReader->name() == "audioDevice") {
m_audioDeviceId = xmlReader->attributes().value("id").toLocal8Bit().toInt();
if(xmlReader->name() == "lmsAudio") {
m_ui = xmlReader->attributes().value("ui").toLocal8Bit().toInt();
m_layersNumber = xmlReader->attributes().value("layersNumber").toLocal8Bit().toInt();
m_pathmedia = xmlReader->attributes().value("path").toLocal8Bit();
continue;
}
QString add = "layer";
add.append(QString("%1").arg(counter));
if((xmlReader->name() == add)) {
if(xmlReader->name() == "audioDevice") {
m_audioDeviceQty = xmlReader->attributes().value("devicesNumber").toLocal8Bit().toInt();
for (uint i = 0; i < m_audioDeviceQty; i++)
{
m_audioDeviceId[i] = xmlReader->attributes().value(QString("id%1").arg(i)).toLocal8Bit().toInt();
}
}
if(xmlReader->name() == "layer") {
dmxSetting temp;
temp.address = xmlReader->attributes().value("dmx").toLocal8Bit().toInt() - 1;
temp.universe = xmlReader->attributes().value("universe").toLocal8Bit().toInt();
temp.layer = counter;
temp.layer = xmlReader->attributes().value("id").toLocal8Bit().toInt();
m_settings.append(temp);
if (!m_universe.contains(temp.universe)) {
m_universe.insert(temp.universe);
}
counter++;
}
}
}
if(xmlReader->hasError()) {
QMessageBox::critical(NULL,"File xml Parse Error ", xmlReader->errorString(), QMessageBox::Ok);
qWarning("File xml Parse Error %s", xmlReader->errorString().toLatin1().constData());
return;
// ToDo: manage this, open a dialog to load a new file.
}
xmlReader->clear();
xmlFile->close();
delete xmlReader;
delete xmlFile;
this->printSettings();
}
void Settings::printSettings() {
qInfo() << "Settings readed:\nShow Ui:" << m_ui << "Layers:" << m_layersNumber << "Path:" << m_pathmedia <<"Audio Device qty:" << m_audioDeviceQty;
for (uint i = 0; i < m_audioDeviceQty; i++)
qInfo() << "Audio device internal id:" << i << "system id:" << m_audioDeviceId[i];
for (int i = 0; i < m_layersNumber; i++)
qInfo() << "Layer:" << m_settings[i].layer << "Address:" << m_settings[i].address << "Universe:" << m_settings[i].universe;
}
void Settings::readFile() {

View file

@ -5,6 +5,7 @@
#include <QFile>
#include <QMessageBox>
#include <QSet>
#include <QDebug>
#include "medialibrary.h"
#include "audiowidget.h"
@ -15,25 +16,27 @@ class Settings : public QObject
Q_OBJECT
public:
Settings(QObject *parent = 0);
static Settings *getInstance();
inline QSet<int> getUniverses() { return m_universe; }
inline QString getPathMedia() { return m_pathmedia; }
inline QList<dmxSetting> getDmxSettings() { return m_settings; }
inline int getLayersNumber() { return m_layersNumber; }
inline int getAudioDeviceId() { return m_audioDeviceId[0]; }
inline bool getShowUi() { return m_ui; }
void readFile();
inline int getAudioDeviceId() { return m_audioDeviceId; }
void readFromFile(QString file);
void printSettings();
private:
static Settings *_instance;
QList<dmxSetting> m_settings;
QString m_pathmedia;
uint m_audioDeviceId;
uint m_audioDeviceId[MAX_AUDIODEVICES];
uint m_audioDeviceQty;
QSet<int> m_universe;
int m_layersNumber;
explicit Settings(QObject *parent = 0);
void readFromFile(QString file);
bool m_ui;
};
#endif // SETTINGS_H

View file

@ -1,45 +1,81 @@
#include "slidergroup.h"
SliderGroup::SliderGroup(const QString &title, \
#include <QCursor>
SliderGroup::SliderGroup(QString name,
int min,
int max,
int decimals,
QWidget *parent)
: QGroupBox(title, parent)
: QWidget(parent)
{
this->setFlat(true);
this->setTitle(title);
QVBoxLayout *layout = new QVBoxLayout;
layout->setAlignment(Qt::AlignHCenter);
layout->setContentsMargins(0, 0, 0, 0);
//this->setMaximumWidth(40);
slider = new QSlider(Qt::Orientation::Vertical);
slider->setFocusPolicy(Qt::StrongFocus);
slider->setTickPosition(QSlider::TicksBothSides);
slider->setTickInterval((max - min) / 11);
slider->setMinimumHeight(0);
slider->setSingleStep(1);
slider->setRange(min, max);
slider->setValue(0);
slider->setMinimumWidth(50);
slider->setToolTip(name);
slider->setStyleSheet("QSlider {"
"border: 1px solid #5a4855;"
"margin: 0px;"
"height: 200px;"
"width: 50px;}"
);
slider->setContentsMargins(0, 0, 0, 0);
valueBox = new QDoubleSpinBox();
valueBox->setFocusPolicy(Qt::NoFocus);
valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
valueBox->setMaximumWidth(45);
valueBox->setMinimumWidth(50);
valueBox->setRange(min, max);
valueBox->setValue(0);
valueBox->setDecimals(decimals);
valueBox->setObjectName(name);
valueBox->setToolTip(name);
valueBox->setAlignment(Qt::AlignHCenter);
valueBox->setContentsMargins(0, 0, 0, 0);
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
QVBoxLayout *slidersLayout = new QVBoxLayout();
slidersLayout->addWidget(valueBox);
slidersLayout->addWidget(slider);
setLayout(slidersLayout);
//connect(slider, SIGNAL(mousePressEvent(QMouseEvent)), this, SLOT(mousePressEvent(QMouseEvent *)));
layout->addWidget(slider);
layout->addWidget(valueBox);
this->setStyleSheet("border: 1px solid #5a4855;"
"width: 50px;"
"margin: 0px;"
"background-color: #383034;"
);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
this->setLayout(layout);
}
void SliderGroup::sliderValueChanged(int value)
{
if (valueBox->decimals() > 1)
value /= 100.0f;
emit valueChanged(value);
valueBox->blockSignals(true);
valueBox->setValue(value);
valueBox->blockSignals(false);
emit valueChanged(value);
};
void SliderGroup::setValue(float value)
{
if (valueBox->decimals() > 1)
value *= 100.0f;
slider->setValue(value);
slider->blockSignals(true);
valueBox->blockSignals(true);
if (int(value) != slider->value())
slider->setValue(value);
valueBox->setValue(value);
slider->blockSignals(false);
valueBox->blockSignals(false);
}
void SliderGroup::mousePressEvent(QMouseEvent* event) {
Q_UNUSED(event);
if (slider->isEnabled())
slider->setDisabled(true);
else
slider->setDisabled(false);
}

View file

@ -6,19 +6,19 @@
#include <QVBoxLayout>
#include <QSlider>
class SliderGroup : public QGroupBox
class SliderGroup : public QWidget
{
Q_OBJECT
public:
SliderGroup(const QString &title,
int min,
int max,
int decimals,
QWidget *parent = nullptr);
SliderGroup(QString name,
int min,
int max,
int decimals,
QWidget *parent = nullptr);
signals:
void valueChanged(float value);
void valueChanged(int value);
public slots:
void setValue(float value);
@ -27,6 +27,8 @@ public slots:
private:
QSlider *slider;
QDoubleSpinBox *valueBox;
void mousePressEvent(QMouseEvent* event);
};
#endif