From 0d0b54fc009846b6ce27880b31cec1c8a80596fa Mon Sep 17 00:00:00 2001 From: =?utf8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 16 Jan 2019 12:08:43 +0100 Subject: [PATCH] New upstream version 0.15.3+ds1 --- ChangeLog | 18 +- Makefile.am | 19 +- README.md | 12 +- extras/giada-logo.png | Bin 0 -> 4616 bytes extras/giada-logotype.png | Bin 0 -> 8486 bytes src/core/action.h | 58 ++ src/core/channel.cpp | 45 +- src/core/channel.h | 87 +-- src/core/channelManager.cpp | 50 +- src/core/channelManager.h | 18 +- src/core/clock.cpp | 6 +- src/core/conf.cpp | 3 - src/core/conf.h | 1 - src/core/const.h | 127 ++-- src/core/init.cpp | 166 +++-- src/core/init.h | 15 +- src/core/kernelMidi.cpp | 10 +- src/core/midiChannel.cpp | 85 +-- src/core/midiChannel.h | 26 +- src/core/midiChannelProc.cpp | 56 +- src/core/midiChannelProc.h | 12 +- src/core/midiDispatcher.cpp | 10 +- src/core/midiEvent.cpp | 3 +- src/core/midiEvent.h | 6 +- src/core/midiMapConf.cpp | 50 +- src/core/midiMapConf.h | 12 +- src/core/mixer.cpp | 5 + src/core/mixer.h | 9 +- src/core/mixerHandler.cpp | 7 +- src/core/mixerHandler.h | 8 +- src/core/patch.cpp | 101 ++- src/core/patch.h | 12 +- src/core/plugin.cpp | 13 +- src/core/plugin.h | 5 + src/core/pluginHost.cpp | 2 +- src/core/pluginHost.h | 8 +- src/core/recorder.cpp | 633 +++++------------- src/core/recorder.h | 187 ++---- src/core/recorderHandler.cpp | 290 ++++++++ src/core/recorderHandler.h | 76 +++ src/core/sampleChannel.cpp | 76 ++- src/core/sampleChannel.h | 25 +- src/core/sampleChannelProc.cpp | 52 +- src/core/sampleChannelProc.h | 13 +- src/core/sampleChannelRec.cpp | 86 +-- src/core/sampleChannelRec.h | 6 +- src/core/wave.cpp | 4 +- src/glue/actionEditor.cpp | 339 ++++++++++ src/glue/actionEditor.h | 69 ++ src/glue/channel.cpp | 50 +- src/glue/channel.h | 47 +- src/glue/io.cpp | 30 +- src/glue/io.h | 15 +- src/glue/main.cpp | 51 +- src/glue/main.h | 2 +- src/glue/plugin.h | 19 +- src/glue/recorder.cpp | 338 +--------- src/glue/recorder.h | 52 +- src/glue/sampleEditor.cpp | 34 +- src/glue/sampleEditor.h | 37 +- src/glue/storage.cpp | 31 +- src/glue/storage.h | 10 +- src/glue/transport.cpp | 81 ++- src/glue/transport.h | 19 +- .../dialogs/actionEditor/baseActionEditor.cpp | 39 +- .../dialogs/actionEditor/baseActionEditor.h | 16 +- .../dialogs/actionEditor/midiActionEditor.cpp | 25 +- .../dialogs/actionEditor/midiActionEditor.h | 11 +- .../actionEditor/sampleActionEditor.cpp | 28 +- .../dialogs/actionEditor/sampleActionEditor.h | 16 +- src/gui/dialogs/beatsInput.cpp | 14 +- src/gui/dialogs/beatsInput.h | 1 - src/gui/dialogs/browser/browserBase.h | 7 +- src/gui/dialogs/browser/browserDir.h | 3 - src/gui/dialogs/browser/browserLoad.cpp | 3 +- src/gui/dialogs/browser/browserLoad.h | 5 +- src/gui/dialogs/browser/browserSave.cpp | 1 + src/gui/dialogs/browser/browserSave.h | 3 +- src/gui/dialogs/channelNameInput.cpp | 2 +- src/gui/dialogs/channelNameInput.h | 5 +- src/gui/dialogs/gd_keyGrabber.cpp | 11 +- src/gui/dialogs/gd_keyGrabber.h | 19 +- src/gui/dialogs/gd_mainWindow.cpp | 19 +- src/gui/dialogs/midiIO/midiInputChannel.h | 5 +- src/gui/dialogs/midiIO/midiOutputMidiCh.cpp | 5 +- src/gui/dialogs/midiIO/midiOutputMidiCh.h | 4 +- src/gui/dialogs/midiIO/midiOutputSampleCh.cpp | 5 +- src/gui/dialogs/midiIO/midiOutputSampleCh.h | 7 +- src/gui/dialogs/pluginChooser.cpp | 14 +- src/gui/dialogs/pluginChooser.h | 27 +- src/gui/dialogs/pluginList.cpp | 2 +- src/gui/dialogs/pluginList.h | 5 +- src/gui/dialogs/pluginWindow.cpp | 5 +- src/gui/dialogs/pluginWindow.h | 6 +- src/gui/dialogs/pluginWindowGUI.h | 7 +- src/gui/dialogs/sampleEditor.cpp | 2 +- src/gui/dialogs/sampleEditor.h | 5 +- src/gui/elems/actionEditor/baseAction.cpp | 2 +- src/gui/elems/actionEditor/baseAction.h | 12 +- .../elems/actionEditor/baseActionEditor.cpp | 8 +- src/gui/elems/actionEditor/baseActionEditor.h | 7 +- src/gui/elems/actionEditor/envelopeEditor.cpp | 54 +- src/gui/elems/actionEditor/envelopeEditor.h | 16 +- src/gui/elems/actionEditor/envelopePoint.cpp | 4 +- src/gui/elems/actionEditor/envelopePoint.h | 2 +- src/gui/elems/actionEditor/noteEditor.cpp | 4 +- src/gui/elems/actionEditor/pianoItem.cpp | 37 +- src/gui/elems/actionEditor/pianoItem.h | 13 +- src/gui/elems/actionEditor/pianoRoll.cpp | 77 ++- src/gui/elems/actionEditor/pianoRoll.h | 8 +- src/gui/elems/actionEditor/sampleAction.cpp | 9 +- src/gui/elems/actionEditor/sampleAction.h | 12 +- .../elems/actionEditor/sampleActionEditor.cpp | 74 +- .../elems/actionEditor/sampleActionEditor.h | 13 +- src/gui/elems/actionEditor/velocityEditor.cpp | 31 +- src/gui/elems/actionEditor/velocityEditor.h | 7 +- src/gui/elems/basics/resizerBar.cpp | 216 +++--- src/gui/elems/browser.h | 6 +- src/gui/elems/mainWindow/keyboard/channel.cpp | 2 +- src/gui/elems/mainWindow/keyboard/channel.h | 6 +- .../elems/mainWindow/keyboard/channelMode.cpp | 2 +- .../elems/mainWindow/keyboard/channelMode.h | 4 +- .../mainWindow/keyboard/channelStatus.cpp | 10 +- .../elems/mainWindow/keyboard/channelStatus.h | 4 +- src/gui/elems/mainWindow/keyboard/column.cpp | 10 +- src/gui/elems/mainWindow/keyboard/column.h | 7 +- .../elems/mainWindow/keyboard/keyboard.cpp | 9 +- src/gui/elems/mainWindow/keyboard/keyboard.h | 4 +- .../elems/mainWindow/keyboard/midiChannel.cpp | 20 +- .../elems/mainWindow/keyboard/midiChannel.h | 6 +- .../mainWindow/keyboard/sampleChannel.cpp | 35 +- .../elems/mainWindow/keyboard/sampleChannel.h | 4 +- .../keyboard/sampleChannelButton.cpp | 4 +- src/gui/elems/mainWindow/mainTransport.cpp | 29 +- src/gui/elems/mainWindow/mainTransport.h | 11 +- src/gui/elems/midiLearner.h | 12 +- src/gui/elems/plugin/pluginElement.cpp | 2 +- src/gui/elems/plugin/pluginElement.h | 7 +- src/gui/elems/plugin/pluginParameter.cpp | 3 +- src/gui/elems/plugin/pluginParameter.h | 5 +- src/gui/elems/sampleEditor/boostTool.cpp | 5 +- src/gui/elems/sampleEditor/boostTool.h | 27 +- src/gui/elems/sampleEditor/panTool.cpp | 10 +- src/gui/elems/sampleEditor/panTool.h | 21 +- src/gui/elems/sampleEditor/pitchTool.cpp | 30 +- src/gui/elems/sampleEditor/pitchTool.h | 53 +- src/gui/elems/sampleEditor/rangeTool.cpp | 10 +- src/gui/elems/sampleEditor/rangeTool.h | 9 +- src/gui/elems/sampleEditor/shiftTool.cpp | 2 +- src/gui/elems/sampleEditor/shiftTool.h | 5 +- src/gui/elems/sampleEditor/volumeTool.cpp | 11 +- src/gui/elems/sampleEditor/volumeTool.h | 19 +- src/gui/elems/sampleEditor/waveTools.cpp | 6 +- src/gui/elems/sampleEditor/waveTools.h | 7 +- src/gui/elems/sampleEditor/waveform.cpp | 2 +- src/gui/elems/sampleEditor/waveform.h | 7 +- src/gui/elems/soundMeter.cpp | 58 +- src/gui/elems/soundMeter.h | 6 +- src/main.cpp | 59 +- src/utils/math.h | 10 +- tests/audioBuffer.cpp | 1 - tests/conf.cpp | 2 - tests/patch.cpp | 40 +- tests/recorder.cpp | 524 ++------------- tests/sampleChannel.cpp | 2 +- 165 files changed, 2864 insertions(+), 2934 deletions(-) create mode 100644 extras/giada-logo.png create mode 100644 extras/giada-logotype.png create mode 100644 src/core/action.h create mode 100644 src/core/recorderHandler.cpp create mode 100644 src/core/recorderHandler.h create mode 100644 src/glue/actionEditor.cpp create mode 100644 src/glue/actionEditor.h diff --git a/ChangeLog b/ChangeLog index 01eb682..ea1d5b2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,7 +12,23 @@ -------------------------------------------------------------------------------- -0.15.2 --- +0.15.3 --- 2018 . 12 . 24 +- Action recorder refactoring +- Optional midimap parameters (thank you @tomek-szczesny) +- Support for "inaudible" MIDI lightning events (thank you @tomek-szczesny) +- Build AppImage for Linux on Travis CI instance +- Huge optimization of the AppImage binary file +- Fix Action Editor repaint on min/max zoom levels +- "Resize recording" flag has been removed +- Change text labels for channel operations +- Smarter column assignment while loading a patch/project +- Fix wrong resizer bar width between Action Editor widgets when zooming +- Can't display custom channel name in Sample Channel (fixed) +- Fix crash when cloning Sample Channel with audio data in it +- Clone channel doesn't clone channel name (fix #219) + + +0.15.2 --- 2018 . 09 . 05 - New sample-accurate Action Editor - New MIDI Velocity Editor widget - Ability to move MIDI events vertically in piano roll (i.e. change note) diff --git a/Makefile.am b/Makefile.am index e101bce..4cb3529 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ sourcesCore = \ src/core/const.h \ src/core/types.h \ src/core/range.h \ + src/core/action.h \ src/core/channel.h \ src/core/channel.cpp \ src/core/sampleChannel.h \ @@ -33,7 +34,7 @@ sourcesCore = \ src/core/conf.cpp \ src/core/kernelAudio.h \ src/core/kernelAudio.cpp \ - src/core/pluginHost.h \ + src/core/pluginHost.h \ src/core/pluginHost.cpp \ src/core/mixerHandler.h \ src/core/mixerHandler.cpp \ @@ -51,6 +52,8 @@ sourcesCore = \ src/core/graphics.cpp \ src/core/patch.h \ src/core/patch.cpp \ + src/core/recorderHandler.h \ + src/core/recorderHandler.cpp \ src/core/recorder.h \ src/core/recorder.cpp \ src/core/mixer.h \ @@ -85,7 +88,9 @@ sourcesCore = \ src/glue/recorder.cpp \ src/glue/sampleEditor.h \ src/glue/sampleEditor.cpp \ - src/gui/dialogs/window.h \ + src/glue/actionEditor.h \ + src/glue/actionEditor.cpp \ + src/gui/dialogs/window.h \ src/gui/dialogs/window.cpp \ src/gui/dialogs/gd_keyGrabber.h \ src/gui/dialogs/gd_keyGrabber.cpp \ @@ -101,11 +106,11 @@ sourcesCore = \ src/gui/dialogs/bpmInput.cpp \ src/gui/dialogs/channelNameInput.h \ src/gui/dialogs/channelNameInput.cpp \ - src/gui/dialogs/gd_config.h \ + src/gui/dialogs/gd_config.h \ src/gui/dialogs/gd_config.cpp \ src/gui/dialogs/gd_devInfo.h \ - src/gui/dialogs/gd_devInfo.cpp \ - src/gui/dialogs/pluginList.h \ + src/gui/dialogs/gd_devInfo.cpp \ + src/gui/dialogs/pluginList.h \ src/gui/dialogs/pluginList.cpp \ src/gui/dialogs/pluginWindow.h \ src/gui/dialogs/pluginWindow.cpp \ @@ -280,7 +285,7 @@ sourcesCore = \ src/utils/string.cpp \ src/deps/rtaudio-mod/RtAudio.h \ src/deps/rtaudio-mod/RtAudio.cpp -sourcesTests = \ +sourcesTests = \ tests/main.cpp \ tests/conf.cpp \ tests/wave.cpp \ @@ -364,7 +369,7 @@ cppFlags += -D__LINUX_ALSA__ -D__LINUX_PULSE__ -D__UNIX_JACK__ ldAdd += -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm -ljack -lasound \ -lpthread -ldl -lpulse-simple -lpulse -lsamplerate -lrtmidi -ljansson \ - -lfreetype + -lfreetype -lfontconfig -lXrender -lXfixes -lXcursor -lXinerama endif diff --git a/README.md b/README.md index a5af7a7..91998f3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,18 @@ -# Giada - Your Hardcore Loopmachine +

+ Giada - Your Hardcore Loop Machine +

-Official website: http://www.giadamusic.com | Travis CI status: [![Build Status](https://travis-ci.org/monocasual/giada.svg?branch=master)](https://travis-ci.org/monocasual/giada) +

+Giada - Your Hardcore Loop Machine | Official website: giadamusic.com | Travis CI status: Build status +

## What is Giada? Giada is a free, minimal, hardcore audio tool for DJs, live performers and electronic musicians. How does it work? Just pick up your channel, fill it with samples or MIDI events and start the show by using this tiny piece of software as a loop machine, drum machine, sequencer, live sampler or yet as a plugin/effect host. Giada aims to be a compact and portable virtual device for Linux, Mac OS X and Windows for production use and live sets. -➔➔➔ [See Giada in action!](http://www.youtube.com/user/GiadaLoopMachine) +

+✦✦✦ See Giada in action! ✦✦✦ +

![Giada Loop Machine screenshot](http://giadamusic.com/public/img/screenshots/giada-loop-machine-screenshot-14-carousel.jpg) diff --git a/extras/giada-logo.png b/extras/giada-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..66b17f314e5b7e7ee5fcbbb7123bce878265f716 GIT binary patch literal 4616 zcmV+j68G(iP)rgJ_YL8kKgeR; zckjNxyZ1i(?7h$4L@^#l^IzuW+>L+g{iX2oBvD{4=Z(CVOSqD&xsjW=iYxgD@8b9P zH8J13n>~C3U&mJ)mj-?>=%OCxg@04O$1a}1ukt27z-Jj6vx@ied;Fr9Bz_mTtNS7b ztgAVRKj$Vbh{De;W?uUDFG7woy0YLWc$Y&kwVogGL+s|uFBZUh@f&=cYoh{HV4mX= z{)_LYL`2?>2NC;$x`z9jJE96xn<+M#hIv(og3AAOxd$)dBe8_4dG6!+=FfS#b()O-QH$bB*`siDNcAk{H@5;%qd=-qI63Kuev3E?DQ>pWM+l*deulSYGy5txl-SdSezf=Zl5 z0~%0`aunlJti$to5W}72uxh?GcA}Wab@q5E0|rLn0W85r6r%}kM^lLZt3wgqK>_YZ zE;8V@$#3E`i>Py$kBG!#J7$9iDHx08ID$&l1%ab7{o_0;a1hHd2&pjaKvvHqqX?Km zMEyFOv#j%!55w^cj%h&@3AlZji(&9u1+9s}cM$cYIZ+eC7py_$hKc$31ZN$9sx@ep z*n)@QLZ(II)to|<>O4?_L}&9ks{%L=m5zZFYL;V`WnCuz5FO5XSf7*(48{VKIm%Pv z`0Nn=h%9SdHp)>Q_FE$2MOMIi8f9pTGE^;)HQ+dABHapL{Tz#0_9h}RLU%P37=yhr z0;)A=8!=2*Fq;@6*%8155gDeQo#lZKt8qRKVEF;7!t?reSt(!Z->=vO?ALmnB`_Kl zae}G^vIEG`1?-puwpP{;c&83jCKjVE&S3cgJB!DVp|=@()7{yId#67U@d}*>_QCe3 zuYx+Ne?8oKfK9UO4|5ZdIYq~6ABH0d0#yrSpJ1St)ob5=LW%=CBG(&`|r{ zOrk8!m~<-FtAaHfwFwJV0I<`Ts;O~hcpnr6aR|yg1XO`}6#Z5CYpwcx_9Z9^PR-+dKQ)!?eUwTnWI7SC7HT8kWe9sj67CBDHi z6yY;$!4`ahgD626s&LLx@SyH_mHSzJKUTR>ChkOoBQF2|#rP-kF$ts47wK@pg>8kYl~+rg0)+TEbDVHzP1S1SQYiKrCor`%;fXYF;0Q) z$chqYwQ&6iOB@DlMw$u-|99I}aju-{=cCcW_dAS%u{Z&QAvkM^^KVo5lwv}g5E*|a z`$-o5ZGo5?j8=D<*aYlB3tJ>$t%?_awGpiDU00}>5a%oqGsPh*OKs!-x&?tei9rgR zZS(42i|cMVDOMadD`T89pNd`DRMzAFRPO!D3s8B8h_}lwPsRytLMk!9vS3*SYz$6o zOTcOsW8$u0GeWB7@6-khm~B}Q`y$Ko8o5T3Qs9)r=P}m?#}uU&*Gg?7wka*q0d7ji zAk4x_>_v66>$w7-;w4N&wg&71y>VKb);r{@V}0;JC@B%en!Htu-)q?Aeeeb<(9p)o z9LS?Tjs-|l7Em|lX~Xpoxa6_zfNO$ZmR&ju6`F8;t|(3dUOXt}2jD2iD+6YtR0}ZR zw2CC$>$m7H;|;P)r)xR{fjNrSRyH;$`9^{Bctu$#|E#@+=Se??C?v`uGQ079+2faJ zeywu~%Ml(|KZPMu z%ggLbMEoZiWB*a>!3}s^L0Hz=isHB43n*vs!TNl>12#)GKn6xQ z09Oh6O3x?laoT6UMsy-s0gW`RfC2C?iB;sm-44K2Ax+BlVXsEoCh)G@np@BDfa@Ly z;8JmHkH<7TiUoKP+i)D^sEq&|@SYSd@s8+3vKmQATFx6_lT;XQcL1($6cQ#4c#sJ< z+;G8zVYna5@KqSdfVbts_$GHEE_YVdZ~BqJ5+5kT-Cv=5ZOx>x=IrV1i+p-j`{YdUc96&yJ4 z*mB@J5>jyb;R(ErV$=X=LIcXM2d`psMDLq{*%$6PQq6(G_sN*{4oz3cDh2o85Y$?B z5AJKVhGoLQB>WA)z&wG@d&yQGy>l`Mw zimy+@LDWbrcL1oyyBH91^~=QLD3hDLXE>FJL=U-AEYW;5l~aRWY_JsCSk)|u0z$utk%%a83iQVT?ZDaEX4RdFh1$uV0I(Mm zL#Tg+a9pW*H4%w!TqL`6sU}=Cs%Hi@0}$^vi@Bls30wPOr&-CBJ>XA;yG&+zF&@xN zV+3Y99`vupiVH6(&<7vbT3LZ5`f$B|p~;RsrDQa|(4>SUQK}SR9nFWNL<5BAwaLN!K%O4#4G- zr^Bt*R;nLbNQZkJdz#bX8c>RDcpg*HJ81dO#C*q&8Q0@JWgR9Oo73Tjc9DPo-&;tw zoM|K3(iet~;f@|?FOs^jo8ej)RB2aU(kunvXcJnYUbR6nmDkiZJ!GHsImOyI<~_lw zyc6YC_!0|*zV2`$_~Vw&jYM7}`>OdiZVZAe{VJ7X2ct_>f;C}Ab z`jE?)a$-M;>me7zk&sBJAc=x6;vb@QO)rrzC--SfqEPJ65vnwN7^S);@{A##)C#Nt z#D=`bU6$0GjNSn~ft)DS+W|h5r}6%z?E$^3Ww{cJ3_NS`_~$Omr!ZQ!z;mNko~Ty# zF}H;}T)bL^=}8j4A9=S>cF)%(uVDcFm+VTK`O;A7R9CPMy`#|B&X{LQo=p?oN$E% z>sI0)tMu!1V}Bfqy4)VcvVA}`lEXduV0!o}s=Qc5e~q?XGUyB3rCBm)bX40<)FCO{ zlk*~!)H82XBYw3b1*zs~6r{@2KJ%tSO;tGVDv{N+d`oTjR_(~LUXM5A>HlCbM%s#d z%iuZ|Mt6x}GPtf$vx3GuSWNE--o(5M#q=_e7F0~nzyOTHbQEB76f6G%ABSEU_ZvSn zWS#N2KyAajI4Y$YOK=dMV+%gOb{xQQl%XnO)}J%ae>rsK;0%TS!`w@)5U3YBI$$Bt z&`+Ei%Brs(_&v3nyf3L1wd-hQX2V5=LgsYjD0fCsEn+-WJD`?$6WAVVb$*M~OJs{& zRhQii)OTGZFsgrSYp76_Fmn8Wp_5o=HQz;Uc*5-SnJ!b zhb3fxFcGm&x1FUIt8gwsAp`30xAqR36XH6{TB}{(ppp8`Qb#+G5*ZD!&f^bl6?_we zQ~gyK9T-E1NIa__y@_orUNxw;!VKZ8F$X%{e_cFg1+c+b7^6nd@+&rB5{&lVhbA85 zh#0mWnVos5)%=F1Q599VzLlK0tixIi4M*S1X!WQkUS}8ELh}#`k@FDpuPtOi(&u1C%AP+OJ1RHP&jc7aChByGA7W=Rk z1(=Fq$k1;S-qz4L&BC$9^iqGAM^>yu>-Y$AF%y5qdVGpvl%pCAs6{nOaR?vdpID0f zG0@qjCe?f`#cJgEw?E}R{U_28+pk}wH2Pnx}B%2&beom+@V^iqu(CBR&6bhq4u-a#IF z)Q)KpquD6N4emtoA527?!dnv>tTo=BQ=*KXL}XmfgXEot;xWb7rYrN3C~Ttbj4^&r zdx5JH3am1oALyBzV8c%m_u{AQ+5C=iRXslMp0{vTc&XRw-hk*5oFgyFYpjPATh zCG9*4^DCam*Ac~RM`(ZeV|C&h?rZLdsQ_+bk9ie!6|L!!6A%)Pw}U92Opwf)T{B8{i>E4-15 z)N_0o46fw8{2nhe?=ycJ9M-#tM@#V2yo|e<*K-fkDm7xMeOn~!oOS92pbaTTB9 yBjQW(F2BN0@?FBsH}G}b)q82+_kH6){eJ)j5WT=f(O8@S0000A=%!EP)yDQwjx2H$zsXy@N^sQTU?|JU2I&}h3 znh~k~FWHh0;-jPOM3hI1iE=8x&5L<8mvR~JtCI=667)!}!nsUmGy@E!b>Pn4?LKH%#(#4X*kcKE%Ks{_BtYj{^6w5n=5cs2ju zqN9n3qVAD9{C2@HQXR@YD^a5(&yw3X<<5AfawCRS{5xQB1%o`_^i9?0bxMXRdzBj?LzH3!xX4vhqoh`1BKpOLhxYA;9s z=-!k=qXXZR8M`ZG+Z!2iW)t_W92x<}(U~!`#d{e~tEx6Na-2I-c4!>>>IzHu+J)!{2`lh1-o)d$ z76bj&TxEQudy5X4w;UY8yHnFq1&ClMuExW78!J$O`lv|o|8`?3-oiv&i9skpC`COc zwz6=V%OPV>L_}gzDyH@@I$;E+VkJtkyB3j5S`0g}8Ot#leUXbuD)idJgB`gXG5`@G z>e*D@;9XZ0VIUsBk5+V2k!%TW!2m?P60Cy3qlntmIQMcL8hk1Egg07+AaOT7!Zsgh zRhM9;n1>sYk3x?J$?`IyT=!xf8V-U)Q~8M3Mc{2H^^H)8+9urKdG96u?%u9L!)mM> z?ZvhL{cs;P_^PXt>96l_D~i2ouc2~3IU#mKb{<56JeaP>;4sS7!ANCSEi5l^!b&aU_*aBHBASz{(bcH|oE;<+3GM~H~z zWj5Xx1F$kfXjMb6kI=`;TSK1c4Dk*DqZ^6#=J}bzxDuN(j94*peTQ>xbk|xwz`c8i zfFVCH*I1XLJmY9p)k-naW?A@8ZtC8>LqNzSBEHF-SmQHhhm}}!ZN`NbCXHwzQLz&O zhhPxRSORFk!ZN=s0d#+#H`*#QgMHI>AcTM+Lmu0ZNs_Vi#)SO(?-0`u4 z@90WIJkV5Qjmd~pEHllu0fP}Xqt;H&J;8~Bga0sH43=P1qo7q)TZLYxdh0bO1rGjO z1}B?&UUb8%MxwE5G}l~Hy#+3IDb*bOq!sZgyu~aJ(OL%A=)4gf3g5+-0YhN1^LBOm$bjLyhM9zy7a;kXQU;Z3adjb7`})39`_*w4jq zIrxBLBI1`#gsuo%d=c~Acme&4z3K;Jt}i|xz`KUA$fB>43I~4{gvXnS`Sg^9y8>Tg z0wTz_h^mSp7u_)ti&EBKz-?xJg{z!QIQRmS3>X4Ku_r~r{tV}$o5%ct5p=^PSdk*R zHsEZt`wen|{SGN&IEjP#dy`}`U#B3}z((AOoK(|FBZSBBYf9soVl)P^^kko(bExv) zaF%StZDl(VmLvEEIi4r-z4B&xg*aD4_;_wB4lK;7dxg=*k%)*QEh5?RO=O3fd5K5k z%JGWqNW#Y1N5$@H-Sa3S;ueu?(-v+KZ60Zs=xO?-T4MHwq$A=MHNUcg=xIW%`#lRZ z08B+cujx4BMlu9%r=+_Eo9NB$96$z8naelx>%5qMN$$tUU-PSci>xfcTw*RBSv-kn z^PGh3C7I(%E~b$*$r@I!nZ58(DyJ&p6n zghxbt7(dK2759IdPxG*jkqb=xFV=e&f2KOj?TjzmTP53Q?4EiN!*neVrq$~q-<5>gkKQZsxE%OCpnnT{i3Z>r z4KFmh*G4|kBA$;A;MMxw%>y2Zwa`d}y|;e&!6Ugwm@HA}r9vqtBHmv-;mb^SiGE$O z)O!nmsYMh%!q~VpgXpM+qE>7KbLG+AbDQw##%TeETYQ^jA@2<4pcwcB#Q|xo8f|)x zC(Q-Ci#!v}cvwp0I+8!}akf+XW~%623&gORe4J zSZGIai+_c0BMWiaH0 z4NfgE_(qSPz&+;oh_*I9Y2nXwofYFh9t3wghM9+Pq)4u7J-9&uubE`cm=W<{%7_pV zx8hF&nCnN~_D>JB>S}wk_9_1cfW72m!TT-1)MDp?gwRA6{yx^&|!g5?^Q$|pW zQGBG;NKWE1JKZRH>McM-q~B)eV4bzlmZG=k#hup&IviWAHR}wMui$z18{@*(ygtAW z$!_uAL2E6;6Rq0MX`#*OR*=-pjK5hE3mEHpG1vpeo^6FM zTaFaiXh3D@Ay)>EwHWzkxr7(;IuQIxJ4Id=~q!u6Q>5%(LqK^JhYd1f}Pu6ME_!XXSycT%J zv;=(Q5KrXI(@*dUy}+)p)LFamzZU)!UGX9|qr6_AehfRY2KOP)c##;wZPwKK7xMMy zR!bLnzSQ7DFp(3w=ZO8~Ge)x&L2518hs!+(+-mcz`5J1K#a~g_O#GKPJetQJ8jYpB z*@>38(F?WY%dymgstG==>trZ4TT<(D!zGv% zk2ClVB9TzgWfLO)hrwL$35r_XNi^Xnm2Ou_Jw~|LCu(g?d{s=G#Yh8J+G&R4_*LOR zr8w>C)lzpTI9E5st1N>bfpLbXS=}(#C}tSgiD!%#hL2g>GKXl1E2h|F)RjkR4~iGE z(qlWfg@HMER&ZLYq#18d+RF;QMsbe^OuZqzQH!XAa)?ND&@NcFMq3)Zt8U5#`-_Ud z-k|RIApS=83Lm!A0y_*ASTUWo)T-wo?t1Gv%XnidEnp zOKMFvNb4fd%aT|X0G>C}h9P`s+4BrNwbqC>7S!seCq*wcv4}`sqv_?@0I9W95t~&+ z#BVBg&oVvKBH|?)&x}hoCOnddvEuAboTOYXZsoryrdsqTJqcD>Qp@B#D~dUu9BQW- zM16&Dr)A&s_4;s=ut)>X*J*Utczqwa)TJ4pF8j-pgss1jkyb%bD{`u$kF|VAn0S=p z7oQQG9XPd$W2P4MTYDg^nXfEVdEpQd$=embxJuQv(9^(XAf5g@-AA@fFeNq|>`lXBr=2Nv#=>TG8)|ot|TZ7aA-#^DP&f8&RS?*^)gh z1pJ?x-a3RR7^F_Eam4$p?3=Rkv%sG#?sIcGs8vE@UrqSp2dp$?YA3rFxE+DDLyV*f$+oOJwug%03qHelescq8cqw{Bm77s8!U2C#l_@MbdcSo+QSqt%D=0p9zms{PN|ZW{Ib_ z#?NpP;5%)0zmTOPQld5oQO#ZY;Cm}-J*_wDKWmw0ozVu=&dpo2zv7oa^C1a8x;kf(j@V{Jtp!?tyh0zU zoP2|B+Q5{hmM*&5{o4yNXliY>q!z^Cep5@Hz?&5TfOY87wTaSnJu5BLx=nLrl2mJn zV^v=Farc?6cnke(T@x#*Ww&Q}SL5Uq!8txqYc+akgsv+V`JE%F|vdm`?W(@V8XCK{GtXyrOHM!A)U zzN$HO3gdhqsI^&J@Vf|$ElRfnv-GEBGtC!-;-fTC>o|X@Ros+cQ`~J_{d6upnk$3c z2d05qvk_@3_ezw1|VV8isAzlSM22YuMV%(8Q<4iQERz@ z@qK;DQ`2J6GttA^JwK%G{atog-?U!V$f~8h(;=8>V z-?_hvCW*sOG|%=<4@bs}t%$Hmdzc$e$%8P~o=T}wmp~Mk*2!QGJgkple#AT@E5GlF zTB{qFTF-b11?A2Pp9YAWKpmZ$X!_V3?~NfzQ+?&W*e)``-Of^6^;2BNUX6Pr^6;*u zqtigGK#Wb1T6=J5%0=)Z5csGTwI=AjF>bN&#+c)US|ZA&ysb{#oi1ucNF2<|yacNk zwB+v;*FBVZj!sc!2B(N3AclWbxORo>6@cZbI``ZumhQP1YQ_3&D<`D*Md!MNG!O_O zADz$4mhSENskMYM zX`UD@`ip@HTW_V&<|}!?>48y;b?&*}%T@_=mg>H1#qcEa94O9K7MJ*ts7@(1dB1LF zG{G{cWP(9Z$y+G}mE4qY@zxV};0>(6b^sM9#|A9Iv$#|lJr=>(%037C8dM@*(By1r zZ>aq=g=xZVeWcbij38R%lt6<@)a!#UIWD-RxJ8OZ1e2eJ9aDFD7wZ;Iqb$QSrW=Gp zmSC6XP{<{9Y$f|HTVE`V+cYC^->sj04T!%-GeLVp61lzM>J31FW;N> zqe+?8x4B=`ie?2vEQXkvtgyBZd##rIg|d%%yieR+yYQ}n=6ZGA!A1QJ;9q1Ez<-lR zYVE)!wfZT*6IiKZ5?1b1kSfUL$S;4f~m??#CbZ++rdU zmj*>Gt_VtNN#gx9GH%%^^|sIdi7#Z zpg-O=a$~9?*hF+s5SJCf4f=DkO?;vSwT5e`^>i{TR2@ySuR0f@-0h#|J1-AR;2?)2 z)?2AjYa9{DPMUk)TQ{KzNM!T(fz5PVoh6|>O6Q;}FwatNJ#G*MEYJ(ftdpg^R&AEZ z#r@U=U;$t;E=ge8Fi63+*1X16lvrpS$PDT;3E~qpyMizMqL%!NRf#&=wjc&H7TssGiI7pFkuChI9Nv-YLW8pJwgl5FrMf!TUtgCzWxIfg=WPeD#mCBQYSP+WSH21BS;$-)RcZ2!=nt9NRl-UaNdU10shhp3}{qyMedB!m2* z7L#nk9|bCK;0ua;rB;N9qOG*4ySIhw*$4TMfai%w?H#$koWd~tVky203>}eT(4f~B>Xtt{zj`Gh&f<0Y9ZX@%)U{SWBI3sarxp?wdb2MA6mAmE z<^%ayX|#Y9^)kMPvjeLwU291gIifmYCFkpYUVs_a27`ZlC|&m)7FCz7E5};Q$K$vh zU22)k3vstE#EQ8~U2MGP5_`!SI>+bAW#J?iooj_&$WwSUzhgA2zbCw_wbm+Vu5+jI zrQI5D6SXc~_YBYAS^Tv!<5W_w$9@!PXP+X$y6u%Ue)Ija^m^rgJrrEMK81p-MM=gn z&^KaLpaM&^X#lFJ^`R+?wNhb{c!BQco$!;jP;WLm-%6~-lB{E`&GkC^KVoo9z+lJ=tcgXuuP&+}r3F@x z#AX#+YK5EdtD2+ob;eCuiCOeYE499n2iDFTc?J>jx9SC`@jTVm-yNN6d}LV5=0eLF zTe0?>&yVYN?DdgYvE5t`GavdB$piSDzy#;zNS5?LYek8VRHw_6qxf*+_O{N(nm%gD zK5X4kDZ;t@T#dtTXG6?qJ@cyr#s;C7_)~Z`H)nhzWYpzQ@yY zYz6xG^0@Nw??9*pgUTvAnmmL5psi|bX$}y|U}rn66*@>GBEoXE^Od?}Mp~$~v>-d- z;#}o=_A?)7MXjb9`^3F983rPHNBd&!(QdLtG{s&Vi|QFRx(7n6*fD{&MEDVu0{A#h zSx}p{f%~YvLd>bvlZeE9>gAyITze{sioAV;rj~f2Q&!Adqt0Tm89%GMZfZEoW}aHt zumxO|;Yl2*|F#z3Ay48Be1+l^A6OW-1VpV0?9vf`eoDk5B6$$M=!@X|hmY3Y&q8bE zWvjDs$P43lEZX}kmtFEYq8>p~Yk0U>Jh9H9&e^g|8H;ze^?N;y%ajyvXXBWIFn;k! zt_A3mlIAMJ%)qDxT%g*05UCl7e?AJ@5`Y$Np1tt@8dq z1g5pN$Ty<<5p_#EssU=3s=}5YKAMYkN90vMJjG;LHqmoz@V6LgVv9=}-L)_vVpRZm zLUp>e!HWr_E@D}ZqkrIA#dc5g-_AFQqpA!KnioFHG!+d zytCRQACjjTJQal2#ZBr5 z;An|OL|vOE8WCroRO#&JK|jYF1rv^6)VDWkidR?GfgaAVkxQv8I~ zSdPyz5AWdm+KK*MLtFSt(z4 zQsCe-x)PBXWE!40274O0<^t|blyx=lf1D^d_`lzh`|x|F!5)`4a%yeCh=f@jx;sH| z2m&(SbWs?=&DhchqzO2vL$2{@wCDLvt#jfXuEO>KfDR58 z5FeV8>r%^t3z>fGEEe0I|$EjqNF{7q+#V3ts*9ANoUtsT27eeBY28<)3 z;#T~*U9R?r8pTN$FfBgX`PFVsoyKxV8~9}2YnL%_7&d1(xqzARc0m`L<&ail5fK;J z)n5uDis{&qq4WZF;~()sBG2OK&1IY#K#NX>1FDe+EYm$h} z!NFIQ5RveZF|GjjU_)iNex}gseaHj>B(hITKRY-y0K|AN4+H8eI zP32hcrRRz;5clH;ALtdsVvI$9L@Ukc0uh7}MR)W@UzDH(eb5~;(Gsx6crl9TY!&z$Priqv(#j)T(xsi@!SKg+pfPuOW8l zT=Dv=0AUQl7(9%*_zrtfANAAE0KhIR!E2a^%P{~22zf5dPWmt>v@F8Bpqqq`%QlwJ-U`IaEy+wzNLw6$LllcoD zi)5k@1;|G(Iw1!+=!lNUK@K`05BVrS*k5AJEZE;I1syWhq!Dc;rvy|#J=k_#L6%v4 z3kQdWquAxVtx?fxz3k%jl|zPO9Gyu-qJwGfrwnLQBgeV4oeqsjcio(dIX+EouNc{{ ziF;QLjcnh=o%sEXCf3W*Ke{*N(CE;fh$1byFYnAmT8STIeql4`fa}mmF+8Thp0KPg zA9UNmcN4k&l|!ROOnkWm-<6uLhHu(x58ot?bMM9Bx3v2@oL_5TVpZ^i;Vkzi9DZ|V z4Wh_#{2;Gv5L$iBw{cq{=OO6u8$>LZpE#6z$WPO{2z-abvbB5u4!;YC_B@?e1#CUs z&Oh>_k)xcha`>I;ucCcrSWd7>7HZMn=c_qC+R{C1hu`x)thKz5KjjLamVa~Qpuaj} zg@Z$tTkNULL>^zo&+yy4)Vvl7gTbY|m}m23d9ggbs@D?tU>zKYYA4c^`|v^XC_X_- zzKHMRDLjW4@+vOnGTzG-yoERNO7W$b#n14ae4YsL@!Xc%M6;`|@8KS%Lk8pj11ZsX U!yqZ~2LJ#707*qoM6N<$f. + * + * -------------------------------------------------------------------------- */ + + +#ifndef G_ACTION_H +#define G_ACTION_H + + +#include "types.h" +#include "midiEvent.h" + + +namespace giada { +namespace m +{ +struct Action +{ + int id; // For persistence only + int channel; + Frame frame; + MidiEvent event; + int pluginIndex; + int pluginParam; + const Action* prev; + const Action* next; + + bool isVolumeEnvelope() const + { + return event.getStatus() == MidiEvent::ENVELOPE && pluginIndex == -1; + } +}; + +}} // giada::m:: + +#endif \ No newline at end of file diff --git a/src/core/channel.cpp b/src/core/channel.cpp index 6185d74..5c5e554 100644 --- a/src/core/channel.cpp +++ b/src/core/channel.cpp @@ -39,6 +39,7 @@ #include "wave.h" #include "mixer.h" #include "mixerHandler.h" +#include "recorderHandler.h" #include "conf.h" #include "patch.h" #include "waveFx.h" @@ -47,10 +48,11 @@ using std::string; -using namespace giada; -using namespace giada::m; +namespace giada { +namespace m +{ Channel::Channel(ChannelType type, ChannelStatus status, int bufferSize) : guiChannel (nullptr), type (type), @@ -90,12 +92,11 @@ Channel::Channel(ChannelType type, ChannelStatus status, int bufferSize) void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex) { - using namespace giada::m; - key = src->key; volume = src->volume; volume_i = src->volume_i; volume_d = src->volume_d; + name = src->name; pan = src->pan; mute = src->mute; solo = src->solo; @@ -114,25 +115,12 @@ void Channel::copy(const Channel* src, pthread_mutex_t* pluginMutex) midiOutLmute = src->midiOutLmute; midiOutLsolo = src->midiOutLsolo; - /* clone plugins */ - #ifdef WITH_VST - for (unsigned i=0; iplugins.size(); i++) - pluginHost::clonePlugin(src->plugins.at(i), pluginHost::CHANNEL, - pluginMutex, this); + for (Plugin* plugin : src->plugins) + pluginHost::clonePlugin(plugin, pluginHost::CHANNEL, pluginMutex, this); #endif - /* clone actions */ - - for (unsigned i=0; ichan == src->index) { - recorder::rec(index, a->type, a->frame, a->iValue, a->fValue); - hasActions = true; - } - } - } + hasActions = recorderHandler::cloneActions(src->index, index); } @@ -157,9 +145,9 @@ void Channel::writePatch(int i, bool isProject) /* -------------------------------------------------------------------------- */ -void Channel::readPatch(const string& path, int i) +void Channel::readPatch(const string& path, const patch::channel_t& pch) { - channelManager::readPatch(this, i); + channelManager::readPatch(this, pch); } @@ -202,15 +190,19 @@ void Channel::sendMidiLstatus() case ChannelStatus::OFF: kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopped); break; - case ChannelStatus::PLAY: - kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playing); - break; case ChannelStatus::WAIT: kernelMidi::sendMidiLightning(midiOutLplaying, midimap::waiting); break; case ChannelStatus::ENDING: kernelMidi::sendMidiLightning(midiOutLplaying, midimap::stopping); break; + case ChannelStatus::PLAY: + if ((mixer::isChannelAudible(this) && !(this->mute)) || + !midimap::isDefined(midimap::playing_inaudible)) + kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playing); + else + kernelMidi::sendMidiLightning(midiOutLplaying, midimap::playing_inaudible); + break; default: break; } @@ -309,5 +301,6 @@ void Channel::clearMidiBuffer() midiBuffer.clear(); } - #endif + +}} // giada::m:: diff --git a/src/core/channel.h b/src/core/channel.h index a827d54..45ec277 100644 --- a/src/core/channel.h +++ b/src/core/channel.h @@ -33,6 +33,7 @@ #include #include #include "types.h" +#include "patch.h" #include "mixer.h" #include "midiMapConf.h" #include "midiEvent.h" @@ -44,11 +45,14 @@ #endif -class Plugin; -class MidiMapConf; class geChannel; +namespace giada { +namespace m +{ +class Plugin; + class Channel { public: @@ -63,14 +67,14 @@ public: /* parseEvents Prepares channel for rendering. This is called on each frame. */ - virtual void parseEvents(giada::m::mixer::FrameEvents fe) = 0; + virtual void parseEvents(mixer::FrameEvents fe) = 0; /* process Merges working buffers into 'out', plus plugin processing (if any). Warning: inBuffer might be nullptr if no input devices are available for recording. */ - virtual void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in, - bool audible, bool running) = 0; + virtual void process(AudioBuffer& out, const AudioBuffer& in, bool audible, + bool running) = 0; /* start Action to do when channel starts. doQuantize = false (don't quantize) @@ -88,11 +92,16 @@ public: virtual void kill(int localFrame) = 0; - /* set + /* set mute What to do when channel is un/muted. */ virtual void setMute(bool value) = 0; + /* set solo + What to do when channel is un/soloed. */ + + virtual void setSolo(bool value) = 0; + /* empty Frees any associated resources (e.g. waveform for SAMPLE). */ @@ -136,13 +145,13 @@ public: virtual void stopInputRec(int globalFrame) {}; - virtual void readPatch(const std::string& basePath, int i); + virtual void readPatch(const std::string& basePath, const patch::channel_t& pch); virtual void writePatch(int i, bool isProject); /* receiveMidi Receives and processes midi messages from external devices. */ - virtual void receiveMidi(const giada::m::MidiEvent& midiEvent) {}; + virtual void receiveMidi(const MidiEvent& midiEvent) {}; /* calcPanning Given an audio channel (stereo: 0 or 1) computes the current panning value. */ @@ -191,21 +200,21 @@ public: Pointer to a gChannel object, part of the GUI. TODO - remove this and send signals instead. */ - geChannel* guiChannel; + geChannel* guiChannel; /* buffer Working buffer for internal processing. */ - giada::m::AudioBuffer buffer; + AudioBuffer buffer; - giada::ChannelType type; - giada::ChannelStatus status; - giada::ChannelStatus recStatus; + ChannelType type; + ChannelStatus status; + ChannelStatus recStatus; /* previewMode Whether the channel is in audio preview mode or not. */ - giada::PreviewMode previewMode; + PreviewMode previewMode; float pan; float volume; // global volume @@ -221,26 +230,26 @@ public: the delta during volume changes (or the line slope between two volume points). */ - float volume_i; - float volume_d; + double volume_i; + double volume_d; - bool hasActions; // has something recorded - bool readActions; // read what's recorded - - bool midiIn; // enable midi input - uint32_t midiInKeyPress; - uint32_t midiInKeyRel; - uint32_t midiInKill; - uint32_t midiInArm; - uint32_t midiInVolume; - uint32_t midiInMute; - uint32_t midiInSolo; - - /* midiInFilter - Which MIDI channel should be filtered out when receiving MIDI messages. -1 - means 'all'. */ - - int midiInFilter; + bool hasActions; // has something recorded + bool readActions; // read what's recorded + + bool midiIn; // enable midi input + uint32_t midiInKeyPress; + uint32_t midiInKeyRel; + uint32_t midiInKill; + uint32_t midiInArm; + uint32_t midiInVolume; + uint32_t midiInMute; + uint32_t midiInSolo; + + /* midiInFilter + Which MIDI channel should be filtered out when receiving MIDI messages. -1 + means 'all'. */ + + int midiInFilter; /* midiOutL* * Enable MIDI lightning output, plus a set of midi lighting event to be sent @@ -248,17 +257,17 @@ public: * else gets stripped out. */ bool midiOutL; - uint32_t midiOutLplaying; - uint32_t midiOutLmute; - uint32_t midiOutLsolo; + uint32_t midiOutLplaying; + uint32_t midiOutLmute; + uint32_t midiOutLsolo; #ifdef WITH_VST - std::vector plugins; + std::vector plugins; #endif protected: - Channel(giada::ChannelType type, giada::ChannelStatus status, int bufferSize); + Channel(ChannelType type, ChannelStatus status, int bufferSize); #ifdef WITH_VST @@ -271,5 +280,7 @@ protected: #endif }; +}} // giada::m:: + #endif diff --git a/src/core/channelManager.cpp b/src/core/channelManager.cpp index 0c3e5fd..d138be3 100644 --- a/src/core/channelManager.cpp +++ b/src/core/channelManager.cpp @@ -37,6 +37,8 @@ #include "midiChannel.h" #include "pluginHost.h" #include "plugin.h" +#include "action.h" +#include "recorderHandler.h" #include "channelManager.h" @@ -49,21 +51,6 @@ namespace channelManager { namespace { -void writeActions_(int chanIndex, patch::channel_t& pch) -{ - recorder::forEachAction([&] (const recorder::action* a) { - if (a->chan != chanIndex) - return; - pch.actions.push_back(patch::action_t { - a->type, a->frame, a->fValue, a->iValue - }); - }); -} - - -/* -------------------------------------------------------------------------- */ - - void writePlugins_(const Channel* ch, patch::channel_t& pch) { #ifdef WITH_VST @@ -89,10 +76,8 @@ void writePlugins_(const Channel* ch, patch::channel_t& pch) void readActions_(Channel* ch, const patch::channel_t& pch) { - for (const patch::action_t& ac : pch.actions) { - recorder::rec(ch->index, ac.type, ac.frame, ac.iValue, ac.fValue); - ch->hasActions = true; - } + recorderHandler::readPatch(pch.actions); + ch->hasActions = pch.actions.size() > 0; } @@ -132,13 +117,12 @@ void readPlugins_(Channel* ch, const patch::channel_t& pch) /* -------------------------------------------------------------------------- */ -int create(ChannelType type, int bufferSize, bool inputMonitorOn, Channel** out) +Channel* create(ChannelType type, int bufferSize, bool inputMonitorOn) { if (type == ChannelType::SAMPLE) - *out = new SampleChannel(inputMonitorOn, bufferSize); + return new SampleChannel(inputMonitorOn, bufferSize); else - *out = new MidiChannel(bufferSize); - return G_RES_OK; + return new MidiChannel(bufferSize); } @@ -173,7 +157,7 @@ int writePatch(const Channel* ch, bool isProject) pch.midiOutLmute = ch->midiOutLmute; pch.midiOutLsolo = ch->midiOutLsolo; - writeActions_(ch->index, pch); + recorderHandler::writePatch(ch->index, pch.actions); writePlugins_(ch, pch); patch::channels.push_back(pch); @@ -223,10 +207,8 @@ void writePatch(const SampleChannel* ch, bool isProject, int index) /* -------------------------------------------------------------------------- */ -void readPatch(Channel* ch, int i) +void readPatch(Channel* ch, const patch::channel_t& pch) { - const patch::channel_t& pch = patch::channels.at(i); - ch->key = pch.key; ch->armed = pch.armed; ch->type = static_cast(pch.type); @@ -257,21 +239,19 @@ void readPatch(Channel* ch, int i) /* -------------------------------------------------------------------------- */ -void readPatch(SampleChannel* ch, const string& basePath, int i) +void readPatch(SampleChannel* ch, const string& basePath, const patch::channel_t& pch) { - const patch::channel_t& pch = patch::channels.at(i); - ch->mode = static_cast(pch.mode); ch->readActions = pch.recActive; ch->recStatus = pch.recActive ? ChannelStatus::PLAY : ChannelStatus::OFF; ch->midiInVeloAsVol = pch.midiInVeloAsVol; ch->midiInReadActions = pch.midiInReadActions; ch->midiInPitch = pch.midiInPitch; - ch->inputMonitor = pch.inputMonitor; + ch->inputMonitor = pch.inputMonitor; ch->setBoost(pch.boost); - Wave* w = nullptr; - int res = waveManager::create(basePath + pch.samplePath, &w); + Wave* w = nullptr; + int res = waveManager::create(basePath + pch.samplePath, &w); if (res == G_RES_OK) { ch->pushWave(w); @@ -293,10 +273,8 @@ void readPatch(SampleChannel* ch, const string& basePath, int i) /* -------------------------------------------------------------------------- */ -void readPatch(MidiChannel* ch, int i) +void readPatch(MidiChannel* ch, const patch::channel_t& pch) { - const patch::channel_t& pch = patch::channels.at(i); - ch->midiOut = pch.midiOut; ch->midiOutChan = pch.midiOutChan; } diff --git a/src/core/channelManager.h b/src/core/channelManager.h index 327e27b..d9fd490 100644 --- a/src/core/channelManager.h +++ b/src/core/channelManager.h @@ -33,24 +33,28 @@ #include "types.h" +namespace giada { +namespace m +{ class Channel; class SampleChannel; class MidiChannel; - -namespace giada { -namespace m { +namespace patch +{ +struct channel_t; +} namespace channelManager { -int create(ChannelType type, int bufferSize, bool inputMonitorOn, Channel** out); +Channel* create(ChannelType type, int bufferSize, bool inputMonitorOn); int writePatch(const Channel* ch, bool isProject); void writePatch(const SampleChannel* ch, bool isProject, int index); void writePatch(const MidiChannel* ch, bool isProject, int index); -void readPatch(Channel* ch, int index); -void readPatch(SampleChannel* ch, const std::string& basePath, int index); -void readPatch(MidiChannel* ch, int index); +void readPatch(Channel* ch, const patch::channel_t& pch); +void readPatch(SampleChannel* ch, const std::string& basePath, const patch::channel_t& pch); +void readPatch(MidiChannel* ch, const patch::channel_t& pch); }}}; // giada::m::channelManager diff --git a/src/core/clock.cpp b/src/core/clock.cpp index 66abecc..ea6dfbe 100644 --- a/src/core/clock.cpp +++ b/src/core/clock.cpp @@ -340,11 +340,11 @@ void recvJackSync() if (jackState.running != jackStatePrev.running) { if (jackState.running) { if (!isRunning()) - glue_startSeq(false); // not from UI + c::transport::startSeq(false); // not from UI } else { if (isRunning()) - glue_stopSeq(false); // not from UI + c::transport::stopSeq(false); // not from UI } } if (jackState.bpm != jackStatePrev.bpm) @@ -352,7 +352,7 @@ void recvJackSync() glue_setBpm(jackState.bpm); if (jackState.frame == 0 && jackState.frame != jackStatePrev.frame) - glue_rewindSeq(false, false); // not from UI, don't notify jack (avoid loop) + c::transport::rewindSeq(false, false); // not from UI, don't notify jack (avoid loop) jackStatePrev = jackState; } diff --git a/src/core/conf.cpp b/src/core/conf.cpp index 1185eda..975543c 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -185,7 +185,6 @@ uint32_t midiInMetronome = 0x0; bool recsStopOnChanHalt = false; bool chansStopOnSeqHalt = false; bool treatRecsAsLoops = false; -bool resizeRecordings = true; bool inputMonitorDefaultOn = false; string pluginPath = ""; @@ -345,7 +344,6 @@ int read() if (!storager::setBool(jRoot, CONF_KEY_RECS_STOP_ON_CHAN_HALT, recsStopOnChanHalt)) return 0; if (!storager::setBool(jRoot, CONF_KEY_CHANS_STOP_ON_SEQ_HALT, chansStopOnSeqHalt)) return 0; if (!storager::setBool(jRoot, CONF_KEY_TREAT_RECS_AS_LOOPS, treatRecsAsLoops)) return 0; - if (!storager::setBool(jRoot, CONF_KEY_RESIZE_RECORDINGS, resizeRecordings)) return 0; if (!storager::setBool(jRoot, CONF_KEY_INPUT_MONITOR_DEFAULT_ON, inputMonitorDefaultOn)) return 0; if (!storager::setString(jRoot, CONF_KEY_PLUGINS_PATH, pluginPath)) return 0; if (!storager::setString(jRoot, CONF_KEY_PATCHES_PATH, patchPath)) return 0; @@ -457,7 +455,6 @@ int write() json_object_set_new(jRoot, CONF_KEY_RECS_STOP_ON_CHAN_HALT, json_boolean(recsStopOnChanHalt)); json_object_set_new(jRoot, CONF_KEY_CHANS_STOP_ON_SEQ_HALT, json_boolean(chansStopOnSeqHalt)); json_object_set_new(jRoot, CONF_KEY_TREAT_RECS_AS_LOOPS, json_boolean(treatRecsAsLoops)); - json_object_set_new(jRoot, CONF_KEY_RESIZE_RECORDINGS, json_boolean(resizeRecordings)); json_object_set_new(jRoot, CONF_KEY_INPUT_MONITOR_DEFAULT_ON, json_boolean(inputMonitorDefaultOn)); json_object_set_new(jRoot, CONF_KEY_PLUGINS_PATH, json_string(pluginPath.c_str())); json_object_set_new(jRoot, CONF_KEY_PATCHES_PATH, json_string(patchPath.c_str())); diff --git a/src/core/conf.h b/src/core/conf.h index 7b9a83b..dd2c53b 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -83,7 +83,6 @@ extern uint32_t midiInBeatHalf; extern bool recsStopOnChanHalt; extern bool chansStopOnSeqHalt; extern bool treatRecsAsLoops; -extern bool resizeRecordings; extern bool inputMonitorDefaultOn; extern std::string pluginPath; diff --git a/src/core/const.h b/src/core/const.h index 2e244b8..81e38c9 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -46,10 +46,10 @@ /* -- version --------------------------------------------------------------- */ #define G_APP_NAME "Giada" -#define G_VERSION_STR "0.15.2" +#define G_VERSION_STR "0.15.3" #define G_VERSION_MAJOR 0 #define G_VERSION_MINOR 15 -#define G_VERSION_PATCH 2 +#define G_VERSION_PATCH 3 #define CONF_FILENAME "giada.conf" @@ -92,36 +92,37 @@ /* -- MIN/MAX values -------------------------------------------------------- */ -#define G_MIN_BPM 20.0f -#define G_MIN_BPM_STR "20.0" -#define G_MAX_BPM 999.0f -#define G_MAX_BPM_STR "999.0" -#define G_MAX_BEATS 32 -#define G_MAX_BARS 32 -#define G_MAX_QUANTIZE 8 -#define G_MIN_DB_SCALE 60.0f -#define G_MIN_COLUMN_WIDTH 140 -#define G_MAX_BOOST_DB 20.0f -#define G_MIN_PITCH 0.1f -#define G_MAX_PITCH 4.0f -#define G_MAX_GRID_VAL 64 -#define G_MIN_BUF_SIZE 8 -#define G_MAX_BUF_SIZE 4096 -#define G_MIN_GUI_WIDTH 816 -#define G_MIN_GUI_HEIGHT 510 -#define G_MAX_IO_CHANS 2 -#define G_MAX_VELOCITY 0x7F -#define G_MAX_MIDI_CHANS 16 +constexpr float G_MIN_BPM = 20.0f; +constexpr auto G_MIN_BPM_STR = "20.0"; +constexpr float G_MAX_BPM = 999.0f; +constexpr auto G_MAX_BPM_STR = "999.0"; +constexpr int G_MAX_BEATS = 32; +constexpr int G_MAX_BARS = 32; +constexpr int G_MAX_QUANTIZE = 8; +constexpr float G_MIN_DB_SCALE = 60.0f; +constexpr int G_MIN_COLUMN_WIDTH = 140; +constexpr float G_MAX_BOOST_DB = 20.0f; +constexpr float G_MIN_PITCH = 0.1f; +constexpr float G_MAX_PITCH = 4.0f; +constexpr int G_MAX_GRID_VAL = 64; +constexpr int G_MIN_BUF_SIZE = 8; +constexpr int G_MAX_BUF_SIZE = 4096; +constexpr int G_MIN_GUI_WIDTH = 816; +constexpr int G_MIN_GUI_HEIGHT = 510; +constexpr int G_MAX_IO_CHANS = 2; +constexpr int G_MAX_VELOCITY = 0x7F; +constexpr int G_MAX_MIDI_CHANS = 16; +constexpr int G_MAX_POLYPHONY = 32; /* -- kernel audio ---------------------------------------------------------- */ -#define G_SYS_API_NONE 0x00 // 0000 0000 -#define G_SYS_API_JACK 0x01 // 0000 0001 -#define G_SYS_API_ALSA 0x02 // 0000 0010 -#define G_SYS_API_DS 0x04 // 0000 0100 -#define G_SYS_API_ASIO 0x08 // 0000 1000 -#define G_SYS_API_CORE 0x10 // 0001 0000 +#define G_SYS_API_NONE 0x00 // 0000 0000 +#define G_SYS_API_JACK 0x01 // 0000 0001 +#define G_SYS_API_ALSA 0x02 // 0000 0010 +#define G_SYS_API_DS 0x04 // 0000 0100 +#define G_SYS_API_ASIO 0x08 // 0000 1000 +#define G_SYS_API_CORE 0x10 // 0001 0000 #define G_SYS_API_PULSE 0x20 // 0010 0000 #define G_SYS_API_WASAPI 0x40 // 0100 0000 #define G_SYS_API_ANY 0x7F // 0111 1111 @@ -129,8 +130,8 @@ /* -- kernel midi ----------------------------------------------------------- */ -#define G_MIDI_API_JACK 0x01 // 0000 0001 -#define G_MIDI_API_ALSA 0x02 // 0000 0010 +#define G_MIDI_API_JACK 0x01 // 0000 0001 +#define G_MIDI_API_ALSA 0x02 // 0000 0010 @@ -261,30 +262,13 @@ it drives knobs, volume, faders and such. */ #define MIDI_STOP 0xFC #define MIDI_EOX 0xF7 // end of sysex -/* channels */ - -#define MIDI_CHAN_0 0x00 << 24 -#define MIDI_CHAN_1 0x01 << 24 -#define MIDI_CHAN_2 0x02 << 24 -#define MIDI_CHAN_3 0x03 << 24 -#define MIDI_CHAN_4 0x04 << 24 -#define MIDI_CHAN_5 0x05 << 24 -#define MIDI_CHAN_6 0x06 << 24 -#define MIDI_CHAN_7 0x07 << 24 -#define MIDI_CHAN_8 0x08 << 24 -#define MIDI_CHAN_9 0x09 << 24 -#define MIDI_CHAN_10 0x0A << 24 -#define MIDI_CHAN_11 0x0B << 24 -#define MIDI_CHAN_12 0x0C << 24 -#define MIDI_CHAN_13 0x0D << 24 -#define MIDI_CHAN_14 0x0E << 24 -#define MIDI_CHAN_15 0x0F << 24 - -const int MIDI_CHANS[G_MAX_MIDI_CHANS] = { - MIDI_CHAN_0, MIDI_CHAN_1, MIDI_CHAN_2, MIDI_CHAN_3, - MIDI_CHAN_4, MIDI_CHAN_5, MIDI_CHAN_6, MIDI_CHAN_7, - MIDI_CHAN_8, MIDI_CHAN_9, MIDI_CHAN_10, MIDI_CHAN_11, - MIDI_CHAN_12, MIDI_CHAN_13, MIDI_CHAN_14, MIDI_CHAN_15 +/* Channels */ + +constexpr int G_MIDI_CHANS[G_MAX_MIDI_CHANS] = { + 0x00 << 24, 0x01 << 24, 0x02 << 24, 0x03 << 24, + 0x04 << 24, 0x05 << 24, 0x06 << 24, 0x07 << 24, + 0x08 << 24, 0x09 << 24, 0x0A << 24, 0x0B << 24, + 0x0C << 24, 0x0D << 24, 0x0E << 24, 0x0F << 24 }; /* midi sync constants */ @@ -367,6 +351,13 @@ const int MIDI_CHANS[G_MAX_MIDI_CHANS] = { #define PATCH_KEY_COLUMN_WIDTH "width" #define PATCH_KEY_COLUMN_CHANNELS "channels" +#define G_PATCH_KEY_ACTION_ID "id" +#define G_PATCH_KEY_ACTION_CHANNEL "channel" +#define G_PATCH_KEY_ACTION_FRAME "frame" +#define G_PATCH_KEY_ACTION_EVENT "event" +#define G_PATCH_KEY_ACTION_PREV "prev" +#define G_PATCH_KEY_ACTION_NEXT "next" + /* JSON config keys */ #define CONF_KEY_HEADER "header" @@ -402,7 +393,6 @@ const int MIDI_CHANS[G_MAX_MIDI_CHANS] = { #define CONF_KEY_RECS_STOP_ON_CHAN_HALT "recs_stop_on_chan_halt" #define CONF_KEY_CHANS_STOP_ON_SEQ_HALT "chans_stop_on_seq_halt" #define CONF_KEY_TREAT_RECS_AS_LOOPS "treat_recs_as_loops" -#define CONF_KEY_RESIZE_RECORDINGS "resize_recordings" #define CONF_KEY_INPUT_MONITOR_DEFAULT_ON "input_monitor_default_on" #define CONF_KEY_PLUGINS_PATH "plugins_path" #define CONF_KEY_PATCHES_PATH "patches_path" @@ -460,18 +450,19 @@ const int MIDI_CHANS[G_MAX_MIDI_CHANS] = { /* JSON midimaps keys */ -#define MIDIMAP_KEY_BRAND "brand" -#define MIDIMAP_KEY_DEVICE "device" -#define MIDIMAP_KEY_INIT_COMMANDS "init_commands" -#define MIDIMAP_KEY_MUTE_ON "mute_on" -#define MIDIMAP_KEY_MUTE_OFF "mute_off" -#define MIDIMAP_KEY_SOLO_ON "solo_on" -#define MIDIMAP_KEY_SOLO_OFF "solo_off" -#define MIDIMAP_KEY_WAITING "waiting" -#define MIDIMAP_KEY_PLAYING "playing" -#define MIDIMAP_KEY_STOPPING "stopping" -#define MIDIMAP_KEY_STOPPED "stopped" -#define MIDIMAP_KEY_CHANNEL "channel" -#define MIDIMAP_KEY_MESSAGE "message" +#define MIDIMAP_KEY_BRAND "brand" +#define MIDIMAP_KEY_DEVICE "device" +#define MIDIMAP_KEY_INIT_COMMANDS "init_commands" +#define MIDIMAP_KEY_MUTE_ON "mute_on" +#define MIDIMAP_KEY_MUTE_OFF "mute_off" +#define MIDIMAP_KEY_SOLO_ON "solo_on" +#define MIDIMAP_KEY_SOLO_OFF "solo_off" +#define MIDIMAP_KEY_WAITING "waiting" +#define MIDIMAP_KEY_PLAYING "playing" +#define MIDIMAP_KEY_PLAYING_INAUDIBLE "playing_inaudible" +#define MIDIMAP_KEY_STOPPING "stopping" +#define MIDIMAP_KEY_STOPPED "stopped" +#define MIDIMAP_KEY_CHANNEL "channel" +#define MIDIMAP_KEY_MESSAGE "message" #endif diff --git a/src/core/init.cpp b/src/core/init.cpp index f9cac87..e03ff65 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -25,6 +25,7 @@ * -------------------------------------------------------------------------- */ +#include #include #ifdef __APPLE__ #include @@ -32,13 +33,14 @@ #if defined(__linux__) && defined(WITH_VST) #include // For XInitThreads #endif +#include #include "../utils/log.h" #include "../utils/fs.h" +#include "../utils/time.h" #include "../utils/gui.h" #include "../gui/dialogs/gd_mainWindow.h" #include "../gui/dialogs/gd_warnings.h" #include "../glue/main.h" -#include "init.h" #include "mixer.h" #include "wave.h" #include "const.h" @@ -52,40 +54,64 @@ #include "midiMapConf.h" #include "kernelMidi.h" #include "kernelAudio.h" +#include "init.h" + +extern std::atomic G_quit; +extern gdMainWindow* G_MainWin; -extern bool G_quit; -extern gdMainWindow *G_MainWin; +namespace giada { +namespace m { +namespace init +{ +namespace +{ +std::thread videoThread; -using namespace giada::m; +/* -------------------------------------------------------------------------- */ -void init_prepareParser() + +void videoThreadCallback_() { - time_t t; - time (&t); - gu_log("[init] Giada " G_VERSION_STR " - %s", ctime(&t)); + while (G_quit.load() == false) { + if (m::kernelAudio::getStatus()) + gu_refreshUI(); + u::time::sleep(G_GUI_REFRESH_RATE); + } +} + + +/* -------------------------------------------------------------------------- */ - conf::read(); - patch::init(); +void initConf_() +{ + if (!conf::read()) + gu_log("[init] Can't read configuration file! Using default values\n"); + + patch::init(); + midimap::init(); + midimap::setDefault(); + if (!gu_logInit(conf::logMode)) gu_log("[init] log init failed! Using default stdout\n"); - gu_log("[init] configuration file ready\n"); + if (midimap::read(conf::midiMapPath) != MIDIMAP_READ_OK) + gu_log("[init] MIDI map read failed!\n"); } /* -------------------------------------------------------------------------- */ -void init_prepareKernelAudio() +void initAudio_() { kernelAudio::openDevice(); clock::init(conf::samplerate, conf::midiTCfps); mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize()); - recorder::init(); + recorder::init(&mixer::mutex); #ifdef WITH_VST @@ -101,38 +127,33 @@ void init_prepareKernelAudio() #endif + if (!kernelAudio::getStatus()) + return; + + kernelAudio::startStream(); } /* -------------------------------------------------------------------------- */ -void init_prepareKernelMIDI() +void initMIDI_() { kernelMidi::setApi(conf::midiSystem); kernelMidi::openOutDevice(conf::midiPortOut); - kernelMidi::openInDevice(conf::midiPortIn); + kernelMidi::openInDevice(conf::midiPortIn); } /* -------------------------------------------------------------------------- */ -void init_prepareMidiMap() +void initGUI_(int argc, char** argv) { - midimap::init(); - midimap::setDefault(); + /* This enables the FLTK lock and start the runtime multithreading support. */ - if (midimap::read(conf::midiMapPath) != MIDIMAP_READ_OK) - gu_log("[init_prepareMidiMap] MIDI map read failed!\n"); -} + Fl::lock(); - -/* -------------------------------------------------------------------------- */ - - -void init_startGUI(int argc, char** argv) -{ /* This is of paramount importance on Linux with VST enabled, otherwise many plug-ins go nuts and crash hard. It seems that some plug-ins or our Juce-based PluginHost use Xlib concurrently. */ @@ -147,71 +168,86 @@ void init_startGUI(int argc, char** argv) gu_updateMainWinLabel(patch::name == "" ? G_DEFAULT_PATCH_NAME : patch::name); - /* never update the GUI elements if kernelAudio::getStatus() is bad, segfaults - * are around the corner */ - - if (kernelAudio::getStatus()) - gu_updateControls(); - else + if (!kernelAudio::getStatus()) gdAlert("Your soundcard isn't configured correctly.\n" "Check the configuration and restart Giada."); + + gu_updateControls(); + + videoThread = std::thread(videoThreadCallback_); } + /* -------------------------------------------------------------------------- */ -void init_startKernelAudio() +void shutdownAudio_() { - if (kernelAudio::getStatus()) - kernelAudio::startStream(); +#ifdef WITH_VST + + pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex); + pluginHost::close(); + gu_log("[init] PluginHost cleaned up\n"); + +#endif + + if (kernelAudio::getStatus()) { + kernelAudio::closeDevice(); + gu_log("[init] KernelAudio closed\n"); + mixer::close(); + gu_log("[init] Mixer closed\n"); + } } /* -------------------------------------------------------------------------- */ -void init_shutdown() +void shutdownGUI_() { - G_quit = true; + gu_closeAllSubwindows(); + videoThread.join(); - /* store position and size of the main window for the next startup */ + gu_log("[init] All subwindows and UI thread closed\n"); +} +} // {anonymous} - conf::mainWindowX = G_MainWin->x(); - conf::mainWindowY = G_MainWin->y(); - conf::mainWindowW = G_MainWin->w(); - conf::mainWindowH = G_MainWin->h(); - /* close any open subwindow, especially before cleaning PluginHost to - * avoid mess */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ - gu_closeAllSubwindows(); - gu_log("[init] all subwindows closed\n"); - /* write configuration file */ +void startup(int argc, char** argv) +{ + time_t t; + time (&t); + gu_log("[init] Giada " G_VERSION_STR " - %s", ctime(&t)); - if (!conf::write()) - gu_log("[init] error while saving configuration file!\n"); - else - gu_log("[init] configuration saved\n"); + initConf_(); + initAudio_(); + initMIDI_(); + initGUI_(argc, argv); +} - recorder::clearAll(); - gu_log("[init] Recorder cleaned up\n"); -#ifdef WITH_VST +/* -------------------------------------------------------------------------- */ - pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex); - pluginHost::close(); - gu_log("[init] PluginHost cleaned up\n"); -#endif +void shutdown() +{ + G_quit.store(true); - if (kernelAudio::getStatus()) { - kernelAudio::closeDevice(); - gu_log("[init] KernelAudio closed\n"); - mixer::close(); - gu_log("[init] Mixer closed\n"); - } + shutdownGUI_(); + + if (!conf::write()) + gu_log("[init] error while saving configuration file!\n"); + else + gu_log("[init] configuration saved\n"); + + shutdownAudio_(); gu_log("[init] Giada " G_VERSION_STR " closed\n\n"); gu_logClose(); } +}}} // giada::m::init \ No newline at end of file diff --git a/src/core/init.h b/src/core/init.h index 8e10de6..27a5f6e 100644 --- a/src/core/init.h +++ b/src/core/init.h @@ -29,13 +29,12 @@ #define G_INIT_H -void init_prepareParser(); -void init_startGUI(int argc, char** argv); -void init_prepareKernelAudio(); -void init_prepareKernelMIDI(); -void init_prepareMidiMap(); -void init_startKernelAudio(); -void init_shutdown(); - +namespace giada { +namespace m { +namespace init +{ +void startup(int argc, char** argv); +void shutdown(); +}}} // giada::m::init #endif diff --git a/src/core/kernelMidi.cpp b/src/core/kernelMidi.cpp index 52f760b..485f7c1 100644 --- a/src/core/kernelMidi.cpp +++ b/src/core/kernelMidi.cpp @@ -77,7 +77,7 @@ void sendMidiLightningInitMsgs() midimap::message_t msg = midimap::initCommands.at(i); if (msg.value != 0x0 && msg.channel != -1) { gu_log("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", msg.channel, msg.value); - send(msg.value | MIDI_CHANS[msg.channel]); + send(msg.value | G_MIDI_CHANS[msg.channel]); } } } @@ -258,6 +258,14 @@ void send(int b1, int b2, int b3) void sendMidiLightning(uint32_t learn, const midimap::message_t& msg) { + // Skip lightning message if not defined in midi map + + if (!midimap::isDefined(msg)) + { + gu_log("[KM] message skipped (not defined in midimap)"); + return; + } + gu_log("[KM] learn=%#X, chan=%d, msg=%#X, offset=%d\n", learn, msg.channel, msg.value, msg.offset); diff --git a/src/core/midiChannel.cpp b/src/core/midiChannel.cpp index 3dd4867..7cc871f 100644 --- a/src/core/midiChannel.cpp +++ b/src/core/midiChannel.cpp @@ -25,13 +25,16 @@ * -------------------------------------------------------------------------- */ +#include #include "../utils/log.h" +#include "recorder.h" #include "midiChannelProc.h" #include "channelManager.h" #include "channel.h" +#include "recorderHandler.h" +#include "action.h" #include "patch.h" #include "const.h" -#include "clock.h" #include "conf.h" #include "mixer.h" #include "pluginHost.h" @@ -40,14 +43,15 @@ using std::string; -using namespace giada; -using namespace giada::m; +namespace giada { +namespace m +{ MidiChannel::MidiChannel(int bufferSize) - : Channel (ChannelType::MIDI, ChannelStatus::OFF, bufferSize), + : Channel (ChannelType::MIDI, ChannelStatus::OFF, bufferSize), midiOut (false), - midiOutChan(MIDI_CHANS[0]) + midiOutChan(G_MIDI_CHANS[0]) { } @@ -75,8 +79,8 @@ void MidiChannel::parseEvents(mixer::FrameEvents fe) /* -------------------------------------------------------------------------- */ -void MidiChannel::process(giada::m::AudioBuffer& out, - const giada::m::AudioBuffer& in, bool audible, bool running) +void MidiChannel::process(AudioBuffer& out, const AudioBuffer& in, bool audible, + bool running) { midiChannelProc::process(this, out, in, audible); } @@ -130,10 +134,19 @@ void MidiChannel::setMute(bool value) /* -------------------------------------------------------------------------- */ -void MidiChannel::readPatch(const string& basePath, int i) +void MidiChannel::setSolo(bool value) { - Channel::readPatch("", i); - channelManager::readPatch(this, i); + midiChannelProc::setSolo(this, value); +} + + +/* -------------------------------------------------------------------------- */ + + +void MidiChannel::readPatch(const string& basePath, const patch::channel_t& pch) +{ + Channel::readPatch("", pch); + channelManager::readPatch(this, pch); } @@ -176,26 +189,16 @@ void MidiChannel::empty() /* -------------------------------------------------------------------------- */ -void MidiChannel::sendMidi(recorder::action* a, int localFrame) +void MidiChannel::sendMidi(const Action* a, int localFrame) { if (isPlaying() && !mute) { - if (midiOut) - kernelMidi::send(a->iValue | MIDI_CHANS[midiOutChan]); - + if (midiOut) { + MidiEvent event = a->event; + event.setChannel(midiOutChan); + kernelMidi::send(event.getRaw()); + } #ifdef WITH_VST - addVstMidiEvent(a->iValue, localFrame); -#endif - } -} - - -void MidiChannel::sendMidi(uint32_t data) -{ - if (isPlaying() && !mute) { - if (midiOut) - kernelMidi::send(data | MIDI_CHANS[midiOutChan]); -#ifdef WITH_VST - addVstMidiEvent(data, 0); + addVstMidiEvent(a->event.getRaw(), localFrame); #endif } } @@ -206,31 +209,29 @@ void MidiChannel::sendMidi(uint32_t data) void MidiChannel::receiveMidi(const MidiEvent& midiEvent) { + namespace mrh = m::recorderHandler; + namespace mr = m::recorder; + if (!armed) return; - /* Now all messages are turned into Channel-0 messages. Giada doesn't care about - holding MIDI channel information. Moreover, having all internal messages on - channel 0 is way easier. */ + /* Now all messages are turned into Channel-0 messages. Giada doesn't care + about holding MIDI channel information. Moreover, having all internal + messages on channel 0 is way easier. */ MidiEvent midiEventFlat(midiEvent); midiEventFlat.setChannel(0); #ifdef WITH_VST - while (true) { - if (pthread_mutex_trylock(&pluginHost::mutex_midi) != 0) - continue; - gu_log("[MidiChannel::processMidi] msg=%X\n", midiEventFlat.getRaw()); - addVstMidiEvent(midiEventFlat.getRaw(), 0); - pthread_mutex_unlock(&pluginHost::mutex_midi); - break; - } + pthread_mutex_lock(&pluginHost::mutex_midi); + addVstMidiEvent(midiEventFlat.getRaw(), 0); + pthread_mutex_unlock(&pluginHost::mutex_midi); #endif - if (recorder::canRec(this, clock::isRunning(), mixer::recording)) { - recorder::rec(index, G_ACTION_MIDI, clock::getCurrentFrame(), midiEventFlat.getRaw()); + if (mr::isActive()) { + mrh::liveRec(index, midiEventFlat); hasActions = true; } } @@ -242,4 +243,6 @@ void MidiChannel::receiveMidi(const MidiEvent& midiEvent) bool MidiChannel::canInputRec() { return false; // midi channels don't handle input audio -} \ No newline at end of file +} + +}} // giada::m:: diff --git a/src/core/midiChannel.h b/src/core/midiChannel.h index 7185397..ee22e3f 100644 --- a/src/core/midiChannel.h +++ b/src/core/midiChannel.h @@ -35,10 +35,9 @@ #include "channel.h" -class MidiMapConf; -class Patch; - - +namespace giada { +namespace m +{ class MidiChannel : public Channel { public: @@ -46,9 +45,8 @@ public: MidiChannel(int bufferSize); void copy(const Channel* src, pthread_mutex_t* pluginMutex) override; - void parseEvents(giada::m::mixer::FrameEvents fe) override; - void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in, - bool audible, bool running) override; + void parseEvents(mixer::FrameEvents fe) override; + void process(AudioBuffer& out, const AudioBuffer& in, bool audible, bool running) override; void start(int frame, bool doQuantize, int velocity) override; void kill(int localFrame) override; void empty() override; @@ -56,16 +54,16 @@ public: void stop() override {}; void rewindBySeq() override; void setMute(bool value) override; - void readPatch(const std::string& basePath, int i) override; + void setSolo(bool value) override; + void readPatch(const std::string& basePath, const patch::channel_t& pch) override; void writePatch(int i, bool isProject) override; - void receiveMidi(const giada::m::MidiEvent& midiEvent) override; + void receiveMidi(const MidiEvent& midiEvent) override; bool canInputRec() override; /* sendMidi * send Midi event to the outside world. */ - void sendMidi(giada::m::recorder::action* a, int localFrame); - void sendMidi(uint32_t data); + void sendMidi(const Action* a, int localFrame); #ifdef WITH_VST @@ -78,9 +76,11 @@ public: #endif - bool midiOut; // enable midi output - uint8_t midiOutChan; // midi output channel + bool midiOut; // enable midi output + int midiOutChan; // midi output channel }; +}} // giada::m:: + #endif diff --git a/src/core/midiChannelProc.cpp b/src/core/midiChannelProc.cpp index 965d440..646c84d 100644 --- a/src/core/midiChannelProc.cpp +++ b/src/core/midiChannelProc.cpp @@ -2,8 +2,9 @@ #include "pluginHost.h" #include "kernelMidi.h" #include "const.h" +#include "action.h" #include "midiChannelProc.h" - +#include "mixerHandler.h" namespace giada { namespace m { @@ -23,22 +24,6 @@ void onFirstBeat_(MidiChannel* ch) ch->sendMidiLstatus(); } } - - -/* -------------------------------------------------------------------------- */ - - -void parseAction_(MidiChannel* ch, const recorder::action* a, int localFrame) -{ - if (ch->isPlaying() && !ch->mute) { - if (ch->midiOut) - kernelMidi::send(a->iValue | MIDI_CHANS[ch->midiOutChan]); -#ifdef WITH_VST - ch->addVstMidiEvent(a->iValue, localFrame); -#endif - } -} - }; // {anonymous} @@ -51,21 +36,20 @@ void parseEvents(MidiChannel* ch, mixer::FrameEvents fe) { if (fe.onFirstBeat) onFirstBeat_(ch); - for (const recorder::action* action : fe.actions) - if (action->chan == ch->index && action->type == G_ACTION_MIDI) - parseAction_(ch, action, fe.frameLocal); + for (const Action* action : fe.actions) + if (action->channel == ch->index) + ch->sendMidi(action, fe.frameLocal); } /* -------------------------------------------------------------------------- */ -void process(MidiChannel* ch, giada::m::AudioBuffer& out, - const giada::m::AudioBuffer& in, bool audible) +void process(MidiChannel* ch, AudioBuffer& out, const AudioBuffer& in, bool audible) { - #ifdef WITH_VST - pluginHost::processStack(ch->buffer, pluginHost::CHANNEL, ch); - #endif +#ifdef WITH_VST + pluginHost::processStack(ch->buffer, pluginHost::CHANNEL, ch); +#endif /* Process the plugin stack first, then quit if the channel is muted/soloed. This way there's no risk of cutting midi event pairs such as note-on and @@ -149,6 +133,10 @@ void setMute(MidiChannel* ch, bool v) ch->addVstMidiEvent(MIDI_ALL_NOTES_OFF, 0); #endif } + + // This is for processing playing_inaudible + ch->sendMidiLstatus(); + ch->sendMidiLmute(); } @@ -156,8 +144,24 @@ void setMute(MidiChannel* ch, bool v) /* -------------------------------------------------------------------------- */ +void setSolo(MidiChannel* ch, bool v) +{ + ch->solo = v; + m::mh::updateSoloCount(); + + // This is for processing playing_inaudible + for (Channel* channel : mixer::channels) + channel->sendMidiLstatus(); + + ch->sendMidiLsolo(); +} + + +/* -------------------------------------------------------------------------- */ + + void stopBySeq(MidiChannel* ch) { kill(ch, 0); } -}}}; \ No newline at end of file +}}}; diff --git a/src/core/midiChannelProc.h b/src/core/midiChannelProc.h index ffd4929..6abd143 100644 --- a/src/core/midiChannelProc.h +++ b/src/core/midiChannelProc.h @@ -6,11 +6,11 @@ #include "audioBuffer.h" +namespace giada { +namespace m +{ class MidiChannel; - -namespace giada { -namespace m { namespace midiChannelProc { /* parseEvents @@ -19,8 +19,7 @@ Parses events gathered by Mixer::masterPlay(). */ void parseEvents(MidiChannel* ch, mixer::FrameEvents ev); /**/ -void process(MidiChannel* ch, giada::m::AudioBuffer& out, - const giada::m::AudioBuffer& in, bool audible); +void process(MidiChannel* ch, AudioBuffer& out, const AudioBuffer& in, bool audible); /* kill Stops a channel abruptly. */ @@ -46,7 +45,8 @@ void rewindBySeq(MidiChannel* ch); Mutes/unmutes a channel. */ void setMute(MidiChannel* ch, bool v); +void setSolo(MidiChannel* ch, bool v); }}}; -#endif \ No newline at end of file +#endif diff --git a/src/core/midiDispatcher.cpp b/src/core/midiDispatcher.cpp index 201a385..bb65bd8 100644 --- a/src/core/midiDispatcher.cpp +++ b/src/core/midiDispatcher.cpp @@ -131,7 +131,7 @@ void processChannels(const MidiEvent& midiEvent) c::channel::toggleSolo(ch, false); } else if (pure == ch->midiInVolume) { - float vf = midiEvent.getVelocity() / 127.0f; + float vf = midiEvent.getVelocity() / 127.0f; // TODO: u::math::map gu_log(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n", ch->index, pure, midiEvent.getVelocity(), vf); c::channel::setVolume(ch, vf, false); @@ -139,7 +139,7 @@ void processChannels(const MidiEvent& midiEvent) else { SampleChannel* sch = static_cast(ch); if (pure == sch->midiInPitch) { - float vf = midiEvent.getVelocity() / (127/4.0f); // [0-127] ~> [0.0-4.0] + float vf = midiEvent.getVelocity() / (127/4.0f); // [0-127] ~> [0.0-4.0] TODO: u::math::map gu_log(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)\n", sch->index, pure, midiEvent.getVelocity(), vf); c::channel::setPitch(sch, vf); @@ -173,11 +173,11 @@ void processMaster(const MidiEvent& midiEvent) if (pure == conf::midiInRewind) { gu_log(" >>> rewind (master) (pure=0x%X)\n", pure); - glue_rewindSeq(false); + c::transport::rewindSeq(false); } else if (pure == conf::midiInStartStop) { gu_log(" >>> startStop (master) (pure=0x%X)\n", pure); - glue_startStopSeq(false); + c::transport::startStopSeq(false); } else if (pure == conf::midiInActionRec) { gu_log(" >>> actionRec (master) (pure=0x%X)\n", pure); @@ -189,7 +189,7 @@ void processMaster(const MidiEvent& midiEvent) } else if (pure == conf::midiInMetronome) { gu_log(" >>> metronome (master) (pure=0x%X)\n", pure); - glue_startStopMetronome(false); + c::transport::startStopMetronome(false); } else if (pure == conf::midiInVolumeIn) { float vf = midiEvent.getVelocity() / 127.0f; diff --git a/src/core/midiEvent.cpp b/src/core/midiEvent.cpp index ac3aabc..fdf63e0 100644 --- a/src/core/midiEvent.cpp +++ b/src/core/midiEvent.cpp @@ -26,6 +26,7 @@ #include +#include "const.h" #include "midiEvent.h" @@ -85,7 +86,7 @@ void MidiEvent::setChannel(int c) void MidiEvent::setVelocity(int v) { - assert(v >= 0 && v < G_MAX_VELOCITY); + assert(v >= 0 && v <= G_MAX_VELOCITY); m_velocity = v; } diff --git a/src/core/midiEvent.h b/src/core/midiEvent.h index 4c29e52..3694b57 100644 --- a/src/core/midiEvent.h +++ b/src/core/midiEvent.h @@ -39,8 +39,10 @@ class MidiEvent { public: - static const int NOTE_ON = 0x90; - static const int NOTE_OFF = 0x80; + static const int NOTE_ON = 0x90; + static const int NOTE_OFF = 0x80; + static const int NOTE_KILL = 0x70; + static const int ENVELOPE = 0xB0; MidiEvent(); MidiEvent(uint32_t raw); diff --git a/src/core/midiMapConf.cpp b/src/core/midiMapConf.cpp index 9c13008..0b04a8c 100644 --- a/src/core/midiMapConf.cpp +++ b/src/core/midiMapConf.cpp @@ -96,7 +96,11 @@ void parse(message_t *message) { /* Remove '0x' part from the original string. */ - string input = message->valueStr.replace(0, 2, ""); + string input = message->valueStr; + + size_t f = input.find("0x"); // check if "0x" is there + if (f!=std::string::npos) + input = message->valueStr.replace(f, 2, ""); /* Then transform string value into the actual uint32_t value, by parsing * each char (i.e. nibble) in the original string. Substitute 'n' with @@ -141,6 +145,7 @@ message_t waiting; message_t playing; message_t stopping; message_t stopped; +message_t playing_inaudible; string midimapsPath; vector maps; @@ -221,6 +226,19 @@ void setDefault() stopped.valueStr = ""; stopped.offset = -1; stopped.value = 0; + playing_inaudible.channel = 0; + playing_inaudible.valueStr = ""; + playing_inaudible.offset = -1; + playing_inaudible.value = 0; +} + + +/* -------------------------------------------------------------------------- */ + + +bool isDefined(message_t msg) +{ + return (msg.offset!=-1); } @@ -245,27 +263,17 @@ int read(const string &file) } if (!storager::setString(jRoot, MIDIMAP_KEY_BRAND, brand)) return MIDIMAP_UNREADABLE; - if (!storager::setString(jRoot, MIDIMAP_KEY_DEVICE, device)) return MIDIMAP_UNREADABLE; + if (!storager::setString(jRoot, MIDIMAP_KEY_DEVICE, device)) return MIDIMAP_UNREADABLE; if (!readInitCommands(jRoot)) return MIDIMAP_UNREADABLE; - if (!readCommand(jRoot, &muteOn, MIDIMAP_KEY_MUTE_ON)) return MIDIMAP_UNREADABLE; - if (!readCommand(jRoot, &muteOff, MIDIMAP_KEY_MUTE_OFF)) return MIDIMAP_UNREADABLE; - if (!readCommand(jRoot, &soloOn, MIDIMAP_KEY_SOLO_ON)) return MIDIMAP_UNREADABLE; - if (!readCommand(jRoot, &soloOff, MIDIMAP_KEY_SOLO_OFF)) return MIDIMAP_UNREADABLE; - if (!readCommand(jRoot, &waiting, MIDIMAP_KEY_WAITING)) return MIDIMAP_UNREADABLE; - if (!readCommand(jRoot, &playing, MIDIMAP_KEY_PLAYING)) return MIDIMAP_UNREADABLE; - if (!readCommand(jRoot, &stopping, MIDIMAP_KEY_STOPPING)) return MIDIMAP_UNREADABLE; - if (!readCommand(jRoot, &stopped, MIDIMAP_KEY_STOPPED)) return MIDIMAP_UNREADABLE; - - /* parse messages */ - - parse(&muteOn); - parse(&muteOff); - parse(&soloOn); - parse(&soloOff); - parse(&waiting); - parse(&playing); - parse(&stopping); - parse(&stopped); + if (readCommand(jRoot, &muteOn, MIDIMAP_KEY_MUTE_ON)) parse(&muteOn); + if (readCommand(jRoot, &muteOff, MIDIMAP_KEY_MUTE_OFF)) parse(&muteOff); + if (readCommand(jRoot, &soloOn, MIDIMAP_KEY_SOLO_ON)) parse(&soloOn); + if (readCommand(jRoot, &soloOff, MIDIMAP_KEY_SOLO_OFF)) parse(&soloOff); + if (readCommand(jRoot, &waiting, MIDIMAP_KEY_WAITING)) parse(&waiting); + if (readCommand(jRoot, &playing, MIDIMAP_KEY_PLAYING)) parse(&playing); + if (readCommand(jRoot, &stopping, MIDIMAP_KEY_STOPPING)) parse(&stopping); + if (readCommand(jRoot, &stopped, MIDIMAP_KEY_STOPPED)) parse(&stopped); + if (readCommand(jRoot, &playing_inaudible, MIDIMAP_KEY_PLAYING_INAUDIBLE)) parse(&playing_inaudible); return MIDIMAP_READ_OK; } diff --git a/src/core/midiMapConf.h b/src/core/midiMapConf.h index 0e73e7d..6584d35 100644 --- a/src/core/midiMapConf.h +++ b/src/core/midiMapConf.h @@ -39,8 +39,8 @@ namespace midimap { struct message_t { - int channel; - std::string valueStr; + int channel; + std::string valueStr; int offset; uint32_t value; }; @@ -56,6 +56,7 @@ extern message_t waiting; extern message_t playing; extern message_t stopping; extern message_t stopped; +extern message_t playing_inaudible; /* midimapsPath * path of midimap files, different between OSes. */ @@ -74,10 +75,15 @@ Parse the midi maps folders and find the available maps. */ void init(); /* setDefault -Set default values in case no maps are available/choosen. */ +Set default values in case no maps are available/chosen. */ void setDefault(); +/* isDefined +Check whether a specific message has been defined within midi map file.*/ + +bool isDefined(message_t msg); + /* read Read a midi map from file 'file'. */ diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 05d64e0..2707572 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -41,6 +41,7 @@ #include "sampleChannel.h" #include "midiChannel.h" #include "audioBuffer.h" +#include "action.h" #include "mixer.h" @@ -352,6 +353,8 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, prepareBuffers(out); + // TODO - move lock here + for (unsigned j=0; j actions; + std::vector actions; }; extern std::vector channels; diff --git a/src/core/mixerHandler.cpp b/src/core/mixerHandler.cpp index 66ecc81..8601ed8 100644 --- a/src/core/mixerHandler.cpp +++ b/src/core/mixerHandler.cpp @@ -135,11 +135,8 @@ bool uniqueSamplePath(const SampleChannel* skip, const string& path) Channel* addChannel(ChannelType type) { - Channel* ch = nullptr; - channelManager::create(type, kernelAudio::getRealBufSize(), - conf::inputMonitorDefaultOn, &ch); - if (ch == nullptr) - return nullptr; + Channel* ch = channelManager::create(type, kernelAudio::getRealBufSize(), + conf::inputMonitorDefaultOn); while (true) { if (pthread_mutex_trylock(&mixer::mutex) != 0) diff --git a/src/core/mixerHandler.h b/src/core/mixerHandler.h index b37a14c..895a1dc 100644 --- a/src/core/mixerHandler.h +++ b/src/core/mixerHandler.h @@ -33,12 +33,14 @@ #include "types.h" -class Channel; -class SampleChannel; namespace giada { -namespace m { +namespace m +{ +class Channel; +class SampleChannel; + namespace mh { /* addChannel diff --git a/src/core/patch.cpp b/src/core/patch.cpp index 6691b6d..2add0b9 100644 --- a/src/core/patch.cpp +++ b/src/core/patch.cpp @@ -28,9 +28,11 @@ #include "../utils/log.h" #include "../utils/string.h" #include "../utils/ver.h" +#include "../utils/math.h" #include "const.h" #include "types.h" #include "storager.h" +#include "midiEvent.h" #include "conf.h" #include "mixer.h" #include "patch.h" @@ -51,27 +53,28 @@ Internal sanity check. */ void sanitize() { - bpm = bpm < G_MIN_BPM || bpm > G_MAX_BPM ? G_DEFAULT_BPM : bpm; - bars = bars <= 0 || bars > G_MAX_BARS ? G_DEFAULT_BARS : bars; - beats = beats <= 0 || beats > G_MAX_BEATS ? G_DEFAULT_BEATS : beats; - quantize = quantize < 0 || quantize > G_MAX_QUANTIZE ? G_DEFAULT_QUANTIZE : quantize; - masterVolIn = masterVolIn < 0.0f || masterVolIn > 1.0f ? G_DEFAULT_VOL : masterVolIn; - masterVolOut = masterVolOut < 0.0f || masterVolOut > 1.0f ? G_DEFAULT_VOL : masterVolOut; + namespace um = u::math; + + bpm = um::bound(bpm, G_MIN_BPM, G_MAX_BPM, G_DEFAULT_BPM); + bars = um::bound(bars, 1, G_MAX_BARS, G_DEFAULT_BARS); + beats = um::bound(beats, 1, G_MAX_BEATS, G_DEFAULT_BEATS); + quantize = um::bound(quantize, 0, G_MAX_QUANTIZE, G_DEFAULT_QUANTIZE); + masterVolIn = um::bound(masterVolIn, 0.0f, 1.0f, G_DEFAULT_VOL); + masterVolOut = um::bound(masterVolOut, 0.0f, 1.0f, G_DEFAULT_VOL); samplerate = samplerate <= 0 ? G_DEFAULT_SAMPLERATE : samplerate; - for (unsigned i=0; iindex = col->index < 0 ? 0 : col->index; - col->width = col->width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col->width; + for (column_t& col : columns) { + col.index = col.index < 0 ? 0 : col.index; + col.width = col.width < G_MIN_COLUMN_WIDTH ? G_MIN_COLUMN_WIDTH : col.width; } - for (unsigned i=0; isize = ch->size < G_GUI_CHANNEL_H_1 || ch->size > G_GUI_CHANNEL_H_4 ? G_GUI_CHANNEL_H_1 : ch->size; - ch->volume = ch->volume < 0.0f || ch->volume > 1.0f ? G_DEFAULT_VOL : ch->volume; - ch->pan = ch->pan < 0.0f || ch->pan > 1.0f ? 1.0f : ch->pan; - ch->boost = ch->boost < 1.0f ? G_DEFAULT_BOOST : ch->boost; - ch->pitch = ch->pitch < 0.1f || ch->pitch > G_MAX_PITCH ? G_DEFAULT_PITCH : ch->pitch; + for (channel_t& ch : channels) { + ch.size = um::bound(ch.size, G_GUI_CHANNEL_H_1, G_GUI_CHANNEL_H_4, G_GUI_CHANNEL_H_1); + ch.volume = um::bound(ch.volume, 0.0f, 1.0f, G_DEFAULT_VOL); + ch.pan = um::bound(ch.pan, 0.0f, 1.0f, 1.0f); + ch.boost = um::bound(ch.boost, 1.0f, G_MAX_BOOST_DB, G_DEFAULT_BOOST); + ch.pitch = um::bound(ch.pitch, 0.1f, G_MAX_PITCH, G_DEFAULT_PITCH); + ch.midiOutChan = um::bound(ch.midiOutChan, 0, G_MAX_MIDI_CHANS - 1, 0); } } @@ -196,23 +199,50 @@ bool readActions(json_t* jContainer, channel_t* channel) { json_t* jActions = json_object_get(jContainer, PATCH_KEY_CHANNEL_ACTIONS); if (!storager::checkArray(jActions, PATCH_KEY_CHANNEL_ACTIONS)) - return 0; + return false; size_t actionIndex; json_t* jAction; json_array_foreach(jActions, actionIndex, jAction) { if (!storager::checkObject(jAction, "")) // TODO pass actionIndex as string - return 0; + return false; action_t action; - if (!storager::setInt (jAction, PATCH_KEY_ACTION_TYPE, action.type)) return 0; - if (!storager::setInt (jAction, PATCH_KEY_ACTION_FRAME, action.frame)) return 0; - if (!storager::setFloat (jAction, PATCH_KEY_ACTION_F_VALUE, action.fValue)) return 0; - if (!storager::setUint32(jAction, PATCH_KEY_ACTION_I_VALUE, action.iValue)) return 0; + + /* TODO - temporary code for backward compatibility with old actions. + To be removed in 0.16.0. */ + if (u::ver::isLess(versionMajor, versionMinor, versionPatch, 0, 15, 3)) { + + action.id = -1; + action.channel = channel->index; + if (!storager::setInt (jAction, "frame", action.frame)) return 0; + if (!storager::setUint32(jAction, "type", action.event)) return 0; + action.prev = -1; + action.next = -1; + + if (action.event == 0x01) // KEY_PRESS + action.event = MidiEvent(MidiEvent::NOTE_ON, 0, 0).getRaw(); + else if (action.event == 0x02) // KEY_REL + action.event = MidiEvent(MidiEvent::NOTE_OFF, 0, 0).getRaw(); + else if (action.event == 0x04) // KEY_KILL + action.event = MidiEvent(MidiEvent::NOTE_KILL, 0, 0).getRaw(); + else if (action.event == 0x20) // VOLUME not supported, sorry :) + continue; + else if (action.event == 0x40) // MIDI EVENT + if (!storager::setUint32(jAction, "i_value", action.event)) return 0; + } + else { + if (!storager::setInt (jAction, G_PATCH_KEY_ACTION_ID, action.id)) return 0; + if (!storager::setInt (jAction, G_PATCH_KEY_ACTION_CHANNEL, action.channel)) return 0; + if (!storager::setInt (jAction, G_PATCH_KEY_ACTION_FRAME, action.frame)) return 0; + if (!storager::setUint32(jAction, G_PATCH_KEY_ACTION_EVENT, action.event)) return 0; + if (!storager::setInt (jAction, G_PATCH_KEY_ACTION_PREV, action.prev)) return 0; + if (!storager::setInt (jAction, G_PATCH_KEY_ACTION_NEXT, action.next)) return 0; + } channel->actions.push_back(action); } - return 1; + return true; } @@ -269,8 +299,8 @@ bool readChannels(json_t* jContainer) if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_INPUT_MONITOR, channel.inputMonitor)) return 0; if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, channel.midiInReadActions)) return 0; if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_IN_PITCH, channel.midiInPitch)) return 0; - if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, channel.midiOut)) return 0; - if (!storager::setUint32(jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, channel.midiOutChan)) return 0; + if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT, channel.midiOut)) return 0; + if (!storager::setInt (jChannel, PATCH_KEY_CHANNEL_MIDI_OUT_CHAN, channel.midiOutChan)) return 0; if (!storager::setBool (jChannel, PATCH_KEY_CHANNEL_ARMED, channel.armed)) return 0; readActions(jChannel, &channel); @@ -365,16 +395,17 @@ void writeColumns(json_t* jContainer, vector* columns) /* -------------------------------------------------------------------------- */ -void writeActions(json_t*jContainer, vector*actions) +void writeActions(json_t* jContainer, vector& actions) { json_t* jActions = json_array(); - for (unsigned k=0; ksize(); k++) { - json_t* jAction = json_object(); - action_t action = actions->at(k); - json_object_set_new(jAction, PATCH_KEY_ACTION_TYPE, json_integer(action.type)); - json_object_set_new(jAction, PATCH_KEY_ACTION_FRAME, json_integer(action.frame)); - json_object_set_new(jAction, PATCH_KEY_ACTION_F_VALUE, json_real(action.fValue)); - json_object_set_new(jAction, PATCH_KEY_ACTION_I_VALUE, json_integer(action.iValue)); + for (action_t action : actions) { + json_t* jAction = json_object(); + json_object_set_new(jAction, G_PATCH_KEY_ACTION_ID, json_integer(action.id)); + json_object_set_new(jAction, G_PATCH_KEY_ACTION_CHANNEL, json_integer(action.channel)); + json_object_set_new(jAction, G_PATCH_KEY_ACTION_FRAME, json_integer(action.frame)); + json_object_set_new(jAction, G_PATCH_KEY_ACTION_EVENT, json_integer(action.event)); + json_object_set_new(jAction, G_PATCH_KEY_ACTION_PREV, json_integer(action.prev)); + json_object_set_new(jAction, G_PATCH_KEY_ACTION_NEXT, json_integer(action.next)); json_array_append_new(jActions, jAction); } json_object_set_new(jContainer, PATCH_KEY_CHANNEL_ACTIONS, jActions); @@ -452,7 +483,7 @@ void writeChannels(json_t* jContainer, vector* channels) json_object_set_new(jChannel, PATCH_KEY_CHANNEL_ARMED, json_boolean(channel.armed)); json_array_append_new(jChannels, jChannel); - writeActions(jChannel, &channel.actions); + writeActions(jChannel, channel.actions); #ifdef WITH_VST diff --git a/src/core/patch.h b/src/core/patch.h index 77d48d7..afc5180 100644 --- a/src/core/patch.h +++ b/src/core/patch.h @@ -40,10 +40,12 @@ namespace patch { struct action_t { - int type; + int id; + int channel; int frame; - float fValue; - uint32_t iValue; + uint32_t event; + int prev; + int next; }; #ifdef WITH_VST @@ -95,8 +97,8 @@ struct channel_t uint32_t midiInReadActions; uint32_t midiInPitch; // midi channel - uint32_t midiOut; - uint32_t midiOutChan; + int midiOut; // TODO - should be bool + int midiOutChan; std::vector actions; diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp index b3c4e80..2af8fae 100644 --- a/src/core/plugin.cpp +++ b/src/core/plugin.cpp @@ -37,9 +37,11 @@ using std::string; -using namespace giada::u; +namespace giada { +namespace m +{ int Plugin::idGenerator = 1; @@ -49,9 +51,9 @@ int Plugin::idGenerator = 1; Plugin::Plugin(juce::AudioPluginInstance* plugin, double samplerate, int buffersize) : ui (nullptr), - plugin(plugin), - id (idGenerator++), - bypass(false) + plugin(plugin), + id (idGenerator++), + bypass(false) { using namespace juce; @@ -287,4 +289,7 @@ void Plugin::closeEditor() ui = nullptr; } +}} // giada::m:: + + #endif diff --git a/src/core/plugin.h b/src/core/plugin.h index 62633c4..61202b6 100644 --- a/src/core/plugin.h +++ b/src/core/plugin.h @@ -34,6 +34,9 @@ #include "../deps/juce-config.h" +namespace giada { +namespace m +{ class Plugin { private: @@ -104,6 +107,8 @@ public: std::vector midiInParams; }; +}} // giada::m:: + #endif #endif // #ifdef WITH_VST diff --git a/src/core/pluginHost.cpp b/src/core/pluginHost.cpp index 6ba0465..2e84fe8 100644 --- a/src/core/pluginHost.cpp +++ b/src/core/pluginHost.cpp @@ -184,7 +184,7 @@ int scanDirs(const string& dirs, const std::function& cb) searchPath.add(juce::File(dir)); juce::PluginDirectoryScanner scanner(knownPluginList, format, searchPath, - true, juce::File::nonexistent); // true: recursive + true, juce::File()); // true: recursive juce::String name; while (scanner.scanNextFile(false, name)) { diff --git a/src/core/pluginHost.h b/src/core/pluginHost.h index dcb749f..aff17a9 100644 --- a/src/core/pluginHost.h +++ b/src/core/pluginHost.h @@ -37,12 +37,14 @@ #include "audioBuffer.h" -class Plugin; -class Channel; namespace giada { -namespace m { +namespace m +{ +class Plugin; +class Channel; + namespace pluginHost { enum stackType diff --git a/src/core/recorder.cpp b/src/core/recorder.cpp index a54524d..0f7803b 100644 --- a/src/core/recorder.cpp +++ b/src/core/recorder.cpp @@ -25,14 +25,16 @@ * -------------------------------------------------------------------------- */ +#include +#include #include -#include #include "../utils/log.h" -#include "const.h" -#include "sampleChannel.h" +#include "action.h" +#include "channel.h" #include "recorder.h" +using std::map; using std::vector; @@ -42,460 +44,224 @@ namespace recorder { namespace { -/* Composite -A group of two actions (keypress+keyrel, muteon+muteoff) used during the overdub -process. */ +/* actions +The big map of actions {frame : actions[]}. This belongs to Recorder, but it +is often parsed by Mixer. So every "write" action performed on it (add, +remove, ...) must be guarded by a lock on the mixerMutex. Until a proper +lock-free solution will be implemented. */ -Composite cmp; +ActionMap actions; - -/* -------------------------------------------------------------------------- */ - - -/* fixOverdubTruncation -Fixes underlying action truncation when overdubbing over a longer action. I.e.: - Original: |#############| - Overdub: ---|#######|--- - fix: |#||#######|--- */ - -void fixOverdubTruncation(const Composite& comp, pthread_mutex_t* mixerMutex) -{ - action* next = nullptr; - int res = getNextAction(comp.a2.chan, comp.a1.type | comp.a2.type, comp.a2.frame, - &next); - if (res != 1 || next->type != comp.a2.type) - return; - gu_log("[recorder::fixOverdubTruncation] add truncation at frame %d, type=%d\n", - next->frame, next->type); - deleteAction(next->chan, next->frame, next->type, false, mixerMutex); -} - -}; // {anonymous} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - -vector frames; -vector> global; -vector actions; // used internally - -bool active = false; -bool sortedActions = false; +pthread_mutex_t* mixerMutex = nullptr; +bool active = false; +int actionId = 0; /* -------------------------------------------------------------------------- */ -void init() +void lock_(std::function f) { - active = false; - sortedActions = false; - clearAll(); + assert(mixerMutex != nullptr); + pthread_mutex_lock(mixerMutex); + f(); + pthread_mutex_unlock(mixerMutex); } /* -------------------------------------------------------------------------- */ +/* optimize +Removes frames without actions. */ -bool canRec(Channel* ch, bool clockRunning, bool mixerRecording) +void optimize_(ActionMap& map) { - /* Can record on a channel if: - - recorder is on - - mixer is running - - mixer is not recording a take somewhere - - channel is MIDI or SAMPLE type with data in it */ - return active && clockRunning && !mixerRecording && - (ch->type == ChannelType::MIDI || (ch->type == ChannelType::SAMPLE && ch->hasData())); + for (auto it = map.cbegin(); it != map.cend();) + it->second.size() == 0 ? it = map.erase(it) : ++it; } /* -------------------------------------------------------------------------- */ -void rec(int index, int type, int frame, uint32_t iValue, float fValue) +void removeIf_(std::function f) { - /* allocating the action */ - - action* a = (action*) malloc(sizeof(action)); /* TODO - AAARRRGGHHHHHH!!!! */ - a->chan = index; - a->type = type; - a->frame = frame; - a->iValue = iValue; - a->fValue = fValue; - - /* check if the frame exists in the stack. If it exists, we don't extend - * the stack, but we add (or push) a new action to it. */ - - int frameToExpand = frames.size(); - for (int i=0; ichan == index && - ac->type == type && - ac->frame == frame && - ac->iValue == iValue && - ac->fValue == fValue) - return; + ActionMap temp = actions; + + /* + for (auto& kv : temp) { + vector& as = kv.second; + as.erase(std::remove_if(as.begin(), as.end(), f), as.end()); + }*/ + for (auto& kv : temp) { + auto i = std::begin(kv.second); + while (i != std::end(kv.second)) { + if (f(*i)) { + delete *i; + i = kv.second.erase(i); + } + else + ++i; } - - global.at(frameToExpand).push_back(a); // expand array } + optimize_(temp); - sortedActions = false; - - gu_log("[recorder::rec] action recorded, type=%d frame=%d chan=%d iValue=%d (0x%X) fValue=%f\n", - a->type, a->frame, a->chan, a->iValue, a->iValue, a->fValue); - //print(); + lock_([&](){ actions = std::move(temp); }); } +} // {anonymous} +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void clearChan(int index) +void init(pthread_mutex_t* m) { - gu_log("[recorder::clearChan] clearing chan %d...\n", index); - - for (unsigned i=0; ichan == index) { - free(a); - global.at(i).erase(global.at(i).begin() + j); - } - else - j++; - } - } - optimize(); - //print(); + mixerMutex = m; + active = false; + actionId = 0; + clearAll(); } /* -------------------------------------------------------------------------- */ -void clearAction(int index, char act) +void debug() { - gu_log("[recorder::clearAction] clearing action %d from chan %d...\n", act, index); - for (unsigned i=0; ichan == index && (act & a->type) == a->type) { // bitmask - free(a); - global.at(i).erase(global.at(i).begin() + j); - } - else - j++; + int total = 0; + puts("-------------"); + for (auto& kv : actions) { + printf("frame: %d\n", kv.first); + for (const Action* a : kv.second) { + total++; + printf(" this=%p - id=%d, frame=%d, channel=%d, value=0x%X, prev=%p, next=%p\n", + (void*) a, a->id, a->frame, a->channel, a->event.getRaw(), (void*) a->prev, (void*) a->next); } } - optimize(); - //print(); + printf("TOTAL: %d\n", total); + puts("-------------"); } /* -------------------------------------------------------------------------- */ -void deleteAction(int chan, int frame, char type, bool checkValues, - pthread_mutex_t* mixerMutex, uint32_t iValue, float fValue) +void clearAll() { - /* find the frame 'frame' */ - - bool found = false; - for (unsigned i=0; ichan == chan && a->type == (type & a->type)); - if (checkValues) - doit &= (a->iValue == iValue && a->fValue == fValue); - - if (!doit) - continue; - - while (true) { - if (pthread_mutex_trylock(mixerMutex)) { - free(a); - global.at(i).erase(global.at(i).begin() + j); - pthread_mutex_unlock(mixerMutex); - found = true; - break; - } - else - gu_log("[recorder::deleteAction] waiting for mutex...\n"); - } - } - } - if (found) { - optimize(); - gu_log("[recorder::deleteAction] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", - type, frame, chan, iValue, iValue, fValue); - } - else - gu_log("[recorder::deleteAction] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n", - type, frame, chan, iValue, iValue, fValue); + removeIf_([=](const Action* a) { return true; }); // TODO optimize this } /* -------------------------------------------------------------------------- */ -void deleteActions(int chan, int frame_a, int frame_b, char type, - pthread_mutex_t* mixerMutex) +void clearChannel(int channel) { - sortActions(); - vector dels; - - for (unsigned i=0; i frame_a && frames.at(i) < frame_b) - dels.push_back(frames.at(i)); - - for (unsigned i=0; ichannel == channel; }); } /* -------------------------------------------------------------------------- */ -void clearAll() +void clearActions(int channel, int type) { - while (global.size() > 0) { - for (unsigned i=0; ichannel == channel && a->event.getStatus() == type; + }); } /* -------------------------------------------------------------------------- */ -void optimize() +void deleteAction(const Action* target) { - /* do something until the i frame is empty. */ - - unsigned i = 0; - while (true) { - if (i == global.size()) return; - if (global.at(i).size() == 0) { - global.erase(global.begin() + i); - frames.erase(frames.begin() + i); - } - else - i++; - } - - sortActions(); + removeIf_([=](const Action* a) { return a == target; }); } /* -------------------------------------------------------------------------- */ -void sortActions() +void updateKeyFrames(std::function f) { - if (sortedActions) - return; - for (unsigned i=0; i frames.at(i)) { - std::swap(frames.at(j), frames.at(i)); - std::swap(global.at(j), global.at(i)); - } - sortedActions = true; - //print(); + /* This stuff must be performed in a lock, because we are moving the vector + of actions from the real ActionMap to the temporary one. */ + + ActionMap temp; + + lock_([&]() + { + for (auto& kv : actions) { + Frame frame = f(kv.first); + temp[frame] = std::move(kv.second); // Move std::vector + for (const Action* action : temp[frame]) + const_cast(action)->frame = frame; + gu_log("[recorder::updateKeyFrames] %d -> %d\n", kv.first, frame); + } + actions = std::move(temp); + }); } /* -------------------------------------------------------------------------- */ -void updateBpm(float oldval, float newval, int oldquanto) +void updateEvent(const Action* a, MidiEvent e) { - for (unsigned i=0; i 0 && scarto <= 6) - frames.at(i) = frames.at(i) + scarto; - } - } - - /* update structs */ - - for (unsigned i=0; iframe = frames.at(i); - } - } - - //print(); + assert(a != nullptr); + lock_([&] { const_cast(a)->event = e; }); } /* -------------------------------------------------------------------------- */ -void updateSamplerate(int systemRate, int patchRate) +void updateSiblings(const Action* a, const Action* prev, const Action* next) { - /* diff ratio: systemRate / patchRate - * e.g. 44100 / 96000 = 0.4... */ - - if (systemRate == patchRate) - return; - - gu_log("[recorder::updateSamplerate] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate); - - float ratio = systemRate / (float) patchRate; - for (unsigned i=0; iframe = frames.at(i); - } - } + assert(a != nullptr); + lock_([&] + { + const_cast(a)->prev = prev; + const_cast(a)->next = next; + if (prev != nullptr) const_cast(prev)->next = a; + if (next != nullptr) const_cast(next)->prev = a; + }); } /* -------------------------------------------------------------------------- */ -void expand(int old_fpb, int new_fpb) +void updateActionMap(ActionMap&& am) { - /* this algorithm requires multiple passages if we expand from e.g. 2 - * to 16 beats, precisely 16 / 2 - 1 = 7 times (-1 is the first group, - * which exists yet). If we expand by a non-multiple, the result is zero, - * due to float->int implicit cast */ - - unsigned pass = (int) (new_fpb / old_fpb) - 1; - if (pass == 0) pass = 1; - - unsigned init_fs = frames.size(); - - for (unsigned z=1; z<=pass; z++) { - for (unsigned i=0; ichan, a->type, newframe, a->iValue, a->fValue); - } - } - } - gu_log("[recorder::expand] expanded recs\n"); - //print(); + lock_([&](){ actions = am; }); } /* -------------------------------------------------------------------------- */ -void shrink(int new_fpb) +void updateActionId(int id) { - /* easier than expand(): here we delete eveything beyond old_framesPerBars. */ - - unsigned i=0; - while (true) { - if (i == frames.size()) break; - - if (frames.at(i) >= new_fpb) { - for (unsigned k=0; kchan == chanIndex) - if (type == -1 || global.at(i).at(j)->type == type) - return true; - } - } + for (auto& kv : actions) + for (const Action* action : kv.second) + if (action->channel == channel && (type == 0 || type == action->event.getStatus())) + return true; return false; } @@ -503,172 +269,109 @@ bool hasActions(int chanIndex, int type) /* -------------------------------------------------------------------------- */ -int getNextAction(int chan, char type, int fromFrame, action** out, - uint32_t iValue, uint32_t mask) -{ - sortActions(); // mandatory - - /* Increase 'i' until it reaches 'fromFrame'. That's the point where to start - to look for the next action. */ - - unsigned i = 0; - while (i < frames.size() && frames.at(i) <= fromFrame) i++; +bool isActive() { return active; } +void enable() { active = true; } +void disable() { active = false; } - /* No other actions past 'fromFrame': there are no more actions to look for. - Return -1. */ - if (i == frames.size()) - return -1; - - for (; ichan != chan || (type & a->type) != a->type) - continue; +/* -------------------------------------------------------------------------- */ - /* If no iValue has been specified (iValue == 0), then the next action has - been found, return it. Otherwise, make sure the iValue matches the - action's iValue, according to the mask provided. */ - if (iValue == 0 || (iValue != 0 && (a->iValue | mask) == (iValue | mask))) { - *out = global.at(i).at(j); - return 1; - } - } - } - return -2; // no 'type' actions found +const Action* rec(int channel, Frame frame, MidiEvent event) +{ + /* If key frame doesn't exist yet, the [] operator in std::map is smart + enough to insert a new item first. No plug-in data for now. */ + + lock_([&] + { + actions[frame].push_back(makeAction(actionId++, channel, frame, event)); + }); + return actions[frame].back(); } /* -------------------------------------------------------------------------- */ -int getAction(int chan, char action, int frame, struct action** out) +void rec(const std::vector& as) { - for (unsigned i=0; iframe && - action == global.at(i).at(j)->type && - chan == global.at(i).at(j)->chan) - { - *out = global.at(i).at(j); - return 1; - } - return 0; + ActionMap temp = actions; + + for (const Action* a : as) { + const_cast(a)->id = actionId++; + temp[a->frame].push_back(a); // Memory is already allocated by recorderHandler + } + + lock_([&](){ actions = std::move(temp); }); } /* -------------------------------------------------------------------------- */ -void startOverdub(int index, char actionMask, int frame, unsigned bufferSize) +vector getActionsOnFrame(Frame frame) { - /* prepare the composite struct */ - - cmp.a1.type = G_ACTION_KEYPRESS; - cmp.a2.type = G_ACTION_KEYREL; - cmp.a1.chan = index; - cmp.a2.chan = index; - cmp.a1.frame = frame; - // cmp.a2.frame doesn't exist yet - - /* avoid underlying action truncation: if action2.type == nextAction: - * you are in the middle of a composite action, truncation needed */ - - rec(index, cmp.a1.type, frame); - - action* act = nullptr; - int res = getNextAction(index, cmp.a1.type | cmp.a2.type, cmp.a1.frame, &act); - if (res == 1) { - if (act->type == cmp.a2.type) { - int truncFrame = cmp.a1.frame - bufferSize; - if (truncFrame < 0) - truncFrame = 0; - gu_log("[recorder::startOverdub] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type); - rec(index, cmp.a2.type, truncFrame); - } - } + return actions.count(frame) ? actions[frame] : vector(); } /* -------------------------------------------------------------------------- */ -void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t* mixerMutex) +const Action* getClosestAction(int channel, Frame f, int type) { - cmp.a2.frame = currentFrame; - bool ringLoop = false; - bool nullLoop = false; - - /* Check for ring loops or null loops. Ring loop: a composite action with - key_press at frame N and key_release at frame M, with M <= N. - Null loop: a composite action that begins and ends on the very same frame, - i.e. with 0 size. Very unlikely. - If ring loop: record the last action at the end of the sequencer (that - is 'totalFrames'). - If null loop: remove previous action and do nothing. Also make sure to avoid - underlying action truncation, if the null loop occurs inside a composite - action. */ - - if (cmp.a2.frame < cmp.a1.frame) { // ring loop - ringLoop = true; - gu_log("[recorder::stopOverdub] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame); - rec(cmp.a2.chan, cmp.a2.type, totalFrames); - } - else - if (cmp.a2.frame == cmp.a1.frame) { // null loop - nullLoop = true; - gu_log("[recorder::stopOverdub] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame); - deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false, mixerMutex); // false == don't check values - fixOverdubTruncation(cmp, mixerMutex); - } - - if (nullLoop) - return; + const Action* out = nullptr; + forEachAction([&](const Action* a) + { + if (a->event.getStatus() != type || a->channel != channel) + return; + if (out == nullptr || (a->frame <= f && a->frame > out->frame)) + out = a; + }); + return out; +} - /* Remove any nested action between keypress----keyrel. */ - deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type, mixerMutex); - deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type, mixerMutex); +/* -------------------------------------------------------------------------- */ - if (ringLoop) - return; - /* Record second part of the composite action. Also make sure to avoid - underlying action truncation, if keyrel happens inside a composite action. */ +ActionMap getActionMap() { return actions; } - rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame); - fixOverdubTruncation(cmp, mixerMutex); -} +int getLatestActionId() { return actionId; } /* -------------------------------------------------------------------------- */ -vector getActionsOnFrame(int frame) +vector getActionsOnChannel(int channel) { - for (size_t i=0; i(); + vector out; + forEachAction([&](const Action* a) + { + if (a->channel == channel) + out.push_back(a); + }); + return out; } /* -------------------------------------------------------------------------- */ -void forEachAction(std::function f) +void forEachAction(std::function f) { - - for (const vector actions : recorder::global) - for (const action* action : actions) + for (auto& kv : actions) + for (const Action* action : kv.second) f(action); } + }}}; // giada::m::recorder:: diff --git a/src/core/recorder.h b/src/core/recorder.h index d62017b..dfb6b0a 100644 --- a/src/core/recorder.h +++ b/src/core/recorder.h @@ -29,169 +29,130 @@ #define G_RECORDER_H -#include +#include +#include #include #include -#include - - -class Channel; +#include "types.h" +#include "midiEvent.h" namespace giada { -namespace m { -namespace recorder +namespace m { -/* action - * struct containing fields to describe an atomic action. Note from - * VST sdk: parameter values, like all VST parameters, are declared as - * floats with an inclusive range of 0.0 to 1.0 (fValue). */ +struct Action; -struct action +namespace recorder { - int chan; // channel index, i.e. Channel->index - int type; - int frame; // redundant info, used by helper functions - float fValue; // used only for envelopes (volumes, vst params). - uint32_t iValue; // used only for MIDI events -}; - -/* Composite -A group of two actions (keypress+keyrel, muteon+muteoff). */ +using ActionMap = std::map>; -struct Composite -{ - action a1; - action a2; -}; +void debug(); +/* init +Initializes the recorder: everything starts from here. */ -/* frames -Frame counter sentinel. It tells which frames contain actions. E.g.: - frames[0] = 155 // some actions on frame 155 - frames[1] = 2048 // some actions on frame 2048 -It always matches 'global''s size: frames.size() == global.size() */ +void init(pthread_mutex_t* mixerMutex); -extern std::vector frames; +/* clearAll +Deletes all recorded actions. */ -/* global -Contains the actual actions. E.g.: - global[0] = - global[1] = */ +void clearAll(); -extern std::vector> global; -/* TODO - this frames vs global madness must be replaced with a map: -std::map> */ +/* clearChannel +Clears all actions from a channel. */ -extern bool active; -extern bool sortedActions; // are actions sorted via sortActions()? +void clearChannel(int channel); -/* init - * everything starts from here. */ +/* clearActions +Clears the actions by type from a channel. */ -void init(); +void clearActions(int channel, int type); -/* hasActions -Checks if the channel has at least one action recorded. Used after an -action deletion. Type != -1: check if channel has actions of type 'type'.*/ - -bool hasActions(int chanIndex, int type=-1); - -/* canRec - * can a channel rec an action? Call this one BEFORE rec(). */ +/* deleteAction +Deletes a specific action. */ -bool canRec(Channel* ch, bool clockRunning, bool mixerRecording); +void deleteAction(const Action* a); -/* rec - * record an action. */ +/* updateKeyFrames +Update all the key frames in the internal map of actions, according to a lambda +function 'f'. */ -void rec(int chan, int action, int frame, uint32_t iValue=0, float fValue=0.0f); +void updateKeyFrames(std::function f); -/* clearChan - * clear all actions from a channel. */ +/* updateActionMap +Replaces the current map of actions with a new one. Warning: 'am' will be moved +as a replacement (no copy). */ -void clearChan(int chan); +void updateActionMap(ActionMap&& am); -/* clearAction - * clear the 'action' action type from a channel. */ +/* updateEvent +Changes the event in action 'a'. */ -void clearAction(int chan, char action); +void updateEvent(const Action* a, MidiEvent e); -/* deleteAction - * delete ONE action. Useful in the action editor. 'type' can be a mask. */ +/* updateSiblings +Changes previous and next actions in action 'a'. Mostly used for chained actions +such as envelopes. */ -void deleteAction(int chan, int frame, char type, bool checkValues, - pthread_mutex_t* mixerMutex, uint32_t iValue=0, float fValue=0.0); +void updateSiblings(const Action* a, const Action* prev, const Action* next); -/* deleteActions -Deletes A RANGE of actions from frame_a to frame_b in channel 'chan' of type -'type' (can be a bitmask). Exclusive range (frame_a, frame_b). */ +void updateActionId(int id); -void deleteActions(int chan, int frame_a, int frame_b, char type, - pthread_mutex_t* mixerMutex); +/* hasActions +Checks if the channel has at least one action recorded. */ -/* clearAll - * delete everything. */ +bool hasActions(int channel, int type=0); -void clearAll(); +/* isActive +Is recorder recording something? */ -/* optimize - * clear frames without actions. */ +bool isActive(); -void optimize(); +void enable(); +void disable(); -/* sortActions - * sorts actions by frame, asc mode. */ +const Action* makeAction(int id, int channel, Frame frame, MidiEvent e); -void sortActions(); +/* rec (1) +Records an action and returns it. */ -/* updateBpm - * reassign frames by calculating the new bpm value. */ +const Action* rec(int channel, Frame frame, MidiEvent e); -void updateBpm(float oldval, float newval, int oldquanto); +/* rec (2) +Transfer a vector of actions into the current ActionMap. This is called by +recordHandler when a live session is over and consolidation is required. */ -/* updateSamplerate - * reassign frames taking in account the samplerate. If f_system == - * f_patch nothing changes, otherwise the conversion is mandatory. */ +void rec(const std::vector& actions); -void updateSamplerate(int systemRate, int patchRate); +/* forEachAction +Applies a read-only callback on each action recorded. NEVER do anything inside +the callback that might alter the ActionMap. */ -void expand(int old_fpb, int new_fpb); -void shrink(int new_fpb); +void forEachAction(std::function f); -/* getNextAction -Returns the nearest action in chan 'chan' of type 'action' starting from -'frame'. Action can be a bitmask. If iValue != 0 search for next action with -iValue == iValue with 'mask' to ignore bytes. Useful for MIDI key_release. -Mask example: +/* getActionsOnFrame +Returns a vector of actions recorded on frame 'f'. */ - iValue = 0x803D3F00 - mask = 0x0000FF00 // ignore byte 3 - action = 0x803D3200 // <--- this action will be found */ +std::vector getActionsOnFrame(Frame f); -int getNextAction(int chan, char action, int frame, struct action** out, - uint32_t iValue=0, uint32_t mask=0); +/* getActionsOnChannel +Returns a vector of actions belonging to channel 'ch'. */ -/* getAction -Returns a pointer to action in chan 'chan' of type 'action' at frame 'frame'. */ +std::vector getActionsOnChannel(int ch); -int getAction(int chan, char action, int frame, struct action** out); +/* getClosestAction +Given a frame 'f' returns the closest action. */ -/* getActionsOnFrame -Returns a vector of actions that occur on frame 'frame'. */ +const Action* getClosestAction(int channel, Frame f, int type); -std::vector getActionsOnFrame(int frame); -/* start/stopOverdub -These functions are used when you overwrite existing actions. For example: -pressing Mute button on a channel with some existing mute actions. */ +int getLatestActionId(); -void startOverdub(int chan, char action, int frame, unsigned bufferSize); -void stopOverdub(int currentFrame, int totalFrames, pthread_mutex_t* mixerMutex); +/* getActionMap +Returns a copy of the internal action map. Used only by recorderHandler. */ -/* forEachAction -Applies a read-only callback on each action recorded. */ +ActionMap getActionMap(); -void forEachAction(std::function f); }}}; // giada::m::recorder:: + #endif diff --git a/src/core/recorderHandler.cpp b/src/core/recorderHandler.cpp new file mode 100644 index 0000000..7879be6 --- /dev/null +++ b/src/core/recorderHandler.cpp @@ -0,0 +1,290 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine 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. + * + * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include +#include +#include "../utils/log.h" +#include "../utils/ver.h" +#include "recorder.h" +#include "action.h" +#include "clock.h" +#include "const.h" +#include "recorderHandler.h" + + +namespace giada { +namespace m { +namespace recorderHandler +{ +namespace +{ +std::vector recs_; + + +/* -------------------------------------------------------------------------- */ + + +const Action* getActionById_(int id, const recorder::ActionMap& source) +{ + for (auto& kv : source) + for (const Action* action : kv.second) + if (action->id == id) + return action; + return nullptr; +} + + +/* -------------------------------------------------------------------------- */ + + +/* areComposite_ +Composite: NOTE_ON + NOTE_OFF on the same note. */ + +bool areComposite_(const Action* a1, const Action* a2) +{ + return a1->event.getStatus() == MidiEvent::NOTE_ON && + a2->event.getStatus() == MidiEvent::NOTE_OFF && + a1->event.getNote() == a2->event.getNote() && + a1->channel == a2->channel; +} + + +/* -------------------------------------------------------------------------- */ + + +/* consolidate_ +Given an action 'a1' tries to find the matching NOTE_OFF. This algorithm must +start searching from the element next to 'a1': since live actions are recorded +in linear sequence, the potential partner of 'a1' always lies beyond a1 itself. +Without this trick (i.e. if it loops from vector.begin() each time) the +algorithm would end up matching wrong partners. */ + +void consolidate_(const Action* a1, size_t i) +{ + for (auto it = recs_.begin() + i; it != recs_.end(); ++it) { + + const Action* a2 = *it; + + if (!areComposite_(a1, a2)) + continue; + + const_cast(a1)->next = a2; + const_cast(a2)->prev = a1; + + break; + } +} + + +/* -------------------------------------------------------------------------- */ + + +void consolidate_() +{ + for (auto it = recs_.begin(); it != recs_.end(); ++it) + consolidate_(*it, it - recs_.begin()); // Pass current index +} + +/* -------------------------------------------------------------------------- */ + + +void readPatch_DEPR_(const std::vector& pactions) +{ + for (const patch::action_t paction : pactions) + recs_.push_back(recorder::makeAction(-1, paction.channel, paction.frame, MidiEvent(paction.event))); + + consolidate(); +} +} // {anonymous} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +bool isBoundaryEnvelopeAction(const Action* a) +{ + assert(a->prev != nullptr); + assert(a->next != nullptr); + return a->prev->frame > a->frame || a->next->frame < a->frame; +} + + +/* -------------------------------------------------------------------------- */ + + +void updateBpm(float oldval, float newval, int oldquanto) +{ + recorder::updateKeyFrames([=](Frame old) + { + /* The division here cannot be precise. A new frame can be 44099 and the + quantizer set to 44100. That would mean two recs completely useless. So we + compute a reject value ('scarto'): if it's lower than 6 frames the new frame + is collapsed with a quantized frame. FIXME - maybe 6 frames are too low. */ + Frame frame = (old / newval) * oldval; + if (frame != 0) { + Frame delta = oldquanto % frame; + if (delta > 0 && delta <= 6) + frame = frame + delta; + } + return frame; + }); +} + + +/* -------------------------------------------------------------------------- */ + + +void updateSamplerate(int systemRate, int patchRate) +{ + if (systemRate == patchRate) + return; + + float ratio = systemRate / (float) patchRate; + + recorder::updateKeyFrames([=](Frame old) { return floorf(old * ratio); }); +} + + +/* -------------------------------------------------------------------------- */ + + +bool cloneActions(int chanIndex, int newChanIndex) +{ + recorder::ActionMap temp = recorder::getActionMap(); + + bool cloned = false; + int actionId = recorder::getLatestActionId(); + + recorder::forEachAction([&](const Action* a) + { + if (a->channel == chanIndex) { + Action* clone = new Action(*a); + clone->id = ++actionId; + clone->channel = newChanIndex; + temp[clone->frame].push_back(clone); + cloned = true; + } + }); + + recorder::updateActionId(actionId); + recorder::updateActionMap(std::move(temp)); + + return cloned; +} + + +/* -------------------------------------------------------------------------- */ + + +void liveRec(int channel, MidiEvent e) +{ + assert(e.isNoteOnOff()); // Can't record any other kind of events for now + recs_.push_back(recorder::makeAction(-1, channel, clock::getCurrentFrame(), e)); +} + + +/* -------------------------------------------------------------------------- */ + + +void consolidate() +{ + consolidate_(); + recorder::rec(recs_); + recs_.clear(); +} + + +/* -------------------------------------------------------------------------- */ + + +void writePatch(int chanIndex, std::vector& pactions) +{ + recorder::forEachAction([&] (const Action* a) + { + if (a->channel != chanIndex) + return; + pactions.push_back(patch::action_t { + a->id, + a->channel, + a->frame, + a->event.getRaw(), + a->prev != nullptr ? a->prev->id : -1, + a->next != nullptr ? a->next->id : -1 + }); + }); +} + + + +/* -------------------------------------------------------------------------- */ + + +void readPatch(const std::vector& pactions) +{ + if (u::ver::isLess(patch::versionMajor, patch::versionMinor, patch::versionPatch, 0, 15, 3)) { + readPatch_DEPR_(pactions); + return; + } + + recorder::ActionMap temp = recorder::getActionMap(); + + /* First pass: add actions with no relationship (no prev/next). */ + + for (const patch::action_t paction : pactions) { + temp[paction.frame].push_back(recorder::makeAction( + paction.id, + paction.channel, + paction.frame, + MidiEvent(paction.event))); + recorder::updateActionId(paction.id + 1); + } + + /* Second pass: fill in previous and next actions, if any. */ + + for (const patch::action_t paction : pactions) { + if (paction.next == -1 && paction.prev == -1) + continue; + Action* curr = const_cast(getActionById_(paction.id, temp)); + assert(curr != nullptr); + if (paction.next != -1) { + curr->next = getActionById_(paction.next, temp); + assert(curr->next != nullptr); + } + if (paction.prev != -1) { + curr->prev = getActionById_(paction.prev, temp); + assert(curr->prev != nullptr); + } + } + + recorder::updateActionMap(std::move(temp)); +} +}}}; // giada::m::recorderHandler:: + + diff --git a/src/core/recorderHandler.h b/src/core/recorderHandler.h new file mode 100644 index 0000000..7b256a7 --- /dev/null +++ b/src/core/recorderHandler.h @@ -0,0 +1,76 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine 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. + * + * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef G_RECORDER_HANDLER_H +#define G_RECORDER_HANDLER_H + + +#include "midiEvent.h" +#include "patch.h" + + +namespace giada { +namespace m +{ +struct Action; + +namespace recorderHandler +{ +bool isBoundaryEnvelopeAction(const Action* a); + +/* updateBpm +Changes actions position by calculating the new bpm value. */ + +void updateBpm(float oldval, float newval, int oldquanto); + +/* updateSamplerate +Changes actions position by taking in account the new samplerate. If +f_system == f_patch nothing will change, otherwise the conversion is +mandatory. */ + +void updateSamplerate(int systemRate, int patchRate); + +/* cloneActions +Clones actions in channel 'chanIndex', giving them a new channel index. Returns +whether any action has been cloned. */ + +bool cloneActions(int chanIndex, int newChanIndex); + +/* liveRec +Records a user-generated action. NOTE_ON or NOTE_OFF only for now. */ + +void liveRec(int channel, MidiEvent e); + +void consolidate(); + +void writePatch(int chanIndex, std::vector& pactions); +void readPatch(const std::vector& pactions); + +}}}; // giada::m::recorderHandler:: + + +#endif diff --git a/src/core/sampleChannel.cpp b/src/core/sampleChannel.cpp index 8139a7c..b352430 100644 --- a/src/core/sampleChannel.cpp +++ b/src/core/sampleChannel.cpp @@ -35,26 +35,27 @@ using std::string; -using namespace giada; -using namespace giada::m; +namespace giada { +namespace m +{ SampleChannel::SampleChannel(bool inputMonitor, int bufferSize) : Channel (ChannelType::SAMPLE, ChannelStatus::EMPTY, bufferSize), - mode (ChannelMode::SINGLE_BASIC), - wave (nullptr), - tracker (0), - trackerPreview (0), - shift (0), - qWait (false), - inputMonitor (inputMonitor), - boost (G_DEFAULT_BOOST), - pitch (G_DEFAULT_PITCH), - begin (0), - end (0), - midiInReadActions(0x0), - midiInPitch (0x0), - rsmp_state (nullptr) + mode (ChannelMode::SINGLE_BASIC), + wave (nullptr), + tracker (0), + trackerPreview (0), + shift (0), + qWait (false), + inputMonitor (inputMonitor), + boost (G_DEFAULT_BOOST), + pitch (G_DEFAULT_PITCH), + begin (0), + end (0), + midiInReadActions(0x0), + midiInPitch (0x0), + rsmp_state (nullptr) { rsmp_state = src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr); if (rsmp_state == nullptr) { @@ -99,7 +100,7 @@ void SampleChannel::copy(const Channel* src_, pthread_mutex_t* pluginMutex) /* -------------------------------------------------------------------------- */ -void SampleChannel::parseEvents(m::mixer::FrameEvents fe) +void SampleChannel::parseEvents(mixer::FrameEvents fe) { sampleChannelProc::parseEvents(this, fe); sampleChannelRec::parseEvents(this, fe); @@ -223,8 +224,16 @@ void SampleChannel::setMute(bool value) /* -------------------------------------------------------------------------- */ -void SampleChannel::process(giada::m::AudioBuffer& out, - const giada::m::AudioBuffer& in, bool audible, bool running) +void SampleChannel::setSolo(bool value) +{ + sampleChannelProc::setSolo(this, value); +} + +/* -------------------------------------------------------------------------- */ + + +void SampleChannel::process(AudioBuffer& out, const AudioBuffer& in, + bool audible, bool running) { sampleChannelProc::process(this, out, in, audible, running); } @@ -233,10 +242,10 @@ void SampleChannel::process(giada::m::AudioBuffer& out, /* -------------------------------------------------------------------------- */ -void SampleChannel::readPatch(const string& basePath, int i) +void SampleChannel::readPatch(const string& basePath, const patch::channel_t& pch) { - Channel::readPatch("", i); - channelManager::readPatch(this, basePath, i); + Channel::readPatch("", pch); + channelManager::readPatch(this, basePath, pch); } @@ -387,12 +396,12 @@ float SampleChannel::getBoost() const void SampleChannel::empty() { status = ChannelStatus::EMPTY; - begin = 0; - end = 0; - tracker = 0; - volume = G_DEFAULT_VOL; - boost = G_DEFAULT_BOOST; - hasActions = false; + begin = 0; + end = 0; + tracker = 0; + volume = G_DEFAULT_VOL; + boost = G_DEFAULT_BOOST; + hasActions = false; delete wave; wave = nullptr; sendMidiLstatus(); @@ -408,7 +417,6 @@ void SampleChannel::pushWave(Wave* w) wave = w; begin = 0; end = wave->getSize() - 1; - name = wave->getBasename(); sendMidiLstatus(); } @@ -425,7 +433,7 @@ bool SampleChannel::canInputRec() /* -------------------------------------------------------------------------- */ -int SampleChannel::fillBuffer(giada::m::AudioBuffer& dest, int start, int offset) +int SampleChannel::fillBuffer(AudioBuffer& dest, int start, int offset) { if (pitch == 1.0) return fillBufferCopy(dest, start, offset); else return fillBufferResampled(dest, start, offset); @@ -435,7 +443,7 @@ int SampleChannel::fillBuffer(giada::m::AudioBuffer& dest, int start, int offset /* -------------------------------------------------------------------------- */ -int SampleChannel::fillBufferResampled(giada::m::AudioBuffer& dest, int start, int offset) +int SampleChannel::fillBufferResampled(AudioBuffer& dest, int start, int offset) { rsmp_data.data_in = wave->getFrame(start); // Source data rsmp_data.input_frames = end - start; // How many readable frames @@ -452,7 +460,7 @@ int SampleChannel::fillBufferResampled(giada::m::AudioBuffer& dest, int start, i /* -------------------------------------------------------------------------- */ -int SampleChannel::fillBufferCopy(giada::m::AudioBuffer& dest, int start, int offset) +int SampleChannel::fillBufferCopy(AudioBuffer& dest, int start, int offset) { int used = dest.countFrames() - offset; if (used + start > wave->getSize()) @@ -486,4 +494,6 @@ bool SampleChannel::isAnySingleMode() const bool SampleChannel::isOnLastFrame() const { return tracker >= end; -} \ No newline at end of file +} + +}} // giada::m:: diff --git a/src/core/sampleChannel.h b/src/core/sampleChannel.h index 857e8e1..4514f0e 100644 --- a/src/core/sampleChannel.h +++ b/src/core/sampleChannel.h @@ -35,10 +35,12 @@ #include "channel.h" -class Patch; class Wave; +namespace giada { +namespace m +{ class SampleChannel : public Channel { public: @@ -48,10 +50,9 @@ public: void copy(const Channel* src, pthread_mutex_t* pluginMutex) override; void prepareBuffer(bool running) override; - void parseEvents(giada::m::mixer::FrameEvents fe) override; - void process(giada::m::AudioBuffer& out, const giada::m::AudioBuffer& in, - bool audible, bool running) override; - void readPatch(const std::string& basePath, int i) override; + void parseEvents(mixer::FrameEvents fe) override; + void process(AudioBuffer& out, const AudioBuffer& in, bool audible, bool running) override; + void readPatch(const std::string& basePath, const patch::channel_t& pch) override; void writePatch(int i, bool isProject) override; void start(int frame, bool doQuantize, int velocity) override; @@ -61,6 +62,7 @@ public: bool recordKill() override; void recordStop() override; void setMute(bool value) override; + void setSolo(bool value) override; void startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt) override; void stopReadingActions(bool running, bool treatRecsAsLoops, bool recsStopOnChanHalt) override; @@ -91,7 +93,7 @@ public: Returns how many frames have been used from the original Wave data. It also resamples data if pitch != 1.0f. */ - int fillBuffer(giada::m::AudioBuffer& dest, int start, int offset); + int fillBuffer(AudioBuffer& dest, int start, int offset); /* pushWave Adds a new wave to an existing channel. */ @@ -113,9 +115,9 @@ public: /* bufferPreview Extra buffer for audio preview. */ - giada::m::AudioBuffer bufferPreview; + AudioBuffer bufferPreview; - giada::ChannelMode mode; + ChannelMode mode; Wave* wave; int tracker; // chan position @@ -146,8 +148,11 @@ private: SRC_STATE* rsmp_state; SRC_DATA rsmp_data; - int fillBufferResampled(giada::m::AudioBuffer& dest, int start, int offset); - int fillBufferCopy (giada::m::AudioBuffer& dest, int start, int offset); + int fillBufferResampled(AudioBuffer& dest, int start, int offset); + int fillBufferCopy (AudioBuffer& dest, int start, int offset); }; +}} // giada::m:: + + #endif diff --git a/src/core/sampleChannelProc.cpp b/src/core/sampleChannelProc.cpp index aa23584..ef3569e 100644 --- a/src/core/sampleChannelProc.cpp +++ b/src/core/sampleChannelProc.cpp @@ -31,6 +31,7 @@ #include "pluginHost.h" #include "sampleChannel.h" #include "sampleChannelProc.h" +#include "mixerHandler.h" namespace giada { @@ -188,7 +189,8 @@ void processData_(SampleChannel* ch, m::AudioBuffer& out, const m::AudioBuffer& bool running) { assert(out.countSamples() == ch->buffer.countSamples()); - assert(in.countSamples() == ch->buffer.countSamples()); + if (in.isAllocd()) + assert(in.countSamples() == ch->buffer.countSamples()); /* If armed and input buffer is not empty (i.e. input device available) and input monitor is on, copy input buffer to channel buffer: this enables the @@ -356,6 +358,10 @@ void rewindBySeq(SampleChannel* ch) void setMute(SampleChannel* ch, bool value) { ch->mute = value; + + // This is for processing playing_inaudible + ch->sendMidiLstatus(); + ch->sendMidiLmute(); } @@ -363,6 +369,23 @@ void setMute(SampleChannel* ch, bool value) /* -------------------------------------------------------------------------- */ +void setSolo(SampleChannel* ch, bool value) +{ + ch->solo = value; + m::mh::updateSoloCount(); + + // This is for processing playing_inaudible + for (Channel* channel : mixer::channels) + channel->sendMidiLstatus(); + + ch->sendMidiLsolo(); + +} + + +/* -------------------------------------------------------------------------- */ + + void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity) { /* For one-shot modes, velocity drives the internal volume. */ @@ -427,15 +450,26 @@ void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity) void prepareBuffer(SampleChannel* ch, bool running) { - if (!ch->hasData()) - return; + namespace um = u::math; + ch->buffer.clear(); - if (ch->isPlaying()) { - int framesUsed = ch->fillBuffer(ch->buffer, ch->tracker, 0); - ch->tracker += framesUsed; - if (ch->isOnLastFrame()) - onLastFrame_(ch, framesUsed * (1 / ch->pitch), running); + + if (!ch->hasData() || !ch->isPlaying()) + return; + + Frame framesUsed = ch->fillBuffer(ch->buffer, ch->tracker, 0); + ch->tracker += framesUsed; + + /* The "framesUsed * (1 / ch->pitch)" operation might yield results greater + than the current buffer size. So clamping is mandatory. */ + + if (ch->isOnLastFrame()) { + Frame min = 0; + Frame max = ch->buffer.countFrames() - 1; + framesUsed = static_cast(framesUsed * (1 / ch->pitch)); + onLastFrame_(ch, um::bound(framesUsed, min, max, max), running); } + } @@ -464,4 +498,4 @@ void process(SampleChannel* ch, m::AudioBuffer& out, const m::AudioBuffer& in, if (ch->isPreview()) processPreview_(ch, out); } -}}}; \ No newline at end of file +}}}; diff --git a/src/core/sampleChannelProc.h b/src/core/sampleChannelProc.h index 0c95a69..5f14e85 100644 --- a/src/core/sampleChannelProc.h +++ b/src/core/sampleChannelProc.h @@ -34,11 +34,11 @@ #include "types.h" +namespace giada { +namespace m +{ class SampleChannel; - -namespace giada { -namespace m { namespace sampleChannelProc { /**/ @@ -50,8 +50,8 @@ Parses events gathered by Mixer::masterPlay(). */ void parseEvents(SampleChannel* ch, mixer::FrameEvents ev); /**/ -void process(SampleChannel* ch, giada::m::AudioBuffer& out, - const giada::m::AudioBuffer& in, bool audible, bool running); +void process(SampleChannel* ch, AudioBuffer& out, const AudioBuffer& in, + bool audible, bool running); /* kill Stops a channel abruptly. */ @@ -85,7 +85,8 @@ actions from Recorder. */ void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity); void setMute(SampleChannel* ch, bool value); +void setSolo(SampleChannel* ch, bool value); }}}; -#endif \ No newline at end of file +#endif diff --git a/src/core/sampleChannelRec.cpp b/src/core/sampleChannelRec.cpp index 91db541..ec8a7a8 100644 --- a/src/core/sampleChannelRec.cpp +++ b/src/core/sampleChannelRec.cpp @@ -26,9 +26,13 @@ #include +#include "../utils/math.h" +#include "recorder.h" +#include "recorderHandler.h" #include "const.h" #include "conf.h" #include "clock.h" +#include "action.h" #include "kernelAudio.h" #include "sampleChannel.h" #include "sampleChannelRec.h" @@ -69,7 +73,14 @@ void onFirstBeat_(SampleChannel* ch, bool recsStopOnChanHalt) bool recorderCanRec_(SampleChannel* ch) { - return recorder::canRec(ch, clock::isRunning(), mixer::recording); + /* Can record on a channel if: + - recorder is on + - mixer is running + - mixer is not recording a take somewhere + - channel is MIDI or SAMPLE type with data in it */ + + return recorder::isActive() && clock::isRunning() && !mixer::recording && + (ch->type == ChannelType::MIDI || (ch->type == ChannelType::SAMPLE && ch->hasData())); } @@ -79,48 +90,31 @@ bool recorderCanRec_(SampleChannel* ch) /* calcVolumeEnv Computes any changes in volume done via envelope tool. */ -void calcVolumeEnv_(SampleChannel* ch, int globalFrame) +void calcVolumeEnv_(SampleChannel* ch, const Action* a1) { - /* method: check this frame && next frame, then calculate delta */ + assert(a1 != nullptr); + assert(a1->next != nullptr); - recorder::action* a0 = nullptr; - recorder::action* a1 = nullptr; - int res; + const Action* a2 = a1->next; - /* get this action on frame 'frame'. It's unlikely that the action - * is not found. */ + double vf1 = u::math::map(a1->event.getVelocity(), 0, G_MAX_VELOCITY, 0, 1.0); + double vf2 = u::math::map(a2->event.getVelocity(), 0, G_MAX_VELOCITY, 0, 1.0); - res = recorder::getAction(ch->index, G_ACTION_VOLUME, globalFrame, &a0); - - assert(res != 0); - - /* get the action next to this one. - * res == -1: a1 not found, this is the last one. Rewind the search - * and use action at frame number 0 (actions[0]). - * res == -2 G_ACTION_VOLUME not found. This should never happen */ - - res = recorder::getNextAction(ch->index, G_ACTION_VOLUME, globalFrame, &a1); - if (res == -1) - res = recorder::getAction(ch->index, G_ACTION_VOLUME, 0, &a1); - - assert(res != -2); - - ch->volume_i = a0->fValue; - ch->volume_d = ((a1->fValue - a0->fValue) / (a1->frame - a0->frame)) * 1.003f; + ch->volume_i = vf1; + ch->volume_d = a2->frame == a1->frame ? 0 : (vf2 - vf1) / (a2->frame - a1->frame); } /* -------------------------------------------------------------------------- */ -void parseAction_(SampleChannel* ch, const recorder::action* a, int localFrame, - int globalFrame) +void parseAction_(SampleChannel* ch, const Action* a, int localFrame, int globalFrame) { if (!ch->readActions) return; - switch (a->type) { - case G_ACTION_KEYPRESS: + switch (a->event.getStatus()) { + case MidiEvent::NOTE_ON: if (ch->isAnySingleMode()) { ch->start(localFrame, false, 0); /* This is not a user-generated event, so fill the first chunk of buffer. @@ -130,16 +124,16 @@ void parseAction_(SampleChannel* ch, const recorder::action* a, int localFrame, ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, localFrame); } break; - case G_ACTION_KEYREL: + case MidiEvent::NOTE_OFF: if (ch->isAnySingleMode()) ch->stop(); break; - case G_ACTION_KILL: + case MidiEvent::NOTE_KILL: if (ch->isAnySingleMode()) ch->kill(localFrame); break; - case G_ACTION_VOLUME: - calcVolumeEnv_(ch, globalFrame); + case MidiEvent::ENVELOPE: + calcVolumeEnv_(ch, a); break; } } @@ -153,15 +147,11 @@ void recordKeyPressAction_(SampleChannel* ch) if (!recorderCanRec_(ch)) return; - /* SINGLE_PRESS mode needs overdub. Also, disable reading actions while - overdubbing. */ - if (ch->mode == ChannelMode::SINGLE_PRESS) { - recorder::startOverdub(ch->index, G_ACTION_KEYS, clock::getCurrentFrame(), - kernelAudio::getRealBufSize()); + /* Disable reading actions while recording SINGLE_PRESS mode. */ + if (ch->mode == ChannelMode::SINGLE_PRESS) ch->readActions = false; - } - else - recorder::rec(ch->index, G_ACTION_KEYPRESS, clock::getCurrentFrame()); + + recorderHandler::liveRec(ch->index, MidiEvent(MidiEvent::NOTE_ON, 0, 0)); ch->hasActions = true; } @@ -191,8 +181,8 @@ void parseEvents(SampleChannel* ch, mixer::FrameEvents fe) quantize_(ch, fe.quantoPassed); if (fe.onFirstBeat) onFirstBeat_(ch, conf::recsStopOnChanHalt); - for (const recorder::action* action : fe.actions) - if (action->chan == ch->index) + for (const Action* action : fe.actions) + if (action->channel == ch->index) parseAction_(ch, action, fe.frameLocal, fe.frameGlobal); } @@ -230,9 +220,9 @@ bool recordStart(SampleChannel* ch, bool canQuantize) bool recordKill(SampleChannel* ch) { - /* Don't record G_ACTION_KILL actions for LOOP channels. */ + /* Don't record NOTE_KILL actions for LOOP channels. */ if (recorderCanRec_(ch) && !ch->isAnyLoopMode()) { - recorder::rec(ch->index, G_ACTION_KILL, clock::getCurrentFrame()); + recorder::rec(ch->index, clock::getCurrentFrame(), MidiEvent(MidiEvent::NOTE_KILL, 0, 0)); ch->hasActions = true; } return true; @@ -246,10 +236,8 @@ void recordStop(SampleChannel* ch) { /* Record a stop event only if channel is SINGLE_PRESS. For any other mode the stop event is meaningless. */ - if (recorderCanRec_(ch) && ch->mode == ChannelMode::SINGLE_PRESS) { - recorder::stopOverdub(clock::getCurrentFrame(), clock::getFramesInLoop(), - &mixer::mutex); - } + if (recorderCanRec_(ch) && ch->mode == ChannelMode::SINGLE_PRESS) + recorderHandler::liveRec(ch->index, MidiEvent(MidiEvent::NOTE_OFF, 0, 0)); } diff --git a/src/core/sampleChannelRec.h b/src/core/sampleChannelRec.h index 6694b2e..720a748 100644 --- a/src/core/sampleChannelRec.h +++ b/src/core/sampleChannelRec.h @@ -29,11 +29,13 @@ #define G_SAMPLE_CHANNEL_REC_H -class SampleChannel; namespace giada { -namespace m { +namespace m +{ +class SampleChannel; + namespace sampleChannelRec { void parseEvents(SampleChannel* ch, mixer::FrameEvents fe); diff --git a/src/core/wave.cpp b/src/core/wave.cpp index 1276dc6..40a68d4 100644 --- a/src/core/wave.cpp +++ b/src/core/wave.cpp @@ -61,12 +61,12 @@ float* Wave::operator [](int offset) const Wave::Wave(const Wave& other) : m_rate (other.m_rate), m_bits (other.m_bits), - m_logical (true), // a cloned wave does not exist on disk + m_logical (true), // A cloned wave does not exist on disk m_edited (false), m_path (other.m_path) { buffer.alloc(other.getSize(), other.getChannels()); - buffer.copyData(other.getFrame(0), other.getSize(), other.getChannels()); + buffer.copyData(other.getFrame(0), other.getSize()); } diff --git a/src/glue/actionEditor.cpp b/src/glue/actionEditor.cpp new file mode 100644 index 0000000..22c8d22 --- /dev/null +++ b/src/glue/actionEditor.cpp @@ -0,0 +1,339 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine 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. + * + * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#include +#include "../core/clock.h" +#include "../core/const.h" +#include "../core/sampleChannel.h" +#include "../core/midiChannel.h" +#include "../core/recorderHandler.h" +#include "../core/recorder.h" +#include "../core/action.h" +#include "recorder.h" +#include "actionEditor.h" + + +using std::vector; + + +namespace giada { +namespace c { +namespace actionEditor +{ +namespace +{ +Frame fixVerticalEnvActions_(Frame f, const m::Action* a1, const m::Action* a2) +{ + if (a1->frame == f) f += 1; + else if (a2->frame == f) f -= 1; + if (a1->frame == f || a2->frame == f) + return -1; + return f; +} + + +/* -------------------------------------------------------------------------- */ + +/* recordFirstEnvelopeAction_ +First action ever? Add actions at boundaries. */ + +void recordFirstEnvelopeAction_(int channel, Frame frame, int value) +{ + namespace mr = m::recorder; + + m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, G_MAX_VELOCITY); + m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value); + const m::Action* a1 = mr::rec(channel, 0, e1); + const m::Action* a2 = mr::rec(channel, frame, e2); + const m::Action* a3 = mr::rec(channel, m::clock::getFramesInLoop() - 1, e1); + mr::updateSiblings(a1, a3, a2); // Circular loop (begin) + mr::updateSiblings(a2, a1, a3); + mr::updateSiblings(a3, a2, a1); // Circular loop (end) +} + + +/* -------------------------------------------------------------------------- */ + + +/* recordNonFirstEnvelopeAction_ +Find action right before frame 'frame' and inject a new action in there. +Vertical envelope points are forbidden. */ + +void recordNonFirstEnvelopeAction_(int channel, Frame frame, int value) +{ + namespace mr = m::recorder; + + m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value); + const m::Action* a1 = mr::getClosestAction(channel, frame, m::MidiEvent::ENVELOPE); + const m::Action* a3 = a1->next; + assert(a1 != nullptr); + assert(a3 != nullptr); + frame = fixVerticalEnvActions_(frame, a1, a3); + if (frame == -1) // Vertical points, nothing to do here + return; + const m::Action* a2 = mr::rec(channel, frame, e2); + mr::updateSiblings(a2, a1, a3); +} +}; // {anonymous} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +void recordMidiAction(m::MidiChannel* ch, int note, int velocity, Frame f1, Frame f2) +{ + namespace mr = m::recorder; + namespace cr = c::recorder; + + if (f2 == 0) + f2 = f1 + G_DEFAULT_ACTION_SIZE; + + /* Avoid frame overflow. */ + + int overflow = f2 - (m::clock::getFramesInLoop()); + if (overflow > 0) { + f2 -= overflow; + f1 -= overflow; + } + + m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::NOTE_ON, note, velocity); + m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::NOTE_OFF, note, velocity); + + const m::Action* a1 = mr::rec(ch->index, f1, e1); + const m::Action* a2 = mr::rec(ch->index, f2, e2); + + mr::updateSiblings(a1, nullptr, a2); + + cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); +} + + +/* -------------------------------------------------------------------------- */ + + +void deleteMidiAction(m::MidiChannel* ch, const m::Action* a) +{ + namespace mr = m::recorder; + namespace cr = c::recorder; + + assert(a != nullptr); + assert(a->event.getStatus() == m::MidiEvent::NOTE_ON); + + /* Send a note-off first in case we are deleting it in a middle of a + key_on/key_off sequence. */ + + if (a->next != nullptr) { + ch->sendMidi(a->next, 0); + mr::deleteAction(a->next); + } + mr::deleteAction(a); + + cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); +} + +/* -------------------------------------------------------------------------- */ + + +void updateMidiAction(m::MidiChannel* ch, const m::Action* a, int note, int velocity, + Frame f1, Frame f2) +{ + namespace mr = m::recorder; + + mr::deleteAction(a->next); + mr::deleteAction(a); + + recordMidiAction(ch, note, velocity, f1, f2); +} + + +/* -------------------------------------------------------------------------- */ + + +void recordSampleAction(const m::SampleChannel* ch, int type, Frame f1, Frame f2) +{ + namespace mr = m::recorder; + namespace cr = c::recorder; + + if (ch->mode == ChannelMode::SINGLE_PRESS) { + m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::NOTE_ON, 0, 0); + m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::NOTE_OFF, 0, 0); + const m::Action* a1 = mr::rec(ch->index, f1, e1); + const m::Action* a2 = mr::rec(ch->index, f2 == 0 ? f1 + G_DEFAULT_ACTION_SIZE : f2, e2); + mr::updateSiblings(a1, nullptr, a2); + } + else { + m::MidiEvent e1 = m::MidiEvent(type, 0, 0); + mr::rec(ch->index, f1, e1); + } + + cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); +} + + +/* -------------------------------------------------------------------------- */ + + +void updateSampleAction(m::SampleChannel* ch, const m::Action* a, int type, Frame f1, + Frame f2) +{ + namespace mr = m::recorder; + + if (ch->mode == ChannelMode::SINGLE_PRESS) + mr::deleteAction(a->next); + mr::deleteAction(a); + + recordSampleAction(ch, type, f1, f2); +} + + +/* -------------------------------------------------------------------------- */ + + +void recordEnvelopeAction(m::Channel* ch, int frame, int value) +{ + namespace mr = m::recorder; + namespace cr = c::recorder; + + assert(value >= 0 && value <= G_MAX_VELOCITY); + + /* First action ever? Add actions at boundaries. Else, find action right + before frame 'f' and inject a new action in there. Vertical envelope points + are forbidden for now. */ + + if (!mr::hasActions(ch->index, m::MidiEvent::ENVELOPE)) + recordFirstEnvelopeAction_(ch->index, frame, value); + else + recordNonFirstEnvelopeAction_(ch->index, frame, value); + + cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); +} + + +/* -------------------------------------------------------------------------- */ + + +void deleteEnvelopeAction(m::Channel* ch, const m::Action* a) +{ + namespace mr = m::recorder; + namespace cr = c::recorder; + namespace mrh = m::recorderHandler; + + assert(a != nullptr); + + /* Delete a boundary action wipes out everything. If is volume, remember to + restore _i and _d members in channel. */ + + if (mrh::isBoundaryEnvelopeAction(a)) { + if (a->isVolumeEnvelope()) { + ch->volume_i = 1.0; + ch->volume_d = 0.0; + } + mr::clearActions(ch->index, a->event.getStatus()); + return; + } + + const m::Action* a1 = a->prev; + const m::Action* a3 = a->next; + + /* Original status: a1--->a--->a3 + Modified status: a1-------->a3 */ + + mr::deleteAction(a); + mr::updateSiblings(a1, a1->prev, a3); + mr::updateSiblings(a3, a1, a3->next); + + cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); +} + + +/* -------------------------------------------------------------------------- */ + + +void updateEnvelopeAction(m::Channel* ch, const m::Action* a, int frame, int value) +{ + namespace mr = m::recorder; + namespace cr = c::recorder; + namespace mrh = m::recorderHandler; + + assert(a != nullptr); + + /* Update the action directly if it is a boundary one. Else, delete the + previous one and record a new action. */ + + if (mrh::isBoundaryEnvelopeAction(a)) + mr::updateEvent(a, m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value)); + else { + deleteEnvelopeAction(ch, a); + recordEnvelopeAction(ch, frame, value); + } + + cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); +} + + +/* -------------------------------------------------------------------------- */ + + +void deleteSampleAction(m::SampleChannel* ch, const m::Action* a) +{ + namespace mr = m::recorder; + namespace cr = c::recorder; + + assert(a != nullptr); + + if (a->next != nullptr) // For ChannelMode::SINGLE_PRESS combo + mr::deleteAction(a->next); + mr::deleteAction(a); + + cr::updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); +} + + +/* -------------------------------------------------------------------------- */ + + +vector getActions(const m::Channel* ch) +{ + return m::recorder::getActionsOnChannel(ch->index); +} + + +/* -------------------------------------------------------------------------- */ + + +void updateVelocity(const m::MidiChannel* ch, const m::Action* a, int value) +{ + namespace mr = m::recorder; + + m::MidiEvent event(a->event); + event.setVelocity(value); + + mr::updateEvent(a, event); +} +}}}; // giada::c::actionEditor:: diff --git a/src/glue/actionEditor.h b/src/glue/actionEditor.h new file mode 100644 index 0000000..17fd7fb --- /dev/null +++ b/src/glue/actionEditor.h @@ -0,0 +1,69 @@ +/* ----------------------------------------------------------------------------- + * + * Giada - Your Hardcore Loopmachine + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual + * + * This file is part of Giada - Your Hardcore Loopmachine. + * + * Giada - Your Hardcore Loopmachine 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. + * + * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see + * . + * + * -------------------------------------------------------------------------- */ + + +#ifndef G_GLUE_ACTION_EDITOR_H +#define G_GLUE_ACTION_EDITOR_H + + +#include +#include "../core/types.h" + + +namespace giada { +namespace m +{ +class Action; +class SampleChannel; +class MidiChannel; +} +namespace c { +namespace actionEditor +{ +std::vector getActions(const m::Channel* ch); + +/* MIDI actions. */ + +void recordMidiAction(m::MidiChannel* ch, int note, int velocity, Frame f1, Frame f2=0); +void deleteMidiAction(m::MidiChannel* ch, const m::Action* a); +void updateMidiAction(m::MidiChannel* ch, const m::Action* a, int note, int velocity, + Frame f1, Frame f2); +void updateVelocity(const m::MidiChannel* ch, const m::Action* a, int value); + +/* Sample Actions. */ + +void recordSampleAction(const m::SampleChannel* ch, int type, Frame f1, Frame f2=0); +void deleteSampleAction(m::SampleChannel* ch, const m::Action* a); +void updateSampleAction(m::SampleChannel* ch, const m::Action* a, int type, Frame f1, Frame f2=0); + +/* Envelope actions (only volume for now). */ + +void recordEnvelopeAction(m::Channel* ch, int frame, int value); +void deleteEnvelopeAction(m::Channel* ch, const m::Action* a); +void updateEnvelopeAction(m::Channel* ch, const m::Action* a, int frame, int value); +}}}; // giada::c::actionEditor:: + +#endif diff --git a/src/glue/channel.cpp b/src/glue/channel.cpp index aaa805c..74db5c9 100644 --- a/src/glue/channel.cpp +++ b/src/glue/channel.cpp @@ -56,6 +56,7 @@ #include "../core/channel.h" #include "../core/sampleChannel.h" #include "../core/midiChannel.h" +#include "../core/recorder.h" #include "../core/plugin.h" #include "../core/waveManager.h" #include "main.h" @@ -72,7 +73,7 @@ namespace giada { namespace c { namespace channel { -int loadChannel(SampleChannel* ch, const string& fname) +int loadChannel(m::SampleChannel* ch, const string& fname) { using namespace giada::m; @@ -113,9 +114,9 @@ int loadChannel(SampleChannel* ch, const string& fname) /* -------------------------------------------------------------------------- */ -Channel* addChannel(int column, ChannelType type, int size) +m::Channel* addChannel(int column, ChannelType type, int size) { - Channel* ch = m::mh::addChannel(type); + m::Channel* ch = m::mh::addChannel(type); geChannel* gch = G_MainWin->keyboard->addChannel(column, ch, size); ch->guiChannel = gch; return ch; @@ -125,13 +126,13 @@ Channel* addChannel(int column, ChannelType type, int size) /* -------------------------------------------------------------------------- */ -void deleteChannel(Channel* ch) +void deleteChannel(m::Channel* ch) { using namespace giada::m; if (!gdConfirmWin("Warning", "Delete channel: are you sure?")) return; - recorder::clearChan(ch->index); + recorder::clearChannel(ch->index); ch->hasActions = false; #ifdef WITH_VST pluginHost::freeStack(pluginHost::CHANNEL, &mixer::mutex, ch); @@ -147,7 +148,7 @@ void deleteChannel(Channel* ch) /* -------------------------------------------------------------------------- */ -void freeChannel(Channel* ch) +void freeChannel(m::Channel* ch) { if (ch->isPlaying()) { if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?")) @@ -158,7 +159,7 @@ void freeChannel(Channel* ch) return; G_MainWin->keyboard->freeChannel(ch->guiChannel); - m::recorder::clearChan(ch->index); + m::recorder::clearChannel(ch->index); ch->empty(); /* delete any related subwindow */ @@ -173,7 +174,7 @@ void freeChannel(Channel* ch) /* -------------------------------------------------------------------------- */ -void toggleArm(Channel* ch, bool gui) +void toggleArm(m::Channel* ch, bool gui) { ch->armed = !ch->armed; if (!gui) @@ -184,9 +185,9 @@ void toggleArm(Channel* ch, bool gui) /* -------------------------------------------------------------------------- */ -void toggleInputMonitor(Channel* ch) +void toggleInputMonitor(m::Channel* ch) { - SampleChannel* sch = static_cast(ch); + m::SampleChannel* sch = static_cast(ch); sch->inputMonitor = !sch->inputMonitor; } @@ -194,7 +195,7 @@ void toggleInputMonitor(Channel* ch) /* -------------------------------------------------------------------------- */ -int cloneChannel(Channel* src) +int cloneChannel(m::Channel* src) { using namespace giada::m; @@ -213,7 +214,7 @@ int cloneChannel(Channel* src) /* -------------------------------------------------------------------------- */ -void setVolume(Channel* ch, float v, bool gui, bool editor) +void setVolume(m::Channel* ch, float v, bool gui, bool editor) { ch->volume = v; @@ -239,7 +240,7 @@ void setVolume(Channel* ch, float v, bool gui, bool editor) /* -------------------------------------------------------------------------- */ -void setPitch(SampleChannel* ch, float val) +void setPitch(m::SampleChannel* ch, float val) { ch->setPitch(val); gdSampleEditor* gdEditor = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); @@ -254,7 +255,7 @@ void setPitch(SampleChannel* ch, float val) /* -------------------------------------------------------------------------- */ -void setPanning(SampleChannel* ch, float val) +void setPanning(m::SampleChannel* ch, float val) { ch->setPan(val); gdSampleEditor* gdEditor = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); @@ -269,7 +270,7 @@ void setPanning(SampleChannel* ch, float val) /* -------------------------------------------------------------------------- */ -void toggleMute(Channel* ch, bool gui) +void toggleMute(m::Channel* ch, bool gui) { ch->setMute(!ch->mute); if (!gui) { @@ -283,10 +284,9 @@ void toggleMute(Channel* ch, bool gui) /* -------------------------------------------------------------------------- */ -void toggleSolo(Channel* ch, bool gui) +void toggleSolo(m::Channel* ch, bool gui) { - ch->solo = !ch->solo; - m::mh::updateSoloCount(); + ch->setSolo(!ch->solo); if (!gui) { Fl::lock(); ch->guiChannel->solo->value(ch->solo); @@ -298,7 +298,7 @@ void toggleSolo(Channel* ch, bool gui) /* -------------------------------------------------------------------------- */ -void kill(Channel* ch) +void kill(m::Channel* ch) { ch->kill(0); // on frame 0: it's a user-generated event } @@ -307,7 +307,7 @@ void kill(Channel* ch) /* -------------------------------------------------------------------------- */ -void setBoost(SampleChannel* ch, float val) +void setBoost(m::SampleChannel* ch, float val) { ch->setBoost(val); gdSampleEditor *gdEditor = static_cast(gu_getSubwindow(G_MainWin, WID_SAMPLE_EDITOR)); @@ -322,7 +322,7 @@ void setBoost(SampleChannel* ch, float val) /* -------------------------------------------------------------------------- */ -void setName(Channel* ch, const string& name) +void setName(m::Channel* ch, const string& name) { ch->name = name; ch->guiChannel->update(); @@ -332,7 +332,7 @@ void setName(Channel* ch, const string& name) /* -------------------------------------------------------------------------- */ -void toggleReadingActions(Channel* ch, bool gui) +void toggleReadingActions(m::Channel* ch, bool gui) { /* When you call startReadingRecs with conf::treatRecsAsLoops, the @@ -353,7 +353,7 @@ void toggleReadingActions(Channel* ch, bool gui) /* -------------------------------------------------------------------------- */ -void startReadingActions(Channel* ch, bool gui) +void startReadingActions(m::Channel* ch, bool gui) { using namespace giada::m; @@ -370,7 +370,7 @@ void startReadingActions(Channel* ch, bool gui) /* -------------------------------------------------------------------------- */ -void stopReadingActions(Channel* ch, bool gui) +void stopReadingActions(m::Channel* ch, bool gui) { using namespace giada::m; @@ -384,4 +384,4 @@ void stopReadingActions(Channel* ch, bool gui) } } -}}}; // giada::c::channel:: \ No newline at end of file +}}}; // giada::c::channel:: diff --git a/src/glue/channel.h b/src/glue/channel.h index 8f6c1f9..9c18db7 100644 --- a/src/glue/channel.h +++ b/src/glue/channel.h @@ -1,4 +1,4 @@ -/* ----------------------------------------------------------------------------- + /* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * @@ -33,62 +33,65 @@ #include "../core/types.h" -class Channel; -class SampleChannel; class gdSampleEditor; namespace giada { -namespace c { +namespace m +{ +class Channel; +class SampleChannel; +} +namespace c { namespace channel { /* addChannel Adds an empty new channel to the stack. Returns the new channel. */ -Channel* addChannel(int column, ChannelType type, int size); +m::Channel* addChannel(int column, ChannelType type, int size); /* loadChannel Fills an existing channel with a wave. */ -int loadChannel(SampleChannel* ch, const std::string& fname); +int loadChannel(m::SampleChannel* ch, const std::string& fname); /* deleteChannel Removes a channel from Mixer. */ -void deleteChannel(Channel* ch); +void deleteChannel(m::Channel* ch); /* freeChannel Unloads the sample from a sample channel. */ -void freeChannel(Channel* ch); +void freeChannel(m::Channel* ch); /* cloneChannel Makes an exact copy of Channel *ch. */ -int cloneChannel(Channel* ch); +int cloneChannel(m::Channel* ch); /* toggle/set* Toggles or set several channel properties. If gui == true the signal comes from a manual interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ -void toggleArm(Channel* ch, bool gui=true); -void toggleInputMonitor(Channel* ch); -void kill(Channel* ch); -void toggleMute(Channel* ch, bool gui=true); -void toggleSolo(Channel* ch, bool gui=true); -void setVolume(Channel* ch, float v, bool gui=true, bool editor=false); -void setName(Channel* ch, const std::string& name); -void setPitch(SampleChannel* ch, float val); -void setPanning(SampleChannel* ch, float val); -void setBoost(SampleChannel* ch, float val); +void toggleArm(m::Channel* ch, bool gui=true); +void toggleInputMonitor(m::Channel* ch); +void kill(m::Channel* ch); +void toggleMute(m::Channel* ch, bool gui=true); +void toggleSolo(m::Channel* ch, bool gui=true); +void setVolume(m::Channel* ch, float v, bool gui=true, bool editor=false); +void setName(m::Channel* ch, const std::string& name); +void setPitch(m::SampleChannel* ch, float val); +void setPanning(m::SampleChannel* ch, float val); +void setBoost(m::SampleChannel* ch, float val); /* toggleReadingRecs Handles the 'R' button. If gui == true the signal comes from an user interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */ -void toggleReadingActions(Channel* ch, bool gui=true); -void startReadingActions(Channel* ch, bool gui=true); -void stopReadingActions(Channel* ch, bool gui=true); +void toggleReadingActions(m::Channel* ch, bool gui=true); +void startReadingActions(m::Channel* ch, bool gui=true); +void stopReadingActions(m::Channel* ch, bool gui=true); }}}; // giada::c::channel:: diff --git a/src/glue/io.cpp b/src/glue/io.cpp index 767473f..d2f1ea4 100644 --- a/src/glue/io.cpp +++ b/src/glue/io.cpp @@ -45,6 +45,7 @@ #include "../core/clock.h" #include "../core/sampleChannel.h" #include "../core/midiChannel.h" +#include "../core/recorderHandler.h" #include "main.h" #include "channel.h" #include "transport.h" @@ -55,10 +56,10 @@ extern gdMainWindow* G_MainWin; namespace giada { -namespace c { +namespace c { namespace io { -void keyPress(Channel* ch, bool ctrl, bool shift, int velocity) +void keyPress(m::Channel* ch, bool ctrl, bool shift, int velocity) { /* Everything occurs on frame 0 here: they are all user-generated events. */ if (ctrl) @@ -78,7 +79,7 @@ void keyPress(Channel* ch, bool ctrl, bool shift, int velocity) /* -------------------------------------------------------------------------- */ -void keyRelease(Channel* ch, bool ctrl, bool shift) +void keyRelease(m::Channel* ch, bool ctrl, bool shift) { if (!ctrl && !shift) { ch->recordStop(); @@ -92,7 +93,7 @@ void keyRelease(Channel* ch, bool ctrl, bool shift) void startStopActionRec(bool gui) { - m::recorder::active ? stopActionRec(gui) : startActionRec(gui); + m::recorder::isActive() ? stopActionRec(gui) : startActionRec(gui); } @@ -101,15 +102,13 @@ void startStopActionRec(bool gui) void startActionRec(bool gui) { - using namespace giada::m; - - if (kernelAudio::getStatus() == false) + if (m::kernelAudio::getStatus() == false) return; - recorder::active = true; + m::recorder::enable(); - if (!clock::isRunning()) - glue_startSeq(false); // update gui + if (!m::clock::isRunning()) + c::transport::startSeq(false); // update gui if (!gui) { Fl::lock(); @@ -124,13 +123,10 @@ void startActionRec(bool gui) void stopActionRec(bool gui) { - /* Stop the recorder and sort newly recorder actions. */ - - m::recorder::active = false; - m::recorder::sortActions(); + m::recorder::disable(); + m::recorderHandler::consolidate(); - for (Channel* ch : m::mixer::channels) - { + for (m::Channel* ch : m::mixer::channels) { if (ch->type == ChannelType::MIDI) continue; G_MainWin->keyboard->setChannelWithActions(static_cast(ch->guiChannel)); @@ -179,7 +175,7 @@ int startInputRec(bool gui) } if (!clock::isRunning()) - glue_startSeq(false); // update gui anyway + transport::startSeq(false); // update gui anyway Fl::lock(); if (!gui) diff --git a/src/glue/io.h b/src/glue/io.h index 799a56e..e8e8eee 100644 --- a/src/glue/io.h +++ b/src/glue/io.h @@ -36,12 +36,13 @@ #define G_GLUE_IO_H -class Channel; -class SampleChannel; -class MidiChannel; - namespace giada { -namespace c { +namespace m +{ +class Plugin; +class Channel; +} +namespace c { namespace io { /* keyPress / keyRelease @@ -49,8 +50,8 @@ Handle the key pressure, either via mouse/keyboard or MIDI. If gui is true the event comes from the main window (mouse, keyboard or MIDI), otherwise the event comes from the action recorder. */ -void keyPress (Channel* ch, bool ctrl, bool shift, int velocity); -void keyRelease(Channel* ch, bool ctrl, bool shift); +void keyPress (m::Channel* ch, bool ctrl, bool shift, int velocity); +void keyRelease(m::Channel* ch, bool ctrl, bool shift); /* start/stopActionRec Handles the action recording. If gui == true the signal comes from an user diff --git a/src/glue/main.cpp b/src/glue/main.cpp index 95d1138..1aad7a0 100644 --- a/src/glue/main.cpp +++ b/src/glue/main.cpp @@ -41,6 +41,8 @@ #include "../core/clock.h" #include "../core/kernelMidi.h" #include "../core/kernelAudio.h" +#include "../core/recorder.h" +#include "../core/recorderHandler.h" #include "../core/conf.h" #include "../core/const.h" #ifdef WITH_VST @@ -72,7 +74,7 @@ void setBpm_(float f, string s) float vPre = clock::getBpm(); clock::setBpm(f); - recorder::updateBpm(vPre, f, clock::getQuanto()); + recorderHandler::updateBpm(vPre, f, clock::getQuanto()); mixer::allocVirtualInput(clock::getFramesInLoop()); gu_refreshActionEditor(); @@ -133,29 +135,18 @@ void glue_setBpm(float f) /* -------------------------------------------------------------------------- */ -void glue_setBeats(int beats, int bars, bool expand) +void glue_setBeats(int beats, int bars) { /* Never change this stuff while recording audio */ if (mixer::recording) return; - /* Temp vars to store old data (they are necessary) */ - - int oldBeats = clock::getBeats(); - unsigned oldTotalFrames = clock::getFramesInLoop(); - clock::setBeats(beats); clock::setBars(bars); clock::updateFrameBars(); mixer::allocVirtualInput(clock::getFramesInLoop()); - /* Update recorded actions, if 'expand' required and an expansion is taking - place. */ - - if (expand && clock::getBeats() > oldBeats) - recorder::expand(oldTotalFrames, clock::getFramesInLoop()); - G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars()); gu_refreshActionEditor(); // in case the action editor is open } @@ -164,27 +155,6 @@ void glue_setBeats(int beats, int bars, bool expand) /* -------------------------------------------------------------------------- */ -void glue_rewindSeq(bool gui, bool notifyJack) -{ - mh::rewindSequencer(); - - /* FIXME - potential desync when Quantizer is enabled from this point on. - Mixer would wait, while the following calls would be made regardless of its - state. */ - -#ifdef __linux__ - if (notifyJack) - kernelAudio::jackSetPosition(0); -#endif - - if (conf::midiSync == MIDI_SYNC_CLOCK_M) - kernelMidi::send(MIDI_POSITION_PTR, 0, 0); -} - - -/* -------------------------------------------------------------------------- */ - - void glue_quantize(int val) { clock::setQuantize(val); @@ -229,7 +199,7 @@ void glue_clearAllSamples() ch->empty(); ch->guiChannel->reset(); } - recorder::init(); + recorder::clearAll(); return; } @@ -239,7 +209,7 @@ void glue_clearAllSamples() void glue_clearAllActions() { - recorder::init(); + recorder::clearAll(); for (Channel* ch : mixer::channels) ch->hasActions = false; gu_updateControls(); @@ -255,7 +225,7 @@ void glue_resetToInitState(bool resetGui, bool createColumns) mixer::close(); clock::init(conf::samplerate, conf::midiTCfps); mixer::init(clock::getFramesInLoop(), kernelAudio::getRealBufSize()); - recorder::init(); + recorder::init(&mixer::mutex); #ifdef WITH_VST pluginHost::freeAllStacks(&mixer::channels, &mixer::mutex); #endif @@ -274,15 +244,12 @@ void glue_resetToInitState(bool resetGui, bool createColumns) /* -------------------------------------------------------------------------- */ -/* never expand or shrink recordings (last param of setBeats = false): - * this is live manipulation */ - void glue_beatsMultiply() { - glue_setBeats(clock::getBeats() * 2, clock::getBars(), false); + glue_setBeats(clock::getBeats() * 2, clock::getBars()); } void glue_beatsDivide() { - glue_setBeats(clock::getBeats() / 2, clock::getBars(), false); + glue_setBeats(clock::getBeats() / 2, clock::getBars()); } diff --git a/src/glue/main.h b/src/glue/main.h index 5dc304d..ef7c794 100644 --- a/src/glue/main.h +++ b/src/glue/main.h @@ -46,7 +46,7 @@ Sets bpm value. Usually called from the Jack callback or non-UI components. */ void glue_setBpm(float v); -void glue_setBeats(int beats, int bars, bool expand); +void glue_setBeats(int beats, int bars); void glue_quantize(int val); void glue_setOutVol(float v, bool gui=true); void glue_setInVol(float v, bool gui=true); diff --git a/src/glue/plugin.h b/src/glue/plugin.h index d581fe2..4b55afd 100644 --- a/src/glue/plugin.h +++ b/src/glue/plugin.h @@ -35,19 +35,20 @@ #ifdef WITH_VST +namespace giada { +namespace m +{ class Plugin; class Channel; - - -namespace giada { -namespace c { +} +namespace c { namespace plugin { -Plugin* addPlugin(Channel* ch, int index, int stackType); -void swapPlugins(Channel* ch, int indexP1, int indexP2, int stackType); -void freePlugin(Channel* ch, int index, int stackType); -void setParameter(Plugin* p, int index, float value, bool gui=true); -void setProgram(Plugin* p, int index); +m::Plugin* addPlugin(m::Channel* ch, int index, int stackType); +void swapPlugins(m::Channel* ch, int indexP1, int indexP2, int stackType); +void freePlugin(m::Channel* ch, int index, int stackType); +void setParameter(m::Plugin* p, int index, float value, bool gui=true); +void setProgram(m::Plugin* p, int index); /* setPluginPathCb Callback attached to the DirBrowser for adding new Plug-in search paths in the diff --git a/src/glue/recorder.cpp b/src/glue/recorder.cpp index 47e5b56..2b166cb 100644 --- a/src/glue/recorder.cpp +++ b/src/glue/recorder.cpp @@ -33,7 +33,9 @@ #include "../core/clock.h" #include "../core/kernelMidi.h" #include "../core/channel.h" +#include "../core/recorderHandler.h" #include "../core/recorder.h" +#include "../core/action.h" #include "../core/mixer.h" #include "../core/sampleChannel.h" #include "../core/midiChannel.h" @@ -47,34 +49,15 @@ using namespace giada; namespace giada { -namespace c { +namespace c { namespace recorder { -namespace -{ -void updateChannel(geChannel* gch, bool refreshActionEditor=true) -{ - gch->ch->hasActions = m::recorder::hasActions(gch->ch->index); - if (gch->ch->type == ChannelType::SAMPLE) { - geSampleChannel* gsch = static_cast(gch); - gsch->ch->hasActions ? gsch->showActionButton() : gsch->hideActionButton(); - } - if (refreshActionEditor) - gu_refreshActionEditor(); // refresh a.editor window, it could be open -} -}; // {anonymous} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - - void clearAllActions(geChannel* gch) { if (!gdConfirmWin("Warning", "Clear all actions: are you sure?")) return; - m::recorder::clearChan(gch->ch->index); + gch->ch->kill(0); + m::recorder::clearChannel(gch->ch->index); updateChannel(gch); } @@ -86,7 +69,7 @@ void clearVolumeActions(geChannel* gch) { if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?")) return; - m::recorder::clearAction(gch->ch->index, G_ACTION_VOLUME); + m::recorder::clearActions(gch->ch->index, m::MidiEvent::ENVELOPE); updateChannel(gch); } @@ -98,7 +81,10 @@ void clearStartStopActions(geChannel* gch) { if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?")) return; - m::recorder::clearAction(gch->ch->index, G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL); + gch->ch->kill(0); + m::recorder::clearActions(gch->ch->index, m::MidiEvent::NOTE_ON); + m::recorder::clearActions(gch->ch->index, m::MidiEvent::NOTE_OFF); + m::recorder::clearActions(gch->ch->index, m::MidiEvent::NOTE_KILL); updateChannel(gch); } @@ -106,305 +92,15 @@ void clearStartStopActions(geChannel* gch) /* -------------------------------------------------------------------------- */ -bool midiActionCanFit(int chan, int note, int frame_a, int frame_b) -{ - namespace mr = m::recorder; - - /* TODO - This is insane, to say the least. Let's wait for recorder refactoring... */ - - vector comps = getMidiActions(chan); - for (mr::Composite c : comps) - if (frame_b >= c.a1.frame && c.a2.frame >= frame_a && m::MidiEvent(c.a1.iValue).getNote() == note) - return false; - return true; -} - - -bool sampleActionCanFit(const SampleChannel* ch, int frame_a, int frame_b) -{ - namespace mr = m::recorder; - - /* TODO - Even more insanity... Let's wait for recorder refactoring... */ - - vector comps = getSampleActions(ch); - for (mr::Composite c : comps) - if (frame_b >= c.a1.frame && c.a2.frame >= frame_a) - return false; - return true; -} - - -/* -------------------------------------------------------------------------- */ - - -void recordMidiAction(int chan, int note, int velocity, int frame_a, int frame_b) -{ - if (frame_b == 0) - frame_b = frame_a + G_DEFAULT_ACTION_SIZE; - - /* Avoid frame overflow. */ - - int overflow = frame_b - (m::clock::getFramesInLoop()); - if (overflow > 0) { - frame_b -= overflow; - frame_a -= overflow; - } - - /* Prepare MIDI events. Due to some nasty restrictions on the ancient Recorder, - checks for overlapping are done by the caller. TODO ... */ - - m::MidiEvent event_a = m::MidiEvent(m::MidiEvent::NOTE_ON, note, velocity); - m::MidiEvent event_b = m::MidiEvent(m::MidiEvent::NOTE_OFF, note, velocity); - - m::recorder::rec(chan, G_ACTION_MIDI, frame_a, event_a.getRaw()); - m::recorder::rec(chan, G_ACTION_MIDI, frame_b, event_b.getRaw()); -} - - -/* -------------------------------------------------------------------------- */ - - -void deleteMidiAction(MidiChannel* ch, m::recorder::action a1, m::recorder::action a2) -{ - m::recorder::deleteAction(ch->index, a1.frame, G_ACTION_MIDI, true, - &m::mixer::mutex, a1.iValue, 0.0); - - /* If action 1 is not orphaned, send a note-off first in case we are deleting - it in a middle of a key_on/key_off sequence. Conversely, orphaned actions - should not play, so no need to fire the note-off. */ - - if (a2.frame != -1) { - ch->sendMidi(a2.iValue); - m::recorder::deleteAction(ch->index, a2.frame, G_ACTION_MIDI, true, - &m::mixer::mutex, a2.iValue, 0.0); - } - - ch->hasActions = m::recorder::hasActions(ch->index); -} - -/* -------------------------------------------------------------------------- */ - - -void recordSampleAction(SampleChannel* ch, int type, int frame_a, int frame_b) -{ - if (ch->mode == ChannelMode::SINGLE_PRESS) { - m::recorder::rec(ch->index, G_ACTION_KEYPRESS, frame_a); - m::recorder::rec(ch->index, G_ACTION_KEYREL, frame_b == 0 ? frame_a + G_DEFAULT_ACTION_SIZE : frame_b); - } - else - m::recorder::rec(ch->index, type, frame_a); - - updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); -} - - -/* -------------------------------------------------------------------------- */ - - -void recordEnvelopeAction(Channel* ch, int type, int frame, float fValue) +void updateChannel(geChannel* gch, bool refreshActionEditor) { - namespace mr = m::recorder; - - if (!mr::hasActions(ch->index, type)) { // First action ever? Add actions at boundaries. - mr::rec(ch->index, type, 0, 0, 1.0); - mr::rec(ch->index, type, m::clock::getFramesInLoop() - 1, 0, 1.0); - } - mr::rec(ch->index, type, frame, 0, fValue); - - updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); -} - - -/* -------------------------------------------------------------------------- */ - - -void deleteEnvelopeAction(Channel* ch, m::recorder::action a, bool moved) -{ - namespace mr = m::recorder; - - /* Deleting first or last action: clear everything. Otherwise delete the - selected action only. */ - - if (!moved && (a.frame == 0 || a.frame == m::clock::getFramesInLoop() - 1)) - mr::clearAction(ch->index, a.type); - else - mr::deleteAction(ch->index, a.frame, a.type, false, &m::mixer::mutex); - - updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); -} - - -/* -------------------------------------------------------------------------- */ - - -void deleteSampleAction(SampleChannel* ch, m::recorder::action a1, - m::recorder::action a2) -{ - namespace mr = m::recorder; - - /* if SINGLE_PRESS delete both the keypress and the keyrelease pair. */ - - if (ch->mode == ChannelMode::SINGLE_PRESS) { - mr::deleteAction(ch->index, a1.frame, G_ACTION_KEYPRESS, false, &m::mixer::mutex); - mr::deleteAction(ch->index, a2.frame, G_ACTION_KEYREL, false, &m::mixer::mutex); - } - else - mr::deleteAction(ch->index, a1.frame, a1.type, false, &m::mixer::mutex); - - updateChannel(ch->guiChannel, /*refreshActionEditor=*/false); -} - - -/* -------------------------------------------------------------------------- */ - - -vector getSampleActions(const SampleChannel* ch) -{ - namespace mr = m::recorder; - - vector out; - - mr::sortActions(); - mr::forEachAction([&](const mr::action* a1) - { - /* Exclude: - - actions beyond clock::getFramesInLoop(); - - actions that don't belong to channel ch; - - actions != G_ACTION_KEYPRESS, G_ACTION_KEYREL or G_ACTION_KILL; - - G_ACTION_KEYREL actions in a SINGLE_PRESS context. */ - - if (a1->frame > m::clock::getFramesInLoop() || - a1->chan != ch->index || - a1->type & ~(G_ACTION_KEYPRESS | G_ACTION_KEYREL | G_ACTION_KILL) || - (ch->mode == ChannelMode::SINGLE_PRESS && a1->type == G_ACTION_KEYREL)) - return; - - mr::Composite cmp; - cmp.a1 = *a1; - cmp.a2.frame = -1; - - /* If SINGLE_PRESS mode and the current action is G_ACTION_KEYPRESS, let's - fetch the corresponding G_ACTION_KEYREL. */ - - if (ch->mode == ChannelMode::SINGLE_PRESS && a1->type == G_ACTION_KEYPRESS) { - m::recorder::action* a2 = nullptr; - mr::getNextAction(ch->index, G_ACTION_KEYREL, a1->frame, &a2); - if (a2 != nullptr) - cmp.a2 = *a2; - } - - out.push_back(cmp); - }); - - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -vector getEnvelopeActions(const Channel* ch, int type) -{ - namespace mr = m::recorder; - - vector out; - - mr::sortActions(); - mr::forEachAction([&](const mr::action* a) - { - /* Exclude: - - actions beyond clock::getFramesInLoop(); - - actions that don't belong to channel ch; - - actions with wrong type. */ - - if (a->frame > m::clock::getFramesInLoop() || - a->chan != ch->index || - a->type != type) - return; - - out.push_back(*a); - }); - - return out; -} - - -/* -------------------------------------------------------------------------- */ - - -void setVelocity(const Channel* ch, m::recorder::action a, int value) -{ - /* TODO - this is super ugly: delete the action and add a new one with the - modified values. This shit will go away as soon as we'll refactor m::recorder - for good. */ - - m::MidiEvent event = m::MidiEvent(a.iValue); - event.setVelocity(value); - - m::recorder::deleteAction(ch->index, a.frame, G_ACTION_MIDI, true, - &m::mixer::mutex, a.iValue, 0.0); - m::recorder::rec(ch->index, G_ACTION_MIDI, a.frame, event.getRaw()); -} - - -/* -------------------------------------------------------------------------- */ - - -vector getMidiActions(int chan) -{ - vector out; - - m::recorder::sortActions(); - - for (unsigned i=0; i m::clock::getFramesInLoop()) - continue; - - for (unsigned j=0; jiValue); - - /* Skip action if: - - does not belong to this channel - - is not a MIDI action (we only want MIDI things here) - - is not a MIDI Note On type. We don't want any other kind of action here */ - - if (a1->chan != chan || a1->type != G_ACTION_MIDI || - a1midi.getStatus() != m::MidiEvent::NOTE_ON) - continue; - - /* Prepare the composite action. Action 1 exists for sure, so fill it up - right away. */ - - m::recorder::Composite cmp; - cmp.a1 = *a1; - - /* Search for the next action. Must have: same channel, G_ACTION_MIDI, - greater than a1->frame and with MIDI properties of note_off (0x80), same - note of a1 and random velocity: we don't care about it (and so we mask it - with 0x0000FF00). */ - - m::recorder::getNextAction(chan, G_ACTION_MIDI, a1->frame, &a2, - m::MidiEvent(m::MidiEvent::NOTE_OFF, a1midi.getNote(), 0x0).getRaw(), - 0x0000FF00); - - /* If action 2 has been found, add it to the composite duo. Otherwise - set the action 2 frame to -1: it should be intended as "orphaned". */ - - if (a2 != nullptr) - cmp.a2 = *a2; - else - cmp.a2.frame = -1; - - out.push_back(cmp); - } + gch->ch->hasActions = m::recorder::hasActions(gch->ch->index); + if (gch->ch->type == ChannelType::SAMPLE) { + geSampleChannel* gsch = static_cast(gch); + gsch->ch->hasActions ? gsch->showActionButton() : gsch->hideActionButton(); } - - return out; + if (refreshActionEditor) + gu_refreshActionEditor(); // refresh a.editor window, it could be open } }}} // giada::c::recorder:: \ No newline at end of file diff --git a/src/glue/recorder.h b/src/glue/recorder.h index 1bf4a40..f28a3cc 100644 --- a/src/glue/recorder.h +++ b/src/glue/recorder.h @@ -29,65 +29,17 @@ #define G_GLUE_RECORDER_H -#include -#include "../core/recorder.h" - - -class SampleChannel; -class MidiChannel; class geChannel; namespace giada { -namespace c { +namespace c { namespace recorder { void clearAllActions(geChannel* gch); void clearVolumeActions(geChannel* gch); void clearStartStopActions(geChannel* gch); - - - -/* MOVE ALL THESE FUNCTIONS TO c::actionEditor*/ - - -bool midiActionCanFit(int chan, int note, int frame_a, int frame_b); -bool sampleActionCanFit(const SampleChannel* ch, int frame_a, int frame_b); - -/* recordMidiAction -Records a new MIDI action at frame_a. If frame_b == 0, uses the default action -size. This function is designed for the Piano Roll (not for live recording). */ - -void recordMidiAction(int chan, int note, int velocity, int frame_a, int frame_b=0); - -void recordEnvelopeAction(Channel* ch, int type, int frame, float fValue); - -void recordSampleAction(SampleChannel* ch, int type, int frame_a, int frame_b=0); - -void setVelocity(const Channel* ch, m::recorder::action a, int value); - -/* getMidiActions -Returns a list of Composite actions, ready to be displayed in a MIDI note -editor as pairs of NoteOn+NoteOff. */ - -std::vector getMidiActions(int channel); - -std::vector getEnvelopeActions(const Channel* ch, int type); - -/* getSampleActions -Returns a list of Composite actions, ready to be displayed in a Sample Action -Editor. If actions are not keypress+keyrelease combos, the second action in -the Composite struct if left empty (with action2.frame = -1). */ - -std::vector getSampleActions(const SampleChannel* ch); - -void deleteMidiAction(MidiChannel* ch, m::recorder::action a1, m::recorder::action a2); - -void deleteSampleAction(SampleChannel* ch, m::recorder::action a1, - m::recorder::action a2); - -void deleteEnvelopeAction(Channel* ch, m::recorder::action a, bool moved); - +void updateChannel(geChannel* gch, bool refreshActionEditor=true); }}} // giada::c::recorder:: #endif diff --git a/src/glue/sampleEditor.cpp b/src/glue/sampleEditor.cpp index 1793a92..f86c67b 100644 --- a/src/glue/sampleEditor.cpp +++ b/src/glue/sampleEditor.cpp @@ -51,7 +51,7 @@ #include "sampleEditor.h" -extern gdMainWindow *G_MainWin; +extern gdMainWindow* G_MainWin; namespace giada { @@ -81,7 +81,7 @@ gdSampleEditor* getSampleEditorWindow() /* -------------------------------------------------------------------------- */ -void setBeginEnd(SampleChannel* ch, int b, int e) +void setBeginEnd(m::SampleChannel* ch, int b, int e) { ch->setBegin(b); ch->setEnd(e); @@ -99,7 +99,7 @@ void setBeginEnd(SampleChannel* ch, int b, int e) /* -------------------------------------------------------------------------- */ -void cut(SampleChannel* ch, int a, int b) +void cut(m::SampleChannel* ch, int a, int b) { copy(ch, a, b); if (!m::wfx::cut(*ch->wave, a, b)) { @@ -117,7 +117,7 @@ void cut(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ -void copy(SampleChannel* ch, int a, int b) +void copy(m::SampleChannel* ch, int a, int b) { if (m_waveBuffer != nullptr) delete m_waveBuffer; @@ -128,7 +128,7 @@ void copy(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ -void paste(SampleChannel* ch, int a) +void paste(m::SampleChannel* ch, int a) { if (!isWaveBufferFull()) { gu_log("[sampleEditor::paste] Buffer is empty, nothing to paste\n"); @@ -155,7 +155,7 @@ void paste(SampleChannel* ch, int a) /* -------------------------------------------------------------------------- */ -void silence(SampleChannel* ch, int a, int b) +void silence(m::SampleChannel* ch, int a, int b) { m::wfx::silence(*ch->wave, a, b); gdSampleEditor* gdEditor = getSampleEditorWindow(); @@ -166,7 +166,7 @@ void silence(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ -void fade(SampleChannel* ch, int a, int b, int type) +void fade(m::SampleChannel* ch, int a, int b, int type) { m::wfx::fade(*ch->wave, a, b, type); gdSampleEditor* gdEditor = getSampleEditorWindow(); @@ -177,7 +177,7 @@ void fade(SampleChannel* ch, int a, int b, int type) /* -------------------------------------------------------------------------- */ -void smoothEdges(SampleChannel* ch, int a, int b) +void smoothEdges(m::SampleChannel* ch, int a, int b) { m::wfx::smooth(*ch->wave, a, b); gdSampleEditor* gdEditor = getSampleEditorWindow(); @@ -188,7 +188,7 @@ void smoothEdges(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ -void reverse(SampleChannel* ch, int a, int b) +void reverse(m::SampleChannel* ch, int a, int b) { m::wfx::reverse(*ch->wave, a, b); gdSampleEditor* gdEditor = getSampleEditorWindow(); @@ -199,7 +199,7 @@ void reverse(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ -void normalizeHard(SampleChannel* ch, int a, int b) +void normalizeHard(m::SampleChannel* ch, int a, int b) { m::wfx::normalizeHard(*ch->wave, a, b); gdSampleEditor* gdEditor = getSampleEditorWindow(); @@ -210,7 +210,7 @@ void normalizeHard(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ -void trim(SampleChannel* ch, int a, int b) +void trim(m::SampleChannel* ch, int a, int b) { if (!m::wfx::trim(*ch->wave, a, b)) { gdAlert("Unable to trim the sample!"); @@ -227,7 +227,7 @@ void trim(SampleChannel* ch, int a, int b) /* -------------------------------------------------------------------------- */ -void setPlayHead(SampleChannel* ch, int f) +void setPlayHead(m::SampleChannel* ch, int f) { ch->trackerPreview = f; gdSampleEditor* gdEditor = getSampleEditorWindow(); @@ -238,7 +238,7 @@ void setPlayHead(SampleChannel* ch, int f) /* -------------------------------------------------------------------------- */ -void setPreview(SampleChannel* ch, PreviewMode mode) +void setPreview(m::SampleChannel* ch, PreviewMode mode) { ch->previewMode = mode; gdSampleEditor* gdEditor = getSampleEditorWindow(); @@ -249,7 +249,7 @@ void setPreview(SampleChannel* ch, PreviewMode mode) /* -------------------------------------------------------------------------- */ -void rewindPreview(SampleChannel* ch) +void rewindPreview(m::SampleChannel* ch) { geWaveform* waveform = getSampleEditorWindow()->waveTools->waveform; if (waveform->isSelected() && ch->trackerPreview != waveform->getSelectionA()) @@ -262,9 +262,9 @@ void rewindPreview(SampleChannel* ch) /* -------------------------------------------------------------------------- */ -void toNewChannel(SampleChannel* ch, int a, int b) +void toNewChannel(m::SampleChannel* ch, int a, int b) { - SampleChannel* newCh = static_cast(c::channel::addChannel( + m::SampleChannel* newCh = static_cast(c::channel::addChannel( ch->guiChannel->getColumnIndex(), ChannelType::SAMPLE, G_GUI_CHANNEL_H_1)); Wave* wave = nullptr; @@ -287,7 +287,7 @@ bool isWaveBufferFull() /* -------------------------------------------------------------------------- */ -void shift(SampleChannel* ch, int offset) +void shift(m::SampleChannel* ch, int offset) { m::wfx::shift(*ch->wave, offset - ch->shift); ch->shift = offset; diff --git a/src/glue/sampleEditor.h b/src/glue/sampleEditor.h index 4933100..3cd4bc8 100644 --- a/src/glue/sampleEditor.h +++ b/src/glue/sampleEditor.h @@ -32,50 +32,53 @@ #include "../core/types.h" -class SampleChannel; class geWaveform; namespace giada { -namespace c { +namespace m +{ +class SampleChannel; +} +namespace c { namespace sampleEditor { /* setBeginEnd Sets start/end points in the sample editor. */ -void setBeginEnd(SampleChannel* ch, int b, int e); +void setBeginEnd(m::SampleChannel* ch, int b, int e); -void cut(SampleChannel* ch, int a, int b); -void copy(SampleChannel* ch, int a, int b); +void cut(m::SampleChannel* ch, int a, int b); +void copy(m::SampleChannel* ch, int a, int b); /* paste Pastes what's defined in m_copyBuffer into channel 'ch' at point 'a'. If m_copyBuffer is empty, does nothing. */ -void paste(SampleChannel* ch, int a); +void paste(m::SampleChannel* ch, int a); -void trim(SampleChannel* ch, int a, int b); -void reverse(SampleChannel* ch, int a, int b); -void normalizeHard(SampleChannel* ch, int a, int b); -void silence(SampleChannel* ch, int a, int b); -void fade(SampleChannel* ch, int a, int b, int type); -void smoothEdges(SampleChannel* ch, int a, int b); -void shift(SampleChannel* ch, int offset); +void trim(m::SampleChannel* ch, int a, int b); +void reverse(m::SampleChannel* ch, int a, int b); +void normalizeHard(m::SampleChannel* ch, int a, int b); +void silence(m::SampleChannel* ch, int a, int b); +void fade(m::SampleChannel* ch, int a, int b, int type); +void smoothEdges(m::SampleChannel* ch, int a, int b); +void shift(m::SampleChannel* ch, int offset); bool isWaveBufferFull(); /* setPlayHead Changes playhead's position. Used in preview. */ -void setPlayHead(SampleChannel* ch, int f); +void setPlayHead(m::SampleChannel* ch, int f); -void setPreview(SampleChannel* ch, PreviewMode mode); -void rewindPreview(SampleChannel* ch); +void setPreview(m::SampleChannel* ch, PreviewMode mode); +void rewindPreview(m::SampleChannel* ch); /* toNewChannel Copies the selected range into a new sample channel. */ -void toNewChannel(SampleChannel* ch, int a, int b); +void toNewChannel(m::SampleChannel* ch, int a, int b); }}}; // giada::c::sampleEditor:: #endif diff --git a/src/glue/storage.cpp b/src/glue/storage.cpp index 2a79efc..e3779e2 100644 --- a/src/glue/storage.cpp +++ b/src/glue/storage.cpp @@ -28,6 +28,7 @@ #include "../core/mixer.h" #include "../core/mixerHandler.h" #include "../core/channel.h" +#include "../core/recorderHandler.h" #include "../core/pluginHost.h" #include "../core/plugin.h" #include "../core/conf.h" @@ -63,7 +64,7 @@ using namespace giada; #ifdef WITH_VST -static void glue_fillPatchGlobalsPlugins__(vector * host, vector* patch) +static void glue_fillPatchGlobalsPlugins__(vector * host, vector* patch) { using namespace giada::m; @@ -90,14 +91,13 @@ static void glue_fillPatchColumns__() using namespace giada::m; for (unsigned i=0; ikeyboard->getTotalColumns(); i++) { - geColumn *gCol = G_MainWin->keyboard->getColumn(i); + geColumn* gCol = G_MainWin->keyboard->getColumn(i); patch::column_t pCol; pCol.index = gCol->getIndex(); pCol.width = gCol->w(); for (int k=0; kcountChannels(); k++) { - Channel *colChannel = gCol->getChannel(k); - for (unsigned j=0; jgetChannel(k); + for (const Channel* mixerChannel : mixer::channels) { if (colChannel == mixerChannel) { pCol.channels.push_back(mixerChannel->index); break; @@ -185,7 +185,7 @@ static string glue_makeSamplePath__(const string& base, const Wave* w, int k) } -static string glue_makeUniqueSamplePath__(const string& base, const SampleChannel* ch) +static string glue_makeUniqueSamplePath__(const string& base, const m::SampleChannel* ch) { using namespace giada::m; @@ -261,7 +261,7 @@ void glue_loadPatch(void* data) return; } - /* Close all other windows. This prevents segfault if plugin windows GUIs are + /* Close all other windows. This prevents problems if plugin windows are open. */ gu_closeAllSubwindows(); @@ -274,32 +274,27 @@ void glue_loadPatch(void* data) browser->setStatusBar(0.1f); /* Add common stuff, columns and channels. Also increment the progress bar by - 0.8 / total_channels steps. */ + 0.8 / total_channels steps. */ float steps = 0.8 / patch::channels.size(); for (const patch::column_t& col : patch::columns) { G_MainWin->keyboard->addColumn(col.width); - unsigned k = 0; for (const patch::channel_t& pch : patch::channels) { if (pch.column == col.index) { Channel* ch = c::channel::addChannel(pch.column, static_cast(pch.type), pch.size); - ch->readPatch(basePath, k); + ch->readPatch(basePath, pch); } browser->setStatusBar(steps); - k++; } } - /* Prepare Mixer. */ + /* Prepare Mixer and Recorder. The latter has to recompute the actions' + positions if the current samplerate != patch samplerate.*/ mh::updateSoloCount(); mh::readPatch(); - - /* Let recorder recompute the actions' positions if the current - samplerate != patch samplerate. */ - - recorder::updateSamplerate(conf::samplerate, patch::samplerate); + recorderHandler::updateSamplerate(conf::samplerate, patch::samplerate); /* Save patchPath by taking the last dir of the broswer, in order to reuse it the next time. */ @@ -394,7 +389,7 @@ void glue_loadSample(void* data) if (fullPath.empty()) return; - int res = c::channel::loadChannel(static_cast(browser->getChannel()), + int res = c::channel::loadChannel(static_cast(browser->getChannel()), fullPath); if (res == G_RES_OK) { diff --git a/src/glue/storage.h b/src/glue/storage.h index 9a3e568..35f15d2 100644 --- a/src/glue/storage.h +++ b/src/glue/storage.h @@ -29,11 +29,11 @@ #define G_GLUE_STORAGE_H -void glue_loadPatch (void *data); -void glue_savePatch (void *data); -void glue_saveProject(void *data); -void glue_saveSample (void *data); -void glue_loadSample (void *data); +void glue_loadPatch (void* data); +void glue_savePatch (void* data); +void glue_saveProject(void* data); +void glue_saveSample (void* data); +void glue_loadSample (void* data); #endif diff --git a/src/glue/transport.cpp b/src/glue/transport.cpp index f735f7e..4e958dc 100644 --- a/src/glue/transport.cpp +++ b/src/glue/transport.cpp @@ -29,29 +29,36 @@ #include "../gui/elems/mainWindow/mainTransport.h" #include "../gui/dialogs/gd_mainWindow.h" #include "../core/clock.h" +#include "../core/conf.h" +#include "../core/const.h" #include "../core/kernelAudio.h" +#include "../core/kernelMidi.h" #include "../core/mixerHandler.h" #include "../core/mixer.h" #include "../core/recorder.h" #include "transport.h" -extern gdMainWindow *G_MainWin; - +extern gdMainWindow* G_MainWin; + using namespace giada::m; -void glue_startStopSeq(bool gui) +namespace giada { +namespace c { +namespace transport +{ +void startStopSeq(bool gui) { - clock::isRunning() ? glue_stopSeq(gui) : glue_startSeq(gui); + clock::isRunning() ? stopSeq(gui) : startSeq(gui); } /* -------------------------------------------------------------------------- */ -void glue_startSeq(bool gui) +void startSeq(bool gui) { clock::start(); @@ -60,9 +67,9 @@ void glue_startSeq(bool gui) #endif if (!gui) { - Fl::lock(); - G_MainWin->mainTransport->updatePlay(1); - Fl::unlock(); + Fl::lock(); + G_MainWin->mainTransport->updatePlay(1); + Fl::unlock(); } } @@ -70,7 +77,7 @@ void glue_startSeq(bool gui) /* -------------------------------------------------------------------------- */ -void glue_stopSeq(bool gui) +void stopSeq(bool gui) { mh::stopSequencer(); @@ -78,38 +85,58 @@ void glue_stopSeq(bool gui) kernelAudio::jackStop(); #endif - /* what to do if we stop the sequencer and some action recs are active? - * Deactivate the button and delete any 'rec on' status */ + /* What to do if we stop the sequencer and some action recs are active? + Deactivate the button and delete any 'rec on' status. */ - if (recorder::active) { - recorder::active = false; - Fl::lock(); - G_MainWin->mainTransport->updateRecAction(0); - Fl::unlock(); + if (recorder::isActive()) { + recorder::disable(); + Fl::lock(); + G_MainWin->mainTransport->updateRecAction(0); + Fl::unlock(); } - /* if input recs are active (who knows why) we must deactivate them. - * One might stop the sequencer while an input rec is running. */ + /* If input recs are active (who knows why) we must deactivate them. One + might stop the sequencer while an input rec is running. */ if (mixer::recording) { mh::stopInputRec(); - Fl::lock(); - G_MainWin->mainTransport->updateRecInput(0); - Fl::unlock(); + Fl::lock(); + G_MainWin->mainTransport->updateRecInput(0); + Fl::unlock(); } if (!gui) { - Fl::lock(); - G_MainWin->mainTransport->updatePlay(0); - Fl::unlock(); - } + Fl::lock(); + G_MainWin->mainTransport->updatePlay(0); + Fl::unlock(); + } } /* -------------------------------------------------------------------------- */ -void glue_startStopMetronome(bool gui) +void rewindSeq(bool gui, bool notifyJack) +{ + mh::rewindSequencer(); + + /* FIXME - potential desync when Quantizer is enabled from this point on. + Mixer would wait, while the following calls would be made regardless of its + state. */ + +#ifdef __linux__ + if (notifyJack) + kernelAudio::jackSetPosition(0); +#endif + + if (conf::midiSync == MIDI_SYNC_CLOCK_M) + kernelMidi::send(MIDI_POSITION_PTR, 0, 0); +} + +/* -------------------------------------------------------------------------- */ + + +void startStopMetronome(bool gui) { mixer::metronome = !mixer::metronome; if (!gui) { @@ -118,3 +145,5 @@ void glue_startStopMetronome(bool gui) Fl::unlock(); } } + +}}} // giada::c::transport:: \ No newline at end of file diff --git a/src/glue/transport.h b/src/glue/transport.h index 3b10ead..3be492f 100644 --- a/src/glue/transport.h +++ b/src/glue/transport.h @@ -29,15 +29,16 @@ #define G_GLUE_TRANSPORT_H -/* start, stop, rewind sequencer -If gui == true the signal comes from an user interaction on the GUI, -otherwise it's a MIDI/Jack/external signal. */ - -void glue_startStopSeq(bool gui=true); -void glue_startSeq(bool gui=true); -void glue_stopSeq(bool gui=true); -void glue_rewindSeq(bool gui=true, bool notifyJack=true); -void glue_startStopMetronome(bool gui=true); +namespace giada { +namespace c { +namespace transport +{ +void startStopSeq(bool gui=true); +void startSeq(bool gui=true); +void stopSeq(bool gui=true); +void rewindSeq(bool gui=true, bool notifyJack=true); +void startStopMetronome(bool gui=true); +}}} // giada::c::transport:: #endif diff --git a/src/gui/dialogs/actionEditor/baseActionEditor.cpp b/src/gui/dialogs/actionEditor/baseActionEditor.cpp index b85a30e..c7fc692 100644 --- a/src/gui/dialogs/actionEditor/baseActionEditor.cpp +++ b/src/gui/dialogs/actionEditor/baseActionEditor.cpp @@ -47,7 +47,7 @@ using std::string; namespace giada { namespace v { -gdBaseActionEditor::gdBaseActionEditor(Channel* ch) +gdBaseActionEditor::gdBaseActionEditor(m::Channel* ch) : gdWindow (640, 284), ch (ch), ratio (G_DEFAULT_ZOOM_RATIO) @@ -86,6 +86,15 @@ void gdBaseActionEditor::cb_zoomOut(Fl_Widget *w, void *p) { ((gdBaseActionEdito /* -------------------------------------------------------------------------- */ +const std::vector& gdBaseActionEditor::getActions() +{ + return m_actions; +} + + +/* -------------------------------------------------------------------------- */ + + void gdBaseActionEditor::computeWidth() { fullWidth = frameToPixel(m::clock::getFramesInSeq()); @@ -113,14 +122,17 @@ Frame gdBaseActionEditor::pixelToFrame(Pixel p, bool snap) const void gdBaseActionEditor::zoomIn() { - if (ratio / 2 > MIN_RATIO) { - ratio /= 2; + float ratioPrev = ratio; + + ratio /= 2; + if (ratio < MIN_RATIO) + ratio = MIN_RATIO; + + if (ratioPrev != ratio) { rebuild(); centerViewportIn(); redraw(); } - else - ratio = MIN_RATIO; } @@ -129,14 +141,17 @@ void gdBaseActionEditor::zoomIn() void gdBaseActionEditor::zoomOut() { - if (ratio * 2 < MAX_RATIO) { - ratio *= 2; + float ratioPrev = ratio; + + ratio *= 2; + if (ratio > MAX_RATIO) + ratio = MAX_RATIO; + + if (ratioPrev != ratio) { rebuild(); centerViewportOut(); redraw(); } - else - ratio = MAX_RATIO; } @@ -164,13 +179,13 @@ void gdBaseActionEditor::centerViewportOut() int gdBaseActionEditor::getActionType() const { if (actionType->value() == 0) - return G_ACTION_KEYPRESS; + return m::MidiEvent::NOTE_ON; else if (actionType->value() == 1) - return G_ACTION_KEYREL; + return m::MidiEvent::NOTE_OFF; else if (actionType->value() == 2) - return G_ACTION_KILL; + return m::MidiEvent::NOTE_KILL; assert(false); return -1; diff --git a/src/gui/dialogs/actionEditor/baseActionEditor.h b/src/gui/dialogs/actionEditor/baseActionEditor.h index 1b35d79..da7ce46 100644 --- a/src/gui/dialogs/actionEditor/baseActionEditor.h +++ b/src/gui/dialogs/actionEditor/baseActionEditor.h @@ -33,13 +33,17 @@ #include "../window.h" -class Channel; class geChoice; class geButton; class geScroll; namespace giada { +namespace m +{ +class Channel; +class Action; +} namespace v { class geGridTool; @@ -54,7 +58,9 @@ protected: static constexpr float MIN_RATIO = 25.0f; static constexpr float MAX_RATIO = 40000.0f; - gdBaseActionEditor(Channel* ch); + std::vector m_actions; + + gdBaseActionEditor(m::Channel* ch); void zoomIn(); void zoomOut(); @@ -87,16 +93,18 @@ public: Frame pixelToFrame(Pixel p, bool snap=true) const; int getActionType() const; + const std::vector& getActions(); + geChoice* actionType; geGridTool* gridTool; geButton* zoomInBtn; geButton* zoomOutBtn; geScroll* viewport; // widget container - Channel* ch; + m::Channel* ch; float ratio; - Pixel fullWidth; // Full widgets width, i.e. scaled-down full sequencer + Pixel fullWidth; // Full widgets width, i.e. scaled-down full sequencer Pixel loopWidth; // Loop width, i.e. scaled-down sequencer range }; }} // giada::v:: diff --git a/src/gui/dialogs/actionEditor/midiActionEditor.cpp b/src/gui/dialogs/actionEditor/midiActionEditor.cpp index ab65cef..28bd61a 100644 --- a/src/gui/dialogs/actionEditor/midiActionEditor.cpp +++ b/src/gui/dialogs/actionEditor/midiActionEditor.cpp @@ -28,6 +28,7 @@ #include #include "../../../core/graphics.h" #include "../../../core/midiChannel.h" +#include "../../../glue/actionEditor.h" #include "../../elems/basics/scroll.h" #include "../../elems/basics/button.h" #include "../../elems/basics/resizerBar.h" @@ -45,7 +46,7 @@ using std::string; namespace giada { namespace v { -gdMidiActionEditor::gdMidiActionEditor(MidiChannel* ch) +gdMidiActionEditor::gdMidiActionEditor(m::MidiChannel* ch) : gdBaseActionEditor(ch) { computeWidth(); @@ -70,16 +71,19 @@ gdMidiActionEditor::gdMidiActionEditor(MidiChannel* ch) viewport = new geScroll(8, 36, w()-16, h()-44); - ne = new geNoteEditor(viewport->x(), viewport->y(), this); - viewport->add(ne); - viewport->add(new geResizerBar(ne->x(), ne->y()+ne->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H)); + m_ne = new geNoteEditor(viewport->x(), viewport->y(), this); + m_ner = new geResizerBar(m_ne->x(), m_ne->y()+m_ne->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H); + viewport->add(m_ne); + viewport->add(m_ner); - ve = new geVelocityEditor(viewport->x(), ne->y()+ne->h()+RESIZER_BAR_H, ch); - viewport->add(ve); - viewport->add(new geResizerBar(ve->x(), ve->y()+ve->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H)); + m_ve = new geVelocityEditor(viewport->x(), m_ne->y()+m_ne->h()+RESIZER_BAR_H, ch); + m_ver = new geResizerBar(m_ve->x(), m_ve->y()+m_ve->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H); + viewport->add(m_ve); + viewport->add(m_ver); end(); prepareWindow(); + rebuild(); } @@ -88,8 +92,11 @@ gdMidiActionEditor::gdMidiActionEditor(MidiChannel* ch) void gdMidiActionEditor::rebuild() { + m_actions = c::actionEditor::getActions(ch); computeWidth(); - ne->rebuild(); - ve->rebuild(); + m_ne->rebuild(); + m_ner->size(m_ne->w(), m_ner->h()); + m_ve->rebuild(); + m_ver->size(m_ve->w(), m_ver->h()); } }} // giada::v:: diff --git a/src/gui/dialogs/actionEditor/midiActionEditor.h b/src/gui/dialogs/actionEditor/midiActionEditor.h index 15da033..f850099 100644 --- a/src/gui/dialogs/actionEditor/midiActionEditor.h +++ b/src/gui/dialogs/actionEditor/midiActionEditor.h @@ -32,7 +32,7 @@ #include "baseActionEditor.h" -class MidiChannel; +class geResizerBar; namespace giada { @@ -46,12 +46,15 @@ class gdMidiActionEditor : public gdBaseActionEditor { private: - geNoteEditor* ne; - geVelocityEditor* ve; + geNoteEditor* m_ne; + geResizerBar* m_ner; + + geVelocityEditor* m_ve; + geResizerBar* m_ver; public: - gdMidiActionEditor(MidiChannel* ch); + gdMidiActionEditor(m::MidiChannel* ch); void rebuild() override; }; diff --git a/src/gui/dialogs/actionEditor/sampleActionEditor.cpp b/src/gui/dialogs/actionEditor/sampleActionEditor.cpp index 2f2e268..e69a89c 100644 --- a/src/gui/dialogs/actionEditor/sampleActionEditor.cpp +++ b/src/gui/dialogs/actionEditor/sampleActionEditor.cpp @@ -27,8 +27,10 @@ #include #include "../../../core/const.h" +#include "../../../core/midiEvent.h" #include "../../../core/graphics.h" #include "../../../core/sampleChannel.h" +#include "../../../glue/actionEditor.h" #include "../../elems/basics/scroll.h" #include "../../elems/basics/button.h" #include "../../elems/basics/resizerBar.h" @@ -46,7 +48,7 @@ using std::string; namespace giada { namespace v { -gdSampleActionEditor::gdSampleActionEditor(SampleChannel* ch) +gdSampleActionEditor::gdSampleActionEditor(m::SampleChannel* ch) : gdBaseActionEditor(ch) { computeWidth(); @@ -82,16 +84,19 @@ gdSampleActionEditor::gdSampleActionEditor(SampleChannel* ch) viewport = new geScroll(8, 36, w()-16, h()-44); - ac = new geSampleActionEditor(viewport->x(), viewport->y(), ch); - viewport->add(ac); - viewport->add(new geResizerBar(ac->x(), ac->y()+ac->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H)); + m_ae = new geSampleActionEditor(viewport->x(), viewport->y(), ch); + m_aer = new geResizerBar(m_ae->x(), m_ae->y()+m_ae->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H); + viewport->add(m_ae); + viewport->add(m_aer); - vc = new geEnvelopeEditor(viewport->x(), ac->y()+ac->h()+RESIZER_BAR_H, G_ACTION_VOLUME, "volume", ch); - viewport->add(vc); - viewport->add(new geResizerBar(vc->x(), vc->y()+vc->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H)); + m_ee = new geEnvelopeEditor(viewport->x(), m_ae->y()+m_ae->h()+RESIZER_BAR_H, "volume", ch); + m_eer = new geResizerBar(m_ee->x(), m_ee->y()+m_ee->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H); + viewport->add(m_ee); + viewport->add(m_eer); end(); prepareWindow(); + rebuild(); } @@ -100,7 +105,7 @@ gdSampleActionEditor::gdSampleActionEditor(SampleChannel* ch) bool gdSampleActionEditor::canChangeActionType() { - SampleChannel* sch = static_cast(ch); + m::SampleChannel* sch = static_cast(ch); return sch->mode != ChannelMode::SINGLE_PRESS && !sch->isAnyLoopMode(); } @@ -110,9 +115,12 @@ bool gdSampleActionEditor::canChangeActionType() void gdSampleActionEditor::rebuild() { + m_actions = c::actionEditor::getActions(ch); canChangeActionType() ? actionType->activate() : actionType->deactivate(); computeWidth(); - ac->rebuild(); - vc->rebuild(); + m_ae->rebuild(); + m_aer->size(m_ae->w(), m_aer->h()); + m_ee->rebuild(); + m_eer->size(m_ee->w(), m_eer->h()); } }} // giada::v:: diff --git a/src/gui/dialogs/actionEditor/sampleActionEditor.h b/src/gui/dialogs/actionEditor/sampleActionEditor.h index 9832f21..9d459a8 100644 --- a/src/gui/dialogs/actionEditor/sampleActionEditor.h +++ b/src/gui/dialogs/actionEditor/sampleActionEditor.h @@ -32,26 +32,30 @@ #include "baseActionEditor.h" -class SampleChannel; -class geSampleActionEditor; -class geEnvelopeEditor; +class geResizerBar; namespace giada { namespace v { +class geSampleActionEditor; +class geEnvelopeEditor; + class gdSampleActionEditor : public gdBaseActionEditor { private: - geSampleActionEditor* ac; - geEnvelopeEditor* vc; + geSampleActionEditor* m_ae; + geResizerBar* m_aer; + + geEnvelopeEditor* m_ee; + geResizerBar* m_eer; bool canChangeActionType(); public: - gdSampleActionEditor(SampleChannel* ch); + gdSampleActionEditor(m::SampleChannel* ch); void rebuild() override; }; diff --git a/src/gui/dialogs/beatsInput.cpp b/src/gui/dialogs/beatsInput.cpp index 3f134bf..710893b 100644 --- a/src/gui/dialogs/beatsInput.cpp +++ b/src/gui/dialogs/beatsInput.cpp @@ -47,17 +47,16 @@ using namespace giada::m; gdBeatsInput::gdBeatsInput() - : gdWindow(180, 60, "Beats") + : gdWindow(180, 36, "Beats") { if (conf::beatsX) resize(conf::beatsX, conf::beatsY, w(), h()); set_modal(); - beats = new geInput(8, 8, 43, G_GUI_UNIT); - bars = new geInput(beats->x()+beats->w()+4, 8, 43, G_GUI_UNIT); - ok = new geButton(bars->x()+bars->w()+4, 8, 70, G_GUI_UNIT, "Ok"); - resizeRec = new geCheck(8, 40, 12, 12, "resize recorded actions"); + beats = new geInput(8, 8, 43, G_GUI_UNIT); + bars = new geInput(beats->x()+beats->w()+4, 8, 43, G_GUI_UNIT); + ok = new geButton(bars->x()+bars->w()+4, 8, 70, G_GUI_UNIT, "Ok"); end(); beats->maximum_size(2); @@ -70,8 +69,6 @@ gdBeatsInput::gdBeatsInput() ok->shortcut(FL_Enter); ok->callback(cb_update, (void*)this); - - resizeRec->value(conf::resizeRecordings); gu_setFavicon(this); setId(WID_BEATS); @@ -86,7 +83,6 @@ gdBeatsInput::~gdBeatsInput() { conf::beatsX = x(); conf::beatsY = y(); - conf::resizeRecordings = resizeRec->value(); } @@ -103,6 +99,6 @@ void gdBeatsInput::cb_update() { if (!strcmp(beats->value(), "") || !strcmp(bars->value(), "")) return; - glue_setBeats(atoi(beats->value()), atoi(bars->value()), resizeRec->value()); + glue_setBeats(atoi(beats->value()), atoi(bars->value())); do_callback(); } diff --git a/src/gui/dialogs/beatsInput.h b/src/gui/dialogs/beatsInput.h index 938bd71..6b06da8 100644 --- a/src/gui/dialogs/beatsInput.h +++ b/src/gui/dialogs/beatsInput.h @@ -47,7 +47,6 @@ private: geInput* beats; geInput* bars; geButton* ok; - geCheck* resizeRec; public: diff --git a/src/gui/dialogs/browser/browserBase.h b/src/gui/dialogs/browser/browserBase.h index ccc2485..883afdf 100644 --- a/src/gui/dialogs/browser/browserBase.h +++ b/src/gui/dialogs/browser/browserBase.h @@ -30,10 +30,11 @@ #include "../window.h" +#include "../../../core/plugin.h" +#include "../../../core/channel.h" class Fl_Group; -class Channel; class geCheck; class geBrowser; class geButton; @@ -45,7 +46,7 @@ class gdBrowserBase : public gdWindow { protected: - Channel* channel; + giada::m::Channel* channel; Fl_Group* groupTop; geCheck* hiddenFiles; @@ -81,7 +82,7 @@ public: std::string getSelectedItem() const; std::string getCurrentPath() const; - Channel* getChannel() const; + giada::m::Channel* getChannel() const; void fireCallback() const; /* setStatusBar diff --git a/src/gui/dialogs/browser/browserDir.h b/src/gui/dialogs/browser/browserDir.h index 0c97065..18f34b2 100644 --- a/src/gui/dialogs/browser/browserDir.h +++ b/src/gui/dialogs/browser/browserDir.h @@ -32,9 +32,6 @@ #include "browserBase.h" -class Channel; - - class gdBrowserDir : public gdBrowserBase { private: diff --git a/src/gui/dialogs/browser/browserLoad.cpp b/src/gui/dialogs/browser/browserLoad.cpp index 266c4f3..a46c42e 100644 --- a/src/gui/dialogs/browser/browserLoad.cpp +++ b/src/gui/dialogs/browser/browserLoad.cpp @@ -33,10 +33,11 @@ using std::string; +using namespace giada; gdBrowserLoad::gdBrowserLoad(int x, int y, int w, int h, const string& title, - const string& path, void (*cb)(void*), Channel* ch) + const string& path, void (*cb)(void*), m::Channel* ch) : gdBrowserBase(x, y, w, h, title, path, cb) { channel = ch; diff --git a/src/gui/dialogs/browser/browserLoad.h b/src/gui/dialogs/browser/browserLoad.h index e132a34..9ef7d18 100644 --- a/src/gui/dialogs/browser/browserLoad.h +++ b/src/gui/dialogs/browser/browserLoad.h @@ -32,9 +32,6 @@ #include "browserBase.h" -class Channel; - - class gdBrowserLoad : public gdBrowserBase { private: @@ -47,7 +44,7 @@ private: public: gdBrowserLoad(int x, int y, int w, int h, const std::string& title, - const std::string& path, void (*callback)(void*), Channel* ch); + const std::string& path, void (*callback)(void*), giada::m::Channel* ch); }; diff --git a/src/gui/dialogs/browser/browserSave.cpp b/src/gui/dialogs/browser/browserSave.cpp index b1559a8..018b81b 100644 --- a/src/gui/dialogs/browser/browserSave.cpp +++ b/src/gui/dialogs/browser/browserSave.cpp @@ -33,6 +33,7 @@ using std::string; +using namespace giada::m; gdBrowserSave::gdBrowserSave(int x, int y, int w, int h, const string& title, diff --git a/src/gui/dialogs/browser/browserSave.h b/src/gui/dialogs/browser/browserSave.h index acfa0d6..867089c 100644 --- a/src/gui/dialogs/browser/browserSave.h +++ b/src/gui/dialogs/browser/browserSave.h @@ -32,7 +32,6 @@ #include "browserBase.h" -class Channel; class geInput; @@ -51,7 +50,7 @@ public: gdBrowserSave(int x, int y, int w, int h, const std::string& title, const std::string& path, const std::string& name, void (*callback)(void*), - Channel* ch); + giada::m::Channel* ch); std::string getName() const; }; diff --git a/src/gui/dialogs/channelNameInput.cpp b/src/gui/dialogs/channelNameInput.cpp index 9380852..bca1a35 100644 --- a/src/gui/dialogs/channelNameInput.cpp +++ b/src/gui/dialogs/channelNameInput.cpp @@ -38,7 +38,7 @@ using namespace giada; -gdChannelNameInput::gdChannelNameInput(Channel* ch) +gdChannelNameInput::gdChannelNameInput(m::Channel* ch) : gdWindow(400, 64, "New channel name"), m_ch (ch) { diff --git a/src/gui/dialogs/channelNameInput.h b/src/gui/dialogs/channelNameInput.h index 7e4a113..8f32147 100644 --- a/src/gui/dialogs/channelNameInput.h +++ b/src/gui/dialogs/channelNameInput.h @@ -32,7 +32,6 @@ #include "window.h" -class Channel; class geInput; class geButton; @@ -46,7 +45,7 @@ private: void cb_update(); void cb_cancel(); - Channel* m_ch; + giada::m::Channel* m_ch; geInput* m_name; geButton* m_ok; @@ -54,7 +53,7 @@ private: public: - gdChannelNameInput(Channel* ch); + gdChannelNameInput(giada::m::Channel* ch); ~gdChannelNameInput(); }; diff --git a/src/gui/dialogs/gd_keyGrabber.cpp b/src/gui/dialogs/gd_keyGrabber.cpp index ea861b7..418eb50 100644 --- a/src/gui/dialogs/gd_keyGrabber.cpp +++ b/src/gui/dialogs/gd_keyGrabber.cpp @@ -45,9 +45,10 @@ extern gdMainWindow *mainWin; using std::string; +using namespace giada; -gdKeyGrabber::gdKeyGrabber(Channel *ch) +gdKeyGrabber::gdKeyGrabber(m::Channel* ch) : gdWindow(300, 126, "Key configuration"), ch(ch) { set_modal(); @@ -69,14 +70,14 @@ gdKeyGrabber::gdKeyGrabber(Channel *ch) /* -------------------------------------------------------------------------- */ -void gdKeyGrabber::cb_clear (Fl_Widget *w, void *p) { ((gdKeyGrabber*)p)->__cb_clear(); } -void gdKeyGrabber::cb_cancel(Fl_Widget *w, void *p) { ((gdKeyGrabber*)p)->__cb_cancel(); } +void gdKeyGrabber::cb_clear (Fl_Widget* w, void* p) { ((gdKeyGrabber*)p)->cb_clear(); } +void gdKeyGrabber::cb_cancel(Fl_Widget* w, void* p) { ((gdKeyGrabber*)p)->cb_cancel(); } /* -------------------------------------------------------------------------- */ -void gdKeyGrabber::__cb_cancel() +void gdKeyGrabber::cb_cancel() { do_callback(); } @@ -85,7 +86,7 @@ void gdKeyGrabber::__cb_cancel() /* -------------------------------------------------------------------------- */ -void gdKeyGrabber::__cb_clear() +void gdKeyGrabber::cb_clear() { updateText(0); setButtonLabel(0); diff --git a/src/gui/dialogs/gd_keyGrabber.h b/src/gui/dialogs/gd_keyGrabber.h index d66a403..92f2af1 100644 --- a/src/gui/dialogs/gd_keyGrabber.h +++ b/src/gui/dialogs/gd_keyGrabber.h @@ -33,7 +33,6 @@ #include "window.h" -class Channel; class geBox; class geButton; @@ -42,23 +41,23 @@ class gdKeyGrabber : public gdWindow { private: - Channel *ch; + giada::m::Channel* ch; - geBox *text; - geButton *clear; - geButton *cancel; + geBox* text; + geButton* clear; + geButton* cancel; - static void cb_clear (Fl_Widget *w, void *p); - static void cb_cancel(Fl_Widget *w, void *p); - inline void __cb_clear (); - inline void __cb_cancel(); + static void cb_clear (Fl_Widget* w, void* p); + static void cb_cancel(Fl_Widget* w, void* p); + void cb_clear (); + void cb_cancel(); void setButtonLabel(int key); void updateText(int key); public: - gdKeyGrabber(Channel *ch); + gdKeyGrabber(giada::m::Channel* ch); int handle(int e); }; diff --git a/src/gui/dialogs/gd_mainWindow.cpp b/src/gui/dialogs/gd_mainWindow.cpp index d03714f..cab1399 100644 --- a/src/gui/dialogs/gd_mainWindow.cpp +++ b/src/gui/dialogs/gd_mainWindow.cpp @@ -27,6 +27,7 @@ #include #include "../../core/const.h" +#include "../../core/conf.h" #include "../../core/init.h" #include "../../utils/gui.h" #include "../elems/basics/boxtypes.h" @@ -40,7 +41,10 @@ #include "gd_mainWindow.h" -extern gdMainWindow *G_MainWin; +extern gdMainWindow* G_MainWin; + + +using namespace giada; gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** argv) @@ -54,9 +58,9 @@ gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** arg Fl::set_boxtype(G_CUSTOM_UP_BOX, g_customUpBox, 1, 1, 2, 2); Fl::set_boxtype(G_CUSTOM_DOWN_BOX, g_customDownBox, 1, 1, 2, 2); - Fl::set_boxtype(FL_BORDER_BOX, G_CUSTOM_BORDER_BOX); - Fl::set_boxtype(FL_UP_BOX, G_CUSTOM_UP_BOX); - Fl::set_boxtype(FL_DOWN_BOX, G_CUSTOM_DOWN_BOX); + Fl::set_boxtype(FL_BORDER_BOX, G_CUSTOM_BORDER_BOX); + Fl::set_boxtype(FL_UP_BOX, G_CUSTOM_UP_BOX); + Fl::set_boxtype(FL_DOWN_BOX, G_CUSTOM_DOWN_BOX); size_range(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT); @@ -115,7 +119,12 @@ void gdMainWindow::cb_endprogram() { if (!gdConfirmWin("Warning", "Quit Giada: are you sure?")) return; - init_shutdown(); + + m::conf::mainWindowX = x(); + m::conf::mainWindowY = y(); + m::conf::mainWindowW = w(); + m::conf::mainWindowH = h(); + hide(); delete this; } diff --git a/src/gui/dialogs/midiIO/midiInputChannel.h b/src/gui/dialogs/midiIO/midiInputChannel.h index 26e50bb..3a06680 100644 --- a/src/gui/dialogs/midiIO/midiInputChannel.h +++ b/src/gui/dialogs/midiIO/midiInputChannel.h @@ -34,7 +34,6 @@ #include "midiInputBase.h" -class Channel; class geScroll; class geCheck; class geChoice; @@ -44,7 +43,7 @@ class gdMidiInputChannel : public gdMidiInputBase { private: - Channel* ch; + giada::m::Channel* ch; geScroll* container; geCheck* enable; @@ -68,7 +67,7 @@ private: public: - gdMidiInputChannel(Channel* ch); + gdMidiInputChannel(giada::m::Channel* ch); ~gdMidiInputChannel(); }; diff --git a/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp b/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp index ad425a5..1e19ad2 100644 --- a/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp +++ b/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp @@ -36,7 +36,10 @@ #include "midiOutputMidiCh.h" -gdMidiOutputMidiCh::gdMidiOutputMidiCh(MidiChannel* ch) +using namespace giada; + + +gdMidiOutputMidiCh::gdMidiOutputMidiCh(m::MidiChannel* ch) : gdMidiOutputBase(300, 168), ch(ch) { setTitle(ch->index+1); diff --git a/src/gui/dialogs/midiIO/midiOutputMidiCh.h b/src/gui/dialogs/midiIO/midiOutputMidiCh.h index 93fff1f..b1fb4d8 100644 --- a/src/gui/dialogs/midiIO/midiOutputMidiCh.h +++ b/src/gui/dialogs/midiIO/midiOutputMidiCh.h @@ -50,11 +50,11 @@ private: class geCheck *enableOut; class geChoice *chanListOut; - class MidiChannel *ch; + class giada::m::MidiChannel *ch; public: - gdMidiOutputMidiCh(class MidiChannel *ch); + gdMidiOutputMidiCh(class giada::m::MidiChannel *ch); }; diff --git a/src/gui/dialogs/midiIO/midiOutputSampleCh.cpp b/src/gui/dialogs/midiIO/midiOutputSampleCh.cpp index cd12d6c..fdb21df 100644 --- a/src/gui/dialogs/midiIO/midiOutputSampleCh.cpp +++ b/src/gui/dialogs/midiIO/midiOutputSampleCh.cpp @@ -33,7 +33,10 @@ #include "midiOutputSampleCh.h" -gdMidiOutputSampleCh::gdMidiOutputSampleCh(SampleChannel* ch) +using namespace giada; + + +gdMidiOutputSampleCh::gdMidiOutputSampleCh(m::SampleChannel* ch) : gdMidiOutputBase(300, 140), ch(ch) { setTitle(ch->index+1); diff --git a/src/gui/dialogs/midiIO/midiOutputSampleCh.h b/src/gui/dialogs/midiIO/midiOutputSampleCh.h index 7b679b1..fbd14ec 100644 --- a/src/gui/dialogs/midiIO/midiOutputSampleCh.h +++ b/src/gui/dialogs/midiIO/midiOutputSampleCh.h @@ -32,14 +32,11 @@ #include "midiOutputBase.h" -class SampleChannel; - - class gdMidiOutputSampleCh : public gdMidiOutputBase { private: - SampleChannel* ch; + giada::m::SampleChannel* ch; /* cb_close Override parent method, we need to do more stuff on close. */ @@ -49,7 +46,7 @@ private: public: - gdMidiOutputSampleCh(SampleChannel* ch); + gdMidiOutputSampleCh(giada::m::SampleChannel* ch); }; #endif diff --git a/src/gui/dialogs/pluginChooser.cpp b/src/gui/dialogs/pluginChooser.cpp index 21e5170..361d735 100644 --- a/src/gui/dialogs/pluginChooser.cpp +++ b/src/gui/dialogs/pluginChooser.cpp @@ -44,7 +44,7 @@ using namespace giada::m; using namespace giada::c; -gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, Channel *ch) +gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, int stackType, Channel* ch) : gdWindow(X, Y, W, H, "Available plugins"), ch(ch), stackType(stackType) { /* top area */ @@ -99,15 +99,15 @@ gdPluginChooser::~gdPluginChooser() /* -------------------------------------------------------------------------- */ -void gdPluginChooser::cb_close(Fl_Widget *v, void *p) { ((gdPluginChooser*)p)->__cb_close(); } -void gdPluginChooser::cb_add(Fl_Widget *v, void *p) { ((gdPluginChooser*)p)->__cb_add(); } -void gdPluginChooser::cb_sort(Fl_Widget *v, void *p) { ((gdPluginChooser*)p)->__cb_sort(); } +void gdPluginChooser::cb_close(Fl_Widget* v, void* p) { ((gdPluginChooser*)p)->cb_close(); } +void gdPluginChooser::cb_add(Fl_Widget* v, void* p) { ((gdPluginChooser*)p)->cb_add(); } +void gdPluginChooser::cb_sort(Fl_Widget* v, void* p) { ((gdPluginChooser*)p)->cb_sort(); } /* -------------------------------------------------------------------------- */ -void gdPluginChooser::__cb_close() +void gdPluginChooser::cb_close() { do_callback(); } @@ -116,7 +116,7 @@ void gdPluginChooser::__cb_close() /* -------------------------------------------------------------------------- */ -void gdPluginChooser::__cb_sort() +void gdPluginChooser::cb_sort() { pluginHost::sortPlugins(sortMethod->value()); browser->refresh(); @@ -126,7 +126,7 @@ void gdPluginChooser::__cb_sort() /* -------------------------------------------------------------------------- */ -void gdPluginChooser::__cb_add() +void gdPluginChooser::cb_add() { int index = browser->value() - 3; // subtract header lines if (index < 0) diff --git a/src/gui/dialogs/pluginChooser.h b/src/gui/dialogs/pluginChooser.h index cd8f483..53b3b14 100644 --- a/src/gui/dialogs/pluginChooser.h +++ b/src/gui/dialogs/pluginChooser.h @@ -36,7 +36,6 @@ #include "window.h" -class Channel; class geChoice; class geButton; class geButton; @@ -47,25 +46,25 @@ class gdPluginChooser : public gdWindow { private: - Channel *ch; // ch == nullptr ? masterOut + giada::m::Channel* ch; // ch == nullptr ? masterOut int stackType; - geChoice *sortMethod; - geButton *addBtn; - geButton *cancelBtn; - gePluginBrowser *browser; + geChoice* sortMethod; + geButton* addBtn; + geButton* cancelBtn; + gePluginBrowser* browser; - static void cb_close(Fl_Widget *w, void *p); - static void cb_add (Fl_Widget *w, void *p); - static void cb_sort (Fl_Widget *w, void *p); - inline void __cb_close(); - inline void __cb_add (); - inline void __cb_sort (); + static void cb_close(Fl_Widget* w, void* p); + static void cb_add (Fl_Widget* w, void* p); + static void cb_sort (Fl_Widget* w, void* p); + void cb_close(); + void cb_add (); + void cb_sort (); public: - gdPluginChooser(int x, int y, int w, int h, int stackType, Channel *ch=nullptr); - ~gdPluginChooser(); + gdPluginChooser(int x, int y, int w, int h, int stackType, giada::m::Channel* ch=nullptr); + ~gdPluginChooser(); }; diff --git a/src/gui/dialogs/pluginList.cpp b/src/gui/dialogs/pluginList.cpp index ab0f3fe..b93e51d 100644 --- a/src/gui/dialogs/pluginList.cpp +++ b/src/gui/dialogs/pluginList.cpp @@ -54,7 +54,7 @@ using std::string; using namespace giada; -gdPluginList::gdPluginList(int stackType, Channel* ch) +gdPluginList::gdPluginList(int stackType, m::Channel* ch) : gdWindow(468, 204), ch(ch), stackType(stackType) { using namespace giada::m; diff --git a/src/gui/dialogs/pluginList.h b/src/gui/dialogs/pluginList.h index d63ed05..c3513bc 100644 --- a/src/gui/dialogs/pluginList.h +++ b/src/gui/dialogs/pluginList.h @@ -35,7 +35,6 @@ class Fl_Scroll; -class Channel; class geButton; @@ -51,10 +50,10 @@ private: public: - Channel* ch; // ch == nullptr ? masterOut + giada::m::Channel* ch; // ch == nullptr ? masterOut int stackType; - gdPluginList(int stackType, Channel* ch=nullptr); + gdPluginList(int stackType, giada::m::Channel* ch=nullptr); ~gdPluginList(); /* special callback, passed to browser. When closed (i.e. plugin diff --git a/src/gui/dialogs/pluginWindow.cpp b/src/gui/dialogs/pluginWindow.cpp index 51b4c28..8dfca8b 100644 --- a/src/gui/dialogs/pluginWindow.cpp +++ b/src/gui/dialogs/pluginWindow.cpp @@ -37,7 +37,10 @@ #include "pluginWindow.h" -gdPluginWindow::gdPluginWindow(Plugin* p) +using namespace giada; + + +gdPluginWindow::gdPluginWindow(m::Plugin* p) : gdWindow(450, 156), m_plugin(p) { set_non_modal(); diff --git a/src/gui/dialogs/pluginWindow.h b/src/gui/dialogs/pluginWindow.h index 49f753e..a34befe 100644 --- a/src/gui/dialogs/pluginWindow.h +++ b/src/gui/dialogs/pluginWindow.h @@ -31,10 +31,10 @@ #define GD_PLUGIN_WINDOW_H +#include "../../core/plugin.h" #include "window.h" -class Plugin; class geBox; class geSlider; class geLiquidScroll; @@ -44,7 +44,7 @@ class gdPluginWindow : public gdWindow { private: - Plugin* m_plugin; + giada::m::Plugin* m_plugin; geLiquidScroll* m_list; @@ -52,7 +52,7 @@ private: public: - gdPluginWindow(Plugin* p); + gdPluginWindow(giada::m::Plugin* p); void updateParameter(int index, bool changeSlider=false); void updateParameters(bool changeSlider=false); diff --git a/src/gui/dialogs/pluginWindowGUI.h b/src/gui/dialogs/pluginWindowGUI.h index 004be47..8dd6779 100644 --- a/src/gui/dialogs/pluginWindowGUI.h +++ b/src/gui/dialogs/pluginWindowGUI.h @@ -43,14 +43,11 @@ #endif -class Plugin; - - class gdPluginWindowGUI : public gdWindow { private: - Plugin* m_plugin; + giada::m::Plugin* m_plugin; static void cb_close (Fl_Widget* v, void* p); static void cb_refresh(void* data); @@ -59,7 +56,7 @@ private: public: - gdPluginWindowGUI(Plugin* p); + gdPluginWindowGUI(giada::m::Plugin* p); ~gdPluginWindowGUI(); }; diff --git a/src/gui/dialogs/sampleEditor.cpp b/src/gui/dialogs/sampleEditor.cpp index 4774df2..b13ef70 100644 --- a/src/gui/dialogs/sampleEditor.cpp +++ b/src/gui/dialogs/sampleEditor.cpp @@ -63,7 +63,7 @@ using std::string; using namespace giada; -gdSampleEditor::gdSampleEditor(SampleChannel* ch) +gdSampleEditor::gdSampleEditor(m::SampleChannel* ch) : gdWindow(640, 480), ch(ch) { diff --git a/src/gui/dialogs/sampleEditor.h b/src/gui/dialogs/sampleEditor.h index 2d12054..0ab76e6 100644 --- a/src/gui/dialogs/sampleEditor.h +++ b/src/gui/dialogs/sampleEditor.h @@ -29,6 +29,7 @@ #define GD_EDITOR_H +#include "../../core/sampleChannel.h" #include "window.h" @@ -78,7 +79,7 @@ private: public: - gdSampleEditor(SampleChannel* ch); + gdSampleEditor(giada::m::SampleChannel* ch); ~gdSampleEditor(); void updateInfo(); @@ -106,7 +107,7 @@ public: geCheck* loop; geBox* info; - SampleChannel* ch; + giada::m::SampleChannel* ch; }; diff --git a/src/gui/elems/actionEditor/baseAction.cpp b/src/gui/elems/actionEditor/baseAction.cpp index ba0d5f4..1283c7a 100644 --- a/src/gui/elems/actionEditor/baseAction.cpp +++ b/src/gui/elems/actionEditor/baseAction.cpp @@ -34,7 +34,7 @@ namespace giada { namespace v { geBaseAction::geBaseAction(Pixel X, Pixel Y, Pixel W, Pixel H, bool resizable, - giada::m::recorder::action a1, giada::m::recorder::action a2) + const m::Action* a1, const m::Action* a2) : Fl_Box (X, Y, W, H), m_resizable(resizable), onRightEdge(false), diff --git a/src/gui/elems/actionEditor/baseAction.h b/src/gui/elems/actionEditor/baseAction.h index d8bf4fd..c3b4b12 100644 --- a/src/gui/elems/actionEditor/baseAction.h +++ b/src/gui/elems/actionEditor/baseAction.h @@ -35,11 +35,15 @@ namespace giada { +namespace m +{ +class Action; +} namespace v { class geBaseAction : public Fl_Box { -private: +protected: bool m_resizable; @@ -49,7 +53,7 @@ public: static const Pixel HANDLE_WIDTH = 6; geBaseAction(Pixel x, Pixel y, Pixel w, Pixel h, bool resizable, - m::recorder::action a1, m::recorder::action a2); + const m::Action* a1, const m::Action* a2); int handle(int e) override; @@ -69,8 +73,8 @@ public: bool altered; Pixel pick; - m::recorder::action a1; - m::recorder::action a2; + const m::Action* a1; + const m::Action* a2; }; }} // giada::v:: diff --git a/src/gui/elems/actionEditor/baseActionEditor.cpp b/src/gui/elems/actionEditor/baseActionEditor.cpp index c59b12d..75c6d22 100644 --- a/src/gui/elems/actionEditor/baseActionEditor.cpp +++ b/src/gui/elems/actionEditor/baseActionEditor.cpp @@ -38,11 +38,11 @@ namespace giada { namespace v { -geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, Channel* ch) +geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, m::Channel* ch) : Fl_Group(x, y, w, h), - m_ch (ch), - m_base (static_cast(window())), - m_action(nullptr) + m_ch (ch), + m_base (static_cast(window())), + m_action(nullptr) { } diff --git a/src/gui/elems/actionEditor/baseActionEditor.h b/src/gui/elems/actionEditor/baseActionEditor.h index 0f3d2ed..bcd3cfb 100644 --- a/src/gui/elems/actionEditor/baseActionEditor.h +++ b/src/gui/elems/actionEditor/baseActionEditor.h @@ -33,9 +33,6 @@ #include -class Channel; - - namespace giada { namespace v { @@ -57,7 +54,7 @@ private: protected: - Channel* m_ch; + m::Channel* m_ch; gdBaseActionEditor* m_base; @@ -79,7 +76,7 @@ protected: public: - geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, Channel* ch); + geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, m::Channel* ch); /* updateActions Rebuild the actions widgets from scratch. */ diff --git a/src/gui/elems/actionEditor/envelopeEditor.cpp b/src/gui/elems/actionEditor/envelopeEditor.cpp index b1bba63..ff27f5b 100644 --- a/src/gui/elems/actionEditor/envelopeEditor.cpp +++ b/src/gui/elems/actionEditor/envelopeEditor.cpp @@ -31,8 +31,10 @@ #include "../../../utils/math.h" #include "../../../core/const.h" #include "../../../core/conf.h" +#include "../../../core/action.h" +#include "../../../core/recorder.h" #include "../../../core/sampleChannel.h" -#include "../../../glue/recorder.h" +#include "../../../glue/actionEditor.h" #include "../../dialogs/actionEditor/baseActionEditor.h" #include "envelopePoint.h" #include "envelopeEditor.h" @@ -44,13 +46,11 @@ using std::vector; namespace giada { namespace v { -geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, int actionType, const char* l, - SampleChannel* ch) -: geBaseActionEditor(x, y, 200, m::conf::envelopeEditorH, ch), - m_actionType (actionType) +geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, const char* l, + m::SampleChannel* ch) +: geBaseActionEditor(x, y, 200, m::conf::envelopeEditorH, ch) { copy_label(l); - rebuild(); } @@ -94,7 +94,7 @@ void geEnvelopeEditor::draw() for (int i=0; i(child(i)); if (m_action == nullptr) - p->position(p->x(), valueToY(p->a1.fValue)); + p->position(p->x(), valueToY(p->a1->event.getVelocity())); if (i > 0) { x2 = p->x() + side; y2 = p->y() + side; @@ -114,7 +114,7 @@ void geEnvelopeEditor::draw() void geEnvelopeEditor::rebuild() { namespace mr = m::recorder; - namespace cr = c::recorder; + namespace ca = c::actionEditor; /* Remove all existing actions and set a new width, according to the current zoom level. */ @@ -122,11 +122,10 @@ void geEnvelopeEditor::rebuild() clear(); size(m_base->fullWidth, h()); - vector actions = cr::getEnvelopeActions(m_ch, m_actionType); - - for (mr::action a : actions) { - gu_log("[geEnvelopeEditor::rebuild] Action %d\n", a.frame); - add(new geEnvelopePoint(frameToX(a.frame), valueToY(a.fValue), a)); + for (const m::Action* a : m_base->getActions()) { + if (a->event.getStatus() != m::MidiEvent::ENVELOPE) + continue; + add(new geEnvelopePoint(frameToX(a->frame), valueToY(a->event.getVelocity()), a)); } resizable(nullptr); @@ -159,15 +158,15 @@ Pixel geEnvelopeEditor::frameToX(Frame frame) const } -Pixel geEnvelopeEditor::valueToY(float value) const +Pixel geEnvelopeEditor::valueToY(int value) const { - return u::math::map(value, 0.0, 1.0, y() + (h() - geEnvelopePoint::SIDE), y()); + return u::math::map(value, 0, G_MAX_VELOCITY, y() + (h() - geEnvelopePoint::SIDE), y()); } -float geEnvelopeEditor::yToValue(Pixel pixel) const +int geEnvelopeEditor::yToValue(Pixel pixel, Pixel offset) const { - return u::math::map(pixel, h() - geEnvelopePoint::SIDE, 0, 0.0, 1.0); + return u::math::map(pixel, h() - offset, 0, 0, G_MAX_VELOCITY); } @@ -177,9 +176,11 @@ float geEnvelopeEditor::yToValue(Pixel pixel) const void geEnvelopeEditor::onAddAction() { Frame f = m_base->pixelToFrame(Fl::event_x() - x()); - float v = yToValue(Fl::event_y() - y()); - c::recorder::recordEnvelopeAction(m_ch, m_actionType, f, v); - rebuild(); + int v = yToValue(Fl::event_y() - y()); + + c::actionEditor::recordEnvelopeAction(m_ch, f, v); + + m_base->rebuild(); } @@ -188,8 +189,9 @@ void geEnvelopeEditor::onAddAction() void geEnvelopeEditor::onDeleteAction() { - c::recorder::deleteEnvelopeAction(m_ch, m_action->a1, /*moved=*/false); - rebuild(); + c::actionEditor::deleteEnvelopeAction(m_ch, m_action->a1); + + m_base->rebuild(); } @@ -225,9 +227,9 @@ void geEnvelopeEditor::onMoveAction() void geEnvelopeEditor::onRefreshAction() { Frame f = m_base->pixelToFrame((m_action->x() - x()) + geEnvelopePoint::SIDE / 2); - float v = yToValue(m_action->y() - y()); - c::recorder::deleteEnvelopeAction(m_ch, m_action->a1, /*moved=*/true); - c::recorder::recordEnvelopeAction(m_ch, m_actionType, f, v); - rebuild(); + float v = yToValue(m_action->y() - y(), geEnvelopePoint::SIDE); + c::actionEditor::updateEnvelopeAction(m_ch, m_action->a1, f, v); + + m_base->rebuild(); } }} // giada::v:: \ No newline at end of file diff --git a/src/gui/elems/actionEditor/envelopeEditor.h b/src/gui/elems/actionEditor/envelopeEditor.h index 0bdc8b5..c8046df 100644 --- a/src/gui/elems/actionEditor/envelopeEditor.h +++ b/src/gui/elems/actionEditor/envelopeEditor.h @@ -32,10 +32,13 @@ #include "baseActionEditor.h" -class SampleChannel; namespace giada { +namespace m +{ +class SampleChannel; +} namespace v { class geEnvelopePoint; @@ -45,11 +48,6 @@ class geEnvelopeEditor : public geBaseActionEditor { private: - /* m_actionType - What type of action this envelope editor is dealing with. */ - - int m_actionType; - void onAddAction() override; void onDeleteAction() override; void onMoveAction() override; @@ -57,15 +55,15 @@ private: void onRefreshAction() override; Pixel frameToX(Frame frame) const; - Pixel valueToY(float value) const; - float yToValue(Pixel pixel) const; + Pixel valueToY(int value) const; + int yToValue(Pixel pixel, Pixel offset=0) const; bool isFirstPoint() const; bool isLastPoint() const; public: - geEnvelopeEditor(Pixel x, Pixel y, int actionType, const char* l, SampleChannel* ch); + geEnvelopeEditor(Pixel x, Pixel y, const char* l, m::SampleChannel* ch); ~geEnvelopeEditor(); void draw() override; diff --git a/src/gui/elems/actionEditor/envelopePoint.cpp b/src/gui/elems/actionEditor/envelopePoint.cpp index a1f7e9e..a32965d 100644 --- a/src/gui/elems/actionEditor/envelopePoint.cpp +++ b/src/gui/elems/actionEditor/envelopePoint.cpp @@ -33,8 +33,8 @@ namespace giada { namespace v { -geEnvelopePoint::geEnvelopePoint(Pixel X, Pixel Y, m::recorder::action a) - : geBaseAction(X, Y, SIDE, SIDE, /*resizable=*/false, a, {}) +geEnvelopePoint::geEnvelopePoint(Pixel X, Pixel Y, const m::Action* a) + : geBaseAction(X, Y, SIDE, SIDE, /*resizable=*/false, a, nullptr) { } diff --git a/src/gui/elems/actionEditor/envelopePoint.h b/src/gui/elems/actionEditor/envelopePoint.h index 3e040ff..05f7bc3 100644 --- a/src/gui/elems/actionEditor/envelopePoint.h +++ b/src/gui/elems/actionEditor/envelopePoint.h @@ -42,7 +42,7 @@ public: static const Pixel SIDE = 12; - geEnvelopePoint(Pixel x, Pixel y, m::recorder::action a); + geEnvelopePoint(Pixel x, Pixel y, const m::Action* a); void draw() override; }; diff --git a/src/gui/elems/actionEditor/noteEditor.cpp b/src/gui/elems/actionEditor/noteEditor.cpp index 0614b35..4992af7 100644 --- a/src/gui/elems/actionEditor/noteEditor.cpp +++ b/src/gui/elems/actionEditor/noteEditor.cpp @@ -41,9 +41,7 @@ geNoteEditor::geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base) : geScroll(x, y, 200, 422), m_base (base) { - pianoRoll = new gePianoRoll(x, y, m_base->fullWidth, static_cast(m_base->ch)); - - rebuild(); + pianoRoll = new gePianoRoll(x, y, m_base->fullWidth, static_cast(m_base->ch)); size(m_base->fullWidth, m::conf::pianoRollH); diff --git a/src/gui/elems/actionEditor/pianoItem.cpp b/src/gui/elems/actionEditor/pianoItem.cpp index 5d0077a..d608276 100644 --- a/src/gui/elems/actionEditor/pianoItem.cpp +++ b/src/gui/elems/actionEditor/pianoItem.cpp @@ -27,6 +27,7 @@ #include #include "../../../core/const.h" +#include "../../../core/action.h" #include "../../../core/midiEvent.h" #include "../../../utils/math.h" #include "pianoItem.h" @@ -35,11 +36,22 @@ namespace giada { namespace v { -gePianoItem::gePianoItem(Pixel X, Pixel Y, Pixel W, Pixel H, m::recorder::action a1, - m::recorder::action a2) +gePianoItem::gePianoItem(Pixel X, Pixel Y, Pixel W, Pixel H, const m::Action* a1, + const m::Action* a2) : geBaseAction(X, Y, W, H, /*resizable=*/true, a1, a2), - orphaned (a2.frame == -1) + m_ringLoop (a2 != nullptr && a1->frame > a2->frame), + m_orphaned (a2 == nullptr) { + m_resizable = isResizable(); +} + + +/* -------------------------------------------------------------------------- */ + + +bool gePianoItem::isResizable() const +{ + return !(m_ringLoop || m_orphaned); } @@ -53,14 +65,21 @@ void gePianoItem::draw() Pixel by = y() + 2; Pixel bh = h() - 3; - if (orphaned) { - fl_rect(x(), by, MIN_WIDTH, bh, color); - fl_line(x(), by, x() + MIN_WIDTH, by + bh); + if (m_orphaned) { + fl_rect(x(), by, w(), bh, color); + fl_line(x(), by, x() + w(), by + bh); } else { Pixel vh = calcVelocityH(); - fl_rectf(x(), by + (bh - vh), w(), vh, color); - fl_rect(x(), by, w(), bh, color); + if (m_ringLoop) { + fl_rect(x(), by, MIN_WIDTH, bh, color); + fl_line(x() + MIN_WIDTH, by + bh/2, x() + w(), by + bh/2); + fl_rectf(x(), by + (bh - vh), MIN_WIDTH, vh, color); + } + else { + fl_rect(x(), by, w(), bh, color); + fl_rectf(x(), by + (bh - vh), w(), vh, color); + } } } @@ -70,7 +89,7 @@ void gePianoItem::draw() Pixel gePianoItem::calcVelocityH() const { - int v = m::MidiEvent(a1.iValue).getVelocity(); + int v = a1->event.getVelocity(); return u::math::map(v, 0, G_MAX_VELOCITY, 0, h() - 3); } }} // giada::v:: \ No newline at end of file diff --git a/src/gui/elems/actionEditor/pianoItem.h b/src/gui/elems/actionEditor/pianoItem.h index da7c614..6f84cc9 100644 --- a/src/gui/elems/actionEditor/pianoItem.h +++ b/src/gui/elems/actionEditor/pianoItem.h @@ -29,11 +29,14 @@ #define GE_PIANO_ITEM_H -#include "../../../core/recorder.h" #include "baseAction.h" namespace giada { +namespace m +{ +class Action; +} namespace v { class gdActionEditor; @@ -43,16 +46,18 @@ class gePianoItem : public geBaseAction { private: + bool m_ringLoop; + bool m_orphaned; + Pixel calcVelocityH() const; public: - gePianoItem(int x, int y, int w, int h, m::recorder::action a1, - m::recorder::action a2); + gePianoItem(int x, int y, int w, int h, const m::Action* a1, const m::Action* a2); void draw() override; - bool orphaned; + bool isResizable() const; }; }} // giada::v:: diff --git a/src/gui/elems/actionEditor/pianoRoll.cpp b/src/gui/elems/actionEditor/pianoRoll.cpp index c1b416b..f629101 100644 --- a/src/gui/elems/actionEditor/pianoRoll.cpp +++ b/src/gui/elems/actionEditor/pianoRoll.cpp @@ -30,11 +30,13 @@ #include "../../../core/conf.h" #include "../../../core/const.h" #include "../../../core/clock.h" +#include "../../../core/action.h" +#include "../../../core/midiEvent.h" #include "../../../core/midiChannel.h" #include "../../../utils/log.h" #include "../../../utils/string.h" #include "../../../utils/math.h" -#include "../../../glue/recorder.h" +#include "../../../glue/actionEditor.h" #include "../../dialogs/actionEditor/baseActionEditor.h" #include "pianoItem.h" #include "noteEditor.h" @@ -48,12 +50,11 @@ using std::vector; namespace giada { namespace v { -gePianoRoll::gePianoRoll(Pixel X, Pixel Y, Pixel W, MidiChannel* ch) +gePianoRoll::gePianoRoll(Pixel X, Pixel Y, Pixel W, m::MidiChannel* ch) : geBaseActionEditor(X, Y, W, 40, ch), pick (0) { position(x(), m::conf::pianoRollY == -1 ? y()-(h()/2) : m::conf::pianoRollY); - rebuild(); } @@ -219,7 +220,7 @@ void gePianoRoll::onAddAction() { Frame frame = m_base->pixelToFrame(Fl::event_x() - x()); int note = yToNote(Fl::event_y() - y()); - c::recorder::recordMidiAction(m_ch->index, note, G_MAX_VELOCITY, frame); + c::actionEditor::recordMidiAction(static_cast(m_ch), note, G_MAX_VELOCITY, frame); m_base->rebuild(); // Rebuild velocityEditor as well } @@ -230,7 +231,7 @@ void gePianoRoll::onAddAction() void gePianoRoll::onDeleteAction() { - c::recorder::deleteMidiAction(static_cast(m_ch), m_action->a1, m_action->a2); + c::actionEditor::deleteMidiAction(static_cast(m_ch), m_action->a1); m_base->rebuild(); // Rebuild velocityEditor as well } @@ -264,7 +265,7 @@ void gePianoRoll::onMoveAction() void gePianoRoll::onResizeAction() { - if (static_cast(m_action)->orphaned) + if (!static_cast(m_action)->isResizable()) return; Pixel ex = Fl::event_x(); @@ -286,10 +287,7 @@ void gePianoRoll::onResizeAction() void gePianoRoll::onRefreshAction() { - namespace cr = c::recorder; - - if (static_cast(m_action)->orphaned) - return; + namespace ca = c::actionEditor; Pixel p1 = m_action->x() - x(); Pixel p2 = m_action->x() + m_action->w() - x(); @@ -303,26 +301,24 @@ void gePianoRoll::onRefreshAction() } else if (m_action->onLeftEdge) { f1 = m_base->pixelToFrame(p1); - f2 = m_action->a2.frame; + f2 = m_action->a2->frame; + if (f1 == f2) // If snapping makes an action fall onto the other + f1 -= G_DEFAULT_ACTION_SIZE; } else if (m_action->onRightEdge) { - f1 = m_action->a1.frame; + f1 = m_action->a1->frame; f2 = m_base->pixelToFrame(p2); + if (f1 == f2) // If snapping makes an action fall onto the other + f2 += G_DEFAULT_ACTION_SIZE; } - assert(f1 != 0 && f2 != 0); + assert(f2 != 0); int note = yToNote(m_action->y() - y()); - int velocity = m::MidiEvent(m_action->a1.iValue).getVelocity(); + int velocity = m_action->a1->event.getVelocity(); - /* TODO - less then optimal. Let's wait for recorder refactoring... */ - - int oldNote = m::MidiEvent(m_action->a1.iValue).getNote(); - cr::deleteMidiAction(static_cast(m_ch), m_action->a1, m_action->a2); - if (cr::midiActionCanFit(m_ch->index, note, f1, f2)) - cr::recordMidiAction(m_ch->index, note, velocity, f1, f2); - else - cr::recordMidiAction(m_ch->index, oldNote, velocity, m_action->a1.frame, m_action->a2.frame); + ca::updateMidiAction(static_cast(m_ch), m_action->a1, note, + velocity, f1, f2); m_base->rebuild(); // Rebuild velocityEditor as well } @@ -349,13 +345,23 @@ Pixel gePianoRoll::snapToY(Pixel p) const } +Pixel gePianoRoll::getPianoItemW(Pixel px, const m::Action* a1, const m::Action* a2) const +{ + if (a2 != nullptr) { // Regular + if (a1->frame > a2->frame) // Ring-loop + return m_base->loopWidth - (px - x()); + return m_base->frameToPixel(a2->frame - a1->frame); + } + return geBaseAction::MIN_WIDTH; // Orphaned +} + + /* -------------------------------------------------------------------------- */ void gePianoRoll::rebuild() { - namespace mr = m::recorder; - namespace cr = c::recorder; + namespace ca = c::actionEditor; /* Remove all existing actions and set a new width, according to the current zoom level. */ @@ -363,23 +369,22 @@ void gePianoRoll::rebuild() clear(); size(m_base->fullWidth, (MAX_KEYS + 1) * CELL_H); - vector actions = cr::getMidiActions(m_ch->index); - for (mr::Composite comp : actions) + for (const m::Action* action : m_base->getActions()) { - m::MidiEvent e1 = comp.a1.iValue; - m::MidiEvent e2 = comp.a2.iValue; + if (action->event.getStatus() == m::MidiEvent::NOTE_OFF) + continue; + + const m::Action* a1 = action; + const m::Action* a2 = action->next; - gu_log("[gePianoRoll::rebuild] ((0x%X, 0x%X, f=%d) - (0x%X, 0x%X, f=%d))\n", - e1.getStatus(), e1.getNote(), comp.a1.frame, - e2.getStatus(), e2.getNote(), comp.a2.frame - ); + assert(a1 != nullptr); // a2 might be null if orphaned - Pixel px = x() + m_base->frameToPixel(comp.a1.frame); - Pixel py = y() + noteToY(e1.getNote()); - Pixel pw = m_base->frameToPixel(comp.a2.frame - comp.a1.frame); + Pixel px = x() + m_base->frameToPixel(a1->frame); + Pixel py = y() + noteToY(a1->event.getNote()); Pixel ph = CELL_H; + Pixel pw = getPianoItemW(px, a1, a2); - add(new gePianoItem(px, py, pw, ph, comp.a1, comp.a2)); + add(new gePianoItem(px, py, pw, ph, a1, a2)); } drawSurface1(); diff --git a/src/gui/elems/actionEditor/pianoRoll.h b/src/gui/elems/actionEditor/pianoRoll.h index 1fdfbbc..aedb641 100644 --- a/src/gui/elems/actionEditor/pianoRoll.h +++ b/src/gui/elems/actionEditor/pianoRoll.h @@ -33,10 +33,13 @@ #include "baseActionEditor.h" -class MidiChannel; namespace giada { +namespace m +{ +class MidiChannel; +} namespace v { class gePianoRoll : public geBaseActionEditor @@ -72,6 +75,7 @@ private: Pixel snapToY(Pixel p) const; int yToNote(Pixel y) const; Pixel noteToY(int n) const; + Pixel getPianoItemW(Pixel x, const m::Action* a1, const m::Action* a2) const; public: @@ -81,7 +85,7 @@ public: static const Pixel CELL_H = 20; static const Pixel CELL_W = 40; - gePianoRoll(Pixel x, Pixel y, Pixel w, MidiChannel* ch); + gePianoRoll(Pixel x, Pixel y, Pixel w, m::MidiChannel* ch); void draw() override; int handle(int e) override; diff --git a/src/gui/elems/actionEditor/sampleAction.cpp b/src/gui/elems/actionEditor/sampleAction.cpp index f4e7d9b..bd69755 100644 --- a/src/gui/elems/actionEditor/sampleAction.cpp +++ b/src/gui/elems/actionEditor/sampleAction.cpp @@ -27,6 +27,7 @@ #include #include "../../../core/const.h" +#include "../../../core/action.h" #include "../../../core/sampleChannel.h" #include "sampleAction.h" @@ -35,7 +36,7 @@ namespace giada { namespace v { geSampleAction::geSampleAction(Pixel X, Pixel Y, Pixel W, Pixel H, - const SampleChannel* ch, m::recorder::action a1, m::recorder::action a2) + const m::SampleChannel* ch, const m::Action* a1, const m::Action* a2) : geBaseAction(X, Y, W, H, ch->mode == ChannelMode::SINGLE_PRESS, a1, a2), m_ch (ch) { @@ -53,14 +54,14 @@ void geSampleAction::draw() fl_rectf(x(), y(), w(), h(), color); } else { - if (a1.type == G_ACTION_KILL) + if (a1->event.getStatus() == m::MidiEvent::NOTE_KILL) fl_rect(x(), y(), MIN_WIDTH, h(), color); else { fl_rectf(x(), y(), MIN_WIDTH, h(), color); - if (a1.type == G_ACTION_KEYPRESS) + if (a1->event.getStatus() == m::MidiEvent::NOTE_ON) fl_rectf(x()+3, y()+h()-11, w()-6, 8, G_COLOR_GREY_4); else - if (a1.type == G_ACTION_KEYREL) + if (a1->event.getStatus() == m::MidiEvent::NOTE_OFF) fl_rectf(x()+3, y()+3, w()-6, 8, G_COLOR_GREY_4); } } diff --git a/src/gui/elems/actionEditor/sampleAction.h b/src/gui/elems/actionEditor/sampleAction.h index 47a8116..e85ab13 100644 --- a/src/gui/elems/actionEditor/sampleAction.h +++ b/src/gui/elems/actionEditor/sampleAction.h @@ -33,22 +33,26 @@ #include "baseAction.h" -class SampleChannel; namespace giada { +namespace m +{ +class SampleChannel; +class Action; +} namespace v { class geSampleAction : public geBaseAction { private: - const SampleChannel* m_ch; + const m::SampleChannel* m_ch; public: - geSampleAction(Pixel x, Pixel y, Pixel w, Pixel h, const SampleChannel* ch, - m::recorder::action a1, m::recorder::action a2); + geSampleAction(Pixel x, Pixel y, Pixel w, Pixel h, const m::SampleChannel* ch, + const m::Action* a1, const m::Action* a2); void draw() override; }; diff --git a/src/gui/elems/actionEditor/sampleActionEditor.cpp b/src/gui/elems/actionEditor/sampleActionEditor.cpp index 25bdd91..ae1cb9e 100644 --- a/src/gui/elems/actionEditor/sampleActionEditor.cpp +++ b/src/gui/elems/actionEditor/sampleActionEditor.cpp @@ -27,11 +27,13 @@ #include #include +#include "../../../core/recorder.h" #include "../../../core/const.h" #include "../../../core/conf.h" +#include "../../../core/action.h" #include "../../../core/sampleChannel.h" #include "../../../utils/log.h" -#include "../../../glue/recorder.h" +#include "../../../glue/actionEditor.h" #include "../../dialogs/actionEditor/baseActionEditor.h" #include "sampleAction.h" #include "sampleActionEditor.h" @@ -43,10 +45,9 @@ using std::vector; namespace giada { namespace v { -geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y, SampleChannel* ch) +geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y, m::SampleChannel* ch) : geBaseActionEditor(x, y, 200, m::conf::sampleActionEditorH, ch) { - rebuild(); } @@ -65,9 +66,9 @@ geSampleActionEditor::~geSampleActionEditor() void geSampleActionEditor::rebuild() { namespace mr = m::recorder; - namespace cr = c::recorder; + namespace ca = c::actionEditor; - const SampleChannel* ch = static_cast(m_ch); + const m::SampleChannel* ch = static_cast(m_ch); /* Remove all existing actions and set a new width, according to the current zoom level. */ @@ -75,21 +76,23 @@ void geSampleActionEditor::rebuild() clear(); size(m_base->fullWidth, h()); - vector comps = cr::getSampleActions(ch); + for (const m::Action* a1 : m_base->getActions()) { - for (mr::Composite comp : comps) { - gu_log("[geSampleActionEditor::rebuild] Action [%d, %d)\n", - comp.a1.frame, comp.a2.frame); - Pixel px = x() + m_base->frameToPixel(comp.a1.frame); + if (a1->event.getStatus() == m::MidiEvent::ENVELOPE || isNoteOffSinglePress(a1)) + continue; + + const m::Action* a2 = a1->next; + + Pixel px = x() + m_base->frameToPixel(a1->frame); Pixel py = y() + 4; Pixel pw = 0; Pixel ph = h() - 8; - if (comp.a2.frame != -1) - pw = m_base->frameToPixel(comp.a2.frame - comp.a1.frame); + if (a2 != nullptr && ch->mode == ChannelMode::SINGLE_PRESS) + pw = m_base->frameToPixel(a2->frame - a1->frame); - geSampleAction* a = new geSampleAction(px, py, pw, ph, ch, comp.a1, comp.a2); - add(a); - resizable(a); + geSampleAction* gsa = new geSampleAction(px, py, pw, ph, ch, a1, a2); + add(gsa); + resizable(gsa); } /* If channel is LOOP_ANY, deactivate it: a loop mode channel cannot hold @@ -130,9 +133,10 @@ void geSampleActionEditor::draw() void geSampleActionEditor::onAddAction() { Frame f = m_base->pixelToFrame(Fl::event_x() - x()); - c::recorder::recordSampleAction(static_cast(m_ch), + c::actionEditor::recordSampleAction(static_cast(m_ch), m_base->getActionType(), f); - rebuild(); + + m_base->rebuild(); } @@ -141,8 +145,9 @@ void geSampleActionEditor::onAddAction() void geSampleActionEditor::onDeleteAction() { - c::recorder::deleteSampleAction(static_cast(m_ch), m_action->a1, m_action->a2); - rebuild(); + c::actionEditor::deleteSampleAction(static_cast(m_ch), m_action->a1); + + m_base->rebuild(); } @@ -186,15 +191,15 @@ void geSampleActionEditor::onResizeAction() void geSampleActionEditor::onRefreshAction() { - namespace cr = c::recorder; + namespace ca = c::actionEditor; - SampleChannel* ch = static_cast(m_ch); + m::SampleChannel* ch = static_cast(m_ch); Pixel p1 = m_action->x() - x(); Pixel p2 = m_action->x() + m_action->w() - x(); Frame f1 = 0; Frame f2 = 0; - int type = m_action->a1.type; + int type = m_action->a1->event.getStatus(); if (!m_action->isOnEdges()) { f1 = m_base->pixelToFrame(p1); @@ -202,21 +207,26 @@ void geSampleActionEditor::onRefreshAction() } else if (m_action->onLeftEdge) { f1 = m_base->pixelToFrame(p1); - f2 = m_action->a2.frame; + f2 = m_action->a2->frame; } else if (m_action->onRightEdge) { - f1 = m_action->a1.frame; + f1 = m_action->a1->frame; f2 = m_base->pixelToFrame(p2); } - /* TODO - less then optimal. Let's wait for recorder refactoring... */ + ca::updateSampleAction(ch, m_action->a1, type, f1, f2); + + m_base->rebuild(); +} - cr::deleteSampleAction(ch, m_action->a1, m_action->a2); - if (cr::sampleActionCanFit(ch, f1, f2)) - cr::recordSampleAction(ch, type, f1, f2); - else - cr::recordSampleAction(ch, type, m_action->a1.frame, m_action->a2.frame); - - rebuild(); + +/* -------------------------------------------------------------------------- */ + + +bool geSampleActionEditor::isNoteOffSinglePress(const m::Action* a) +{ + const m::SampleChannel* ch = static_cast(m_ch); + return ch->mode == ChannelMode::SINGLE_PRESS && a->event.getStatus() == m::MidiEvent::NOTE_OFF; } + }} // giada::v:: \ No newline at end of file diff --git a/src/gui/elems/actionEditor/sampleActionEditor.h b/src/gui/elems/actionEditor/sampleActionEditor.h index 197973f..c71d0ad 100644 --- a/src/gui/elems/actionEditor/sampleActionEditor.h +++ b/src/gui/elems/actionEditor/sampleActionEditor.h @@ -32,15 +32,16 @@ #include "baseActionEditor.h" -class SampleChannel; - - namespace giada { +namespace m +{ +class SampleChannel; +class Action; +} namespace v { class geSampleAction; - class geSampleActionEditor : public geBaseActionEditor { private: @@ -51,9 +52,11 @@ private: void onResizeAction() override; void onRefreshAction() override; + bool isNoteOffSinglePress(const m::Action* a); + public: - geSampleActionEditor(Pixel x, Pixel y, SampleChannel* ch); + geSampleActionEditor(Pixel x, Pixel y, m::SampleChannel* ch); ~geSampleActionEditor(); void draw() override; diff --git a/src/gui/elems/actionEditor/velocityEditor.cpp b/src/gui/elems/actionEditor/velocityEditor.cpp index faaefeb..58bcc6e 100644 --- a/src/gui/elems/actionEditor/velocityEditor.cpp +++ b/src/gui/elems/actionEditor/velocityEditor.cpp @@ -25,15 +25,17 @@ * -------------------------------------------------------------------------- */ +#include #include #include #include "../../../utils/log.h" #include "../../../utils/math.h" #include "../../../core/const.h" #include "../../../core/conf.h" +#include "../../../core/action.h" #include "../../../core/clock.h" #include "../../../core/midiChannel.h" -#include "../../../glue/recorder.h" +#include "../../../glue/actionEditor.h" #include "../../dialogs/actionEditor/baseActionEditor.h" #include "envelopePoint.h" #include "velocityEditor.h" @@ -45,10 +47,9 @@ using std::vector; namespace giada { namespace v { -geVelocityEditor::geVelocityEditor(Pixel x, Pixel y, MidiChannel* ch) +geVelocityEditor::geVelocityEditor(Pixel x, Pixel y, m::MidiChannel* ch) : geBaseActionEditor(x, y, 200, m::conf::velocityEditorH, ch) { - rebuild(); } @@ -82,7 +83,7 @@ void geVelocityEditor::draw() for (int i=0; i(child(i)); if (m_action == nullptr) - p->position(p->x(), valueToY(m::MidiEvent(p->a1.iValue).getVelocity())); + p->position(p->x(), valueToY(p->a1->event.getVelocity())); Pixel x1 = p->x() + side; Pixel y1 = p->y(); Pixel y2 = y() + h(); @@ -105,7 +106,7 @@ Pixel geVelocityEditor::valueToY(int v) const int geVelocityEditor::yToValue(Pixel px) const { - return u::math::map(px, h() - geEnvelopePoint::SIDE, 1, 0, G_MAX_VELOCITY); + return u::math::map(px, h() - geEnvelopePoint::SIDE, 0, 0, G_MAX_VELOCITY); } @@ -114,8 +115,7 @@ int geVelocityEditor::yToValue(Pixel px) const void geVelocityEditor::rebuild() { - namespace mr = m::recorder; - namespace cr = c::recorder; + namespace ca = c::actionEditor; /* Remove all existing actions and set a new width, according to the current zoom level. */ @@ -123,15 +123,17 @@ void geVelocityEditor::rebuild() clear(); size(m_base->fullWidth, h()); - vector actions = cr::getMidiActions(m_ch->index); - for (mr::Composite comp : actions) + for (const m::Action* action : m_base->getActions()) { - gu_log("[geVelocityEditor::rebuild] f=%d\n", comp.a1.frame); + if (action->event.getStatus() == m::MidiEvent::NOTE_OFF) + continue; + + //gu_log("[geVelocityEditor::rebuild] f=%d\n", action->frame); - Pixel px = x() + m_base->frameToPixel(comp.a1.frame); - Pixel py = y() + valueToY(m::MidiEvent(comp.a1.iValue).getVelocity()); + Pixel px = x() + m_base->frameToPixel(action->frame); + Pixel py = y() + valueToY(action->event.getVelocity()); - add(new geEnvelopePoint(px, py, comp.a1)); + add(new geEnvelopePoint(px, py, action)); } resizable(nullptr); @@ -161,7 +163,8 @@ void geVelocityEditor::onMoveAction() void geVelocityEditor::onRefreshAction() { - c::recorder::setVelocity(m_ch, m_action->a1, yToValue(m_action->y() - y())); + c::actionEditor::updateVelocity(static_cast(m_ch), m_action->a1, + yToValue(m_action->y() - y())); m_base->rebuild(); // Rebuild pianoRoll as well } diff --git a/src/gui/elems/actionEditor/velocityEditor.h b/src/gui/elems/actionEditor/velocityEditor.h index 40120c2..f3415dd 100644 --- a/src/gui/elems/actionEditor/velocityEditor.h +++ b/src/gui/elems/actionEditor/velocityEditor.h @@ -32,10 +32,13 @@ #include "baseActionEditor.h" -class MidiChannel; namespace giada { +namespace m +{ +class MidiChannel; +} namespace v { class geEnvelopePoint; @@ -56,7 +59,7 @@ private: public: - geVelocityEditor(Pixel x, Pixel y, MidiChannel* ch); + geVelocityEditor(Pixel x, Pixel y, m::MidiChannel* ch); ~geVelocityEditor(); void draw() override; diff --git a/src/gui/elems/basics/resizerBar.cpp b/src/gui/elems/basics/resizerBar.cpp index 6717d33..6831a09 100644 --- a/src/gui/elems/basics/resizerBar.cpp +++ b/src/gui/elems/basics/resizerBar.cpp @@ -49,23 +49,23 @@ geResizerBar::geResizerBar(int X, int Y, int W, int H, int minSize, bool type) - : Fl_Box (X, Y, W, H), - m_type (type), - m_minSize(minSize), - m_lastPos(0), - m_hover (false) + : Fl_Box (X, Y, W, H), + m_type (type), + m_minSize(minSize), + m_lastPos(0), + m_hover (false) { - if (m_type == VERTICAL) { - m_origSize = H; - labelsize(H); - } - else { - m_origSize = W; - labelsize(W); - } - align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE); - labelfont(FL_COURIER); - visible_focus(0); + if (m_type == VERTICAL) { + m_origSize = H; + labelsize(H); + } + else { + m_origSize = W; + labelsize(W); + } + align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE); + labelfont(FL_COURIER); + visible_focus(0); } @@ -74,64 +74,64 @@ geResizerBar::geResizerBar(int X, int Y, int W, int H, int minSize, bool type) void geResizerBar::handleDrag(int diff) { - Fl_Scroll* grp = static_cast(parent()); - int top; - int bot; - if (m_type == VERTICAL) { - top = y(); - bot = y()+h(); - } - else { - top = x(); - bot = x()+w(); - } - - // First pass: find widget directly above us with common edge - // Possibly clamp 'diff' if widget would get too small.. - - for (int t=0; tchildren(); t++) { - Fl_Widget* wd = grp->child(t); - if (m_type == VERTICAL) { - if ((wd->y()+wd->h()) == top) { // found widget directly above? - if ((wd->h()+diff) < m_minSize) - diff = wd->h() - m_minSize; // clamp - wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height - break; // done with first pass - } - } - else { - if ((wd->x()+wd->w()) == top) { // found widget directly above? - if ((wd->w()+diff) < m_minSize) - diff = wd->w() - m_minSize; // clamp - wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height - break; // done with first pass - } - } - } - - // Second pass: find widgets below us, move based on clamped diff - - for (int t=0; tchildren(); t++) { - Fl_Widget* wd = grp->child(t); - if (m_type == VERTICAL) { - if (wd->y() >= bot) // found widget below us? - wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position - } - else { - if (wd->x() >= bot) - wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h()); - } - } - - // Change our position last - - if (m_type == VERTICAL) - resize(x(), y()+diff, w(), h()); - else - resize(x()+diff, y(), w(), h()); - - grp->init_sizes(); - grp->redraw(); + Fl_Scroll* grp = static_cast(parent()); + int top; + int bot; + if (m_type == VERTICAL) { + top = y(); + bot = y()+h(); + } + else { + top = x(); + bot = x()+w(); + } + + // First pass: find widget directly above us with common edge + // Possibly clamp 'diff' if widget would get too small.. + + for (int t=0; tchildren(); t++) { + Fl_Widget* wd = grp->child(t); + if (m_type == VERTICAL) { + if ((wd->y()+wd->h()) == top) { // found widget directly above? + if ((wd->h()+diff) < m_minSize) + diff = wd->h() - m_minSize; // clamp + wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height + break; // done with first pass + } + } + else { + if ((wd->x()+wd->w()) == top) { // found widget directly above? + if ((wd->w()+diff) < m_minSize) + diff = wd->w() - m_minSize; // clamp + wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height + break; // done with first pass + } + } + } + + // Second pass: find widgets below us, move based on clamped diff + + for (int t=0; tchildren(); t++) { + Fl_Widget* wd = grp->child(t); + if (m_type == VERTICAL) { + if (wd->y() >= bot) // found widget below us? + wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position + } + else { + if (wd->x() >= bot) + wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h()); + } + } + + // Change our position last + + if (m_type == VERTICAL) + resize(x(), y()+diff, w(), h()); + else + resize(x()+diff, y(), w(), h()); + + grp->init_sizes(); + grp->redraw(); } @@ -150,40 +150,40 @@ void geResizerBar::draw() int geResizerBar::handle(int e) { - int ret = 0; - int this_y; - if (m_type == VERTICAL) - this_y = Fl::event_y_root(); - else - this_y = Fl::event_x_root(); - switch (e) { - case FL_FOCUS: - ret = 1; - break; - case FL_ENTER: - ret = 1; - fl_cursor(m_type == VERTICAL ? FL_CURSOR_NS : FL_CURSOR_WE); - m_hover = true; - redraw(); - break; - case FL_LEAVE: - ret = 1; - fl_cursor(FL_CURSOR_DEFAULT); - m_hover = false; - redraw(); - break; - case FL_PUSH: - ret = 1; - m_lastPos = this_y; - break; - case FL_DRAG: - handleDrag(this_y-m_lastPos); - m_lastPos = this_y; - ret = 1; - break; - default: break; - } - return(Fl_Box::handle(e) | ret); + int ret = 0; + int this_y; + if (m_type == VERTICAL) + this_y = Fl::event_y_root(); + else + this_y = Fl::event_x_root(); + switch (e) { + case FL_FOCUS: + ret = 1; + break; + case FL_ENTER: + ret = 1; + fl_cursor(m_type == VERTICAL ? FL_CURSOR_NS : FL_CURSOR_WE); + m_hover = true; + redraw(); + break; + case FL_LEAVE: + ret = 1; + fl_cursor(FL_CURSOR_DEFAULT); + m_hover = false; + redraw(); + break; + case FL_PUSH: + ret = 1; + m_lastPos = this_y; + break; + case FL_DRAG: + handleDrag(this_y-m_lastPos); + m_lastPos = this_y; + ret = 1; + break; + default: break; + } + return(Fl_Box::handle(e) | ret); } diff --git a/src/gui/elems/browser.h b/src/gui/elems/browser.h index 25db7e8..12dc383 100644 --- a/src/gui/elems/browser.h +++ b/src/gui/elems/browser.h @@ -40,12 +40,12 @@ class geBrowser : public Fl_File_Browser private: std::string m_currentDir; - bool m_showHiddenFiles; + bool m_showHiddenFiles; /* normalize Makes sure the std::string never ends with a trailing slash. */ - std::string normalize(const std::string &s); + std::string normalize(const std::string& s); public: @@ -56,7 +56,7 @@ public: /* init Initializes browser and show 'dir' as initial directory. */ - void loadDir(const std::string &dir); + void loadDir(const std::string& dir); /* getSelectedItem Returns the full path or just the displayed name of the i-th selected item. diff --git a/src/gui/elems/mainWindow/keyboard/channel.cpp b/src/gui/elems/mainWindow/keyboard/channel.cpp index 83b37e2..2d563ce 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.cpp +++ b/src/gui/elems/mainWindow/keyboard/channel.cpp @@ -49,7 +49,7 @@ extern gdMainWindow* G_MainWin; using namespace giada; -geChannel::geChannel(int X, int Y, int W, int H, Channel* ch) +geChannel::geChannel(int X, int Y, int W, int H, giada::m::Channel* ch) : Fl_Group(X, Y, W, H, nullptr), ch (ch) { diff --git a/src/gui/elems/mainWindow/keyboard/channel.h b/src/gui/elems/mainWindow/keyboard/channel.h index 7bff681..785b94d 100644 --- a/src/gui/elems/mainWindow/keyboard/channel.h +++ b/src/gui/elems/mainWindow/keyboard/channel.h @@ -31,9 +31,9 @@ #include #include "../../../../core/types.h" +#include "../../../../core/channel.h" -class Channel; class geIdButton; class geChannelStatus; class geButton; @@ -101,7 +101,7 @@ protected: public: - geChannel(int x, int y, int w, int h, Channel* ch); + geChannel(int x, int y, int w, int h, giada::m::Channel* ch); /* reset * reset channel to initial status. */ @@ -137,7 +137,7 @@ public: int getSize(); - Channel* ch; + giada::m::Channel* ch; geIdButton* button; geChannelStatus* status; diff --git a/src/gui/elems/mainWindow/keyboard/channelMode.cpp b/src/gui/elems/mainWindow/keyboard/channelMode.cpp index e47efb0..d67abae 100644 --- a/src/gui/elems/mainWindow/keyboard/channelMode.cpp +++ b/src/gui/elems/mainWindow/keyboard/channelMode.cpp @@ -39,7 +39,7 @@ using namespace giada; -geChannelMode::geChannelMode(int x, int y, int w, int h, SampleChannel *ch, +geChannelMode::geChannelMode(int x, int y, int w, int h, m::SampleChannel *ch, const char *L) : Fl_Menu_Button(x, y, w, h, L), ch(ch) { diff --git a/src/gui/elems/mainWindow/keyboard/channelMode.h b/src/gui/elems/mainWindow/keyboard/channelMode.h index 349474f..248c2f5 100644 --- a/src/gui/elems/mainWindow/keyboard/channelMode.h +++ b/src/gui/elems/mainWindow/keyboard/channelMode.h @@ -41,11 +41,11 @@ private: static void cb_changeMode (Fl_Widget *v, void *p); inline void __cb_changeMode(int mode); - class SampleChannel *ch; + giada::m::SampleChannel *ch; public: - geChannelMode(int x, int y, int w, int h, class SampleChannel *ch, + geChannelMode(int x, int y, int w, int h, giada::m::SampleChannel *ch, const char *l=0); void draw(); }; diff --git a/src/gui/elems/mainWindow/keyboard/channelStatus.cpp b/src/gui/elems/mainWindow/keyboard/channelStatus.cpp index cc9f2d8..e5171e3 100644 --- a/src/gui/elems/mainWindow/keyboard/channelStatus.cpp +++ b/src/gui/elems/mainWindow/keyboard/channelStatus.cpp @@ -65,17 +65,17 @@ void geChannelStatus::draw() if (ch->status == ChannelStatus::PLAY) fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1); else - fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); // status empty + fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); // status empty if (mixer::recording && ch->armed) fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_RED); // take in progress else - if (recorder::active && recorder::canRec(ch, clock::isRunning(), mixer::recording)) - fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE); // action record + if (recorder::isActive()) + fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE); // action recording - /* equation for the progress bar: - * ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */ + /* Equation for the progress bar: + ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */ int pos = ch->getPosition(); if (pos == -1) diff --git a/src/gui/elems/mainWindow/keyboard/channelStatus.h b/src/gui/elems/mainWindow/keyboard/channelStatus.h index ec12d29..8e8bd5e 100644 --- a/src/gui/elems/mainWindow/keyboard/channelStatus.h +++ b/src/gui/elems/mainWindow/keyboard/channelStatus.h @@ -37,10 +37,10 @@ class geChannelStatus : public Fl_Box { public: - geChannelStatus(int X, int Y, int W, int H, class SampleChannel *ch, + geChannelStatus(int X, int Y, int W, int H, giada::m::SampleChannel *ch, const char *L=0); void draw(); - class SampleChannel *ch; + giada::m::SampleChannel *ch; }; diff --git a/src/gui/elems/mainWindow/keyboard/column.cpp b/src/gui/elems/mainWindow/keyboard/column.cpp index 7bfdf49..b63f2f2 100644 --- a/src/gui/elems/mainWindow/keyboard/column.cpp +++ b/src/gui/elems/mainWindow/keyboard/column.cpp @@ -111,7 +111,7 @@ int geColumn::handle(int e) int result = 0; for (string& path : paths) { gu_log("[geColumn::handle] loading %s...\n", path.c_str()); - SampleChannel* c = static_cast(c::channel::addChannel( + m::SampleChannel* c = static_cast(c::channel::addChannel( m_index, ChannelType::SAMPLE, G_GUI_CHANNEL_H_1)); result = c::channel::loadChannel(c, gu_stripFileUrl(path)); if (result != G_RES_OK) { @@ -210,7 +210,7 @@ void geColumn::repositionChannels() /* -------------------------------------------------------------------------- */ -geChannel* geColumn::addChannel(Channel* ch, int size) +geChannel* geColumn::addChannel(m::Channel* ch, int size) { geChannel* gch = nullptr; @@ -218,9 +218,9 @@ geChannel* geColumn::addChannel(Channel* ch, int size) repositioned later on during geColumn::resize(). */ if (ch->type == ChannelType::SAMPLE) - gch = new geSampleChannel(x(), 0, w(), size, static_cast(ch)); + gch = new geSampleChannel(x(), 0, w(), size, static_cast(ch)); else - gch = new geMidiChannel(x(), 0, w(), size, static_cast(ch)); + gch = new geMidiChannel(x(), 0, w(), size, static_cast(ch)); add(gch); @@ -297,7 +297,7 @@ void geColumn::clear(bool full) /* -------------------------------------------------------------------------- */ -Channel* geColumn::getChannel(int i) +m::Channel* geColumn::getChannel(int i) { return static_cast(child(i + 1))->ch; // Skip "add channel" } diff --git a/src/gui/elems/mainWindow/keyboard/column.h b/src/gui/elems/mainWindow/keyboard/column.h index 582b052..b980100 100644 --- a/src/gui/elems/mainWindow/keyboard/column.h +++ b/src/gui/elems/mainWindow/keyboard/column.h @@ -32,7 +32,6 @@ #include -class Channel; class geButton; class geChannel; class geResizerBar; @@ -61,11 +60,11 @@ public: Adds a new channel in this column and set the internal pointer to channel to 'ch'. */ - geChannel* addChannel(Channel* ch, int size); + geChannel* addChannel(giada::m::Channel* ch, int size); int handle(int e) override; void draw() override; - void resize(int x, int y, int w, int h) override; + void resize(int x, int y, int w, int h) override; /* clear Removes all channels from the column. If full==true, delete also the "add new @@ -85,7 +84,7 @@ public: void refreshChannels(); - Channel* getChannel(int i); + giada::m::Channel* getChannel(int i); int getIndex(); void setIndex(int i); bool isEmpty(); diff --git a/src/gui/elems/mainWindow/keyboard/keyboard.cpp b/src/gui/elems/mainWindow/keyboard/keyboard.cpp index 5deaea9..10bfbb9 100644 --- a/src/gui/elems/mainWindow/keyboard/keyboard.cpp +++ b/src/gui/elems/mainWindow/keyboard/keyboard.cpp @@ -37,6 +37,9 @@ #include "keyboard.h" +using namespace giada; + + int geKeyboard::indexColumn = 0; @@ -165,7 +168,7 @@ void geKeyboard::cb_addColumn(Fl_Widget* v, void* p) /* -------------------------------------------------------------------------- */ -geChannel* geKeyboard::addChannel(int colIndex, Channel* ch, int size, bool build) +geChannel* geKeyboard::addChannel(int colIndex, m::Channel* ch, int size, bool build) { geColumn* col = getColumnByIndex(colIndex); @@ -232,7 +235,7 @@ int geKeyboard::handle(int e) if (e == FL_KEYDOWN) { if (Fl::event_key() == FL_BackSpace && !bckspcPressed) { bckspcPressed = true; - glue_rewindSeq(false); // not from GUI + transport::rewindSeq(false); // not from GUI ret = 1; break; } @@ -250,7 +253,7 @@ int geKeyboard::handle(int e) } else if (Fl::event_key() == ' ' && !spacePressed) { spacePressed = true; - glue_startStopSeq(false); // unot from GUI + transport::startStopSeq(false); // unot from GUI ret = 1; break; } diff --git a/src/gui/elems/mainWindow/keyboard/keyboard.h b/src/gui/elems/mainWindow/keyboard/keyboard.h index 8446877..339394e 100644 --- a/src/gui/elems/mainWindow/keyboard/keyboard.h +++ b/src/gui/elems/mainWindow/keyboard/keyboard.h @@ -32,9 +32,9 @@ #include #include #include "../../../../core/const.h" +#include "../../../../core/channel.h" -class Channel; class geButton; class geColumn; class geChannel; @@ -89,7 +89,7 @@ public: Requires Channel (and not geChannel). If build is set to true, also generate the corresponding column if column (index) does not exist yet. */ - geChannel* addChannel(int column, Channel* ch, int size, bool build=false); + geChannel* addChannel(int column, giada::m::Channel* ch, int size, bool build=false); /* addColumn * add a new column to the top of the stack. */ diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp index 9c42a8c..ab2c670 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp @@ -29,6 +29,7 @@ #include "../../../../core/const.h" #include "../../../../core/graphics.h" #include "../../../../core/midiChannel.h" +#include "../../../../core/recorder.h" #include "../../../../utils/gui.h" #include "../../../../utils/string.h" #include "../../../../glue/channel.h" @@ -54,6 +55,7 @@ extern gdMainWindow* G_MainWin; +using namespace giada; using std::string; @@ -87,8 +89,8 @@ void menuCallback(Fl_Widget* w, void* v) { using namespace giada; - geMidiChannel* gch = static_cast(w); - MidiChannel* ch = static_cast(gch->ch); + geMidiChannel* gch = static_cast(w); + m::MidiChannel* ch = static_cast(gch->ch); Menu selectedItem = (Menu) (intptr_t) v; @@ -148,7 +150,7 @@ void menuCallback(Fl_Widget* w, void* v) /* -------------------------------------------------------------------------- */ -geMidiChannel::geMidiChannel(int X, int Y, int W, int H, MidiChannel* ch) +geMidiChannel::geMidiChannel(int X, int Y, int W, int H, m::MidiChannel* ch) : geChannel(X, Y, W, H, ch) { begin(); @@ -218,7 +220,7 @@ void geMidiChannel::cb_button() using namespace giada; if (button->value()) - c::io::keyPress(static_cast(ch), Fl::event_ctrl(), Fl::event_shift(), 0); + c::io::keyPress(static_cast(ch), Fl::event_ctrl(), Fl::event_shift(), 0); } @@ -241,9 +243,9 @@ void geMidiChannel::cb_openMenu() {"Large", 0, menuCallback, (void*) Menu::RESIZE_H3}, {"X-Large", 0, menuCallback, (void*) Menu::RESIZE_H4}, {0}, - {"Rename channel", 0, menuCallback, (void*) Menu::RENAME_CHANNEL}, - {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, - {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, + {"Rename", 0, menuCallback, (void*) Menu::RENAME_CHANNEL}, + {"Clone", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, + {"Delete", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, {0} }; @@ -271,6 +273,8 @@ void geMidiChannel::cb_openMenu() void geMidiChannel::refresh() { setColorsByStatus(ch->status, ch->recStatus); + if (m::recorder::isActive() && ch->armed) + mainButton->setActionRecordMode(); mainButton->redraw(); } @@ -290,7 +294,7 @@ void geMidiChannel::reset() void geMidiChannel::update() { - const MidiChannel* mch = static_cast(ch); + const m::MidiChannel* mch = static_cast(ch); string label; if (mch->name.empty()) diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.h b/src/gui/elems/mainWindow/keyboard/midiChannel.h index b3bfcd4..43b9ce8 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.h +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.h @@ -29,13 +29,11 @@ #define GE_MIDI_CHANNEL_H +#include "../../../../core/midiChannel.h" #include "channel.h" #include "channelButton.h" -class MidiChannel; - - class geMidiChannel : public geChannel { private: @@ -47,7 +45,7 @@ private: public: - geMidiChannel(int x, int y, int w, int h, MidiChannel* ch); + geMidiChannel(int x, int y, int w, int h, giada::m::MidiChannel* ch); void resize(int x, int y, int w, int h) override; diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp index e82446f..785f0f9 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp @@ -30,6 +30,7 @@ #include "../../../../core/clock.h" #include "../../../../core/graphics.h" #include "../../../../core/wave.h" +#include "../../../../core/recorder.h" #include "../../../../core/sampleChannel.h" #include "../../../../glue/io.h" #include "../../../../glue/channel.h" @@ -101,8 +102,8 @@ void menuCallback(Fl_Widget* w, void* v) { using namespace giada; - geSampleChannel* gch = static_cast(w); - SampleChannel* ch = static_cast(gch->ch); + geSampleChannel* gch = static_cast(w); + m::SampleChannel* ch = static_cast(gch->ch); Menu selectedItem = (Menu) (intptr_t) v; @@ -209,7 +210,7 @@ void menuCallback(Fl_Widget* w, void* v) /* -------------------------------------------------------------------------- */ -geSampleChannel::geSampleChannel(int X, int Y, int W, int H, SampleChannel* ch) +geSampleChannel::geSampleChannel(int X, int Y, int W, int H, m::SampleChannel* ch) : geChannel(X, Y, W, H, ch) { begin(); @@ -297,12 +298,12 @@ void geSampleChannel::cb_openMenu() /* If you're recording (input or actions) no menu is allowed; you can't do anything, especially deallocate the channel */ - if (m::mixer::recording || m::recorder::active) + if (m::mixer::recording || m::recorder::isActive()) return; Fl_Menu_Item rclick_menu[] = { {"Input monitor", 0, menuCallback, (void*) Menu::INPUT_MONITOR, - FL_MENU_TOGGLE | FL_MENU_DIVIDER | (static_cast(ch)->inputMonitor ? FL_MENU_VALUE : 0)}, + FL_MENU_TOGGLE | FL_MENU_DIVIDER | (static_cast(ch)->inputMonitor ? FL_MENU_VALUE : 0)}, {"Load new sample...", 0, menuCallback, (void*) Menu::LOAD_SAMPLE}, {"Export sample to file...", 0, menuCallback, (void*) Menu::EXPORT_SAMPLE}, {"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT}, @@ -321,10 +322,10 @@ void geSampleChannel::cb_openMenu() {"Large", 0, menuCallback, (void*) Menu::RESIZE_H3}, {"X-Large", 0, menuCallback, (void*) Menu::RESIZE_H4}, {0}, - {"Rename channel", 0, menuCallback, (void*) Menu::RENAME_CHANNEL}, - {"Clone channel", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, - {"Free channel", 0, menuCallback, (void*) Menu::FREE_CHANNEL}, - {"Delete channel", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, + {"Rename", 0, menuCallback, (void*) Menu::RENAME_CHANNEL}, + {"Clone", 0, menuCallback, (void*) Menu::CLONE_CHANNEL}, + {"Free", 0, menuCallback, (void*) Menu::FREE_CHANNEL}, + {"Delete", 0, menuCallback, (void*) Menu::DELETE_CHANNEL}, {0} }; @@ -344,7 +345,7 @@ void geSampleChannel::cb_openMenu() /* No 'clear start/stop actions' for those channels in loop mode: they cannot have start/stop actions. */ - if (static_cast(ch)->isAnyLoopMode()) + if (static_cast(ch)->isAnyLoopMode()) rclick_menu[(int) Menu::CLEAR_ACTIONS_START_STOP].deactivate(); Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50); @@ -366,7 +367,7 @@ void geSampleChannel::cb_openMenu() void geSampleChannel::cb_readActions() { using namespace giada::c::channel; - toggleReadingActions(static_cast(ch)); + toggleReadingActions(static_cast(ch)); } @@ -382,13 +383,11 @@ void geSampleChannel::refresh() setColorsByStatus(ch->status, ch->recStatus); - if (static_cast(ch)->wave != nullptr) { + if (static_cast(ch)->wave != nullptr) { if (m::mixer::recording && ch->armed) mainButton->setInputRecordMode(); - if (m::recorder::active) { - if (m::recorder::canRec(ch, m::clock::isRunning(), m::mixer::recording)) - mainButton->setActionRecordMode(); - } + if (m::recorder::isActive()) + mainButton->setActionRecordMode(); status->redraw(); // status invisible? sampleButton too (see below) } mainButton->redraw(); @@ -412,7 +411,7 @@ void geSampleChannel::reset() void geSampleChannel::update() { - const SampleChannel* sch = static_cast(ch); + const m::SampleChannel* sch = static_cast(ch); switch (sch->status) { case ChannelStatus::EMPTY: @@ -462,7 +461,7 @@ void geSampleChannel::update() void geSampleChannel::showActionButton() { - readActions->value(static_cast(ch)->readActions); + readActions->value(static_cast(ch)->readActions); readActions->show(); packWidgets(); redraw(); diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.h b/src/gui/elems/mainWindow/keyboard/sampleChannel.h index 0d544c0..0d43db9 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.h +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.h @@ -29,10 +29,10 @@ #define GE_SAMPLE_CHANNEL_H +#include "../../../../core/sampleChannel.h" #include "channel.h" -class SampleChannel; class geChannelMode; class geButton; @@ -50,7 +50,7 @@ private: public: - geSampleChannel(int x, int y, int w, int h, SampleChannel* ch); + geSampleChannel(int x, int y, int w, int h, giada::m::SampleChannel* ch); void resize(int x, int y, int w, int h) override; diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp index 405daa5..c5d6317 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp @@ -64,8 +64,8 @@ int geSampleChannelButton::handle(int e) break; } case FL_PASTE: { - geSampleChannel* gch = static_cast(parent()); - SampleChannel* ch = static_cast(gch->ch); + geSampleChannel* gch = static_cast(parent()); + m::SampleChannel* ch = static_cast(gch->ch); int result = c::channel::loadChannel(ch, gu_trim(gu_stripFileUrl(Fl::event_text()))); if (result != G_RES_OK) G_MainWin->keyboard->printChannelMessage(result); diff --git a/src/gui/elems/mainWindow/mainTransport.cpp b/src/gui/elems/mainWindow/mainTransport.cpp index f61207d..3e83d2d 100644 --- a/src/gui/elems/mainWindow/mainTransport.cpp +++ b/src/gui/elems/mainWindow/mainTransport.cpp @@ -32,6 +32,9 @@ #include "mainTransport.h" +using namespace giada; + + geMainTransport::geMainTransport(int x, int y) : Fl_Group(x, y, 131, 25) { @@ -66,35 +69,35 @@ geMainTransport::geMainTransport(int x, int y) /* -------------------------------------------------------------------------- */ -void geMainTransport::cb_rewind (Fl_Widget *v, void *p) { ((geMainTransport*)p)->__cb_rewind(); } -void geMainTransport::cb_play (Fl_Widget *v, void *p) { ((geMainTransport*)p)->__cb_play(); } -void geMainTransport::cb_recAction(Fl_Widget *v, void *p) { ((geMainTransport*)p)->__cb_recAction(); } -void geMainTransport::cb_recInput (Fl_Widget *v, void *p) { ((geMainTransport*)p)->__cb_recInput(); } -void geMainTransport::cb_metronome(Fl_Widget *v, void *p) { ((geMainTransport*)p)->__cb_metronome(); } +void geMainTransport::cb_rewind (Fl_Widget *v, void *p) { ((geMainTransport*)p)->cb_rewind(); } +void geMainTransport::cb_play (Fl_Widget *v, void *p) { ((geMainTransport*)p)->cb_play(); } +void geMainTransport::cb_recAction(Fl_Widget *v, void *p) { ((geMainTransport*)p)->cb_recAction(); } +void geMainTransport::cb_recInput (Fl_Widget *v, void *p) { ((geMainTransport*)p)->cb_recInput(); } +void geMainTransport::cb_metronome(Fl_Widget *v, void *p) { ((geMainTransport*)p)->cb_metronome(); } /* -------------------------------------------------------------------------- */ -void geMainTransport::__cb_rewind() +void geMainTransport::cb_rewind() { - glue_rewindSeq(true); + c::transport::rewindSeq(true); } /* -------------------------------------------------------------------------- */ -void geMainTransport::__cb_play() +void geMainTransport::cb_play() { - glue_startStopSeq(true); + c::transport::startStopSeq(true); } /* -------------------------------------------------------------------------- */ -void geMainTransport::__cb_recAction() +void geMainTransport::cb_recAction() { using namespace giada::c::io; startStopActionRec(true); @@ -104,7 +107,7 @@ void geMainTransport::__cb_recAction() /* -------------------------------------------------------------------------- */ -void geMainTransport::__cb_recInput() +void geMainTransport::cb_recInput() { using namespace giada::c::io; startStopInputRec(true); @@ -114,9 +117,9 @@ void geMainTransport::__cb_recInput() /* -------------------------------------------------------------------------- */ -void geMainTransport::__cb_metronome() +void geMainTransport::cb_metronome() { - glue_startStopMetronome(true); + c::transport::startStopMetronome(true); } diff --git a/src/gui/elems/mainWindow/mainTransport.h b/src/gui/elems/mainWindow/mainTransport.h index 7461df3..cd92909 100644 --- a/src/gui/elems/mainWindow/mainTransport.h +++ b/src/gui/elems/mainWindow/mainTransport.h @@ -50,12 +50,11 @@ private: static void cb_recAction(Fl_Widget *v, void *p); static void cb_recInput (Fl_Widget *v, void *p); static void cb_metronome(Fl_Widget *v, void *p); - - inline void __cb_rewind (); - inline void __cb_play (); - inline void __cb_recAction(); - inline void __cb_recInput (); - inline void __cb_metronome(); + void cb_rewind (); + void cb_play (); + void cb_recAction(); + void cb_recInput (); + void cb_metronome(); public: diff --git a/src/gui/elems/midiLearner.h b/src/gui/elems/midiLearner.h index 5b226a2..28ac2e6 100644 --- a/src/gui/elems/midiLearner.h +++ b/src/gui/elems/midiLearner.h @@ -31,9 +31,9 @@ #include #include "../../core/midiDispatcher.h" +#include "../../core/channel.h" -class Channel; class gdMidiInputBase; class geMidiLearner; class geBox; @@ -54,7 +54,7 @@ private: /* Channel it belongs to. Might be nullptr if the learner comes from the MIDI input master window. */ - Channel* ch; + giada::m::Channel* ch; geBox* text; geButton* value; @@ -72,9 +72,9 @@ public: struct cbData_t { - gdMidiInputBase* window; - geMidiLearner* learner; - Channel* channel; + gdMidiInputBase* window; + geMidiLearner* learner; + giada::m::Channel* channel; } cbData; /* param @@ -83,7 +83,7 @@ public: uint32_t* param; geMidiLearner(int x, int y, int w, const char* l, - giada::m::midiDispatcher::cb_midiLearn* cb, uint32_t* param, Channel* ch); + giada::m::midiDispatcher::cb_midiLearn* cb, uint32_t* param, giada::m::Channel* ch); void updateValue(); }; diff --git a/src/gui/elems/plugin/pluginElement.cpp b/src/gui/elems/plugin/pluginElement.cpp index c2ad697..fcedf0f 100644 --- a/src/gui/elems/plugin/pluginElement.cpp +++ b/src/gui/elems/plugin/pluginElement.cpp @@ -51,7 +51,7 @@ using std::string; using namespace giada; -gePluginElement::gePluginElement(gdPluginList* gdp, Plugin* p, int X, int Y, int W) +gePluginElement::gePluginElement(gdPluginList* gdp, m::Plugin* p, int X, int Y, int W) : Fl_Group (X, Y, W, 20), m_parentWin(gdp), m_plugin (p) diff --git a/src/gui/elems/plugin/pluginElement.h b/src/gui/elems/plugin/pluginElement.h index 317cd87..8409322 100644 --- a/src/gui/elems/plugin/pluginElement.h +++ b/src/gui/elems/plugin/pluginElement.h @@ -36,7 +36,6 @@ class gdPluginList; -class Plugin; class geIdButton; class geChoice; @@ -45,8 +44,8 @@ class gePluginElement : public Fl_Group { private: - gdPluginList* m_parentWin; - Plugin* m_plugin; + gdPluginList* m_parentWin; + giada::m::Plugin* m_plugin; static void cb_removePlugin(Fl_Widget* v, void* p); static void cb_openPluginWindow(Fl_Widget* v, void* p); @@ -70,7 +69,7 @@ public: geIdButton* shiftDown; geIdButton* remove; - gePluginElement(gdPluginList* gdp, Plugin* p, int x, int y, int w); + gePluginElement(gdPluginList* gdp, giada::m::Plugin* p, int x, int y, int w); }; #endif diff --git a/src/gui/elems/plugin/pluginParameter.cpp b/src/gui/elems/plugin/pluginParameter.cpp index edc804d..81bbb3d 100644 --- a/src/gui/elems/plugin/pluginParameter.cpp +++ b/src/gui/elems/plugin/pluginParameter.cpp @@ -38,10 +38,11 @@ using std::string; +using namespace giada; using namespace giada::c; -gePluginParameter::gePluginParameter(int paramIndex, Plugin* p, int X, int Y, +gePluginParameter::gePluginParameter(int paramIndex, m::Plugin* p, int X, int Y, int W, int labelWidth) : Fl_Group (X, Y, W, G_GUI_UNIT), m_paramIndex(paramIndex), diff --git a/src/gui/elems/plugin/pluginParameter.h b/src/gui/elems/plugin/pluginParameter.h index 140ab15..e07c940 100644 --- a/src/gui/elems/plugin/pluginParameter.h +++ b/src/gui/elems/plugin/pluginParameter.h @@ -35,7 +35,6 @@ #include -class Plugin; class geBox; class geSlider; @@ -47,7 +46,7 @@ private: static const int VALUE_WIDTH = 100; int m_paramIndex; - Plugin* m_plugin; + giada::m::Plugin* m_plugin; geBox* m_label; geSlider* m_slider; @@ -58,7 +57,7 @@ private: public: - gePluginParameter(int paramIndex, Plugin* p, int x, int y, int w, int labelWidth); + gePluginParameter(int paramIndex, giada::m::Plugin* p, int x, int y, int w, int labelWidth); void update(bool changeSlider); }; diff --git a/src/gui/elems/sampleEditor/boostTool.cpp b/src/gui/elems/sampleEditor/boostTool.cpp index 624658d..284b3f4 100644 --- a/src/gui/elems/sampleEditor/boostTool.cpp +++ b/src/gui/elems/sampleEditor/boostTool.cpp @@ -42,7 +42,10 @@ #include "boostTool.h" -geBoostTool::geBoostTool(int X, int Y, SampleChannel* ch) +using namespace giada; + + +geBoostTool::geBoostTool(int X, int Y, m::SampleChannel* ch) : Fl_Group(X, Y, 220, 20), ch (ch) { diff --git a/src/gui/elems/sampleEditor/boostTool.h b/src/gui/elems/sampleEditor/boostTool.h index 1dc2d7c..a922a6e 100644 --- a/src/gui/elems/sampleEditor/boostTool.h +++ b/src/gui/elems/sampleEditor/boostTool.h @@ -32,7 +32,6 @@ #include -class SampleChannel; class geDial; class geInput; class geButton; @@ -43,25 +42,25 @@ class geBoostTool : public Fl_Group { private: - SampleChannel* ch; + giada::m::SampleChannel* ch; - geBox* label; - geDial* dial; - geInput* input; - geButton* normalize; + geBox* label; + geDial* dial; + geInput* input; + geButton* normalize; - static void cb_setBoost(Fl_Widget* w, void* p); - static void cb_setBoostNum(Fl_Widget* w, void* p); - static void cb_normalize(Fl_Widget* w, void* p); - inline void cb_setBoost(); - inline void cb_setBoostNum(); - inline void cb_normalize(); + static void cb_setBoost(Fl_Widget* w, void* p); + static void cb_setBoostNum(Fl_Widget* w, void* p); + static void cb_normalize(Fl_Widget* w, void* p); + void cb_setBoost(); + void cb_setBoostNum(); + void cb_normalize(); public: - geBoostTool(int x, int y, SampleChannel* ch); + geBoostTool(int x, int y, giada::m::SampleChannel* ch); - void refresh(); + void refresh(); }; diff --git a/src/gui/elems/sampleEditor/panTool.cpp b/src/gui/elems/sampleEditor/panTool.cpp index 29e4530..e9478a8 100644 --- a/src/gui/elems/sampleEditor/panTool.cpp +++ b/src/gui/elems/sampleEditor/panTool.cpp @@ -46,7 +46,7 @@ using std::string; using namespace giada; -gePanTool::gePanTool(int x, int y, SampleChannel *ch) +gePanTool::gePanTool(int x, int y, m::SampleChannel* ch) : Fl_Group(x, y, 200, 20), ch (ch) { @@ -94,15 +94,15 @@ void gePanTool::refresh() /* -------------------------------------------------------------------------- */ -void gePanTool::cb_panning (Fl_Widget *w, void *p) { ((gePanTool*)p)->__cb_panning(); } -void gePanTool::cb_panReset(Fl_Widget *w, void *p) { ((gePanTool*)p)->__cb_panReset(); } +void gePanTool::cb_panning (Fl_Widget *w, void *p) { ((gePanTool*)p)->cb_panning(); } +void gePanTool::cb_panReset(Fl_Widget *w, void *p) { ((gePanTool*)p)->cb_panReset(); } /* -------------------------------------------------------------------------- */ -void gePanTool::__cb_panning() +void gePanTool::cb_panning() { c::channel::setPanning(ch, dial->value()); } @@ -111,7 +111,7 @@ void gePanTool::__cb_panning() /* -------------------------------------------------------------------------- */ -void gePanTool::__cb_panReset() +void gePanTool::cb_panReset() { c::channel::setPanning(ch, 0.5f); } \ No newline at end of file diff --git a/src/gui/elems/sampleEditor/panTool.h b/src/gui/elems/sampleEditor/panTool.h index e9d0cf6..fd485f5 100644 --- a/src/gui/elems/sampleEditor/panTool.h +++ b/src/gui/elems/sampleEditor/panTool.h @@ -32,7 +32,6 @@ #include -class SampleChannel; class geDial; class geInput; class geButton; @@ -43,21 +42,21 @@ class gePanTool : public Fl_Group { private: - SampleChannel *ch; + giada::m::SampleChannel* ch; - geBox *label; - geDial *dial; - geInput *input; - geButton *reset; + geBox* label; + geDial* dial; + geInput* input; + geButton* reset; - static void cb_panning (Fl_Widget *w, void *p); - static void cb_panReset(Fl_Widget *w, void *p); - inline void __cb_panning(); - inline void __cb_panReset(); + static void cb_panning (Fl_Widget* w, void* p); + static void cb_panReset(Fl_Widget* w, void* p); + void cb_panning(); + void cb_panReset(); public: - gePanTool(int x, int y, SampleChannel *ch); + gePanTool(int x, int y, giada::m::SampleChannel* ch); void refresh(); }; diff --git a/src/gui/elems/sampleEditor/pitchTool.cpp b/src/gui/elems/sampleEditor/pitchTool.cpp index 0cc1e47..e5eac55 100644 --- a/src/gui/elems/sampleEditor/pitchTool.cpp +++ b/src/gui/elems/sampleEditor/pitchTool.cpp @@ -44,7 +44,7 @@ using namespace giada; -gePitchTool::gePitchTool(int x, int y, SampleChannel* ch) +gePitchTool::gePitchTool(int x, int y, m::SampleChannel* ch) : Fl_Group(x, y, 600, 20), ch (ch) { @@ -90,19 +90,19 @@ void gePitchTool::refresh() /* -------------------------------------------------------------------------- */ -void gePitchTool::cb_setPitch (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitch(); } -void gePitchTool::cb_setPitchToBar (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchToBar(); } -void gePitchTool::cb_setPitchToSong(Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchToSong(); } -void gePitchTool::cb_setPitchHalf (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchHalf(); } -void gePitchTool::cb_setPitchDouble(Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchDouble(); } -void gePitchTool::cb_resetPitch (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_resetPitch(); } -void gePitchTool::cb_setPitchNum (Fl_Widget* w, void* p) { ((gePitchTool*)p)->__cb_setPitchNum(); } +void gePitchTool::cb_setPitch (Fl_Widget* w, void* p) { ((gePitchTool*)p)->cb_setPitch(); } +void gePitchTool::cb_setPitchToBar (Fl_Widget* w, void* p) { ((gePitchTool*)p)->cb_setPitchToBar(); } +void gePitchTool::cb_setPitchToSong(Fl_Widget* w, void* p) { ((gePitchTool*)p)->cb_setPitchToSong(); } +void gePitchTool::cb_setPitchHalf (Fl_Widget* w, void* p) { ((gePitchTool*)p)->cb_setPitchHalf(); } +void gePitchTool::cb_setPitchDouble(Fl_Widget* w, void* p) { ((gePitchTool*)p)->cb_setPitchDouble(); } +void gePitchTool::cb_resetPitch (Fl_Widget* w, void* p) { ((gePitchTool*)p)->cb_resetPitch(); } +void gePitchTool::cb_setPitchNum (Fl_Widget* w, void* p) { ((gePitchTool*)p)->cb_setPitchNum(); } /* -------------------------------------------------------------------------- */ -void gePitchTool::__cb_setPitch() +void gePitchTool::cb_setPitch() { c::channel::setPitch(ch, dial->value()); } @@ -111,7 +111,7 @@ void gePitchTool::__cb_setPitch() /* -------------------------------------------------------------------------- */ -void gePitchTool::__cb_setPitchNum() +void gePitchTool::cb_setPitchNum() { c::channel::setPitch(ch, atof(input->value())); } @@ -120,7 +120,7 @@ void gePitchTool::__cb_setPitchNum() /* -------------------------------------------------------------------------- */ -void gePitchTool::__cb_setPitchHalf() +void gePitchTool::cb_setPitchHalf() { c::channel::setPitch(ch, dial->value()/2); } @@ -129,7 +129,7 @@ void gePitchTool::__cb_setPitchHalf() /* -------------------------------------------------------------------------- */ -void gePitchTool::__cb_setPitchDouble() +void gePitchTool::cb_setPitchDouble() { c::channel::setPitch(ch, dial->value()*2); } @@ -138,7 +138,7 @@ void gePitchTool::__cb_setPitchDouble() /* -------------------------------------------------------------------------- */ -void gePitchTool::__cb_setPitchToBar() +void gePitchTool::cb_setPitchToBar() { // TODO - opaque channel's count c::channel::setPitch(ch, (ch->getEnd()) / (float) m::clock::getFramesInBar()); @@ -148,7 +148,7 @@ void gePitchTool::__cb_setPitchToBar() /* -------------------------------------------------------------------------- */ -void gePitchTool::__cb_setPitchToSong() +void gePitchTool::cb_setPitchToSong() { // TODO - opaque channel's count c::channel::setPitch(ch, ch->getEnd() / (float) m::clock::getFramesInLoop()); @@ -158,7 +158,7 @@ void gePitchTool::__cb_setPitchToSong() /* -------------------------------------------------------------------------- */ -void gePitchTool::__cb_resetPitch() +void gePitchTool::cb_resetPitch() { c::channel::setPitch(ch, G_DEFAULT_PITCH); } diff --git a/src/gui/elems/sampleEditor/pitchTool.h b/src/gui/elems/sampleEditor/pitchTool.h index 7ff9562..0ccfcdf 100644 --- a/src/gui/elems/sampleEditor/pitchTool.h +++ b/src/gui/elems/sampleEditor/pitchTool.h @@ -32,7 +32,6 @@ #include -class SampleChannel; class geDial; class geInput; class geButton; @@ -43,35 +42,35 @@ class gePitchTool : public Fl_Group { private: - SampleChannel *ch; - - geBox *label; - geDial *dial; - geInput *input; - geButton *pitchToBar; - geButton *pitchToSong; - geButton *pitchHalf; - geButton *pitchDouble; - geButton *pitchReset; - - static void cb_setPitch (Fl_Widget *w, void *p); - static void cb_setPitchToBar (Fl_Widget *w, void *p); - static void cb_setPitchToSong(Fl_Widget *w, void *p); - static void cb_setPitchHalf (Fl_Widget *w, void *p); - static void cb_setPitchDouble(Fl_Widget *w, void *p); - static void cb_resetPitch (Fl_Widget *w, void *p); - static void cb_setPitchNum (Fl_Widget *w, void *p); - inline void __cb_setPitch(); - inline void __cb_setPitchToBar(); - inline void __cb_setPitchToSong(); - inline void __cb_setPitchHalf(); - inline void __cb_setPitchDouble(); - inline void __cb_resetPitch(); - inline void __cb_setPitchNum(); + giada::m::SampleChannel* ch; + + geBox* label; + geDial* dial; + geInput* input; + geButton* pitchToBar; + geButton* pitchToSong; + geButton* pitchHalf; + geButton* pitchDouble; + geButton* pitchReset; + + static void cb_setPitch (Fl_Widget* w, void* p); + static void cb_setPitchToBar (Fl_Widget* w, void* p); + static void cb_setPitchToSong(Fl_Widget* w, void* p); + static void cb_setPitchHalf (Fl_Widget* w, void* p); + static void cb_setPitchDouble(Fl_Widget* w, void* p); + static void cb_resetPitch (Fl_Widget* w, void* p); + static void cb_setPitchNum (Fl_Widget* w, void* p); + void cb_setPitch(); + void cb_setPitchToBar(); + void cb_setPitchToSong(); + void cb_setPitchHalf(); + void cb_setPitchDouble(); + void cb_resetPitch(); + void cb_setPitchNum(); public: - gePitchTool(int x, int y, SampleChannel *ch); + gePitchTool(int x, int y, giada::m::SampleChannel* ch); void refresh(); }; diff --git a/src/gui/elems/sampleEditor/rangeTool.cpp b/src/gui/elems/sampleEditor/rangeTool.cpp index 88aa05d..7dae165 100644 --- a/src/gui/elems/sampleEditor/rangeTool.cpp +++ b/src/gui/elems/sampleEditor/rangeTool.cpp @@ -43,7 +43,7 @@ using namespace giada::c; -geRangeTool::geRangeTool(int x, int y, SampleChannel* ch) +geRangeTool::geRangeTool(int x, int y, giada::m::SampleChannel* ch) : Fl_Group(x, y, 280, G_GUI_UNIT), m_ch (ch) { @@ -81,14 +81,14 @@ void geRangeTool::refresh() /* -------------------------------------------------------------------------- */ -void geRangeTool::cb_setChanPos (Fl_Widget* w, void* p) { ((geRangeTool*)p)->__cb_setChanPos(); } -void geRangeTool::cb_resetStartEnd(Fl_Widget* w, void* p) { ((geRangeTool*)p)->__cb_resetStartEnd(); } +void geRangeTool::cb_setChanPos (Fl_Widget* w, void* p) { ((geRangeTool*)p)->cb_setChanPos(); } +void geRangeTool::cb_resetStartEnd(Fl_Widget* w, void* p) { ((geRangeTool*)p)->cb_resetStartEnd(); } /* -------------------------------------------------------------------------- */ -void geRangeTool::__cb_setChanPos() +void geRangeTool::cb_setChanPos() { sampleEditor::setBeginEnd(m_ch, atoi(m_begin->value()), atoi(m_end->value())); static_cast(window())->waveTools->updateWaveform(); // TODO - glue's business! @@ -98,7 +98,7 @@ void geRangeTool::__cb_setChanPos() /* -------------------------------------------------------------------------- */ -void geRangeTool::__cb_resetStartEnd() +void geRangeTool::cb_resetStartEnd() { sampleEditor::setBeginEnd(m_ch, 0, m_ch->wave->getSize() - 1); static_cast(window())->waveTools->updateWaveform(); // TODO - glue's business! diff --git a/src/gui/elems/sampleEditor/rangeTool.h b/src/gui/elems/sampleEditor/rangeTool.h index 17dcf7f..69efdfa 100644 --- a/src/gui/elems/sampleEditor/rangeTool.h +++ b/src/gui/elems/sampleEditor/rangeTool.h @@ -32,7 +32,6 @@ #include -class SampleChannel; class geInput; class geButton; class geBox; @@ -42,7 +41,7 @@ class geRangeTool : public Fl_Group { private: - SampleChannel* m_ch; + giada::m::SampleChannel* m_ch; geBox* m_label; geInput* m_begin; @@ -51,12 +50,12 @@ private: static void cb_setChanPos (Fl_Widget* w, void* p); static void cb_resetStartEnd(Fl_Widget* w, void* p); - inline void __cb_setChanPos(); - inline void __cb_resetStartEnd(); + void cb_setChanPos(); + void cb_resetStartEnd(); public: - geRangeTool(int x, int y, SampleChannel* ch); + geRangeTool(int x, int y, giada::m::SampleChannel* ch); void refresh(); }; diff --git a/src/gui/elems/sampleEditor/shiftTool.cpp b/src/gui/elems/sampleEditor/shiftTool.cpp index 5f0a2f8..0ab12c0 100644 --- a/src/gui/elems/sampleEditor/shiftTool.cpp +++ b/src/gui/elems/sampleEditor/shiftTool.cpp @@ -41,7 +41,7 @@ using namespace giada::c; -geShiftTool::geShiftTool(int x, int y, SampleChannel* ch) +geShiftTool::geShiftTool(int x, int y, giada::m::SampleChannel* ch) : Fl_Group(x, y, 300, G_GUI_UNIT), m_ch (ch) { diff --git a/src/gui/elems/sampleEditor/shiftTool.h b/src/gui/elems/sampleEditor/shiftTool.h index 794ddc3..0b1a6d3 100644 --- a/src/gui/elems/sampleEditor/shiftTool.h +++ b/src/gui/elems/sampleEditor/shiftTool.h @@ -32,7 +32,6 @@ #include -class SampleChannel; class geInput; class geButton; class geBox; @@ -42,7 +41,7 @@ class geShiftTool : public Fl_Group { private: - SampleChannel* m_ch; + giada::m::SampleChannel* m_ch; geBox* m_label; geInput* m_shift; @@ -57,7 +56,7 @@ private: public: - geShiftTool(int x, int y, SampleChannel* ch); + geShiftTool(int x, int y, giada::m::SampleChannel* ch); void refresh(); }; diff --git a/src/gui/elems/sampleEditor/volumeTool.cpp b/src/gui/elems/sampleEditor/volumeTool.cpp index 4176150..d937c9c 100644 --- a/src/gui/elems/sampleEditor/volumeTool.cpp +++ b/src/gui/elems/sampleEditor/volumeTool.cpp @@ -42,9 +42,10 @@ using std::string; +using namespace giada; -geVolumeTool::geVolumeTool(int X, int Y, SampleChannel* ch) +geVolumeTool::geVolumeTool(int X, int Y, m::SampleChannel* ch) : Fl_Group(X, Y, 150, 20), ch (ch) { @@ -82,14 +83,14 @@ void geVolumeTool::refresh() /* -------------------------------------------------------------------------- */ -void geVolumeTool::cb_setVolume (Fl_Widget* w, void* p) { ((geVolumeTool*)p)->__cb_setVolume(); } -void geVolumeTool::cb_setVolumeNum(Fl_Widget* w, void* p) { ((geVolumeTool*)p)->__cb_setVolumeNum(); } +void geVolumeTool::cb_setVolume (Fl_Widget* w, void* p) { ((geVolumeTool*)p)->cb_setVolume(); } +void geVolumeTool::cb_setVolumeNum(Fl_Widget* w, void* p) { ((geVolumeTool*)p)->cb_setVolumeNum(); } /* -------------------------------------------------------------------------- */ -void geVolumeTool::__cb_setVolume() +void geVolumeTool::cb_setVolume() { using namespace giada; @@ -101,7 +102,7 @@ void geVolumeTool::__cb_setVolume() /* -------------------------------------------------------------------------- */ -void geVolumeTool::__cb_setVolumeNum() +void geVolumeTool::cb_setVolumeNum() { using namespace giada; diff --git a/src/gui/elems/sampleEditor/volumeTool.h b/src/gui/elems/sampleEditor/volumeTool.h index c526511..b38323c 100644 --- a/src/gui/elems/sampleEditor/volumeTool.h +++ b/src/gui/elems/sampleEditor/volumeTool.h @@ -32,7 +32,6 @@ #include -class SampleChannel; class geDial; class geInput; class geBox; @@ -42,20 +41,20 @@ class geVolumeTool : public Fl_Group { private: - SampleChannel *ch; + giada::m::SampleChannel* ch; - geBox *label; - geDial *dial; - geInput *input; + geBox* label; + geDial* dial; + geInput* input; - static void cb_setVolume (Fl_Widget *w, void *p); - static void cb_setVolumeNum(Fl_Widget *w, void *p); - inline void __cb_setVolume (); - inline void __cb_setVolumeNum(); + static void cb_setVolume (Fl_Widget* w, void* p); + static void cb_setVolumeNum(Fl_Widget* w, void* p); + void cb_setVolume (); + void cb_setVolumeNum(); public: - geVolumeTool(int x, int y, SampleChannel *ch); + geVolumeTool(int x, int y, giada::m::SampleChannel* ch); void refresh(); }; diff --git a/src/gui/elems/sampleEditor/waveTools.cpp b/src/gui/elems/sampleEditor/waveTools.cpp index 7fdc453..622bb2c 100644 --- a/src/gui/elems/sampleEditor/waveTools.cpp +++ b/src/gui/elems/sampleEditor/waveTools.cpp @@ -115,7 +115,7 @@ void menuCallback(Fl_Widget* w, void* v) /* -------------------------------------------------------------------------- */ -geWaveTools::geWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l) +geWaveTools::geWaveTools(int x, int y, int w, int h, m::SampleChannel* ch, const char* l) : Fl_Scroll(x, y, w, h, l), ch (ch) { @@ -236,13 +236,13 @@ void geWaveTools::openMenu() menu[(int)Menu::TO_NEW_CHANNEL].deactivate(); } - Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50); + Fl_Menu_Button* b = new Fl_Menu_Button(0, 0, 100, 50); b->box(G_CUSTOM_BORDER_BOX); b->textsize(G_GUI_FONT_SIZE_BASE); b->textcolor(G_COLOR_LIGHT_2); b->color(G_COLOR_GREY_2); - const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); + const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b); if (m) m->do_callback(this, m->user_data()); return; diff --git a/src/gui/elems/sampleEditor/waveTools.h b/src/gui/elems/sampleEditor/waveTools.h index d352d38..b224c53 100644 --- a/src/gui/elems/sampleEditor/waveTools.h +++ b/src/gui/elems/sampleEditor/waveTools.h @@ -32,7 +32,6 @@ #include -class SampleChannel; class geWaveform; @@ -44,10 +43,10 @@ private: public: - SampleChannel *ch; - geWaveform *waveform; + giada::m::SampleChannel* ch; + geWaveform* waveform; - geWaveTools(int X,int Y,int W, int H, SampleChannel *ch, const char *L=0); + geWaveTools(int x, int y, int w, int h, giada::m::SampleChannel* ch, const char* l=0); void resize(int x, int y, int w, int h); int handle(int e); diff --git a/src/gui/elems/sampleEditor/waveform.cpp b/src/gui/elems/sampleEditor/waveform.cpp index 7e3c447..5b1b77f 100644 --- a/src/gui/elems/sampleEditor/waveform.cpp +++ b/src/gui/elems/sampleEditor/waveform.cpp @@ -48,7 +48,7 @@ using namespace giada::m; using namespace giada::c; -geWaveform::geWaveform(int x, int y, int w, int h, SampleChannel* ch, const char* l) +geWaveform::geWaveform(int x, int y, int w, int h, giada::m::SampleChannel* ch, const char* l) : Fl_Widget (x, y, w, h, l), m_selection {}, m_ch (ch), diff --git a/src/gui/elems/sampleEditor/waveform.h b/src/gui/elems/sampleEditor/waveform.h index 05faaea..1e7b426 100644 --- a/src/gui/elems/sampleEditor/waveform.h +++ b/src/gui/elems/sampleEditor/waveform.h @@ -33,9 +33,6 @@ #include -class SampleChannel; - - class geWaveform : public Fl_Widget { private: @@ -71,7 +68,7 @@ private: std::vector points; } m_grid; - SampleChannel* m_ch; + giada::m::SampleChannel* m_ch; int m_chanStart; bool m_chanStartLit; int m_chanEnd; @@ -137,7 +134,7 @@ public: static const int ZOOM_IN = -1; static const int ZOOM_OUT = 0; - geWaveform(int x, int y, int w, int h, SampleChannel* ch, const char* l=0); + geWaveform(int x, int y, int w, int h, giada::m::SampleChannel* ch, const char* l=0); ~geWaveform(); void draw() override; diff --git a/src/gui/elems/soundMeter.cpp b/src/gui/elems/soundMeter.cpp index b078fea..036fc0a 100644 --- a/src/gui/elems/soundMeter.cpp +++ b/src/gui/elems/soundMeter.cpp @@ -35,13 +35,13 @@ using namespace giada::m; -geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char *L) - : Fl_Box (x, y, w, h, L), - clip (false), - mixerPeak (0.0f), - peak (0.0f), - dbLevel (0.0f), - dbLevelOld(0.0f) +geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char* l) +: Fl_Box (x, y, w, h, l), + clip (false), + mixerPeak (0.0f), + peak (0.0f), + dbLevel (0.0f), + dbLevelOld(0.0f) { } @@ -51,37 +51,37 @@ geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char *L) void geSoundMeter::draw() { - fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); + fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); - /* peak = the highest value inside the frame */ + /* peak = the highest value inside the frame */ - peak = 0.0f; - float tmp_peak = 0.0f; + peak = 0.0f; + float tmp_peak = 0.0f; - tmp_peak = fabs(mixerPeak); - if (tmp_peak > peak) - peak = tmp_peak; + tmp_peak = fabs(mixerPeak); + if (tmp_peak > peak) + peak = tmp_peak; - clip = peak >= 1.0f ? true : false; // 1.0f is considered clip + clip = peak >= 1.0f ? true : false; // 1.0f is considered clip - /* dBFS (full scale) calculation, plus decay of -2dB per frame */ + /* dBFS (full scale) calculation, plus decay of -2dB per frame */ - dbLevel = 20 * log10(peak); - if (dbLevel < dbLevelOld) - if (dbLevelOld > -G_MIN_DB_SCALE) - dbLevel = dbLevelOld - 2.0f; + dbLevel = 20 * log10(peak); + if (dbLevel < dbLevelOld) + if (dbLevelOld > -G_MIN_DB_SCALE) + dbLevel = dbLevelOld - 2.0f; - dbLevelOld = dbLevel; + dbLevelOld = dbLevel; - /* graphical part */ + /* graphical part */ - float px_level = 0.0f; - if (dbLevel < 0.0f) - px_level = ((w()/G_MIN_DB_SCALE) * dbLevel) + w(); - else - px_level = w(); + float px_level = 0.0f; + if (dbLevel < 0.0f) + px_level = ((w()/G_MIN_DB_SCALE) * dbLevel) + w(); + else + px_level = w(); - fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); - fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !kernelAudio::getStatus() ? G_COLOR_RED_ALERT : G_COLOR_GREY_4); + fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); + fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !kernelAudio::getStatus() ? G_COLOR_RED_ALERT : G_COLOR_GREY_4); } diff --git a/src/gui/elems/soundMeter.h b/src/gui/elems/soundMeter.h index 649f106..02de14e 100644 --- a/src/gui/elems/soundMeter.h +++ b/src/gui/elems/soundMeter.h @@ -36,11 +36,11 @@ class geSoundMeter : public Fl_Box { public: - geSoundMeter(int X, int Y, int W, int H, const char *L=0); + geSoundMeter(int x, int y, int w, int h, const char* l=0); - void draw() override; + void draw() override; - bool clip; + bool clip; float mixerPeak; // peak from mixer private: diff --git a/src/main.cpp b/src/main.cpp index cfa9e32..ec809d9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,74 +25,27 @@ * -------------------------------------------------------------------------- */ -#include -#if defined(__linux__) || defined(__APPLE__) - #include -#endif +#include #include #include "core/init.h" -#include "core/const.h" -#include "core/patch.h" -#include "core/conf.h" -#include "core/midiMapConf.h" -#include "core/mixer.h" -#include "core/clock.h" -#include "core/mixerHandler.h" -#include "core/kernelAudio.h" -#include "core/kernelMidi.h" -#include "core/recorder.h" -#include "utils/gui.h" -#include "utils/time.h" -#include "gui/dialogs/gd_mainWindow.h" -#include "core/pluginHost.h" -pthread_t G_videoThread; -bool G_quit; -gdMainWindow* G_MainWin; - - -void* videoThreadCb(void* arg); +std::atomic G_quit(false); +class gdMainWindow* G_MainWin = nullptr; int main(int argc, char** argv) { - G_quit = false; - - init_prepareParser(); - init_prepareMidiMap(); - init_prepareKernelAudio(); - init_prepareKernelMIDI(); - init_startGUI(argc, argv); - - Fl::lock(); - pthread_create(&G_videoThread, nullptr, videoThreadCb, nullptr); - init_startKernelAudio(); + using namespace giada; -#ifdef WITH_VST - juce::initialiseJuce_GUI(); -#endif + m::init::startup(argc, argv); int ret = Fl::run(); -#ifdef WITH_VST - juce::shutdownJuce_GUI(); -#endif + m::init::shutdown(); - pthread_join(G_videoThread, nullptr); return ret; } -void* videoThreadCb(void* arg) -{ - using namespace giada; - if (m::kernelAudio::getStatus()) - while (!G_quit) { - gu_refreshUI(); - u::time::sleep(G_GUI_REFRESH_RATE); - } - pthread_exit(nullptr); - return 0; -} diff --git a/src/utils/math.h b/src/utils/math.h index 16e6f9f..174978a 100644 --- a/src/utils/math.h +++ b/src/utils/math.h @@ -41,12 +41,18 @@ int quantize(int x, int step); Maps 'x' in range [a, b] to a new range [w, z]. Source: https://en.wikipedia.org/wiki/Linear_equation#Two-point_form*/ -template -Tout map(Tin x, Tin a, Tin b, Tout w, Tout z) +template +TO map(TI x, TI a, TI b, TO w, TO z) { return (((x - a) / (float) (b - a)) * (z - w)) + w; } + +template +T bound(T x, T min, T max, T def) +{ + return x < min || x > max ? def : x; +} }}} // giada::u::math:: diff --git a/tests/audioBuffer.cpp b/tests/audioBuffer.cpp index daf869c..b9d1499 100644 --- a/tests/audioBuffer.cpp +++ b/tests/audioBuffer.cpp @@ -42,7 +42,6 @@ TEST_CASE("AudioBuffer") buffer.free(); - REQUIRE(buffer[0] == nullptr); REQUIRE(buffer.countFrames() == 0); REQUIRE(buffer.countSamples() == 0); REQUIRE(buffer.countChannels() == 0); diff --git a/tests/conf.cpp b/tests/conf.cpp index c392b81..00418d5 100644 --- a/tests/conf.cpp +++ b/tests/conf.cpp @@ -44,7 +44,6 @@ TEST_CASE("conf") conf::recsStopOnChanHalt = true; conf::chansStopOnSeqHalt = false; conf::treatRecsAsLoops = true; - conf::resizeRecordings = false; conf::pluginPath = "path/to/plugins"; conf::patchPath = "path/to/patches"; conf::samplePath = "path/to/samples"; @@ -119,7 +118,6 @@ TEST_CASE("conf") REQUIRE(conf::recsStopOnChanHalt == true); REQUIRE(conf::chansStopOnSeqHalt == false); REQUIRE(conf::treatRecsAsLoops == true); - REQUIRE(conf::resizeRecordings == false); REQUIRE(conf::pluginPath == "path/to/plugins"); REQUIRE(conf::patchPath == "path/to/patches"); REQUIRE(conf::samplePath == "path/to/samples"); diff --git a/tests/patch.cpp b/tests/patch.cpp index 3c52f4e..59f7b39 100644 --- a/tests/patch.cpp +++ b/tests/patch.cpp @@ -26,14 +26,18 @@ TEST_CASE("patch") patch::plugin_t plugin3; #endif - action0.type = 0; - action0.frame = 50000; - action0.fValue = 0.3f; - action0.iValue = 1000; - action1.type = 2; - action1.frame = 589; - action1.fValue = 1.0f; - action1.iValue = 130; + action0.id = 0; + action0.channel = 6; + action0.frame = 4000; + action0.event = 0xFF00FF00; + action0.prev = -1; + action0.next = -1; + action1.id = 1; + action1.channel = 2; + action1.frame = 8000; + action1.event = 0x00000000; + action1.prev = -1; + action1.next = -1; channel1.actions.push_back(action0); channel1.actions.push_back(action1); @@ -179,16 +183,20 @@ TEST_CASE("patch") REQUIRE(channel0.midiOutChan == 5); patch::action_t action0 = channel0.actions.at(0); - REQUIRE(action0.type == 0); - REQUIRE(action0.frame == 50000); - REQUIRE(action0.fValue == Approx(0.3f)); - REQUIRE(action0.iValue == 1000); + REQUIRE(action0.id == 0); + REQUIRE(action0.channel == 6); + REQUIRE(action0.frame == 4000); + REQUIRE(action0.event == 0xFF00FF00); + REQUIRE(action0.prev == -1); + REQUIRE(action0.next == -1); patch::action_t action1 = channel0.actions.at(1); - REQUIRE(action1.type == 2); - REQUIRE(action1.frame == 589); - REQUIRE(action1.fValue == Approx(1.0f)); - REQUIRE(action1.iValue == 130); + REQUIRE(action1.id == 1); + REQUIRE(action1.channel == 2); + REQUIRE(action1.frame == 8000); + REQUIRE(action1.event == 0x00000000); + REQUIRE(action1.prev == -1); + REQUIRE(action1.next == -1); #ifdef WITH_VST patch::plugin_t plugin0 = channel0.plugins.at(0); diff --git a/tests/recorder.cpp b/tests/recorder.cpp index 85deb89..5cc6d4c 100644 --- a/tests/recorder.cpp +++ b/tests/recorder.cpp @@ -1,502 +1,72 @@ #include "../src/core/recorder.h" #include "../src/core/const.h" +#include "../src/core/types.h" +#include "../src/core/action.h" #include -using std::string; -using namespace giada::m; - - TEST_CASE("recorder") { - /* Each SECTION the TEST_CASE is executed from the start. The following - code is exectuted before each SECTION. */ + using namespace giada; + using namespace giada::m; pthread_mutex_t mutex; pthread_mutex_init(&mutex, nullptr); - recorder::init(); - REQUIRE(recorder::frames.size() == 0); - REQUIRE(recorder::global.size() == 0); + recorder::init(&mutex); + recorder::enable(); - SECTION("Test record single action") - { - recorder::rec(0, G_ACTION_KEYPRESS, 50, 1, 0.5f); - - REQUIRE(recorder::frames.size() == 1); - REQUIRE(recorder::frames.at(0) == 50); - REQUIRE(recorder::global.at(0).size() == 1); // 1 action on frame #0 - REQUIRE(recorder::global.at(0).at(0)->chan == 0); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(0).at(0)->frame == 50); - REQUIRE(recorder::global.at(0).at(0)->iValue == 1); - REQUIRE(recorder::global.at(0).at(0)->fValue == 0.5f); - } + REQUIRE(recorder::hasActions(/*ch=*/0) == false); - SECTION("Test record, two actions on same frame") + SECTION("Test record") { - recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f); - recorder::rec(0, G_ACTION_KEYREL, 50, 1, 0.5f); + const int ch = 0; + const Frame f1 = 10; + const Frame f2 = 70; + const MidiEvent e1 = MidiEvent(MidiEvent::NOTE_ON, 0x00, 0x00); + const MidiEvent e2 = MidiEvent(MidiEvent::NOTE_OFF, 0x00, 0x00); - REQUIRE(recorder::frames.size() == 1); // same frame, frames.size must stay 1 - REQUIRE(recorder::frames.at(0) == 50); - REQUIRE(recorder::global.at(0).size() == 2); // 2 actions on frame #0 + const Action* a1 = recorder::rec(ch, f1, e1); + const Action* a2 = recorder::rec(ch, f2, e2); - REQUIRE(recorder::global.at(0).at(0)->chan == 0); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(0).at(0)->frame == 50); - REQUIRE(recorder::global.at(0).at(0)->iValue == 6); - REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f); + REQUIRE(recorder::hasActions(ch) == true); + REQUIRE(a1->frame == f1); + REQUIRE(a2->frame == f2); + REQUIRE(a1->prev == nullptr); + REQUIRE(a1->next == nullptr); + REQUIRE(a2->prev == nullptr); + REQUIRE(a2->next == nullptr); - REQUIRE(recorder::global.at(0).at(1)->chan == 0); - REQUIRE(recorder::global.at(0).at(1)->type == G_ACTION_KEYREL); - REQUIRE(recorder::global.at(0).at(1)->frame == 50); - REQUIRE(recorder::global.at(0).at(1)->iValue == 1); - REQUIRE(recorder::global.at(0).at(1)->fValue == 0.5f); - - SECTION("Test record, another action on a different frame") + SECTION("Test clear actions by channel") { - recorder::rec(0, G_ACTION_KEYPRESS, 70, 1, 0.5f); - - REQUIRE(recorder::frames.size() == 2); - REQUIRE(recorder::frames.at(1) == 70); - REQUIRE(recorder::global.at(0).size() == 2); // 2 actions on frame #0 - REQUIRE(recorder::global.at(1).size() == 1); // 1 actions on frame #1 - REQUIRE(recorder::global.at(1).at(0)->chan == 0); - REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(1).at(0)->frame == 70); - REQUIRE(recorder::global.at(1).at(0)->iValue == 1); - REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f); + const int ch = 1; + const Frame f1 = 100; + const Frame f2 = 200; + const MidiEvent e1 = MidiEvent(MidiEvent::NOTE_ON, 0x00, 0x00); + const MidiEvent e2 = MidiEvent(MidiEvent::NOTE_OFF, 0x00, 0x00); + + recorder::rec(ch, f1, e1); + recorder::rec(ch, f2, e2); + + recorder::clearChannel(/*channel=*/0); + + REQUIRE(recorder::hasActions(/*channel=*/0) == false); + REQUIRE(recorder::hasActions(/*channel=*/1) == true); } - } - - SECTION("Test retrieval") - { - recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f); - recorder::rec(0, G_ACTION_KEYREL, 70, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYPRESS, 50, 6, 0.3f); - recorder::rec(1, G_ACTION_KEYREL, 70, 1, 0.5f); - recorder::rec(2, G_ACTION_KEYPRESS, 100, 6, 0.3f); - recorder::rec(2, G_ACTION_KEYREL, 120, 1, 0.5f); - - /* Give me action on chan 1, type G_ACTION_KEYREL, frame 70. */ - recorder::action *action = nullptr; - REQUIRE(recorder::getAction(1, G_ACTION_KEYREL, 70, &action) == 1); - - REQUIRE(action != nullptr); - REQUIRE(action->chan == 1); - REQUIRE(action->type == G_ACTION_KEYREL); - REQUIRE(action->frame == 70); - REQUIRE(action->iValue == 1); - REQUIRE(action->fValue == 0.5f); - - /* Give me *next* action on chan 0, type G_ACTION_KEYREL, starting from frame 20. - Must be action #2 */ - - REQUIRE(recorder::getNextAction(0, G_ACTION_KEYREL, 20, &action) == 1); - REQUIRE(action != nullptr); - REQUIRE(action->chan == 0); - REQUIRE(action->type == G_ACTION_KEYREL); - REQUIRE(action->frame == 70); - - /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from - frame 200. You are requesting frame outside boundaries. */ - - REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 200, &action) == -1); - - /* Give me *next* action on chan 2, type G_ACTION_KEYPRESS, starting from - frame 100. That action does not exist. */ - REQUIRE(recorder::getNextAction(2, G_ACTION_KEYPRESS, 100, &action) == -2); - } - - SECTION("Test retrieval MIDI") - { - recorder::rec(0, G_ACTION_MIDI, 0, 0x903C3F00, 0.0f); - recorder::rec(1, G_ACTION_MIDI, 0, 0x903D3F00, 0.0f); - recorder::rec(0, G_ACTION_MIDI, 1000, 0x803C2000, 0.0f); - recorder::rec(0, G_ACTION_MIDI, 1050, 0x903C3F00, 0.0f); - recorder::rec(0, G_ACTION_MIDI, 2000, 0x803C3F00, 0.0f); - recorder::rec(1, G_ACTION_MIDI, 90, 0x803D3F00, 0.0f); - recorder::rec(1, G_ACTION_MIDI, 1050, 0x903D3F00, 0.0f); - recorder::rec(1, G_ACTION_MIDI, 2000, 0x803D3F00, 0.0f); - - recorder::action* result = nullptr; - recorder::getNextAction(0, G_ACTION_MIDI, 100, &result, 0x803CFF00, 0x0000FF00); - - REQUIRE(result != nullptr); - REQUIRE(result->frame == 1000); - } - - SECTION("Test deletion, single action") - { - recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f); - recorder::rec(0, G_ACTION_KEYREL, 60, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYPRESS, 70, 6, 0.3f); - recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f); - - /* Delete action #0, don't check values. */ - recorder::deleteAction(0, 50, G_ACTION_KEYPRESS, false, &mutex); - - REQUIRE(recorder::frames.size() == 3); - REQUIRE(recorder::global.size() == 3); - - SECTION("Test deletion checked") + SECTION("Test clear actions by type") { - /* Delete action #1, check values. */ - recorder::deleteAction(1, 70, G_ACTION_KEYPRESS, true, &mutex, 6, 0.3f); - - REQUIRE(recorder::frames.size() == 2); - REQUIRE(recorder::global.size() == 2); + recorder::clearActions(/*channel=*/0, MidiEvent::NOTE_ON); + recorder::clearActions(/*channel=*/0, MidiEvent::NOTE_OFF); + + REQUIRE(recorder::hasActions(/*channel=*/0) == false); } - } - - SECTION("Test deletion, range of actions") - { - recorder::rec(0, G_ACTION_KEYPRESS, 50, 6, 0.3f); - recorder::rec(0, G_ACTION_KEYREL, 60, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYPRESS, 70, 6, 0.3f); - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f); - recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f); - - /* Delete any action on channel 0 of types KEYPRESS | KEYREL between - frames 0 and 200. */ - recorder::deleteActions(0, 0, 200, G_ACTION_KEYPRESS | G_ACTION_KEYREL, &mutex); - - REQUIRE(recorder::frames.size() == 2); - REQUIRE(recorder::global.size() == 2); - REQUIRE(recorder::global.at(0).size() == 1); - REQUIRE(recorder::global.at(1).size() == 1); - - REQUIRE(recorder::global.at(0).at(0)->chan == 1); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(0).at(0)->frame == 100); - REQUIRE(recorder::global.at(0).at(0)->iValue == 6); - REQUIRE(recorder::global.at(0).at(0)->fValue == 0.3f); - - REQUIRE(recorder::global.at(1).at(0)->chan == 1); - REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL); - REQUIRE(recorder::global.at(1).at(0)->frame == 120); - REQUIRE(recorder::global.at(1).at(0)->iValue == 1); - REQUIRE(recorder::global.at(1).at(0)->fValue == 0.5f); - } - - SECTION("Test action presence") - { - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f); - recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f); - - recorder::deleteAction(0, 80, G_ACTION_KEYREL, false, &mutex); - - REQUIRE(recorder::hasActions(0) == false); - REQUIRE(recorder::hasActions(1) == true); - } - - SECTION("Test clear actions by channel") - { - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYPRESS, 100, 6, 0.3f); - recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f); - - recorder::clearChan(1); - - REQUIRE(recorder::hasActions(0) == true); - REQUIRE(recorder::hasActions(1) == false); - REQUIRE(recorder::frames.size() == 1); - REQUIRE(recorder::global.size() == 1); - REQUIRE(recorder::global.at(0).size() == 1); - } - - SECTION("Test clear actions by type") - { - recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYPRESS, 100, 6, 0.3f); - recorder::rec(1, G_ACTION_KEYREL, 120, 1, 0.5f); - - /* Clear all actions of type KEYREL from channel 1. */ - - recorder::clearAction(1, G_ACTION_KEYREL); - - REQUIRE(recorder::hasActions(0) == true); - REQUIRE(recorder::hasActions(1) == false); - REQUIRE(recorder::frames.size() == 1); - REQUIRE(recorder::global.size() == 1); - REQUIRE(recorder::global.at(0).size() == 1); - } - - SECTION("Test clear all") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYREL, 100, 6, 0.3f); - recorder::rec(2, G_ACTION_KILL, 120, 1, 0.5f); - - recorder::clearAll(); - REQUIRE(recorder::frames.size() == 0); - REQUIRE(recorder::global.size() == 0); - } - - SECTION("Test optimization") - { - recorder::rec(0, G_ACTION_KEYPRESS, 20, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYPRESS, 20, 1, 0.5f); - recorder::rec(1, G_ACTION_KEYREL, 80, 1, 0.5f); - - /* Fake frame 80 without actions.*/ - recorder::global.at(1).clear(); - - recorder::optimize(); - - REQUIRE(recorder::frames.size() == 1); - REQUIRE(recorder::global.size() == 1); - REQUIRE(recorder::global.at(0).size() == 2); - } - - SECTION("Test BPM update") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::updateBpm(60.0f, 120.0f, 44100); // scaling up - - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 40); - - recorder::updateBpm(120.0f, 60.0f, 44100); // scaling down - - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 80); - } - - SECTION("Test samplerate update") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYPRESS, 120, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 150, 1, 0.5f); - - recorder::updateSamplerate(44100, 22050); // scaling down - - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 160); - REQUIRE(recorder::frames.at(2) == 240); - REQUIRE(recorder::frames.at(3) == 300); - - recorder::updateSamplerate(22050, 44100); // scaling up - - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 80); - REQUIRE(recorder::frames.at(2) == 120); - REQUIRE(recorder::frames.at(3) == 150); - } - - SECTION("Test expand") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(0, G_ACTION_KILL, 200, 1, 0.5f); - - recorder::expand(300, 600); - - REQUIRE(recorder::frames.size() == 6); - REQUIRE(recorder::global.size() == 6); - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 80); - REQUIRE(recorder::frames.at(2) == 200); - REQUIRE(recorder::frames.at(3) == 300); - REQUIRE(recorder::frames.at(4) == 380); - REQUIRE(recorder::frames.at(5) == 500); - } - - SECTION("Test shrink") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(0, G_ACTION_KILL, 200, 1, 0.5f); - - recorder::shrink(100); - - REQUIRE(recorder::frames.size() == 2); - REQUIRE(recorder::global.size() == 2); - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 80); - } - - SECTION("Test overdub, full overwrite") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 80, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYPRESS, 200, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 400, 1, 0.5f); - - /* Should delete all actions in between and keep the first one, plus a - new last action on frame 500. */ - recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 0, 1024); - recorder::stopOverdub(500, 500, &mutex); - - REQUIRE(recorder::frames.size() == 2); - REQUIRE(recorder::global.size() == 2); - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 500); - REQUIRE(recorder::global.at(0).at(0)->frame == 0); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(1).at(0)->frame == 500); - REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL); - } - - SECTION("Test overdub, left overlap") - { - recorder::rec(0, G_ACTION_KEYPRESS, 100, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 400, 1, 0.5f); - - /* Overdub part of the leftmost part of a composite action. Expected result: - a new composite action. - Original: ----|########| - Overdub: |#######|----- - Result: |#######|----- */ - recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 0, 16); - recorder::stopOverdub(300, 500, &mutex); - - REQUIRE(recorder::frames.size() == 2); - REQUIRE(recorder::global.size() == 2); - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 300); - - REQUIRE(recorder::global.at(0).at(0)->frame == 0); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(1).at(0)->frame == 300); - REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL); - } - - SECTION("Test overdub, right overlap") - { - recorder::rec(0, G_ACTION_KEYPRESS, 000, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 400, 1, 0.5f); - - /* Overdub part of the rightmost part of a composite action. Expected result: - a new composite action. - Original: |########|------ - Overdub: -----|#######|-- - Result: |###||#######|-- */ - recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 100, 16); - recorder::stopOverdub(500, 500, &mutex); - - REQUIRE(recorder::frames.size() == 4); - REQUIRE(recorder::global.size() == 4); - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16) - REQUIRE(recorder::frames.at(2) == 100); - REQUIRE(recorder::frames.at(3) == 500); - - REQUIRE(recorder::global.at(0).at(0)->frame == 0); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(1).at(0)->frame == 84); - REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL); - - REQUIRE(recorder::global.at(2).at(0)->frame == 100); - REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(3).at(0)->frame == 500); - REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_KEYREL); - } - - SECTION("Test overdub, hole diggin'") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 400, 1, 0.5f); - - /* Overdub in the middle of a long, composite action. Expected result: - original action trimmed down plus anther action next to it. Total frames - should be 4. - Original: |#############| - Overdub: ---|#######|--- - Result: |#||#######|--- */ - recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 100, 16); - recorder::stopOverdub(300, 500, &mutex); - - REQUIRE(recorder::frames.size() == 4); - REQUIRE(recorder::global.size() == 4); - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 84); // 100 - bufferSize (16) - REQUIRE(recorder::frames.at(2) == 100); - REQUIRE(recorder::frames.at(3) == 300); - - REQUIRE(recorder::global.at(0).at(0)->frame == 0); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(1).at(0)->frame == 84); - REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL); - - REQUIRE(recorder::global.at(2).at(0)->frame == 100); - REQUIRE(recorder::global.at(2).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(3).at(0)->frame == 300); - REQUIRE(recorder::global.at(3).at(0)->type == G_ACTION_KEYREL); - } - - SECTION("Test overdub, cover all") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 100, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYPRESS, 120, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 200, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYPRESS, 220, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 300, 1, 0.5f); - - /* Overdub all existing actions. Expected result: a single composite one. */ - recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 0, 16); - recorder::stopOverdub(500, 500, &mutex); - - REQUIRE(recorder::frames.size() == 2); - REQUIRE(recorder::global.size() == 2); - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 500); - - REQUIRE(recorder::global.at(0).at(0)->frame == 0); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(1).at(0)->frame == 500); - REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL); - } - - SECTION("Test overdub, null loop") - { - recorder::rec(0, G_ACTION_KEYPRESS, 0, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 500, 1, 0.5f); - - /* A null loop is a loop that begins and ends on the very same frame. */ - recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 300, 16); - recorder::stopOverdub(300, 700, &mutex); - - REQUIRE(recorder::frames.size() == 2); - REQUIRE(recorder::frames.at(0) == 0); - REQUIRE(recorder::frames.at(1) == 284); // 300 - bufferSize (16) - - REQUIRE(recorder::global.at(0).at(0)->frame == 0); - REQUIRE(recorder::global.at(0).at(0)->type == G_ACTION_KEYPRESS); - REQUIRE(recorder::global.at(1).at(0)->frame == 284); - REQUIRE(recorder::global.at(1).at(0)->type == G_ACTION_KEYREL); - } - - SECTION("Test overdub, ring loop") - { - /* A ring loop occurs when you record the last action beyond the end of - the sequencer. - Original: ---|#######|--- - Overdub: #####|------|## - Result: ---|#######||#| */ - - recorder::rec(0, G_ACTION_KEYPRESS, 200, 1, 0.5f); - recorder::rec(0, G_ACTION_KEYREL, 300, 1, 0.5f); - - recorder::startOverdub(0, G_ACTION_KEYPRESS | G_ACTION_KEYREL, 400, 16); - recorder::stopOverdub(250, 700, &mutex); - - REQUIRE(recorder::frames.size() == 4); - REQUIRE(recorder::frames.at(0) == 200); - REQUIRE(recorder::frames.at(1) == 300); - REQUIRE(recorder::frames.at(2) == 400); - REQUIRE(recorder::frames.at(3) == 700); + SECTION("Test clear all") + { + recorder::clearAll(); + REQUIRE(recorder::hasActions(/*channel=*/0) == false); + } } -} +} \ No newline at end of file diff --git a/tests/sampleChannel.cpp b/tests/sampleChannel.cpp index fe7eee3..59ecdbe 100644 --- a/tests/sampleChannel.cpp +++ b/tests/sampleChannel.cpp @@ -29,7 +29,7 @@ TEST_CASE("sampleChannel") REQUIRE(ch.wave == w); REQUIRE(ch.begin == 0); REQUIRE(ch.end == w->getSize() - 1); - REQUIRE(ch.name == w->getBasename()); + REQUIRE(ch.name == ""); } SECTION("begin/end") -- 2.30.2