From e121fe84cd59c88e2b5add675220759d920e4bd0 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Sat, 7 Apr 2007 11:06:52 +0000 Subject: [PATCH] patman, reworked resampling, sample extensions git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@471 0778d3d1-df1d-0410-868b-ea421aaaa00d --- ChangeLog | 65 ++ configure.in | 7 +- data/locale/ca.qm | Bin 109371 -> 110435 bytes data/locale/ca.ts | 43 ++ include/engine.h | 23 +- include/file_browser.h | 19 +- include/main_window.h | 4 +- include/pattern.h | 8 +- include/plugin.h | 7 + include/sample_buffer.h | 67 +- include/sample_play_handle.h | 5 +- plugins/Makefile.am | 1 + .../audio_file_processor.cpp | 121 ++- .../audio_file_processor.h | 19 +- plugins/patman/Makefile.am | 33 + plugins/patman/artwork.png | Bin 0 -> 46996 bytes plugins/patman/logo.png | Bin 0 -> 2090 bytes plugins/patman/loop_off.png | Bin 0 -> 554 bytes plugins/patman/loop_on.png | Bin 0 -> 934 bytes plugins/patman/patman.cpp | 693 ++++++++++++++++++ plugins/patman/patman.h | 130 ++++ plugins/patman/tune_off.png | Bin 0 -> 611 bytes plugins/patman/tune_on.png | Bin 0 -> 623 bytes plugins/polyb302/polyb302.cpp | 24 +- plugins/polyb302/polyb302.h | 4 +- plugins/singerbot/singerbot.cpp | 4 +- src/core/engine.cpp | 33 +- src/core/file_browser.cpp | 64 +- src/core/main_window.cpp | 12 +- src/core/sample_play_handle.cpp | 4 +- src/core/track_container.cpp | 5 +- src/lib/sample_buffer.cpp | 476 ++++++------ src/tracks/bb_track.cpp | 4 +- src/tracks/pattern.cpp | 20 +- 34 files changed, 1547 insertions(+), 348 deletions(-) create mode 100644 plugins/patman/Makefile.am create mode 100644 plugins/patman/artwork.png create mode 100644 plugins/patman/logo.png create mode 100644 plugins/patman/loop_off.png create mode 100644 plugins/patman/loop_on.png create mode 100644 plugins/patman/patman.cpp create mode 100644 plugins/patman/patman.h create mode 100644 plugins/patman/tune_off.png create mode 100644 plugins/patman/tune_on.png diff --git a/ChangeLog b/ChangeLog index 431d931e35..fc11e4b225 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,68 @@ +2007-04-07 Javier Serrano Polo + + * plugins/patman/artwork.png: + * plugins/patman/logo.png: + * plugins/patman/loop_off.png: + * plugins/patman/loop_on.png: + * plugins/patman/Makefile.am: + * plugins/patman/patman.cpp: + * plugins/patman/patman.h: + * plugins/patman/tune_off.png: + * plugins/patman/tune_on.png: + initial release, PatMan instrument plugin + + * include/sample_buffer.h: + * src/lib/sample_buffer.cpp: + - added start/end loop points, different from start/end sample points + - added sample frequency + - reworked resampling, fixes resampling underruns/clicks + - reused try-to-make-relative and try-to-make-absolute file handling + - reused samplerate conversion + + * include/sample_buffer.h: + * include/sample_play_handle.h: + * plugins/audio_file_processor/audio_file_processor.cpp: + * plugins/audio_file_processor/audio_file_processor.h: + * plugins/singerbot/singerbot.cpp: + * src/core/sample_play_handle.cpp: + * src/lib/sample_buffer.cpp: + added per handle state + + * include/engine.h: + * include/file_browser.h: + * include/plugin.h: + * plugins/audio_file_processor/audio_file_processor.cpp: + * plugins/audio_file_processor/audio_file_processor.h: + * src/core/engine.cpp: + * src/core/file_browser.cpp: + * src/core/main_window.cpp: + modularized sample extensions + + * plugins/audio_file_processor/audio_file_processor.cpp: + - check dragged file extension + - accept dragged files from desktop + - fit displayed file name in the background box + + * src/core/track_container.cpp: + fixed track swapping + + * src/tracks/bb_track.cpp: + fixed loading last bb-track name + + * include/pattern.h: + * src/tracks/pattern.cpp: + removed obsolete method + + * plugins/polyb302/polyb302.cpp: + * plugins/polyb302/polyb302.h: + simplified inclusions and comments + + * include/main_window.h: + cosmetic, grouped methods + + * data/locale/ca.ts: + updated translation + 2007-04-01 Tobias Doerffel * src/lmms_single_source.cpp: diff --git a/configure.in b/configure.in index c5a3ab63b0..10c0e87673 100644 --- a/configure.in +++ b/configure.in @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT(lmms, 0.2.1-svn20070328, lmms-devel/at/lists/dot/sf/dot/net) -AM_INIT_AUTOMAKE(lmms, 0.2.1-svn20070328) +AC_INIT(lmms, 0.2.1-svn20070407, lmms-devel/at/lists/dot/sf/dot/net) +AM_INIT_AUTOMAKE(lmms, 0.2.1-svn20070407) AM_CONFIG_HEADER(config.h) @@ -590,8 +590,9 @@ AC_CONFIG_FILES([Makefile plugins/live_tool/Makefile plugins/midi_import/Makefile plugins/organic/Makefile - plugins/polyb302/Makefile + plugins/patman/Makefile plugins/plucked_string_synth/Makefile + plugins/polyb302/Makefile plugins/singerbot/Makefile plugins/stk/Makefile plugins/stk/mallets/Makefile diff --git a/data/locale/ca.qm b/data/locale/ca.qm index 6d81a7767231e56687b24830dfd762acbc9e65f4..9285e681aa3e5ccc5f8aa36c935e6fca01a05ea8 100644 GIT binary patch delta 8235 zcmajkdt6P~|3C2eI{R|=+5221Mns~Fa%qww$z8ckQ7X4lgmghjxz`aPau0=>Fc_Ce zCTfI8GKzASYvVqH$$c^m`Myr)%x6BIKYowjc|7X5-s|kW*L$tK*4}5IbL5)%pi->g z<>zrWr7yl)SJtfY@zGn4-RVWtOgd1%u4wG2A!;yyg44f-MheadA+i}pAz@RAY=dA7 zoJf(yIdFl+{S>7tKzxcOa6 zYVoYuc2mr{>FK2VP9nDrR9xi@f1Gr7SL>=Sl=^-4iqcNY}f1gU^-jJD21G)P!zPJ^fz@#OA!!=AZ zCLb1=?WAYAx=6TFe`XVh1A820%?DqEuUN~^*+iXXi$TmSYX#y}%p-G&x@^XVmSyE*Je8$#Xi6iPUm>pb}0n=FNK8#?`Qg(HN z9T}L`*(!nf;o73$|zXCZB{8*^9+q za2ml`eF|TxF7?Ia4AQF}#CC%oss|^I zz!=qoiU7Dt_4M~}_*_-hZz0hTPt~ic1UO#x_AEL!WSy$o_%sKFeAWA6WH{s=$4xU? zV|HkFt}bs)G;|7AZ;vQ&3hx=IEX|cxah;J<( z=G@oRBl=e4jP83*py0rDc0L35irx@D$+*Sn%hIZh#rP#qzZs$y#;xW&-%tmvG#@caNW6+_o#;#n}x(pOb zxdRTXiN-bHP8~~yx4E*gSfVg%u6z<^b=XMmybosm_|@EnA{=P^b?$P zAo3XZ*Ka=&MeX6~7S zX5&=MILJ>B2f%xL%;^=d#_X&Xe9U{)&+f?2NC<;N`5E(z;dG1p_*vftBEG`M_ra;1 z{fuAWQBE{R#V5}+VzSI>%P&p+5qj}UHzJWap%xeMOZVYAFlUp+vlc(_>HUuq&24CL zpv5GMCoDeaH#DA16tD2xveB{lSbkSu%#rv_{O+lHh!TF_GmR@=qfo|Yu0SFQ58>t3*e&z)Hxe&lmkBICp+d~S6ZbmI%I;`)&2&zIi8Tu4mj&w69O z1q=A|(^0=*4}ZZ4*OLWB{B>(bSjk_{FD6Q|;;$b=!bz_D%>|g0N&Wff5!;9sW*PaK zL#S9-CeV;9qD8Wx#I}Rsf_4jrezDf#7(v&g2JvRWYJ)RTa)wYp+Zz5RGze@&v_!Pn zN3bnlfq1IW%oW=&xhpujE+AU^PH2(+oG9gM!MVXkqGhQ0 z;c^n(Z<%Z$xZgqjau=cd{sibPc#UZfhY4O$n6%4p3Es1j@bb4ppX?B#mCi!HPU!hc zFTp_aBU z3pqs@aItW(QGNKA*>p#tct0jvx`%Mab16&^&UvGO^mW3y5%b||;aZmtL>m<0`jkRw zFTC<9g5E;)FH2y8@M%PE4CRL7BJCUqt3}bJoM@w`XyuJ_v}vBj6Jp)>-iV)wjf*0Q zzH<`oDlnAa4Huiv#45?C6dk;K6Kyt#EvFYlSFzn1B(iys*ltq@j1fD|!H90TD0U7< zJ6oQMonNdV+PYipvE8@Zg77vL7 zH=};Li#X^9B(#03=oeNFv&4W*+>CyFBL-$(CfeyI4voUd?c67hXnm1r*M0HZHK{~< zoW)W7W4tY>7noy%QIi z?wVOnVzR?JI9y!T2Z>}Y6;~a>y(8<0xFr(_XWbQdc>D(6i@TTORP662?v224_Ky;? zkDrA1MPp%j529?fXm)`3i_<*B@nXq0H{f^Tr8;}y9r3byF#IUqaKk;~fP+}MHUt=5{JSn@inwKdow&tF}K=ipOyo%PN{KWi)wRyVS*L7bv)G#m#w{JYw=2&?Ca zhq|SL?T(DGn5lMtX@~fsx>a9nf0R?VS>{D_Y`wZeG_D87%G8}@Y+qpPr1rGIq$wDs z?m8J67i?7bS{(w9nLX~O?tlC^(eX01@6XfVLv_%u{zNCdE#|66?85$qR_d`Av3d%f z)DeBqaN%V2j1!m>h0E0Q1|xx@RP~Yy%>Lpr>h+p>aGg4BO(U%T;-Acg;!o<0QQe46 zyQ(*t?)#^2nEfT%>={v=*}NaonG|)_01u*)PU_QNVf&Iq^=Y@;aIO0ExFvA6#fR!& zr(Yo|t*^ej8$(~}s(#jU8T>|Fos6EBE>(XLPh$O--jh^e$oOnyi5r0fo(+=30Ga5V zB#D0_k#p@Njp@;4R2tXmi;0*p3i{QQcEB7xO}+OHo%+c zd@qY}Qu~Mph|{IMUtzMJKP2^?*$iHh`l(Ip|H9u=|GzXu7hNTPJIv0Dm!;s6=R}uW zrLo-wqAP+lZZRg`l~xv4OX1!O@$XWU*IJ^hi>2tPSK-gn^Z@L4t%)=*472^3kCbS= z15T3?oBRnkN{Q*F`a)@efNTG?$I=2%BX+E4AT4x!2;HPbn+sr~l+v#^%$8EZ(UXc_ zq_sOSLKPpRv=uFgt~*F+rhCKneir9QTlh7IGo)P)uxhT~k+T09M|8tgI+*knMoPK! z2Eh4J-e%k*ZmOmHS$m+9ly7_#fQ&G zccTZl{*p?oF+#Vkq_dZUp_9eo(xtz{5YLu=AC6DSw+~BC&)y`uvt6Uv6%7kD+I{zl ze!Z@#r^6(?YtT4u$B5tUrZJie(=;ubV6xoJ&^UJz;4_Wu9ebjCZ8YsJBC&gsnvP4n z;TemcHJuir!TVoY4AOYyV&v|}YP>q&9N)jH@eQvbdcbS^9Wt!pTSYE$znuz`7u(@VRn@2=Xsx&j~S`a;LrAhS2fqgYAHm-oN znsr0az#m&R>w95xR-HEc!d|of32r_w!!%o3cZ2gancQJ`NVE3~B=RakvwuSYOwk-z zgVpfX=%vY-n~K6D%|V9{n65c80|~r+q&d@7Ao}yIrbNsjs&?0u|BAReN^_|*o_4EO zYA)@;IjugaxttaapJ{H4OdxvaXz_^IzeZZD)>NJ-CVC&Qx!bq|UNCF?&`k44kM;Xu zq~^_M4$jd0H4>BSL%PNDnomJ*5Le5phv>oIEoI&TBk^~X%)g%ix5;u2E{fM<;3u`-sM6DF>b2dy~}fAd>^^bqFHdN-0zP|@CVs1^*y{L2V|5I6O!d%S3Nv0 zkMeIwOmdaO4mXD1%j4@|KPguZcWeQl$&6f3vd^y8T1MkS2 z10NF8I?6kPf?#iX=lnQ0M$TStY=^=E`OsPPSeq*!T^0-P$wv>KfFI<7lx@Ux&hqiX z1lUbJnFsYHa_OOLVuls+*|h>ZB%eK13a`m!$!OT{w|v3xXJU2i<;z}IVR!jToq=$^ ze8=k>SSVM{MtfEp{hw*8Ym_To#rLFRoSfidww=DxjUM2WGhRm))8J&~`Un}9>Dq>CB zD-#BI!Ar^{+=f}xKh4@ZSqxI*N;(s>k5OhAy1`UszD)wLW*3y?3pjo(;bo_Lt+ZRKO1KZ&_1N)3yIJ+y*^9=UDSN)xoi+Bee5Ma9rp zYt^U#PS;vBFl{YTLfSsW5i*(su0{ zf{L-)t|c>wx%bohtcrpu+95U8#60S0$GVJ#!P>D;Fj5||+HvQ0z+#JUw9y?9cm6^f zyF-BewR7jAhn;6?muZ~ge(l=5Phh!r&zyW>p3d4mn~{j8k2X8M1nb{3ytcqRFKY8& zU=nt5(jMDd1bww9p4@;xX-_q%B-XWowxmuDe5@@=enYIAmG<1PHL#1t6m2=(M10b0 zcWdo=zr)13`)F^ttRvP#t*u;N3jMVAJeIseP@6|9Hu z+UJi4!7n3)pf`PipbC$y*-4y3&Vgp27Vg^RSr?oC|i^;*dq(CI@GfTH9$cmWn&$^VAS74QH z)v{t@0~=ZN*QFI)Mm$rOVf;{lLZNPF5-vo3KCnHUq{}faOTQJmlQkM*{!Miyxh`lf$p|*FStclnVbr%bPw{%hy}*zUg9fR7Pv#N-VsA=$d`Kc zFGy&Juf+p;ql^zAY)Fk>+3|+hut8?|;7=ysjU1#D&<{ME&qnHN?h!tq-okfy31LP~CB2 z;k-U{TnL=1ADa^n)Ah0YuwQtd*@%G_Uzm+tqBoA4MJ%e0e&L`gFhIZGt|v4`>9f0F z{YS0V=eEZcD(bYwPx_+=3W-f{)c-OXLq1`##d7_b^m1Yo1^w;EI5jcz^uOf~guC@` zYtX>7yZZM@xHrTN)z=89k6UI?$yk0%Fsf8*FwgBsTM&q49Eb$T+K>xiIUv z+1Zl}cD{cRnyxG%Zg6_|$nfBF4zYzc zhG$>jgB=X75+A?>!`n?RFcULq;mw>&_5&NZwxT{1NwGAO!YGEOkv;jLG>$^)HI z6}iw9M6u*7P;f_0j%}y%z%T7-5RJk1Vb~^=CgNRldCEWg3Z^exa~>{xqH9!-d}%U{ zXxg?XjYn(I6e^g$Vod$Kv3btZjd8Y`=KmZo_1CKUb!uA|OkcY&e2}BYsTZfz&xy-@ zBsKrbsfq`%nJZYb>&!Wa)b(dW)7&+n0hUKrlV$ni5w!)2NwaKm-70#uv=eR-Nar~(eo;7HwF8|riKLBrhb?tr_JVAJ@+TI2MEQE zvDh_^+F&!k+SBKY|IvuX!x*}Y(lj&^L({QcOsX=#A=Q1PK5euIvq=qnSx0mcOt;fK zyC8N&usZesYP9#iT`=`E0LQYY|9=jl|4ai@CTSPLS$z)kDDCrF)*>~kt=`l)Zk2ge zBiQ@C+!*#Qt=Tr#VEs1Mh1r>k^@SFg7Hv&M2iCFn&4yi3^Y04}owcuT$j|h)t!7AV zIGUnNVN*k#<^6cLkXD*1t<#*gGi_R%ZA{y9yX9>Y`)kX1^!c9`9)0jCrc3`*YYHmp z0sUb*U|WaR2=S2izb~f#H)eX%hK(CB{BFtYP4%p$>A&g4-0;`6_I-QS6Xm|ra z$Raw%Mx+&PXZ63qld|deKsNWkH#C(Q9_0Rcv1GP_tz(=2iA}N{=7?ol%t^bqgEdt0 z%^@~8nYMZdvu;}YPlZW_pA`Q4#je-x#qOALcCukl{(Y0-mnZWJ=hBob`->YL|BYN9Ltdri8{$3XKk!6&)Kvd3AR( HXa4^HDu-zt delta 7535 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 + + patmanSynth + + Open other patch + Obre altre pedaç + + + Click here to open another patch-file. Loop and Tune settings are not reset. + Pica aquí per a obrir un altre fitxer pedaç. La configuració de Bucle i Afina no es reinicia. + + + Loop mode + Mode Bucle + + + Here you can toggle the Loop mode. If enabled, PatMan will use the loop information available in the file. + Aquí pots canviar el mode Bucle. Si és actiu, PatMan farà servir la informació de bucle disponible al fitxer. + + + Tune mode + Mode Afina + + + Here you can toggle the Tune mode. If enabled, PatMan will tune the sample to match the note's frequency. + Aquí pots canviar el mode Afina. Si és actiu, PatMan afinarà la mostra a la freqüència de la nota. + + + No file selected + Cap fitxer seleccionat + + + Open patch file + Obre fitxer pedaç + + + Patch-Files (*.pat) + Fitxers Pedaç (*.pat) + + pattern @@ -3006,6 +3045,10 @@ usa la roda del ratolí per a ajustar el volum d'un pas Incomplete polyphonic immitation tb303 Imitació polifònica incompleta tb303 + + GUS-compatible patch instrument + Instrument de pedaç compatible GUS + polyb302Synth diff --git a/include/engine.h b/include/engine.h index f52c52a99a..a577474549 100644 --- a/include/engine.h +++ b/include/engine.h @@ -1,7 +1,7 @@ /* * engine.h - engine-system of LMMS * - * Copyright (c) 2006 Tobias Doerffel + * Copyright (c) 2006-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -30,6 +30,16 @@ #include #endif +#ifdef QT4 + +#include + +#else + +#include + +#endif + class automationEditor; class bbEditor; class projectJournal; @@ -124,6 +134,11 @@ public: } void updateFramesPerTact64th( void ); + const QMap & sampleExtensions( void ) + { + return( m_sample_extensions ); + } + private: bool m_hasGUI; @@ -141,7 +156,11 @@ private: #ifdef LADSPA_SUPPORT ladspa2LMMS * m_ladspaManager; #endif - + + QMap m_sample_extensions; + + void load_extensions( void ); + } ; diff --git a/include/file_browser.h b/include/file_browser.h index 28933c93fa..0948d2b9ff 100644 --- a/include/file_browser.h +++ b/include/file_browser.h @@ -133,13 +133,15 @@ private: -class directory : public Q3ListViewItem +class directory : public Q3ListViewItem, public engineObject { public: directory( Q3ListView * _parent, const QString & _filename, - const QString & _path, const QString & _filter ); + const QString & _path, const QString & _filter, + engine * _engine ); directory( directory * _parent, const QString & _filename, - const QString & _path, const QString & _filter ); + const QString & _path, const QString & _filter, + engine * _engine ); void setOpen( bool ); void setup( void ); @@ -187,13 +189,15 @@ private: -class fileItem : public Q3ListViewItem +class fileItem : public Q3ListViewItem, public engineObject { public: fileItem( Q3ListView * _parent, const QString & _name, - const QString & _path ); + const QString & _path, + engine * _engine ); fileItem( Q3ListViewItem * _parent, const QString & _name, - const QString & _path ); + const QString & _path, + engine * _engine ); inline QString fullName( void ) const { @@ -216,6 +220,9 @@ public: return( m_type ); } + QString extension( void ); + static QString extension( const QString & _file ); + private: void initPixmapStuff( void ); diff --git a/include/main_window.h b/include/main_window.h index a68ea71bad..5e3691f91f 100644 --- a/include/main_window.h +++ b/include/main_window.h @@ -144,6 +144,8 @@ private: void finalize( void ); + bool have_www_browser( void ); + QWorkspace * m_workspace; @@ -169,8 +171,6 @@ private: QMenu * m_tools_menu; vlist m_tools; - bool have_www_browser( void ); - friend class engine; diff --git a/include/pattern.h b/include/pattern.h index 904c4c7d46..c680502bd9 100644 --- a/include/pattern.h +++ b/include/pattern.h @@ -2,7 +2,7 @@ * pattern.h - declaration of class pattern, which contains all informations * about a pattern * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -144,12 +144,6 @@ public: return( m_frozenPattern ); } - // if channel-track recognizes that this pattern is frozen, it calls - // this instead of playing all the notes - void FASTCALL playFrozenData( sampleFrame * _ab, - const f_cnt_t _start_frame, - const fpab_t _frames ); - // settings-management virtual void FASTCALL saveSettings( QDomDocument & _doc, QDomElement & _parent ); diff --git a/include/plugin.h b/include/plugin.h index 3ad0da6560..c54ea3091c 100644 --- a/include/plugin.h +++ b/include/plugin.h @@ -144,6 +144,13 @@ public: { } + virtual const QStringList & supportedExtensions( void ) + { + static QStringList no_extensions; + return( no_extensions ); + } + + protected: const plugin::pluginTypes m_type; } diff --git a/include/sample_buffer.h b/include/sample_buffer.h index 5af02eff72..9071b0a4be 100644 --- a/include/sample_buffer.h +++ b/include/sample_buffer.h @@ -70,6 +70,26 @@ public: DOTS } ; + + class handleState + { + public: + handleState( bool _varying_pitch = FALSE ); + virtual ~handleState(); + + + private: + f_cnt_t m_frame_index; + const bool m_varying_pitch; +#ifdef HAVE_SAMPLERATE_H + SRC_STATE * m_resampling_data; +#endif + + friend class sampleBuffer; + + } ; + + // constructor which either loads sample _audio_file or decodes // base64-data out of string sampleBuffer( engine * _engine, const QString & _audio_file = "", @@ -80,11 +100,10 @@ public: virtual ~sampleBuffer(); - bool FASTCALL play( sampleFrame * _ab, const f_cnt_t _start_frame, + bool FASTCALL play( sampleFrame * _ab, handleState * _state, const fpab_t _frames, const float _freq = BASE_FREQ, - const bool _looped = FALSE, - void * * _resampling_data = NULL ); + const bool _looped = FALSE ); void FASTCALL visualize( QPainter & _p, const QRect & _dr, const QRect & _clip, @@ -110,6 +129,16 @@ public: return( m_endFrame ); } + void setLoopStartFrame( f_cnt_t _start ) + { + m_loop_startFrame = _start; + } + + void setLoopEndFrame( f_cnt_t _end ) + { + m_loop_endFrame = _end; + } + inline f_cnt_t frames( void ) const { return( m_frames ); @@ -125,13 +154,21 @@ public: return( m_reversed ); } + inline float frequency( void ) const + { + return( m_frequency ); + } + + inline void setFrequency( float _freq ) + { + m_frequency = _freq; + } + inline const sampleFrame * data( void ) const { return( m_data ); } - static void FASTCALL deleteResamplingData( void * * _ptr ); - QString openAudioFile( void ) const; QString & toBase64( QString & _dst ) const; @@ -152,6 +189,9 @@ public: _dst_sr, _buf->eng() ) ); } + void normalize_sample_rate( const sample_rate_t _src_sr, + bool _keep_settings = FALSE ); + inline sample_t userWaveSample( const float _sample ) { // Precise implementation @@ -182,6 +222,9 @@ public: m_dataMutex.unlock(); } + static QString tryToMakeRelative( const QString & _file ); + static QString tryToMakeAbsolute( const QString & _file ); + public slots: void setAudioFile( const QString & _audio_file ); @@ -195,8 +238,6 @@ public slots: private: void FASTCALL update( bool _keep_settings = FALSE ); - static QString tryToMakeRelative( const QString & _file ); - #ifdef SDL_SDL_SOUND_H f_cnt_t FASTCALL decodeSampleSDL( const char * _f, @@ -224,20 +265,24 @@ private: f_cnt_t m_frames; f_cnt_t m_startFrame; f_cnt_t m_endFrame; + f_cnt_t m_loop_startFrame; + f_cnt_t m_loop_endFrame; float m_amplification; bool m_reversed; + float m_frequency; QMutex m_dataMutex; #ifdef HAVE_SAMPLERATE_H void initResampling( void ); - void quitResampling( void ); - SRC_STATE * createResamplingContext( void ); - static void FASTCALL destroyResamplingContext( SRC_STATE * _context ); SRC_DATA m_srcData; - SRC_STATE * m_srcState; #endif + sampleFrame * m_sample_fragment; + sampleFrame * getSampleFragment( f_cnt_t _start, f_cnt_t _frames, + bool _looped ); + f_cnt_t getLoopedIndex( f_cnt_t _index ); + signals: void sampleUpdated( void ); diff --git a/include/sample_play_handle.h b/include/sample_play_handle.h index cd9dfd3c0b..e639459a86 100644 --- a/include/sample_play_handle.h +++ b/include/sample_play_handle.h @@ -1,7 +1,7 @@ /* * sample_play_handle.h - play-handle for playing a sample * - * Copyright (c) 2005-2006 Tobias Doerffel + * Copyright (c) 2005-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -29,12 +29,12 @@ #include #include "play_handle.h" +#include "sample_buffer.h" #include "types.h" #include "engine.h" class bbTrack; class pattern; -class sampleBuffer; class sampleTCO; class track; class audioPort; @@ -79,6 +79,7 @@ private: bool m_doneMayReturnTrue; f_cnt_t m_frame; + sampleBuffer::handleState m_state; audioPort * m_audioPort; const bool m_ownAudioPort; diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 69cdc45caa..815e5b7101 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -25,6 +25,7 @@ SUBDIRS = \ live_tool \ midi_import \ organic \ + patman \ plucked_string_synth \ polyb302 \ $(SINGERBOT_DIR) \ diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 1a36fce673..6756ce7492 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -51,6 +51,7 @@ #include "note_play_handle.h" #include "interpolation.h" #include "buffer_allocator.h" +#include "file_browser.h" #include "pixmap_button.h" #include "knob.h" #include "tooltip.h" @@ -78,7 +79,7 @@ plugin::descriptor audiofileprocessor_plugin_descriptor = 0x0100, plugin::Instrument, new QPixmap( PLUGIN_NAME::getIconPixmap( "logo" ) ), - NULL + new audioFileProcessor::subPluginFeatures( plugin::Instrument ) } ; } @@ -400,10 +401,15 @@ void audioFileProcessor::playNote( notePlayHandle * _n, bool ) const float note_freq = getInstrumentTrack()->frequency( _n ) / ( eng()->getMixer()->sampleRate() / DEFAULT_SAMPLE_RATE ); - if( m_sampleBuffer.play( buf, _n->totalFramesPlayed(), + + if( !_n->m_pluginData ) + { + _n->m_pluginData = new handleState( _n->hasDetuningInfo() ); + } + + if( m_sampleBuffer.play( buf, (handleState *)_n->m_pluginData, frames, note_freq, - m_loopButton->isChecked(), - &_n->m_pluginData ) == TRUE ) + m_loopButton->isChecked() ) == TRUE ) { getInstrumentTrack()->processAudioBuffer( buf, frames, _n ); } @@ -415,7 +421,7 @@ void audioFileProcessor::playNote( notePlayHandle * _n, bool ) void audioFileProcessor::deleteNotePluginData( notePlayHandle * _n ) { - m_sampleBuffer.deleteResamplingData( &_n->m_pluginData ); + delete (handleState *)_n->m_pluginData; } @@ -423,12 +429,63 @@ void audioFileProcessor::deleteNotePluginData( notePlayHandle * _n ) void audioFileProcessor::dragEnterEvent( QDragEnterEvent * _dee ) { - if( stringPairDrag::processDragEnterEvent( _dee, - QString( "samplefile,tco_%1" ).arg( track::SAMPLE_TRACK ) ) == - FALSE ) +#ifdef QT4 + if( _dee->mimeData()->hasFormat( "lmms/stringpair" ) ) + { + QString txt = _dee->mimeData()->data( "lmms/stringpair" ); + if( txt.section( ':', 0, 0 ) == QString( "tco_%1" ).arg( + track::SAMPLE_TRACK ) ) + { + _dee->acceptProposedAction(); + } + else if( txt.section( ':', 0, 0 ) == "samplefile" ) + { + _dee->acceptProposedAction(); + } + else + { + _dee->ignore(); + } + } + else { _dee->ignore(); } +#else + QString txt = _dee->encodedData( "lmms/stringpair" ); + if( txt != "" ) + { + if( txt.section( ':', 0, 0 ) == QString( "tco_%1" ).arg( + track::SAMPLE_TRACK ) ) + { + _dee->accept(); + return; + } + if( txt.section( ':', 0, 0 ) == "samplefile" + && subPluginFeatures::supported_extensions().contains( + fileItem::extension( txt.section( ':', 1 ) ) ) ) + { + _dee->accept(); + return; + } + _dee->ignore(); + return; + } + + txt = QString( _dee->encodedData( "text/plain" ) ); + if( txt != "" ) + { + QString file = QUriDrag::uriToLocalFile( + txt.stripWhiteSpace() ); + if( file && subPluginFeatures::supported_extensions().contains( + fileItem::extension( file ) ) ) + { + _dee->accept(); + return; + } + } + _dee->ignore(); +#endif } @@ -442,6 +499,7 @@ void audioFileProcessor::dropEvent( QDropEvent * _de ) { setAudioFile( value ); _de->accept(); + return; } else if( type == QString( "tco_%1" ).arg( track::SAMPLE_TRACK ) ) { @@ -449,11 +507,21 @@ void audioFileProcessor::dropEvent( QDropEvent * _de ) setAudioFile( mmp.content().firstChild().toElement(). attribute( "src" ) ); _de->accept(); + return; } - else + +#ifndef QT4 + QString txt = _de->encodedData( "text/plain" ); + if( txt != "" ) { - _de->ignore(); + setAudioFile( QUriDrag::uriToLocalFile( + txt.stripWhiteSpace() ) ); + _de->accept(); + return; } +#endif + + _de->ignore(); } @@ -476,18 +544,18 @@ void audioFileProcessor::paintEvent( QPaintEvent * ) QString file_name = ""; Uint16 idx = m_sampleBuffer.audioFile().length(); - p.setFont( pointSize<8>( p.font() ) ); + p.setFont( pointSize<8>( font() ) ); - QFontMetrics fm( font() ); + QFontMetrics fm( p.font() ); // simple algorithm for creating a text from the filename that // matches in the white rectangle #ifdef QT4 while( idx > 0 && - fm.size( Qt::TextSingleLine, file_name + "..." ).width() < 225 ) + fm.size( Qt::TextSingleLine, file_name + "..." ).width() < 210 ) #else while( idx > 0 && - fm.size( Qt::SingleLine, file_name + "..." ).width() < 225 ) + fm.size( Qt::SingleLine, file_name + "..." ).width() < 210 ) #endif { file_name = m_sampleBuffer.audioFile()[--idx] + file_name; @@ -666,6 +734,31 @@ void audioFileProcessor::openAudioFile( void ) + + + + +audioFileProcessor::subPluginFeatures::subPluginFeatures( + plugin::pluginTypes _type ) : + plugin::descriptor::subPluginFeatures( _type ) +{ +} + + + + +const QStringList & audioFileProcessor::subPluginFeatures::supported_extensions( + void ) +{ + static QStringList extensions = QStringList() + << "wav" << "ogg" << "spx" << "au" << "voc" + << "aif" << "aiff" << "flac" << "raw"; + return( extensions ); +} + + + + extern "C" { diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index d57a4866fb..9e2ae6efad 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -2,7 +2,7 @@ * audio_file_processor.h - declaration of class audioFileProcessor * (instrument-plugin for using audio-files) * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -53,6 +53,21 @@ class audioFileProcessor : public instrument, public specialBgHandlingWidget { Q_OBJECT public: + class subPluginFeatures : public plugin::descriptor::subPluginFeatures + { + public: + subPluginFeatures( plugin::pluginTypes _type ); + + virtual const QStringList & supportedExtensions( void ) + { + return( supported_extensions() ); + } + + static const QStringList & supported_extensions( void ); + + } ; + + audioFileProcessor( instrumentTrack * _channel_track ); virtual ~audioFileProcessor(); @@ -94,6 +109,8 @@ protected: private: + typedef sampleBuffer::handleState handleState; + static QPixmap * s_artwork; diff --git a/plugins/patman/Makefile.am b/plugins/patman/Makefile.am new file mode 100644 index 0000000000..e46770eff8 --- /dev/null +++ b/plugins/patman/Makefile.am @@ -0,0 +1,33 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I. + + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="patman" + + +%.moc: ./%.h + $(MOC) -o $@ $< + + +MOC_FILES = ./patman.moc + +BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h +EMBEDDED_RESOURCES = $(wildcard *png) + +./embedded_resources.h: $(EMBEDDED_RESOURCES) + $(BIN2RES) $(EMBEDDED_RESOURCES) > $@ + +EXTRA_DIST = $(EMBEDDED_RESOURCES) + + +CLEANFILES = $(MOC_FILES) ./embedded_resources.h + + + +pkglib_LTLIBRARIES = libpatman.la + +libpatman_la_SOURCES = patman.cpp patman.h + +$(libpatman_la_SOURCES): ./embedded_resources.h diff --git a/plugins/patman/artwork.png b/plugins/patman/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..4e2782bc6e43f7267df4c301d2e9c2ecd45d0178 GIT binary patch literal 46996 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 IM6N<$g02VdDgXcg literal 0 HcmV?d00001 diff --git a/plugins/patman/logo.png b/plugins/patman/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b27ade7b620c9c1af58e1466fc19d45e4d717101 GIT binary patch literal 2090 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 IM6N<$f> + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifdef QT4 + +#include + +#else + +#include +#include +#include + +#endif + +#include "patman.h" +#include "buffer_allocator.h" +#include "endian_handling.h" +#include "file_browser.h" +#include "note_play_handle.h" +#include "pixmap_button.h" +#include "song_editor.h" +#include "string_pair_drag.h" +#include "tooltip.h" + +#undef SINGLE_SOURCE_COMPILE +#include "embed.cpp" + + + + +extern "C" +{ + +plugin::descriptor patman_plugin_descriptor = +{ + STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), + "PatMan", + QT_TRANSLATE_NOOP( "pluginBrowser", + "GUS-compatible patch instrument" ), + "Javier Serrano Polo ", + 0x0100, + plugin::Instrument, + new QPixmap( PLUGIN_NAME::getIconPixmap( "logo" ) ), + new patmanSynth::subPluginFeatures( plugin::Instrument ) +} ; + + +// necessary for getting instance out of shared lib +plugin * lmms_plugin_main( void * _data ) +{ + return( new patmanSynth( static_cast( _data ) ) ); +} + +} + + + + +patmanSynth::patmanSynth( instrumentTrack * _track ) : + instrument( _track, &patman_plugin_descriptor ), + specialBgHandlingWidget( PLUGIN_NAME::getIconPixmap( "artwork" ) ) +{ + setPaletteBackgroundPixmap( PLUGIN_NAME::getIconPixmap( "artwork" ) ); + + m_openFileButton = new pixmapButton( this, NULL, eng(), NULL ); + m_openFileButton->setCursor( QCursor( Qt::PointingHandCursor ) ); + m_openFileButton->move( 200, 90 ); + m_openFileButton->setActiveGraphic( embed::getIconPixmap( + "project_open_down" ) ); + m_openFileButton->setInactiveGraphic( embed::getIconPixmap( + "project_open" ) ); + m_openFileButton->setBgGraphic( getBackground( + m_openFileButton ) ); + connect( m_openFileButton, SIGNAL( clicked() ), this, + SLOT( openFile() ) ); + toolTip::add( m_openFileButton, tr( "Open other patch" ) ); + +#ifdef QT4 + m_openFileButton->setWhatsThis( +#else + QWhatsThis::add( m_openFileButton, +#endif + tr( "Click here to open another patch-file. Loop and Tune " + "settings are not reset." ) ); + + m_loopButton = new pixmapButton( this, NULL, eng(), NULL ); + m_loopButton->setCheckable( TRUE ); + m_loopButton->move( 160, 160 ); + m_loopButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_on" ) ); + m_loopButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_off" ) ); + m_loopButton->setBgGraphic( getBackground( m_loopButton ) ); + toolTip::add( m_loopButton, tr( "Loop mode" ) ); +#ifdef QT4 + m_loopButton->setWhatsThis( +#else + QWhatsThis::add( m_loopButton, +#endif + tr( "Here you can toggle the Loop mode. If enabled, PatMan " + "will use the loop information available in the " + "file." ) ); + + m_tuneButton = new pixmapButton( this, NULL, eng(), NULL ); + m_tuneButton->setCheckable( TRUE ); + m_tuneButton->setValue( TRUE ); + m_tuneButton->move( 180, 160 ); + m_tuneButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "tune_on" ) ); + m_tuneButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "tune_off" ) ); + m_tuneButton->setBgGraphic( getBackground( m_tuneButton ) ); + toolTip::add( m_tuneButton, tr( "Tune mode" ) ); +#ifdef QT4 + m_tuneButton->setWhatsThis( +#else + QWhatsThis::add( m_tuneButton, +#endif + tr( "Here you can toggle the Tune mode. If enabled, PatMan " + "will tune the sample to match the note's " + "frequency." ) ); + + m_display_filename = tr( "No file selected" ); + + setAcceptDrops( TRUE ); +} + + + + +patmanSynth::~patmanSynth() +{ + unload_current_patch(); +} + + + + +void patmanSynth::saveSettings( QDomDocument & _doc, QDomElement & _this ) +{ + _this.setAttribute( "src", m_patchFile ); + m_loopButton->saveSettings( _doc, _this, "looped" ); + m_tuneButton->saveSettings( _doc, _this, "tuned" ); +} + + + + +void patmanSynth::loadSettings( const QDomElement & _this ) +{ + setFile( _this.attribute( "src" ), FALSE ); + m_loopButton->loadSettings( _this, "looped" ); + m_tuneButton->loadSettings( _this, "tuned" ); +} + + + + +void patmanSynth::setParameter( const QString & _param, const QString & _value ) +{ + if( _param == "samplefile" ) + { + setFile( _value ); + } +} + + + + +QString patmanSynth::nodeName( void ) const +{ + return( patman_plugin_descriptor.name ); +} + + + + +void patmanSynth::playNote( notePlayHandle * _n, bool ) +{ + const Uint32 frames = eng()->getMixer()->framesPerAudioBuffer(); + sampleFrame * buf = bufferAllocator::alloc( frames ); + + float freq = getInstrumentTrack()->frequency( _n ) / + ( eng()->getMixer()->sampleRate() / + DEFAULT_SAMPLE_RATE ); + + if( !_n->m_pluginData ) + { + select_sample( _n ); + } + handle_data * hdata = (handle_data *)_n->m_pluginData; + + float play_freq = hdata->tuned ? freq : hdata->sample->frequency(); + + if( hdata->sample->play( buf, hdata->state, frames, play_freq, + m_loopButton->isChecked() ) ) + { + getInstrumentTrack()->processAudioBuffer( buf, frames, _n ); + } + bufferAllocator::free( buf ); +} + + + + +void patmanSynth::deleteNotePluginData( notePlayHandle * _n ) +{ + handle_data * hdata = (handle_data *)_n->m_pluginData; + sharedObject::unref( hdata->sample ); + delete hdata->state; + delete hdata; +} + + + + +void patmanSynth::dragEnterEvent( QDragEnterEvent * _dee ) +{ +#ifdef QT4 + if( _dee->mimeData()->hasFormat( "lmms/stringpair" ) ) + { + QString txt = _dee->mimeData()->data( "lmms/stringpair" ); + if( txt.section( ':', 0, 0 ) == "samplefile" ) + { + _dee->acceptProposedAction(); + } + else + { + _dee->ignore(); + } + } + else + { + _dee->ignore(); + } +#else + QString txt = _dee->encodedData( "lmms/stringpair" ); + if( txt != "" ) + { + if( txt.section( ':', 0, 0 ) == "samplefile" + && subPluginFeatures::supported_extensions().contains( + fileItem::extension( txt.section( ':', 1 ) ) ) ) + { + _dee->accept(); + return; + } + _dee->ignore(); + return; + } + + txt = QString( _dee->encodedData( "text/plain" ) ); + if( txt != "" ) + { + QString file = QUriDrag::uriToLocalFile( + txt.stripWhiteSpace() ); + if( file && subPluginFeatures::supported_extensions().contains( + fileItem::extension( file ) ) ) + { + _dee->accept(); + return; + } + } + _dee->ignore(); +#endif +} + + + + +void patmanSynth::dropEvent( QDropEvent * _de ) +{ + QString type = stringPairDrag::decodeKey( _de ); + QString value = stringPairDrag::decodeValue( _de ); + if( type == "samplefile" ) + { + setFile( value ); + _de->accept(); + return; + } + +#ifndef QT4 + QString txt = _de->encodedData( "text/plain" ); + if( txt != "" ) + { + setFile( QUriDrag::uriToLocalFile( txt.stripWhiteSpace() ) ); + _de->accept(); + return; + } +#endif + + _de->ignore(); +} + + + + +void patmanSynth::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap pm( rect().size() ); + pm.fill( this, rect().topLeft() ); + + QPainter p( &pm, this ); +#endif + + p.setFont( pointSize<8>( font() ) ); + p.setPen( QColor( 0x66, 0xFF, 0x66 ) ); + p.drawText( 8, 140, m_display_filename ); + +#ifndef QT4 + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void patmanSynth::openFile( void ) +{ +#ifdef QT4 + QFileDialog ofd( NULL, tr( "Open patch file" ) ); +#else + QFileDialog ofd( QString::null, QString::null, NULL, "", TRUE ); + ofd.setWindowTitle( tr( "Open patch file" ) ); +#endif + ofd.setFileMode( QFileDialog::ExistingFiles ); + + // set filters +#ifdef QT4 + QStringList types; + types << tr( "Patch-Files (*.pat)" ); + ofd.setFilters( types ); +#else + ofd.addFilter( tr( "Patch-Files (*.pat)" ) ); +#endif + + if( m_patchFile == "" ) + { + ofd.setDirectory( configManager::inst()->userSamplesDir() ); + } + else if( QFileInfo( m_patchFile ).isRelative() ) + { + QString f = configManager::inst()->userSamplesDir() + + m_patchFile; + if( QFileInfo( f ).exists() == FALSE ) + { + f = configManager::inst()->factorySamplesDir() + + m_patchFile; + } + + ofd.selectFile( f ); + } + else + { + ofd.selectFile( m_patchFile ); + } + + if( ofd.exec() == QDialog::Accepted && !ofd.selectedFiles().isEmpty() ) + { + QString f = ofd.selectedFiles()[0]; + if( f != "" ) + { + setFile( f ); + eng()->getSongEditor()->setModified(); + } + } +} + + + + +void patmanSynth::setFile( const QString & _patch_file, bool _rename ) +{ + // is current channel-name equal to previous-filename?? + if( _rename && + ( getInstrumentTrack()->name() == + QFileInfo( m_patchFile ).fileName() || + m_patchFile == "" ) ) + { + // then set it to new one + getInstrumentTrack()->setName( QFileInfo( _patch_file + ).fileName() ); + } + // else we don't touch the channel-name, because the user named it self + + m_patchFile = sampleBuffer::tryToMakeRelative( _patch_file ); + load_error error = load_patch( sampleBuffer::tryToMakeAbsolute( + _patch_file ) ); + if( error ) + { + printf("Load error\n"); + } + + m_display_filename = ""; + Uint16 idx = m_patchFile.length(); + + QFontMetrics fm( pointSize<8>( font() ) ); + + // simple algorithm for creating a text from the filename that + // matches in the white rectangle + while( idx > 0 && fm.size( +#ifdef QT4 + Qt::TextSingleLine, +#else + Qt::SingleLine, +#endif + m_display_filename + "..." ).width() < 225 ) + { + m_display_filename = m_patchFile[--idx] + m_display_filename; + } + + if( idx > 0 ) + { + m_display_filename = "..." + m_display_filename; + } + + update(); +} + + + + +patmanSynth::load_error patmanSynth::load_patch( const QString & _filename ) +{ + unload_current_patch(); + + FILE * fd = fopen( _filename, "rb" ); + if( !fd ) + { + perror( "fopen" ); + return( LOAD_OPEN ); + } + + unsigned char header[239]; + + if( fread( header, 1, 239, fd ) != 239 || + ( memcmp( header, "GF1PATCH110\0ID#000002", 22 ) + && memcmp( header, "GF1PATCH100\0ID#000002", 22 ) ) ) + { + fclose( fd ); + return( LOAD_NOT_GUS ); + } + + if( header[82] != 1 && header[82] != 0 ) + { + fclose( fd ); + return( LOAD_INSTRUMENTS ); + } + + if( header[151] != 1 && header[151] != 0 ) + { + fclose( fd ); + return( LOAD_LAYERS ); + } + + int sample_count = header[198]; + for( int i = 0; i < sample_count; ++i ) + { + unsigned short tmpshort; + +#define SKIP_BYTES( x ) \ + if ( fseek( fd, x, SEEK_CUR ) == -1 ) \ + { \ + fclose( fd ); \ + return( LOAD_IO ); \ + } + +#define READ_SHORT( x ) \ + if ( fread( &tmpshort, 2, 1, fd ) != 1 ) \ + { \ + fclose( fd ); \ + return( LOAD_IO ); \ + } \ + x = (unsigned short)swap16IfBE( tmpshort ); + +#define READ_LONG( x ) \ + if ( fread( &x, 4, 1, fd ) != 1 ) \ + { \ + fclose( fd ); \ + return( LOAD_IO ); \ + } \ + x = (unsigned)swap32IfBE( x ); + + // skip wave name, fractions + SKIP_BYTES( 7 + 1 ); + unsigned data_length; + READ_LONG( data_length ); + unsigned loop_start; + READ_LONG( loop_start ); + unsigned loop_end; + READ_LONG( loop_end ); + unsigned sample_rate; + READ_SHORT( sample_rate ); + // skip low_freq, high_freq + SKIP_BYTES( 4 + 4 ); + unsigned root_freq; + READ_LONG( root_freq ); + // skip tuning, panning, envelope, tremolo, vibrato + SKIP_BYTES( 2 + 1 + 12 + 3 + 3 ); + unsigned char modes; + if ( fread( &modes, 1, 1, fd ) != 1 ) + { + fclose( fd ); + return( LOAD_IO ); + } + // skip scale frequency, scale factor, reserved space + SKIP_BYTES( 2 + 2 + 36 ); + + f_cnt_t frames; + sample_t * wave_samples; + if( modes & MODES_16BIT ) + { + frames = data_length >> 1; + wave_samples = new sample_t[frames]; + for( f_cnt_t frame = 0; frame < frames; ++frame ) + { + short sample; + if ( fread( &sample, 2, 1, fd ) != 1 ) + { + delete wave_samples; + fclose( fd ); + return( LOAD_IO ); + } + sample = swap16IfBE( sample ); + if( modes & MODES_UNSIGNED ) + { + sample ^= 0x8000; + } + wave_samples[frame] = sample / 32767.0f; + } + + loop_start >>= 1; + loop_end >>= 1; + } + else + { + frames = data_length; + wave_samples = new sample_t[frames]; + for( f_cnt_t frame = 0; frame < frames; ++frame ) + { + char sample; + if ( fread( &sample, 1, 1, fd ) != 1 ) + { + delete wave_samples; + fclose( fd ); + return( LOAD_IO ); + } + if( modes & MODES_UNSIGNED ) + { + sample ^= 0x80; + } + wave_samples[frame] = sample / 127.0f; + } + } + + sampleFrame * data = new sampleFrame[frames]; + + for( f_cnt_t frame = 0; frame < frames; ++frame ) + { + for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; + ++chnl ) + { + data[frame][chnl] = wave_samples[frame]; + } + } + + sampleBuffer * psample = new sampleBuffer( data, frames, + eng() ); + psample->setFrequency( root_freq / 1000.0f ); + psample->normalize_sample_rate( sample_rate ); + + if( modes & MODES_LOOPING ) + { + psample->setLoopStartFrame( loop_start + * SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] + / sample_rate ); + psample->setLoopEndFrame( loop_end + * SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] + / sample_rate ); + } + + m_patch_samples.push_back( psample ); + + delete[] wave_samples; + delete[] data; + } + fclose( fd ); + return( LOAD_OK ); +} + + + + +void patmanSynth::unload_current_patch( void ) +{ + while( !m_patch_samples.empty() ) + { + sharedObject::unref( m_patch_samples.back() ); + m_patch_samples.pop_back(); + } +} + + + + +void patmanSynth::select_sample( notePlayHandle * _n ) +{ + float freq = getInstrumentTrack()->frequency( _n ) / + ( eng()->getMixer()->sampleRate() / + DEFAULT_SAMPLE_RATE ); + + float min_dist = HUGE_VALF; + sampleBuffer * sample = NULL; + + for( vvector::iterator it = m_patch_samples.begin(); + it != m_patch_samples.end(); ++it ) + { + float patch_freq = ( *it )->frequency(); + float dist = freq >= patch_freq ? freq / patch_freq : + patch_freq / freq; + + if( dist < min_dist ) + { + min_dist = dist; + sample = *it; + } + } + + handle_data * hdata = new handle_data; + hdata->tuned = m_tuneButton->isChecked(); + if( sample ) + { + hdata->sample = sharedObject::ref( sample ); + } + else + { + hdata->sample = new sampleBuffer( NULL, 0, eng() ); + } + hdata->state = new sampleBuffer::handleState( _n->hasDetuningInfo() ); + + _n->m_pluginData = hdata; +} + + + + + + + + +patmanSynth::subPluginFeatures::subPluginFeatures( plugin::pluginTypes _type ) : + plugin::descriptor::subPluginFeatures( _type ) +{ +} + + + + +const QStringList & patmanSynth::subPluginFeatures::supported_extensions( void ) +{ + static QStringList extension( "pat" ); + return( extension ); +} + + + + +#include "patman.moc" diff --git a/plugins/patman/patman.h b/plugins/patman/patman.h new file mode 100644 index 0000000000..07dc3f2a91 --- /dev/null +++ b/plugins/patman/patman.h @@ -0,0 +1,130 @@ +/* + * patman.h - header for a GUS-compatible patch instrument plugin + * + * Copyright (c) 2007 Javier Serrano Polo + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef _PATMAN_H_ +#define _PATMAN_H_ + + +#include "instrument.h" +#include "sample_buffer.h" +#include "spc_bg_hndl_widget.h" + + +class pixmapButton; + + +#define MODES_16BIT ( 1 << 0 ) +#define MODES_UNSIGNED ( 1 << 1 ) +#define MODES_LOOPING ( 1 << 2 ) +#define MODES_PINGPONG ( 1 << 3 ) +#define MODES_REVERSE ( 1 << 4 ) +#define MODES_SUSTAIN ( 1 << 5 ) +#define MODES_ENVELOPE ( 1 << 6 ) +#define MODES_CLAMPED ( 1 << 7 ) + + +class patmanSynth : public instrument, public specialBgHandlingWidget +{ + Q_OBJECT +public: + class subPluginFeatures : public plugin::descriptor::subPluginFeatures + { + public: + subPluginFeatures( plugin::pluginTypes _type ); + + virtual const QStringList & supportedExtensions( void ) + { + return( supported_extensions() ); + } + + static const QStringList & supported_extensions( void ); + + } ; + + + patmanSynth( instrumentTrack * _track ); + virtual ~patmanSynth(); + + virtual void FASTCALL playNote( notePlayHandle * _n, + bool _try_parallelizing ); + virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + virtual void FASTCALL setParameter( const QString & _param, + const QString & _value ); + + virtual QString nodeName( void ) const; + + +public slots: + void openFile( void ); + void setFile( const QString & _patch_file, bool _rename = TRUE ); + + +protected: + virtual void dragEnterEvent( QDragEnterEvent * _dee ); + virtual void dropEvent( QDropEvent * _de ); + virtual void paintEvent( QPaintEvent * ); + + +private: + typedef struct + { + sampleBuffer::handleState * state; + bool tuned; + sampleBuffer * sample; + } handle_data; + + QString m_patchFile; + vvector m_patch_samples; + + pixmapButton * m_openFileButton; + pixmapButton * m_loopButton; + pixmapButton * m_tuneButton; + QString m_display_filename; + + enum load_error + { + LOAD_OK, + LOAD_OPEN, + LOAD_NOT_GUS, + LOAD_INSTRUMENTS, + LOAD_LAYERS, + LOAD_IO + } ; + + load_error load_patch( const QString & _filename ); + void unload_current_patch( void ); + + void select_sample( notePlayHandle * _n ); + +} ; + + +#endif diff --git a/plugins/patman/tune_off.png b/plugins/patman/tune_off.png new file mode 100644 index 0000000000000000000000000000000000000000..a5bd16162b899f3209b618a745b39ed6b4f36eb5 GIT binary patch literal 611 zcmV-p0-XJcP)2*q&jgP zV1IwVyt}*mIt)Xl)oQ&9g5VlJbhj=&r(dYo>y3khgIcXts~3yKm!(o^L({Zn(=^>E ziY7Coxqxc5+Q{W{&&K1iP%f9(^7*{6wY7Cf2nlT4?gF3-0fu2TBuRR5a&q$R>gvjO zT{mEiiIOCh`~7}D2m%v8GErB0{QnoK4gN@>dTybJ&Vz!*TBng^hinuEb0T3=s(1VFx>`8R;;+sha20II4k zXEK>OV@!1%r%fsSI&1nn6Nv_eLP54H3zlVFtgf!EWwY6B05kREAQcJ)p;Rhu8;0>l z*Y%^#&CLfv5TMuV!FAnAx7*zSFa$731nlhWJg!!&Z!49`p)AWtr_)eX6^f$Z^73-8 z*=%Y6tVMuVx~?Dkz7NmyVB0nbA>f?Dah!Vq9s*ba5F`OoaU8!0!w{b5fe?b>a0pQp z!5Evg+wJcF26un|$AA#>iF5vZG#W{QAWS*uq3`>H^Yil$uIruw_y}P1Uu0*muIqb> xqBP<-e#$u)eBb|db8~aS82b$12LLI@ literal 0 HcmV?d00001 diff --git a/plugins/patman/tune_on.png b/plugins/patman/tune_on.png new file mode 100644 index 0000000000000000000000000000000000000000..c1777c52a50c0308da0f12f41a5c1e7e9e834c95 GIT binary patch literal 623 zcmV-#0+9WQP)T=cN-ZtUXypS$apl`-8nou&_7byI~b`M8mTq9ddo5k zbKg)E?HIEJnr}v$a&J%4w1O>v+8x?iU0G*$Cl2T~s~O0Kspka|Y30Mx-0PQbu-i@Co>OJPkPlUH z0Cpg64HODnIm3jOHapLs%_B=O4l<&;7OMaRKm|!-3JZ*871v@0j1?ab?szQ<}(3TSrgs#Z)bD0BA)4*T9vgYh9fWuZDu*YKH@C zHr!Q}j2i;k?w;T*)B#^Dfly<^-G*~@;jMzsnkF+XYs?f(_8k?VXr+J`Z~#&(Dgx=v?`*6m$YmFSPe8I*f?qAi=pe~317H9E002ov JPDHLkV1kSl96100 literal 0 HcmV?d00001 diff --git a/plugins/polyb302/polyb302.cpp b/plugins/polyb302/polyb302.cpp index 5da52fc1b7..4a6c295126 100644 --- a/plugins/polyb302/polyb302.cpp +++ b/plugins/polyb302/polyb302.cpp @@ -1,6 +1,6 @@ /* - * polyb302.cpp - implementation of class polyb302 which is a bass synth - * attempting to emulate the Roland TB303 bass synth + * polyb302.cpp - implementation of instrument polyb302, an attempt to emulate + * the Roland TB303 bass synth * * Copyright (c) 2006-2007 Paul Giblock * @@ -28,28 +28,12 @@ * */ -#include "qt3support.h" - -#ifdef QT4 - -#include - -#else - -#include - -#endif - #include "polyb302.h" -#include "audio_device.h" -#include "instrument_track.h" -#include "instrument_play_handle.h" -#include "led_checkbox.h" -#include "note_play_handle.h" -#include "templates.h" #include "buffer_allocator.h" #include "knob.h" +#include "led_checkbox.h" +#include "note_play_handle.h" #undef SINGLE_SOURCE_COMPILE #include "embed.cpp" diff --git a/plugins/polyb302/polyb302.h b/plugins/polyb302/polyb302.h index 0e63d0eaba..681d85eb97 100644 --- a/plugins/polyb302/polyb302.h +++ b/plugins/polyb302/polyb302.h @@ -1,6 +1,6 @@ /* - * polyb302.h - declaration of class polyb302 which is a bass synth attempting - * to emulate the Roland TB303 bass synth + * polyb302.h - declaration of instrument polyb302, an attempt to emulate the + * Roland TB303 bass synth * * Copyright (c) 2006-2007 Paul Giblock * diff --git a/plugins/singerbot/singerbot.cpp b/plugins/singerbot/singerbot.cpp index 99d7b66fb5..35d7ef9ca0 100644 --- a/plugins/singerbot/singerbot.cpp +++ b/plugins/singerbot/singerbot.cpp @@ -147,7 +147,9 @@ void singerBot::playNote( notePlayHandle * _n, bool ) sampleBuffer * sample_buffer = hdata->remaining_frames ? readWave( hdata ) : new sampleBuffer( NULL, 0, eng() ); - if( sample_buffer->play( buf, 0, frames ) ) + sampleBuffer::handleState hstate; + + if( sample_buffer->play( buf, &hstate, frames ) ) { getInstrumentTrack()->processAudioBuffer( buf, frames, _n ); } diff --git a/src/core/engine.cpp b/src/core/engine.cpp index c422f66703..31d9c7c63b 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -3,7 +3,7 @@ /* * engine.cpp - implementation of LMMS' engine-system * - * Copyright (c) 2006 Tobias Doerffel + * Copyright (c) 2006-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -52,6 +52,8 @@ engine::engine( const bool _has_gui ) : m_pianoRoll( NULL ), m_projectJournal( NULL ) { + load_extensions(); + m_projectJournal = new projectJournal( this ); m_mainWindow = new mainWindow( this ); m_mixer = new mixer( this ); @@ -123,6 +125,35 @@ void engine::updateFramesPerTact64th( void ) +void engine::load_extensions( void ) +{ + vvector pluginDescriptors; + plugin::getDescriptorsOfAvailPlugins( pluginDescriptors ); + for( vvector::iterator it = + pluginDescriptors.begin(); + it != pluginDescriptors.end(); ++it ) + { + if( it->sub_plugin_features ) + { + if( it->type == plugin::Instrument ) + { + const QStringList & ext = + it->sub_plugin_features + ->supportedExtensions(); + for( QStringList::const_iterator itExt = + ext.begin(); + itExt != ext.end(); ++itExt ) + { + m_sample_extensions[*itExt] = it->name; + } + } + } + } +} + + + + engineObject::engineObject( engine * _engine ) : m_engine( _engine ) diff --git a/src/core/file_browser.cpp b/src/core/file_browser.cpp index 33bc4745dd..3ea07380ee 100644 --- a/src/core/file_browser.cpp +++ b/src/core/file_browser.cpp @@ -179,7 +179,7 @@ void fileBrowser::addItems( const QString & _path ) { // remove existing file-items delete m_l->findItem( cur_file, 0 ); - (void) new fileItem( m_l, cur_file, _path ); + (void) new fileItem( m_l, cur_file, _path, eng() ); } } @@ -195,7 +195,7 @@ void fileBrowser::addItems( const QString & _path ) if( item == NULL ) { (void) new directory( m_l, cur_file, _path, - m_filter ); + m_filter, eng() ); } else if( dynamic_cast( item ) != NULL ) { @@ -333,7 +333,9 @@ void fileBrowser::sendToActiveInstrumentTrack( void ) fileItem::SAMPLE_FILE ) { instrument * afp = ct->loadInstrument( - "audiofileprocessor" ); + eng()->sampleExtensions() + [m_contextMenuItem + ->extension()] ); if( afp != NULL ) { afp->setParameter( "samplefile", @@ -372,7 +374,9 @@ void fileBrowser::openInNewInstrumentTrack( trackContainer * _tc ) #ifdef LMMS_DEBUG assert( ct != NULL ); #endif - instrument * afp = ct->loadInstrument( "audiofileprocessor" ); + instrument * afp = ct->loadInstrument( eng()->sampleExtensions() + [m_contextMenuItem + ->extension()] ); if( afp != NULL ) { afp->setParameter( "samplefile", @@ -461,7 +465,7 @@ void listView::contentsMouseDoubleClickEvent( QMouseEvent * _me ) assert( it != NULL ); #endif instrument * afp = it->loadInstrument( - "audiofileprocessor" ); + eng()->sampleExtensions()[f->extension()] ); if( afp != NULL ) { afp->setParameter( "samplefile", @@ -649,8 +653,10 @@ QPixmap * directory::s_folderLockedPixmap = NULL; directory::directory( directory * _parent, const QString & _name, - const QString & _path, const QString & _filter ) : + const QString & _path, const QString & _filter, + engine * _engine ) : Q3ListViewItem( _parent, _name ), + engineObject( _engine ), m_p( _parent ), m_pix( NULL ), m_directories( _path ), @@ -663,8 +669,10 @@ directory::directory( directory * _parent, const QString & _name, directory::directory( Q3ListView * _parent, const QString & _name, - const QString & _path, const QString & _filter ) : + const QString & _path, const QString & _filter, + engine * _engine ) : Q3ListViewItem( _parent, _name ), + engineObject( _engine ), m_p( NULL ), m_pix( NULL ), m_directories( _path ), @@ -789,7 +797,7 @@ bool directory::addItems( const QString & _path ) #endif /*QDir::match( FILE_FILTER, cur_file )*/ ) { - (void) new fileItem( this, cur_file, _path ); + (void) new fileItem( this, cur_file, _path, eng() ); added_something = TRUE; } } @@ -806,7 +814,7 @@ bool directory::addItems( const QString & _path ) #endif cur_file, m_filter ) ) { - new directory( this, cur_file, _path, m_filter ); + new directory( this, cur_file, _path, m_filter, eng() ); added_something = TRUE; #if 0 if( firstChild() == NULL ) @@ -852,8 +860,10 @@ QPixmap * fileItem::s_unknownFilePixmap = NULL; fileItem::fileItem( Q3ListView * _parent, const QString & _name, - const QString & _path ) : + const QString & _path, + engine * _engine ) : Q3ListViewItem( _parent, _name ), + engineObject( _engine ), m_pix( NULL ), m_path( _path ) { @@ -866,8 +876,10 @@ fileItem::fileItem( Q3ListView * _parent, const QString & _name, fileItem::fileItem( Q3ListViewItem * _parent, const QString & _name, - const QString & _path ) : + const QString & _path, + engine * _engine ) : Q3ListViewItem( _parent, _name ), + engineObject( _engine ), m_pix( NULL ), m_path( _path ) { @@ -936,11 +948,7 @@ void fileItem::initPixmapStuff( void ) void fileItem::determineFileType( void ) { -#ifdef QT4 - QString ext = QFileInfo( fullName() ).suffix().toLower(); -#else - QString ext = QFileInfo( fullName() ).extension( FALSE ).toLower(); -#endif + QString ext = extension(); if( ext == "mmp" || ext == "mpt" || ext == "mmpz" ) { m_type = PROJECT_FILE; @@ -966,10 +974,7 @@ void fileItem::determineFileType( void ) { m_type = PRESET_FILE; } - else if( ext == "wav" || ext == "ogg" || ext == "mp3" || - ext == "aiff" || ext == "aif" || ext == "voc" || - ext == "au" || ext == "raw" || ext == "flac" || - ext == "spx" ) + else if( eng()->sampleExtensions().contains( ext ) ) { m_type = SAMPLE_FILE; } @@ -990,6 +995,25 @@ void fileItem::determineFileType( void ) +QString fileItem::extension( void ) +{ + return( extension( fullName() ) ); +} + + + + +QString fileItem::extension( const QString & _file ) +{ +#ifdef QT4 + return( QFileInfo( _file ).suffix().toLower() ); +#else + return( QFileInfo( _file ).extension( FALSE ).toLower() ); +#endif +} + + + #include "file_browser.moc" diff --git a/src/core/main_window.cpp b/src/core/main_window.cpp index 6538626644..2dc165e201 100644 --- a/src/core/main_window.cpp +++ b/src/core/main_window.cpp @@ -128,6 +128,14 @@ mainWindow::mainWindow( engine * _engine ) : splitter->setChildrenCollapsible( FALSE ); #endif + QString sample_filter; + vlist ext_keys = eng()->sampleExtensions().keys(); + for( vlist::iterator it = ext_keys.begin(); + it != ext_keys.end(); ++it ) + { + sample_filter += " *." + *it; + } + int id = 0; QString wdir = configManager::inst()->workingDir(); side_bar->appendTab( new pluginBrowser( splitter, eng() ), ++id ); @@ -142,9 +150,7 @@ mainWindow::mainWindow( engine * _engine ) : side_bar->appendTab( new fileBrowser( configManager::inst()->factorySamplesDir() + "*" + configManager::inst()->userSamplesDir(), - "*.wav *.ogg *.spx *.au" - "*.voc *.aif *.aiff *.flac *.raw", - tr( "My samples" ), + sample_filter, tr( "My samples" ), embed::getIconPixmap( "sound_file" ), splitter, eng() ), ++id ); diff --git a/src/core/sample_play_handle.cpp b/src/core/sample_play_handle.cpp index df88446be5..0ffbab53ec 100644 --- a/src/core/sample_play_handle.cpp +++ b/src/core/sample_play_handle.cpp @@ -3,7 +3,7 @@ /* * sample_play_handle.cpp - implementation of class samplePlayHandle * - * Copyright (c) 2005-2006 Tobias Doerffel + * Copyright (c) 2005-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -144,7 +144,7 @@ void samplePlayHandle::play( const fpab_t _frame_base, bool ) , m_volume, m_volume #endif } } ; - m_sampleBuffer->play( buf, m_frame, frames ); + m_sampleBuffer->play( buf, &m_state, frames ); eng()->getMixer()->bufferToPort( buf, frames, _frame_base, v, m_audioPort ); diff --git a/src/core/track_container.cpp b/src/core/track_container.cpp index 7b3f9e8e2a..b528306ef5 100644 --- a/src/core/track_container.cpp +++ b/src/core/track_container.cpp @@ -4,7 +4,7 @@ * track_container.cpp - implementation of base-class for all track-containers * like Song-Editor, BB-Editor... * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -345,13 +345,14 @@ void trackContainer::clearAllTracks( void ) const trackWidget * trackContainer::trackWidgetAt( const int _y ) const { + const int abs_y = _y + m_scrollArea->contentsY(); int y_cnt = 0; for( trackWidgetVector::const_iterator it = m_trackWidgets.begin(); it != m_trackWidgets.end(); ++it ) { const int y_cnt1 = y_cnt; y_cnt += ( *it )->height(); - if( _y >= y_cnt1 && _y < y_cnt ) + if( abs_y >= y_cnt1 && abs_y < y_cnt ) { return( *it ); } diff --git a/src/lib/sample_buffer.cpp b/src/lib/sample_buffer.cpp index 9d6d636448..86a0e94b52 100644 --- a/src/lib/sample_buffer.cpp +++ b/src/lib/sample_buffer.cpp @@ -3,7 +3,7 @@ /* * sample_buffer.cpp - container-class sampleBuffer * - * Copyright (c) 2005-2006 Tobias Doerffel + * Copyright (c) 2005-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -111,9 +111,13 @@ sampleBuffer::sampleBuffer( engine * _engine, const QString & _audio_file, m_frames( 0 ), m_startFrame( 0 ), m_endFrame( 0 ), + m_loop_startFrame( 0 ), + m_loop_endFrame( 0 ), m_amplification( 1.0f ), m_reversed( FALSE ), - m_dataMutex() + m_frequency( BASE_FREQ ), + m_dataMutex(), + m_sample_fragment( NULL ) { #ifdef SDL_SDL_SOUND_H // init sound-file-system of SDL @@ -143,9 +147,13 @@ sampleBuffer::sampleBuffer( const sampleFrame * _data, const f_cnt_t _frames, m_frames( 0 ), m_startFrame( 0 ), m_endFrame( 0 ), + m_loop_startFrame( 0 ), + m_loop_endFrame( 0 ), m_amplification( 1.0f ), m_reversed( FALSE ), - m_dataMutex() + m_frequency( BASE_FREQ ), + m_dataMutex(), + m_sample_fragment( NULL ) { m_origData = new sampleFrame[_frames]; memcpy( m_origData, _data, _frames * BYTES_PER_FRAME ); @@ -173,9 +181,13 @@ sampleBuffer::sampleBuffer( const f_cnt_t _frames, engine * _engine ) : m_frames( 0 ), m_startFrame( 0 ), m_endFrame( 0 ), + m_loop_startFrame( 0 ), + m_loop_endFrame( 0 ), m_amplification( 1.0f ), m_reversed( FALSE ), - m_dataMutex() + m_frequency( BASE_FREQ ), + m_dataMutex(), + m_sample_fragment( NULL ) { m_origData = new sampleFrame[_frames]; memset( m_origData, 0, _frames * BYTES_PER_FRAME ); @@ -201,11 +213,12 @@ sampleBuffer::~sampleBuffer() delete[] m_data; m_data = NULL; -#ifdef HAVE_SAMPLERATE_H - quitResampling(); -#endif - m_dataMutex.unlock(); + + if( m_sample_fragment ) + { + delete[] m_sample_fragment; + } } @@ -230,32 +243,13 @@ void sampleBuffer::update( bool _keep_settings ) if( _keep_settings == FALSE ) { m_frames = m_origFrames; - m_startFrame = 0; - if( m_frames > 0 ) - { - m_endFrame = m_frames - 1; - } - else - { - m_endFrame = 0; - } + m_loop_startFrame = m_startFrame = 0; + m_loop_endFrame = m_endFrame = m_frames; } } else if( m_audioFile != "" ) { - QString file = m_audioFile; - // if there's not an absolute filename, we assume that we made - // it relative before and so we have to add sample-dir to file- - // name - if( file[0] != '/' ) - { - file = configManager::inst()->userSamplesDir() + file; - if( QFileInfo( file ).exists() == FALSE ) - { - file = - configManager::inst()->factorySamplesDir() + m_audioFile; - } - } + QString file = tryToMakeAbsolute( m_audioFile ); const char * f = #ifdef QT4 file.toAscii().constData(); @@ -328,34 +322,7 @@ m_data[frame][chnl] = buf[idx] * fac; delete[] buf; - // do samplerate-conversion if sample-decoder didn't - // convert sample-rate to our default-samplerate - if( samplerate != SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] ) - { - sampleBuffer * resampled = resample( this, - samplerate, - SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] ); - delete[] m_data; - m_frames = resampled->frames(); - m_data = new sampleFrame[m_frames]; - memcpy( m_data, resampled->data(), m_frames * - sizeof( sampleFrame ) ); - delete resampled; - } - - if( _keep_settings == FALSE ) - { - // update frame-variables - m_startFrame = 0; - if( m_frames > 0 ) - { - m_endFrame = m_frames - 1; - } - else - { - m_endFrame = 0; - } - } + normalize_sample_rate( samplerate, _keep_settings ); } else { @@ -364,8 +331,8 @@ m_data[frame][chnl] = buf[idx] * fac; m_data = new sampleFrame[1]; memset( m_data, 0, sizeof( *m_data ) ); m_frames = 1; - m_startFrame = 0; - m_endFrame = 1; + m_loop_startFrame = m_startFrame = 0; + m_loop_endFrame = m_endFrame = 1; } } else @@ -375,8 +342,8 @@ m_data[frame][chnl] = buf[idx] * fac; m_data = new sampleFrame[1]; memset( m_data, 0, sizeof( *m_data ) * 1 ); m_frames = 1; - m_startFrame = 0; - m_endFrame = 1; + m_loop_startFrame = m_startFrame = 0; + m_loop_endFrame = m_endFrame = 1; } m_dataMutex.unlock(); @@ -387,6 +354,33 @@ m_data[frame][chnl] = buf[idx] * fac; +void sampleBuffer::normalize_sample_rate( const sample_rate_t _src_sr, + bool _keep_settings ) +{ + // do samplerate-conversion to our default-samplerate + if( _src_sr != SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] ) + { + sampleBuffer * resampled = resample( this, _src_sr, + SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] ); + delete[] m_data; + m_frames = resampled->frames(); + m_data = new sampleFrame[m_frames]; + memcpy( m_data, resampled->data(), m_frames * + sizeof( sampleFrame ) ); + delete resampled; + } + + if( _keep_settings == FALSE ) + { + // update frame-variables + m_loop_startFrame = m_startFrame = 0; + m_loop_endFrame = m_endFrame = m_frames; + } +} + + + + #ifdef SDL_SDL_SOUND_H f_cnt_t sampleBuffer::decodeSampleSDL( const char * _f, int_sample_t * & _buf, @@ -617,54 +611,17 @@ f_cnt_t sampleBuffer::decodeSampleOGGVorbis( const char * _f, #ifdef HAVE_SAMPLERATE_H void sampleBuffer::initResampling( void ) { - m_srcState = createResamplingContext(); m_srcData.end_of_input = 0; } - - - - -void sampleBuffer::quitResampling( void ) -{ - destroyResamplingContext( m_srcState ); -} - - - - -SRC_STATE * sampleBuffer::createResamplingContext( void ) -{ - int error; - SRC_STATE * state; - if( ( state = src_new(/* - ( eng()->getMixer()->highQuality() == TRUE ) ? - SRC_SINC_FASTEST :*/ - SRC_LINEAR, - DEFAULT_CHANNELS, &error ) ) == NULL ) - { - printf( "Error: src_new() failed in sample_buffer.cpp!\n" ); - } - return( state ); -} - - - - -void sampleBuffer::destroyResamplingContext( SRC_STATE * _context ) -{ - src_delete( _context ); -} #endif -bool FASTCALL sampleBuffer::play( sampleFrame * _ab, - const f_cnt_t _start_frame, +bool FASTCALL sampleBuffer::play( sampleFrame * _ab, handleState * _state, const fpab_t _frames, const float _freq, - const bool _looped, - void * * _resampling_data ) + const bool _looped ) { eng()->getMixer()->clearAudioBuffer( _ab, _frames ); @@ -673,10 +630,7 @@ bool FASTCALL sampleBuffer::play( sampleFrame * _ab, return( FALSE ); } - const double freq_factor = (double) _freq / (double) BASE_FREQ; - const Sint16 freq_diff = static_cast( BASE_FREQ - _freq ); - - fpab_t frames_to_process = _frames; + const double freq_factor = (double) _freq / (double) m_frequency; // calculate how many frames we have in requested pitch const f_cnt_t total_frames_for_current_pitch = static_cast( ( @@ -687,31 +641,41 @@ bool FASTCALL sampleBuffer::play( sampleFrame * _ab, return( FALSE ); } - // do we have frames left?? this is only important when not in - // looping-mode because in looping-mode we loop to start-frame... - if( _start_frame >= total_frames_for_current_pitch && _looped == FALSE ) + // this holds the number of the first frame to play + f_cnt_t play_frame = _state->m_frame_index; + if( play_frame < m_startFrame ) { - return( FALSE ); + play_frame = m_startFrame; } - // this holds the number of the first frame to play - const f_cnt_t play_frame = m_startFrame + ( _start_frame % - total_frames_for_current_pitch ); - // this holds the number of remaining frames in current loop - f_cnt_t frames_for_loop = total_frames_for_current_pitch - - ( play_frame - m_startFrame ); + f_cnt_t frames_for_loop; + if( _looped ) + { + play_frame = getLoopedIndex( play_frame ); + frames_for_loop = static_cast( + ( m_loop_endFrame - play_frame ) / + freq_factor ); + } + else + { + if( play_frame >= m_endFrame ) + { + return( FALSE ); + } + frames_for_loop = static_cast( + ( m_endFrame - play_frame ) / + freq_factor ); + if( frames_for_loop == 0 ) + { + return( FALSE ); + } + } // make sure, data isn't accessed in any other way (e.g. deleting // of this buffer...) m_dataMutex.lock(); - if( _looped == FALSE && frames_for_loop < frames_to_process ) - { - frames_to_process = frames_for_loop; - } - const f_cnt_t f1 = static_cast( m_startFrame + - ( play_frame - m_startFrame ) * freq_factor ); /* Uint32 f2 = 0; while( f2 < f1 ) { @@ -723,76 +687,40 @@ bool FASTCALL sampleBuffer::play( sampleFrame * _ab, }*/ // static int foo = 0; // calc pointer of first frame - sampleFrame * start_frame = (sampleFrame *) m_data + f1; //printf("diff:%d %f %d f2: %d input: %d\n", f2 -foo, play_frame * freq_factor, static_cast( play_frame * freq_factor ), f2, (Uint32)( frames_for_loop * freq_factor ) ); // foo = f2; - sampleFrame * loop_start = (sampleFrame *) m_data + m_startFrame; // check whether we have to change pitch... - if( freq_diff != 0 ) + if( _freq != m_frequency || _state->m_varying_pitch ) { #ifdef HAVE_SAMPLERATE_H - SRC_STATE * state = m_srcState; - if( _resampling_data != NULL ) + // Generate output + const f_cnt_t margin = 64; + f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) + + margin; + m_srcData.data_in = getSampleFragment( play_frame, + fragment_size, _looped )[0]; + m_srcData.data_out = _ab[0]; + m_srcData.input_frames = fragment_size; + m_srcData.output_frames = _frames; + m_srcData.src_ratio = 1.0 / freq_factor; + int error = src_process( _state->m_resampling_data, + &m_srcData ); + if( error ) { - if( *_resampling_data == NULL ) - { - *_resampling_data = createResamplingContext(); - } - state = static_cast( *_resampling_data ); - } - - // Check loop - if( _looped && frames_for_loop < frames_to_process ) - { - f_cnt_t total_frames_copied = 0; - while( total_frames_copied < frames_to_process ) - { - // Generate output - m_srcData.data_in = start_frame[0]; - m_srcData.data_out = _ab[total_frames_copied]; - m_srcData.input_frames = static_cast( - frames_for_loop * freq_factor ); - m_srcData.output_frames = frames_for_loop; - m_srcData.src_ratio = 1.0 / freq_factor; - int error = src_process( state, &m_srcData ); - if( error ) - { - printf( "sampleBuffer: error while " - "resampling: %s\n", + printf( "sampleBuffer: error while resampling: %s\n", src_strerror( error ) ); - } - // Advance - total_frames_copied += frames_for_loop; - - // reset start_frame to start - start_frame = loop_start; - // and calculate frames for next loop - frames_for_loop = frames_to_process - - total_frames_copied; - if( frames_for_loop - > total_frames_for_current_pitch ) - { - frames_for_loop = - total_frames_for_current_pitch; - } - } } - else + if( m_srcData.output_frames_gen != _frames ) { - // Generate output - m_srcData.data_in = start_frame[0]; - m_srcData.data_out = _ab[0]; - m_srcData.input_frames = static_cast( - frames_for_loop * freq_factor ); - m_srcData.output_frames = frames_to_process; - m_srcData.src_ratio = 1.0 / freq_factor; - int error = src_process( state, &m_srcData ); - if( error ) - { - printf( "sampleBuffer: error while resampling: " - "%s\n", src_strerror( error ) ); - } + printf( "sampleBuffer: not enough frames: %ld / %d\n", + m_srcData.output_frames_gen, _frames ); + } + // Advance + play_frame += m_srcData.input_frames_used; + if( _looped ) + { + play_frame = getLoopedIndex( play_frame ); } #else f_cnt_t src_frame_base = 0; @@ -876,41 +804,21 @@ bool FASTCALL sampleBuffer::play( sampleFrame * _ab, // we don't have to pitch, so we just copy the sample-data // as is into pitched-copy-buffer - // Check loop - if( _looped && frames_for_loop < frames_to_process ) + // Generate output + memcpy( _ab, getSampleFragment( play_frame, _frames, _looped ), + _frames * BYTES_PER_FRAME ); + // Advance + play_frame += _frames; + if( _looped ) { - f_cnt_t total_frames_copied = 0; - while( total_frames_copied < frames_to_process ) - { - // Generate output - memcpy( _ab[total_frames_copied], start_frame, - frames_for_loop * BYTES_PER_FRAME ); - // Advance - total_frames_copied += frames_for_loop; - - // reset start_frame to start - start_frame = loop_start; - // and calculate frames for next loop - frames_for_loop = frames_to_process - - total_frames_copied; - if( frames_for_loop - > total_frames_for_current_pitch ) - { - frames_for_loop = - total_frames_for_current_pitch; - } - } - } - else - { - // Generate output - memcpy( _ab, start_frame, - frames_to_process * BYTES_PER_FRAME ); + play_frame = getLoopedIndex( play_frame ); } } m_dataMutex.unlock(); + _state->m_frame_index = play_frame; + return( TRUE ); } @@ -918,6 +826,73 @@ bool FASTCALL sampleBuffer::play( sampleFrame * _ab, +sampleFrame * sampleBuffer::getSampleFragment( f_cnt_t _start, + f_cnt_t _frames, bool _looped ) +{ + if( _looped ) + { + if( _start + _frames <= m_loop_endFrame ) + { + return( m_data + _start ); + } + } + else + { + if( _start + _frames <= m_endFrame ) + { + return( m_data + _start ); + } + } + + if( m_sample_fragment ) + { + delete[] m_sample_fragment; + } + m_sample_fragment = new sampleFrame[_frames]; + + if( _looped ) + { + f_cnt_t copied = m_loop_endFrame - _start; + memcpy( m_sample_fragment, m_data + _start, copied + * BYTES_PER_FRAME ); + f_cnt_t loop_frames = m_loop_endFrame - m_loop_startFrame; + while( _frames - copied > 0 ) + { + f_cnt_t todo = tMin( _frames - copied, loop_frames ); + memcpy( m_sample_fragment + copied, + m_data + m_loop_startFrame, + todo * BYTES_PER_FRAME ); + copied += todo; + } + } + else + { + f_cnt_t available = m_endFrame - _start; + memcpy( m_sample_fragment, m_data + _start, available + * BYTES_PER_FRAME ); + memset( m_sample_fragment + available, 0, ( _frames - + available ) * BYTES_PER_FRAME ); + } + + return( m_sample_fragment ); +} + + + + +f_cnt_t sampleBuffer::getLoopedIndex( f_cnt_t _index ) +{ + if( _index < m_loop_endFrame ) + { + return( _index ); + } + return( m_loop_startFrame + ( _index - m_loop_startFrame ) + % ( m_loop_endFrame - m_loop_startFrame ) ); +} + + + + void sampleBuffer::visualize( QPainter & _p, const QRect & _dr, const QRect & _clip, drawMethods _dm ) { @@ -1459,7 +1434,7 @@ void sampleBuffer::setStartFrame( const f_cnt_t _s ) { // don't set this parameter while playing m_dataMutex.lock(); - m_startFrame = _s; + m_loop_startFrame = m_startFrame = _s; m_dataMutex.unlock(); } @@ -1470,7 +1445,7 @@ void sampleBuffer::setEndFrame( const f_cnt_t _e ) { // don't set this parameter while playing m_dataMutex.lock(); - m_endFrame = _e; + m_loop_endFrame = m_endFrame = _e; m_dataMutex.unlock(); } @@ -1495,34 +1470,19 @@ void sampleBuffer::setReversed( bool _on ) -void sampleBuffer::deleteResamplingData( void * * _ptr ) -{ -#ifdef HAVE_SAMPLERATE_H -#ifdef LMMS_DEBUG - assert( _ptr != NULL ); - assert( *_ptr != NULL ); -#endif - destroyResamplingContext( static_cast( *_ptr ) ); -#endif - *_ptr = NULL; -} - - - - QString sampleBuffer::tryToMakeRelative( const QString & _file ) { if( QFileInfo( _file ).isRelative() == FALSE ) { QString fsd = configManager::inst()->factorySamplesDir(); QString usd = configManager::inst()->userSamplesDir(); - if( _file.contains( fsd ) ) + if( _file.startsWith( fsd ) ) { - return( QString( _file ).replace( fsd, "" ) ); + return( QString( _file ).mid( fsd.length() ) ); } - else if( _file.contains( usd ) ) + else if( _file.startsWith( usd ) ) { - return( QString( _file ).replace( usd, "" ) ); + return( QString( _file ).mid( usd.length() ) ); } } return( _file ); @@ -1530,6 +1490,60 @@ QString sampleBuffer::tryToMakeRelative( const QString & _file ) + +QString sampleBuffer::tryToMakeAbsolute( const QString & _file ) +{ + if( _file[0] == '/' ) + { + return( _file ); + } + + QString f = configManager::inst()->userSamplesDir() + _file; + if( QFileInfo( f ).exists() ) + { + return( f ); + } + + return( configManager::inst()->factorySamplesDir() + _file ); +} + + + + + + + + +sampleBuffer::handleState::handleState( bool _varying_pitch ) : + m_frame_index( 0 ), + m_varying_pitch( _varying_pitch ) +{ +#ifdef HAVE_SAMPLERATE_H + int error; + if( ( m_resampling_data = src_new(/* + ( eng()->getMixer()->highQuality() == TRUE ) ? + SRC_SINC_FASTEST :*/ + SRC_LINEAR, + DEFAULT_CHANNELS, &error ) ) == NULL ) + { + printf( "Error: src_new() failed in sample_buffer.cpp!\n" ); + } +#endif +} + + + + +sampleBuffer::handleState::~handleState() +{ +#ifdef HAVE_SAMPLERATE_H + src_delete( m_resampling_data ); +#endif +} + + + + #undef write #undef read #undef pos diff --git a/src/tracks/bb_track.cpp b/src/tracks/bb_track.cpp index 2bbfa6c9e9..29dc46e100 100644 --- a/src/tracks/bb_track.cpp +++ b/src/tracks/bb_track.cpp @@ -3,7 +3,7 @@ /* * bb_track.cpp - implementation of class bbTrack and bbTCO * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2007 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -508,6 +508,8 @@ void bbTrack::loadTrackSpecificSettings( const QDomElement & _this ) { m_trackLabel->setPixmapFile( _this.attribute( "icon" ) ); } + eng()->getBBEditor()->updateComboBox(); + QDomNode node = _this.namedItem( trackContainer::classNodeName() ); if( node.isElement() ) { diff --git a/src/tracks/pattern.cpp b/src/tracks/pattern.cpp index fe44021db8..a1715a28b6 100644 --- a/src/tracks/pattern.cpp +++ b/src/tracks/pattern.cpp @@ -3,8 +3,8 @@ /* * pattern.cpp - implementation of class pattern which holds notes * - * Copyright (c) 2004-2006 Tobias Doerffel - * Copyright (c) 2005 Danny McRae + * Copyright (c) 2004-2007 Tobias Doerffel + * Copyright (c) 2005-2007 Danny McRae * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -88,6 +88,7 @@ pattern::pattern ( instrumentTrack * _instrument_track ) : m_patternType( BEAT_PATTERN ), m_name( _instrument_track->name() ), m_steps( DEFAULT_STEPS_PER_TACT ), +//TODO: check mutex m_frozenPatternMutex(), m_frozenPattern( NULL ), m_freezing( FALSE ), @@ -399,21 +400,6 @@ void pattern::checkType( void ) -//TODO: remove this method, check mutex -void pattern::playFrozenData( sampleFrame * _ab, const f_cnt_t _start_frame, - const fpab_t _frames ) -{ - m_frozenPatternMutex.lock(); - if( m_frozenPattern != NULL ) - { - m_frozenPattern->play( _ab, _start_frame, _frames ); - } - m_frozenPatternMutex.unlock(); -} - - - - void pattern::saveSettings( QDomDocument & _doc, QDomElement & _this ) { _this.setAttribute( "type", m_patternType );