123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792 |
- /*
- * Ctrl_smp.cpp
- * ------------
- * Purpose: Sample tab, upper panel.
- * Notes : (currently none)
- * Authors: Olivier Lapicque
- * OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Mptrack.h"
- #include "Mainfrm.h"
- #include "InputHandler.h"
- #include "Childfrm.h"
- #include "ImageLists.h"
- #include "Moddoc.h"
- #include "../soundlib/mod_specifications.h"
- #include "Globals.h"
- #include "Ctrl_smp.h"
- #include "View_smp.h"
- #include "SampleEditorDialogs.h"
- #include "dlg_misc.h"
- #include "PSRatioCalc.h"
- #include <soundtouch/include/SoundTouch.h>
- #include <soundtouch/source/SoundTouchDLL/SoundTouchDLL.h>
- #include <smbPitchShift/smbPitchShift.h>
- #include "../tracklib/SampleEdit.h"
- #include "Autotune.h"
- #include "../common/mptStringBuffer.h"
- #include "../common/mptFileIO.h"
- #include "../common/FileReader.h"
- #include "openmpt/soundbase/Copy.hpp"
- #include "openmpt/soundbase/SampleConvert.hpp"
- #include "openmpt/soundbase/SampleDecode.hpp"
- #include "../soundlib/SampleCopy.h"
- #include "FileDialog.h"
- #include "ProgressDialog.h"
- #include "../include/r8brain/CDSPResampler.h"
- #include "../soundlib/MixFuncTable.h"
- #include "mpt/audio/span.hpp"
- OPENMPT_NAMESPACE_BEGIN
- #define BASENOTE_MIN (1*12) // C-1
- #define BASENOTE_MAX (10*12+11) // B-10
- BEGIN_MESSAGE_MAP(CCtrlSamples, CModControlDlg)
- //{{AFX_MSG_MAP(CCtrlSamples)
- ON_WM_VSCROLL()
- ON_WM_XBUTTONUP()
- ON_NOTIFY(TBN_DROPDOWN, IDC_TOOLBAR1, &CCtrlSamples::OnTbnDropDownToolBar)
- ON_COMMAND(IDC_SAMPLE_NEW, &CCtrlSamples::OnSampleNew)
- ON_COMMAND(IDC_SAMPLE_DUPLICATE, &CCtrlSamples::OnSampleDuplicate)
- ON_COMMAND(IDC_SAMPLE_OPEN, &CCtrlSamples::OnSampleOpen)
- ON_COMMAND(IDC_SAMPLE_OPENKNOWN, &CCtrlSamples::OnSampleOpenKnown)
- ON_COMMAND(IDC_SAMPLE_OPENRAW, &CCtrlSamples::OnSampleOpenRaw)
- ON_COMMAND(IDC_SAMPLE_SAVEAS, &CCtrlSamples::OnSampleSave)
- ON_COMMAND(IDC_SAVE_ONE, &CCtrlSamples::OnSampleSaveOne)
- ON_COMMAND(IDC_SAVE_ALL, &CCtrlSamples::OnSampleSaveAll)
- ON_COMMAND(IDC_SAMPLE_PLAY, &CCtrlSamples::OnSamplePlay)
- ON_COMMAND(IDC_SAMPLE_NORMALIZE, &CCtrlSamples::OnNormalize)
- ON_COMMAND(IDC_SAMPLE_AMPLIFY, &CCtrlSamples::OnAmplify)
- ON_COMMAND(IDC_SAMPLE_RESAMPLE, &CCtrlSamples::OnResample)
- ON_COMMAND(IDC_SAMPLE_REVERSE, &CCtrlSamples::OnReverse)
- ON_COMMAND(IDC_SAMPLE_SILENCE, &CCtrlSamples::OnSilence)
- ON_COMMAND(IDC_SAMPLE_INVERT, &CCtrlSamples::OnInvert)
- ON_COMMAND(IDC_SAMPLE_SIGN_UNSIGN, &CCtrlSamples::OnSignUnSign)
- ON_COMMAND(IDC_SAMPLE_DCOFFSET, &CCtrlSamples::OnRemoveDCOffset)
- ON_COMMAND(IDC_SAMPLE_XFADE, &CCtrlSamples::OnXFade)
- ON_COMMAND(IDC_SAMPLE_STEREOSEPARATION, &CCtrlSamples::OnStereoSeparation)
- ON_COMMAND(IDC_SAMPLE_AUTOTUNE, &CCtrlSamples::OnAutotune)
- ON_COMMAND(IDC_CHECK1, &CCtrlSamples::OnSetPanningChanged)
- ON_COMMAND(IDC_CHECK2, &CCtrlSamples::OnKeepSampleOnDisk)
- ON_COMMAND(ID_PREVINSTRUMENT, &CCtrlSamples::OnPrevInstrument)
- ON_COMMAND(ID_NEXTINSTRUMENT, &CCtrlSamples::OnNextInstrument)
- ON_COMMAND(IDC_BUTTON1, &CCtrlSamples::OnPitchShiftTimeStretch)
- ON_COMMAND(IDC_BUTTON2, &CCtrlSamples::OnEstimateSampleSize)
- ON_COMMAND(IDC_CHECK3, &CCtrlSamples::OnEnableStretchToSize)
- ON_COMMAND(IDC_SAMPLE_INITOPL, &CCtrlSamples::OnInitOPLInstrument)
-
- ON_EN_CHANGE(IDC_SAMPLE_NAME, &CCtrlSamples::OnNameChanged)
- ON_EN_CHANGE(IDC_SAMPLE_FILENAME, &CCtrlSamples::OnFileNameChanged)
- ON_EN_CHANGE(IDC_EDIT_SAMPLE, &CCtrlSamples::OnSampleChanged)
- ON_EN_CHANGE(IDC_EDIT1, &CCtrlSamples::OnLoopPointsChanged)
- ON_EN_CHANGE(IDC_EDIT2, &CCtrlSamples::OnLoopPointsChanged)
- ON_EN_CHANGE(IDC_EDIT3, &CCtrlSamples::OnSustainPointsChanged)
- ON_EN_CHANGE(IDC_EDIT4, &CCtrlSamples::OnSustainPointsChanged)
- ON_EN_CHANGE(IDC_EDIT5, &CCtrlSamples::OnFineTuneChanged)
- ON_EN_CHANGE(IDC_EDIT7, &CCtrlSamples::OnVolumeChanged)
- ON_EN_CHANGE(IDC_EDIT8, &CCtrlSamples::OnGlobalVolChanged)
- ON_EN_CHANGE(IDC_EDIT9, &CCtrlSamples::OnPanningChanged)
- ON_EN_CHANGE(IDC_EDIT14, &CCtrlSamples::OnVibSweepChanged)
- ON_EN_CHANGE(IDC_EDIT15, &CCtrlSamples::OnVibDepthChanged)
- ON_EN_CHANGE(IDC_EDIT16, &CCtrlSamples::OnVibRateChanged)
- ON_EN_SETFOCUS(IDC_SAMPLE_NAME, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_SAMPLE_FILENAME, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT1, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT2, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT3, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT4, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT5, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT7, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT8, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT9, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT14, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT15, &CCtrlSamples::OnEditFocus)
- ON_EN_SETFOCUS(IDC_EDIT16, &CCtrlSamples::OnEditFocus)
- ON_EN_KILLFOCUS(IDC_EDIT5, &CCtrlSamples::OnFineTuneChangedDone)
- ON_CBN_SELCHANGE(IDC_COMBO_BASENOTE,&CCtrlSamples::OnBaseNoteChanged)
- ON_CBN_SELCHANGE(IDC_COMBO_ZOOM, &CCtrlSamples::OnZoomChanged)
- ON_CBN_SELCHANGE(IDC_COMBO1, &CCtrlSamples::OnLoopTypeChanged)
- ON_CBN_SELCHANGE(IDC_COMBO2, &CCtrlSamples::OnSustainTypeChanged)
- ON_CBN_SELCHANGE(IDC_COMBO3, &CCtrlSamples::OnVibTypeChanged)
- ON_MESSAGE(WM_MOD_KEYCOMMAND, &CCtrlSamples::OnCustomKeyMsg)
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
- void CCtrlSamples::DoDataExchange(CDataExchange* pDX)
- {
- CModControlDlg::DoDataExchange(pDX);
- //{{AFX_DATA_MAP(CCtrlSamples)
- DDX_Control(pDX, IDC_TOOLBAR1, m_ToolBar1);
- DDX_Control(pDX, IDC_TOOLBAR2, m_ToolBar2);
- DDX_Control(pDX, IDC_SAMPLE_NAME, m_EditName);
- DDX_Control(pDX, IDC_SAMPLE_FILENAME, m_EditFileName);
- DDX_Control(pDX, IDC_SAMPLE_NAME, m_EditName);
- DDX_Control(pDX, IDC_SAMPLE_FILENAME, m_EditFileName);
- DDX_Control(pDX, IDC_COMBO_ZOOM, m_ComboZoom);
- DDX_Control(pDX, IDC_COMBO_BASENOTE, m_CbnBaseNote);
- DDX_Control(pDX, IDC_SPIN_SAMPLE, m_SpinSample);
- DDX_Control(pDX, IDC_EDIT_SAMPLE, m_EditSample);
- DDX_Control(pDX, IDC_CHECK1, m_CheckPanning);
- DDX_Control(pDX, IDC_SPIN1, m_SpinLoopStart);
- DDX_Control(pDX, IDC_SPIN2, m_SpinLoopEnd);
- DDX_Control(pDX, IDC_SPIN3, m_SpinSustainStart);
- DDX_Control(pDX, IDC_SPIN4, m_SpinSustainEnd);
- DDX_Control(pDX, IDC_SPIN5, m_SpinFineTune);
- DDX_Control(pDX, IDC_SPIN7, m_SpinVolume);
- DDX_Control(pDX, IDC_SPIN8, m_SpinGlobalVol);
- DDX_Control(pDX, IDC_SPIN9, m_SpinPanning);
- DDX_Control(pDX, IDC_SPIN11, m_SpinVibSweep);
- DDX_Control(pDX, IDC_SPIN12, m_SpinVibDepth);
- DDX_Control(pDX, IDC_SPIN13, m_SpinVibRate);
- DDX_Control(pDX, IDC_COMBO1, m_ComboLoopType);
- DDX_Control(pDX, IDC_COMBO2, m_ComboSustainType);
- DDX_Control(pDX, IDC_COMBO3, m_ComboAutoVib);
- DDX_Control(pDX, IDC_EDIT1, m_EditLoopStart);
- DDX_Control(pDX, IDC_EDIT2, m_EditLoopEnd);
- DDX_Control(pDX, IDC_EDIT3, m_EditSustainStart);
- DDX_Control(pDX, IDC_EDIT4, m_EditSustainEnd);
- DDX_Control(pDX, IDC_EDIT5, m_EditFineTune);
- DDX_Control(pDX, IDC_EDIT7, m_EditVolume);
- DDX_Control(pDX, IDC_EDIT8, m_EditGlobalVol);
- DDX_Control(pDX, IDC_EDIT9, m_EditPanning);
- DDX_Control(pDX, IDC_EDIT14, m_EditVibSweep);
- DDX_Control(pDX, IDC_EDIT15, m_EditVibDepth);
- DDX_Control(pDX, IDC_EDIT16, m_EditVibRate);
- DDX_Control(pDX, IDC_COMBO4, m_ComboPitch);
- DDX_Control(pDX, IDC_COMBO5, m_ComboQuality);
- DDX_Control(pDX, IDC_COMBO6, m_ComboFFT);
- DDX_Control(pDX, IDC_SPIN10, m_SpinSequenceMs);
- DDX_Control(pDX, IDC_SPIN14, m_SpinSeekWindowMs);
- DDX_Control(pDX, IDC_SPIN15, m_SpinOverlap);
- DDX_Control(pDX, IDC_SPIN16, m_SpinStretchAmount);
- DDX_Text(pDX, IDC_EDIT6, m_dTimeStretchRatio);
- //}}AFX_DATA_MAP
- }
- CCtrlSamples::CCtrlSamples(CModControlView &parent, CModDoc &document)
- : CModControlDlg(parent, document)
- {
- m_nLockCount = 1;
- }
- CCtrlSamples::~CCtrlSamples()
- {
- }
- CRuntimeClass *CCtrlSamples::GetAssociatedViewClass()
- {
- return RUNTIME_CLASS(CViewSample);
- }
- void CCtrlSamples::OnEditFocus()
- {
- m_startedEdit = false;
- }
- BOOL CCtrlSamples::OnInitDialog()
- {
- CModControlDlg::OnInitDialog();
- m_bInitialized = FALSE;
- SetRedraw(FALSE);
- // Zoom Selection
- static constexpr std::pair<const TCHAR *, int> ZoomLevels[] =
- {
- {_T("Auto"), 0},
- {_T("1:1"), 1},
- {_T("2:1"), -2},
- {_T("4:1"), -3},
- {_T("8:1"), -4},
- {_T("16:1"), -5},
- {_T("32:1"), -6},
- {_T("1:2"), 2},
- {_T("1:4"), 3},
- {_T("1:8"), 4},
- {_T("1:16"), 5},
- {_T("1:32"), 6},
- {_T("1:64"), 7},
- {_T("1:128"), 8},
- {_T("1:256"), 9},
- {_T("1:512"), 10},
- };
- m_ComboZoom.SetRedraw(FALSE);
- m_ComboZoom.InitStorage(static_cast<int>(std::size(ZoomLevels)), 4);
- for(const auto &[str, data] : ZoomLevels)
- {
- m_ComboZoom.SetItemData(m_ComboZoom.AddString(str), static_cast<DWORD_PTR>(data));
- }
- m_ComboZoom.SetRedraw(TRUE);
- m_ComboZoom.SetCurSel(0);
- // File ToolBar
- m_ToolBar1.SetExtendedStyle(m_ToolBar1.GetExtendedStyle() | TBSTYLE_EX_DRAWDDARROWS);
- m_ToolBar1.Init(CMainFrame::GetMainFrame()->m_PatternIcons,CMainFrame::GetMainFrame()->m_PatternIconsDisabled);
- m_ToolBar1.AddButton(IDC_SAMPLE_NEW, TIMAGE_SAMPLE_NEW, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN);
- m_ToolBar1.AddButton(IDC_SAMPLE_OPEN, TIMAGE_OPEN, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN);
- m_ToolBar1.AddButton(IDC_SAMPLE_SAVEAS, TIMAGE_SAVE, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN);
- // Edit ToolBar
- m_ToolBar2.Init(CMainFrame::GetMainFrame()->m_PatternIcons,CMainFrame::GetMainFrame()->m_PatternIconsDisabled);
- m_ToolBar2.AddButton(IDC_SAMPLE_PLAY, TIMAGE_PREVIEW);
- m_ToolBar2.AddButton(IDC_SAMPLE_NORMALIZE, TIMAGE_SAMPLE_NORMALIZE);
- m_ToolBar2.AddButton(IDC_SAMPLE_AMPLIFY, TIMAGE_SAMPLE_AMPLIFY);
- m_ToolBar2.AddButton(IDC_SAMPLE_DCOFFSET, TIMAGE_SAMPLE_DCOFFSET);
- m_ToolBar2.AddButton(IDC_SAMPLE_STEREOSEPARATION, TIMAGE_SAMPLE_STEREOSEP);
- m_ToolBar2.AddButton(IDC_SAMPLE_RESAMPLE, TIMAGE_SAMPLE_RESAMPLE);
- m_ToolBar2.AddButton(IDC_SAMPLE_REVERSE, TIMAGE_SAMPLE_REVERSE);
- m_ToolBar2.AddButton(IDC_SAMPLE_SILENCE, TIMAGE_SAMPLE_SILENCE);
- m_ToolBar2.AddButton(IDC_SAMPLE_INVERT, TIMAGE_SAMPLE_INVERT);
- m_ToolBar2.AddButton(IDC_SAMPLE_SIGN_UNSIGN, TIMAGE_SAMPLE_UNSIGN);
- m_ToolBar2.AddButton(IDC_SAMPLE_XFADE, TIMAGE_SAMPLE_FIXLOOP);
- m_ToolBar2.AddButton(IDC_SAMPLE_AUTOTUNE, TIMAGE_SAMPLE_AUTOTUNE);
- // Setup Controls
- m_SpinVolume.SetRange(0, 64);
- m_SpinGlobalVol.SetRange(0, 64);
- m_CbnBaseNote.InitStorage(BASENOTE_MAX - BASENOTE_MIN, 4);
- m_CbnBaseNote.SetRedraw(FALSE);
- for(ModCommand::NOTE i = BASENOTE_MIN; i <= BASENOTE_MAX; i++)
- {
- CString noteName = mpt::ToCString(CSoundFile::GetDefaultNoteName(i % 12)) + mpt::cfmt::val(i / 12);
- m_CbnBaseNote.SetItemData(m_CbnBaseNote.AddString(noteName), i - (NOTE_MIDDLEC - NOTE_MIN));
- }
- m_CbnBaseNote.SetRedraw(TRUE);
- m_ComboFFT.ShowWindow(SW_SHOW);
- m_ComboPitch.ShowWindow(SW_SHOW);
- m_ComboQuality.ShowWindow(SW_SHOW);
- m_ComboFFT.ShowWindow(SW_SHOW);
- // Pitch selection
- // Allow pitch from -12 (1 octave down) to +12 (1 octave up)
- m_ComboPitch.InitStorage(25, 4);
- m_ComboPitch.SetRedraw(FALSE);
- for(int i = -12 ; i <= 12 ; i++)
- {
- mpt::tstring str;
- if(i == 0)
- str = _T("none");
- else if(i < 0)
- str = mpt::tfmt::dec(i);
- else
- str = _T("+") + mpt::tfmt::dec(i);
- m_ComboPitch.SetItemData(m_ComboPitch.AddString(str.c_str()), i + 12);
- }
- m_ComboPitch.SetRedraw(TRUE);
- // Set "none" as default pitch
- m_ComboPitch.SetCurSel(12);
- // Quality selection
- // Allow quality from 4 to 128
- m_ComboQuality.InitStorage(128 - 4, 4);
- m_ComboQuality.SetRedraw(FALSE);
- for(int i = 4; i <= 128; i++)
- {
- m_ComboQuality.SetItemData(m_ComboQuality.AddString(mpt::tfmt::dec(i).c_str()), i - 4);
- }
- m_ComboQuality.SetRedraw(TRUE);
- // Set 32 as default quality
- m_ComboQuality.SetCurSel(32 - 4);
- // FFT size selection
- // Deduce exponent from equation : MAX_FRAME_LENGTH = 2^exponent
- constexpr int exponent = mpt::bit_width(uint32(MAX_FRAME_LENGTH)) - 1;
- // Allow FFT size from 2^8 (256) to 2^exponent (MAX_FRAME_LENGTH)
- m_ComboFFT.InitStorage(exponent - 8, 4);
- m_ComboFFT.SetRedraw(FALSE);
- for(int i = 8 ; i <= exponent ; i++)
- {
- m_ComboFFT.SetItemData(m_ComboFFT.AddString(mpt::tfmt::dec(1 << i).c_str()), i - 8);
- }
- m_ComboFFT.SetRedraw(TRUE);
- // Set 4096 as default FFT size
- m_ComboFFT.SetCurSel(4);
- // Stretch to size check box
- OnEnableStretchToSize();
- m_SpinSequenceMs.SetRange32(0, 9999);
- m_SpinSeekWindowMs.SetRange32(0, 9999);
- m_SpinOverlap.SetRange32(0, 9999);
- m_SpinStretchAmount.SetRange32(50, 200);
- SetRedraw(TRUE);
- return TRUE;
- }
- void CCtrlSamples::RecalcLayout()
- {
- }
- bool CCtrlSamples::SetCurrentSample(SAMPLEINDEX nSmp, LONG lZoom, bool bUpdNum)
- {
- if(m_sndFile.GetNumSamples() < 1)
- m_sndFile.m_nSamples = 1;
- if((nSmp < 1) || (nSmp > m_sndFile.GetNumSamples()))
- return FALSE;
- LockControls();
- if(m_nSample != nSmp)
- {
- m_nSample = nSmp;
- UpdateView(SampleHint(m_nSample).Info());
- m_parent.SampleChanged(m_nSample);
- }
- if(bUpdNum)
- {
- SetDlgItemInt(IDC_EDIT_SAMPLE, m_nSample);
- m_SpinSample.SetRange(1, m_sndFile.GetNumSamples());
- }
- if(lZoom == -1)
- {
- lZoom = static_cast<int>(m_ComboZoom.GetItemData(m_ComboZoom.GetCurSel()));
- } else
- {
- for(int i = 0; i< m_ComboZoom.GetCount(); i++)
- {
- if(static_cast<int>(m_ComboZoom.GetItemData(i)) == lZoom)
- {
- m_ComboZoom.SetCurSel(i);
- break;
- }
- }
- }
- static_assert(MAX_SAMPLES < uint16_max);
- SendViewMessage(VIEWMSG_SETCURRENTSAMPLE, (lZoom << 16) | m_nSample);
- UnlockControls();
- return true;
- }
- void CCtrlSamples::OnActivatePage(LPARAM lParam)
- {
- if (lParam < 0)
- {
- int nIns = m_parent.GetInstrumentChange();
- if (m_sndFile.GetNumInstruments())
- {
- if ((nIns > 0) && (!m_modDoc.IsChildSample((INSTRUMENTINDEX)nIns, m_nSample)))
- {
- SAMPLEINDEX k = m_modDoc.FindInstrumentChild((INSTRUMENTINDEX)nIns);
- if (k > 0) lParam = k;
- }
- } else
- {
- if (nIns > 0) lParam = nIns;
- }
- } else if (lParam > 0)
- {
- if (m_sndFile.GetNumInstruments())
- {
- INSTRUMENTINDEX k = (INSTRUMENTINDEX)m_parent.GetInstrumentChange();
- if (!m_modDoc.IsChildSample(k, (SAMPLEINDEX)lParam))
- {
- INSTRUMENTINDEX nins = m_modDoc.FindSampleParent((SAMPLEINDEX)lParam);
- if(nins != INSTRUMENTINDEX_INVALID)
- {
- m_parent.InstrumentChanged(nins);
- }
- }
- } else
- {
- m_parent.InstrumentChanged(static_cast<int>(lParam));
- }
- }
- CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
- SAMPLEVIEWSTATE &sampleState = pFrame->GetSampleViewState();
- if(sampleState.initialSample != 0)
- {
- m_nSample = sampleState.initialSample;
- sampleState.initialSample = 0;
- }
- SetCurrentSample((lParam > 0) ? ((SAMPLEINDEX)lParam) : m_nSample);
- // Initial Update
- if (!m_bInitialized) UpdateView(SampleHint(m_nSample).Info().ModType(), NULL);
- if (m_hWndView) PostViewMessage(VIEWMSG_LOADSTATE, (LPARAM)&sampleState);
- SwitchToView();
- // Combo boxes randomly disappear without this... why?
- Invalidate();
- }
- void CCtrlSamples::OnDeactivatePage()
- {
- CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
- if ((pFrame) && (m_hWndView)) SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&pFrame->GetSampleViewState());
- m_modDoc.NoteOff(0, true);
- }
- LRESULT CCtrlSamples::OnModCtrlMsg(WPARAM wParam, LPARAM lParam)
- {
- switch(wParam)
- {
- case CTRLMSG_GETCURRENTINSTRUMENT:
- return m_nSample;
- break;
- case CTRLMSG_SMP_PREVINSTRUMENT:
- OnPrevInstrument();
- break;
- case CTRLMSG_SMP_NEXTINSTRUMENT:
- OnNextInstrument();
- break;
- case CTRLMSG_SMP_OPENFILE:
- if(lParam)
- return OpenSample(*reinterpret_cast<const mpt::PathString *>(lParam));
- break;
- case CTRLMSG_SMP_SONGDROP:
- if(lParam)
- {
- const auto &dropInfo = *reinterpret_cast<const DRAGONDROP *>(lParam);
- if(dropInfo.sndFile)
- return OpenSample(*dropInfo.sndFile, static_cast<SAMPLEINDEX>(dropInfo.dropItem)) ? TRUE : FALSE;
- }
- break;
- case CTRLMSG_SMP_SETZOOM:
- SetCurrentSample(m_nSample, static_cast<int>(lParam), FALSE);
- break;
- case CTRLMSG_SETCURRENTINSTRUMENT:
- SetCurrentSample((SAMPLEINDEX)lParam, -1, TRUE);
- break;
- case CTRLMSG_SMP_INITOPL:
- OnInitOPLInstrument();
- break;
- case CTRLMSG_SMP_NEWSAMPLE:
- return InsertSample(false) ? 1 : 0;
- case IDC_SAMPLE_REVERSE:
- OnReverse();
- break;
- case IDC_SAMPLE_SILENCE:
- OnSilence();
- break;
- case IDC_SAMPLE_INVERT:
- OnInvert();
- break;
- case IDC_SAMPLE_XFADE:
- OnXFade();
- break;
- case IDC_SAMPLE_STEREOSEPARATION:
- OnStereoSeparation();
- break;
- case IDC_SAMPLE_AUTOTUNE:
- OnAutotune();
- break;
- case IDC_SAMPLE_SIGN_UNSIGN:
- OnSignUnSign();
- break;
- case IDC_SAMPLE_DCOFFSET:
- RemoveDCOffset(false);
- break;
- case IDC_SAMPLE_NORMALIZE:
- Normalize(false);
- break;
- case IDC_SAMPLE_AMPLIFY:
- OnAmplify();
- break;
- case IDC_SAMPLE_QUICKFADE:
- OnQuickFade();
- break;
- case IDC_SAMPLE_OPEN:
- OnSampleOpen();
- break;
- case IDC_SAMPLE_SAVEAS:
- OnSampleSave();
- break;
- case IDC_SAMPLE_NEW:
- InsertSample(false);
- break;
- default:
- return CModControlDlg::OnModCtrlMsg(wParam, lParam);
- }
- return 0;
- }
- BOOL CCtrlSamples::GetToolTipText(UINT uId, LPTSTR pszText)
- {
- if ((pszText) && (uId))
- {
- UINT val = GetDlgItemInt(uId);
- const TCHAR *s = nullptr;
- CommandID cmd = kcNull;
- switch(uId)
- {
- case IDC_SAMPLE_NEW: s = _T("Insert Sample"); cmd = kcSampleNew; break;
- case IDC_SAMPLE_OPEN: s = _T("Import Sample"); cmd = kcSampleLoad; break;
- case IDC_SAMPLE_SAVEAS: s = _T("Save Sample"); cmd = kcSampleSave; break;
- case IDC_SAMPLE_PLAY: s = _T("Play Sample"); break;
- case IDC_SAMPLE_NORMALIZE: s = _T("Normalize (hold shift to normalize all samples)"); cmd = kcSampleNormalize; break;
- case IDC_SAMPLE_AMPLIFY: s = _T("Amplify"); cmd = kcSampleAmplify; break;
- case IDC_SAMPLE_DCOFFSET: s = _T("Remove DC Offset and Normalize (hold shift to process all samples)"); cmd = kcSampleRemoveDCOffset; break;
- case IDC_SAMPLE_STEREOSEPARATION: s = _T("Change Stereo Separation / Stereo Width of the sample"); cmd = kcSampleStereoSep; break;
- case IDC_SAMPLE_RESAMPLE: s = _T("Resample"); cmd = kcSampleResample; break;
- case IDC_SAMPLE_REVERSE: s = _T("Reverse"); cmd = kcSampleReverse; break;
- case IDC_SAMPLE_SILENCE: s = _T("Silence"); cmd = kcSampleSilence; break;
- case IDC_SAMPLE_INVERT: s = _T("Invert Phase"); cmd = kcSampleInvert; break;
- case IDC_SAMPLE_SIGN_UNSIGN: s = _T("Signed/Unsigned Conversion"); cmd = kcSampleSignUnsign; break;
- case IDC_SAMPLE_XFADE: s = _T("Crossfade Sample Loops"); cmd = kcSampleXFade; break;
- case IDC_SAMPLE_AUTOTUNE: s = _T("Tune the sample to a given note"); cmd = kcSampleAutotune; break;
- case IDC_EDIT7:
- case IDC_EDIT8:
- // Volume to dB
- if(IsOPLInstrument())
- _tcscpy(pszText, (mpt::tfmt::fix((static_cast<int32>(val) - 64) * 0.75, 2) + _T(" dB")).c_str());
- else
- _tcscpy(pszText, CModDoc::LinearToDecibels(val, 64.0));
- return TRUE;
- case IDC_EDIT9:
- // Panning
- if(m_nSample)
- {
- const ModSample &sample = m_sndFile.GetSample(m_nSample);
- _tcscpy(pszText, CModDoc::PanningToString(sample.nPan, 128));
- }
- return TRUE;
- case IDC_EDIT5:
- case IDC_SPIN5:
- case IDC_COMBO_BASENOTE:
- if(m_nSample)
- {
- const ModSample &sample = m_sndFile.GetSample(m_nSample);
- const auto freqHz = sample.GetSampleRate(m_sndFile.GetType());
- if(sample.uFlags[CHN_ADLIB])
- {
- // Translate to actual note frequency
- _tcscpy(pszText, MPT_TFORMAT("{}Hz")(mpt::tfmt::flt(freqHz * (261.625 / 8363.0), 6)).c_str());
- return TRUE;
- }
- if(m_sndFile.UseFinetuneAndTranspose())
- {
- // Transpose + Finetune to Frequency
- _tcscpy(pszText, MPT_TFORMAT("{}Hz")(freqHz).c_str());
- return TRUE;
- }
- }
- break;
- case IDC_EDIT14:
- // Vibrato Sweep
- if(!(m_sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM)))
- {
- s = _T("Only available in IT / MPTM / XM format");
- break;
- } else if(m_nSample)
- {
- const ModSample &sample = m_sndFile.GetSample(m_nSample);
- int ticks = -1;
- if(m_sndFile.m_playBehaviour[kITVibratoTremoloPanbrello])
- {
- if(val > 0)
- ticks = Util::muldivr_unsigned(sample.nVibDepth, 256, val);
- } else if(m_sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
- {
- if(val > 0)
- ticks = Util::muldivr_unsigned(sample.nVibDepth, 128, val);
- } else
- {
- ticks = val;
- }
- if(ticks >= 0)
- _stprintf(pszText, _T("%d ticks"), ticks);
- else
- _tcscpy(pszText, _T("No Vibrato"));
- }
- return TRUE;
- case IDC_EDIT15:
- // Vibrato Depth
- if(!(m_sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM)))
- _tcscpy(pszText, _T("Only available in IT / MPTM / XM format"));
- else
- _stprintf(pszText, _T("%u cents"), Util::muldivr_unsigned(val, 100, 64));
- return TRUE;
- case IDC_EDIT16:
- // Vibrato Rate
- if(!(m_sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM)))
- {
- s = _T("Only available in IT / MPTM / XM format");
- break;
- } else if(val == 0)
- {
- s = _T("Stopped");
- break;
- } else
- {
- const double ticksPerCycle = 256.0 / val;
- const uint32 ticksPerBeat = std::max(1u, m_sndFile.m_PlayState.m_nCurrentRowsPerBeat * m_sndFile.m_PlayState.m_nMusicSpeed);
- _stprintf(pszText, _T("%.2f beats per cycle (%.2f ticks)"), ticksPerCycle / ticksPerBeat, ticksPerCycle);
- }
- return TRUE;
- case IDC_CHECK1:
- case IDC_EDIT3:
- case IDC_EDIT4:
- case IDC_COMBO2:
- if(!(m_sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))
- s = _T("Only available in IT / MPTM format");
- break;
- case IDC_COMBO3:
- if(!(m_sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM)))
- s = _T("Only available in IT / MPTM / XM format");
- break;
- case IDC_CHECK2:
- s = _T("Keep a reference to the original waveform instead of saving it in the module.");
- break;
- }
- if(s != nullptr)
- {
- _tcscpy(pszText, s);
- if(cmd != kcNull)
- {
- auto keyText = CMainFrame::GetInputHandler()->m_activeCommandSet->GetKeyTextFromCommand(cmd, 0);
- if (!keyText.IsEmpty())
- _tcscat(pszText, MPT_TFORMAT(" ({})")(keyText).c_str());
- }
- return TRUE;
- }
- }
- return FALSE;
- }
- void CCtrlSamples::UpdateView(UpdateHint hint, CObject *pObj)
- {
- if(pObj == this) return;
- if (hint.GetType()[HINT_MPTOPTIONS])
- {
- m_ToolBar1.UpdateStyle();
- m_ToolBar2.UpdateStyle();
- }
- const SampleHint sampleHint = hint.ToType<SampleHint>();
- FlagSet<HintType> hintType = sampleHint.GetType();
- if (!m_bInitialized) hintType.set(HINT_MODTYPE);
- if(!hintType[HINT_SMPNAMES | HINT_SAMPLEINFO | HINT_MODTYPE]) return;
- const SAMPLEINDEX updateSmp = sampleHint.GetSample();
- if(updateSmp != m_nSample && updateSmp != 0 && !hintType[HINT_MODTYPE]) return;
- const CModSpecifications &specs = m_sndFile.GetModSpecifications();
- const bool isOPL = IsOPLInstrument();
- LockControls();
- // Updating Ranges
- if(hintType[HINT_MODTYPE])
- {
- // Limit text fields
- m_EditName.SetLimitText(specs.sampleNameLengthMax);
- m_EditFileName.SetLimitText(specs.sampleFilenameLengthMax);
- // Loop Type
- m_ComboLoopType.ResetContent();
- m_ComboLoopType.AddString(_T("Off"));
- m_ComboLoopType.AddString(_T("On"));
- // Sustain Loop Type
- m_ComboSustainType.ResetContent();
- m_ComboSustainType.AddString(_T("Off"));
- m_ComboSustainType.AddString(_T("On"));
- // Bidirectional Loops
- if (m_sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_IT|MOD_TYPE_MPT))
- {
- m_ComboLoopType.AddString(_T("Bidi"));
- m_ComboSustainType.AddString(_T("Bidi"));
- }
- // Loop Start
- m_SpinLoopStart.SetRange(-1, 1);
- m_SpinLoopStart.SetPos(0);
- // Loop End
- m_SpinLoopEnd.SetRange(-1, 1);
- m_SpinLoopEnd.SetPos(0);
- // Sustain Loop Start
- m_SpinSustainStart.SetRange(-1, 1);
- m_SpinSustainStart.SetPos(0);
- // Sustain Loop End
- m_SpinSustainEnd.SetRange(-1, 1);
- m_SpinSustainEnd.SetPos(0);
- // Finetune / C-5 Speed / BaseNote
- BOOL b = m_sndFile.UseFinetuneAndTranspose() ? FALSE : TRUE;
- SetDlgItemText(IDC_TEXT7, (b) ? _T("Freq. (Hz)") : _T("Finetune"));
- m_SpinFineTune.SetRange(-1, 1);
- m_EditFileName.EnableWindow(b);
- // AutoVibrato
- b = (m_sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_IT|MOD_TYPE_MPT)) ? TRUE : FALSE;
- m_ComboAutoVib.EnableWindow(b);
- m_SpinVibSweep.EnableWindow(b);
- m_SpinVibDepth.EnableWindow(b);
- m_SpinVibRate.EnableWindow(b);
- m_EditVibSweep.EnableWindow(b);
- m_EditVibDepth.EnableWindow(b);
- m_EditVibRate.EnableWindow(b);
- m_SpinVibSweep.SetRange(0, 255);
- if(m_sndFile.GetType() & MOD_TYPE_XM)
- {
- m_SpinVibDepth.SetRange(0, 15);
- m_SpinVibRate.SetRange(0, 63);
- } else
- {
- m_SpinVibDepth.SetRange(0, 32);
- m_SpinVibRate.SetRange(0, 64);
- }
- // Global Volume
- b = (m_sndFile.GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT)) ? TRUE : FALSE;
- m_EditGlobalVol.EnableWindow(b);
- m_SpinGlobalVol.EnableWindow(b);
- // Panning
- b = (m_sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_IT|MOD_TYPE_MPT)) ? TRUE : FALSE;
- m_CheckPanning.EnableWindow(b && !(m_sndFile.GetType() & MOD_TYPE_XM));
- m_EditPanning.EnableWindow(b);
- m_SpinPanning.EnableWindow(b);
- m_SpinPanning.SetRange(0, (m_sndFile.GetType() == MOD_TYPE_XM) ? 255 : 64);
- b = (m_sndFile.GetType() & MOD_TYPE_MOD) ? FALSE : TRUE;
- m_CbnBaseNote.EnableWindow(b);
- }
- // Updating Values
- if (hintType[HINT_MODTYPE | HINT_SAMPLEINFO])
- {
- if(m_nSample > m_sndFile.GetNumSamples())
- {
- SetCurrentSample(m_sndFile.GetNumSamples());
- }
- const ModSample &sample = m_sndFile.GetSample(m_nSample);
- CString s;
- DWORD d;
- m_SpinSample.SetRange(1, m_sndFile.GetNumSamples());
- m_SpinSample.Invalidate(FALSE); // In case the spin button was previously disabled
- // Length / Type
- if(isOPL)
- s = _T("OPL instrument");
- else
- s = MPT_CFORMAT("{}-bit {}, len: {}")(sample.GetElementarySampleSize() * 8, CString(sample.uFlags[CHN_STEREO] ? _T("stereo") : _T("mono")), mpt::cfmt::dec(3, ',', sample.nLength));
- SetDlgItemText(IDC_TEXT5, s);
- // File Name
- s = mpt::ToCString(m_sndFile.GetCharsetInternal(), sample.filename);
- if (specs.sampleFilenameLengthMax == 0) s.Empty();
- SetDlgItemText(IDC_SAMPLE_FILENAME, s);
- // Volume
- if(sample.uFlags[SMP_NODEFAULTVOLUME])
- SetDlgItemText(IDC_EDIT7, _T("none"));
- else
- SetDlgItemInt(IDC_EDIT7, sample.nVolume / 4u);
- // Global Volume
- SetDlgItemInt(IDC_EDIT8, sample.nGlobalVol);
- // Panning
- CheckDlgButton(IDC_CHECK1, (sample.uFlags[CHN_PANNING]) ? BST_CHECKED : BST_UNCHECKED);
- if (m_sndFile.GetType() == MOD_TYPE_XM)
- SetDlgItemInt(IDC_EDIT9, sample.nPan); //displayed panning with XM is 0-256, just like MPT's internal engine
- else
- SetDlgItemInt(IDC_EDIT9, sample.nPan / 4u); //displayed panning with anything but XM is 0-64 so we divide by 4
- // FineTune / C-4 Speed / BaseNote
- int transp = 0;
- if (!m_sndFile.UseFinetuneAndTranspose())
- {
- s = mpt::cfmt::val(sample.nC5Speed);
- m_EditFineTune.SetWindowText(s);
- if(sample.nC5Speed != 0)
- transp = ModSample::FrequencyToTranspose(sample.nC5Speed).first;
- } else
- {
- int ftune = ((int)sample.nFineTune);
- // MOD finetune range -8 to 7 translates to -128 to 112
- if(m_sndFile.GetType() & MOD_TYPE_MOD) ftune >>= 4;
- SetDlgItemInt(IDC_EDIT5, ftune);
- transp = (int)sample.RelativeTone;
- }
- int basenote = (NOTE_MIDDLEC - NOTE_MIN) + transp;
- Limit(basenote, BASENOTE_MIN, BASENOTE_MAX);
- basenote -= BASENOTE_MIN;
- if (basenote != m_CbnBaseNote.GetCurSel()) m_CbnBaseNote.SetCurSel(basenote);
- // Auto vibrato
- // Ramp up and ramp down are swapped in XM - probably because they ramp up the *period* instead of *frequency*.
- const VibratoType rampUp = m_sndFile.GetType() == MOD_TYPE_XM ? VIB_RAMP_DOWN : VIB_RAMP_UP;
- const VibratoType rampDown = m_sndFile.GetType() == MOD_TYPE_XM ? VIB_RAMP_UP : VIB_RAMP_DOWN;
- m_ComboAutoVib.ResetContent();
- m_ComboAutoVib.SetItemData(m_ComboAutoVib.AddString(_T("Sine")), VIB_SINE);
- m_ComboAutoVib.SetItemData(m_ComboAutoVib.AddString(_T("Square")), VIB_SQUARE);
- if(m_sndFile.GetType() != MOD_TYPE_IT || sample.nVibType == VIB_RAMP_UP)
- m_ComboAutoVib.SetItemData(m_ComboAutoVib.AddString(_T("Ramp Up")), rampUp);
- m_ComboAutoVib.SetItemData(m_ComboAutoVib.AddString(_T("Ramp Down")), rampDown);
- if(m_sndFile.GetType() != MOD_TYPE_XM || sample.nVibType == VIB_RANDOM)
- m_ComboAutoVib.SetItemData(m_ComboAutoVib.AddString(_T("Random")), VIB_RANDOM);
- for(int i = 0; i < m_ComboAutoVib.GetCount(); i++)
- {
- if(m_ComboAutoVib.GetItemData(i) == sample.nVibType)
- {
- m_ComboAutoVib.SetCurSel(i);
- break;
- }
- }
- SetDlgItemInt(IDC_EDIT14, sample.nVibSweep);
- SetDlgItemInt(IDC_EDIT15, sample.nVibDepth);
- SetDlgItemInt(IDC_EDIT16, sample.nVibRate);
- // Loop
- d = 0;
- if (sample.uFlags[CHN_LOOP]) d = sample.uFlags[CHN_PINGPONGLOOP] ? 2 : 1;
- if (sample.uFlags[CHN_REVERSE]) d |= 4;
- m_ComboLoopType.SetCurSel(d);
- s = mpt::cfmt::val(sample.nLoopStart);
- m_EditLoopStart.SetWindowText(s);
- s = mpt::cfmt::val(sample.nLoopEnd);
- m_EditLoopEnd.SetWindowText(s);
- // Sustain Loop
- d = 0;
- if (sample.uFlags[CHN_SUSTAINLOOP]) d = sample.uFlags[CHN_PINGPONGSUSTAIN] ? 2 : 1;
- m_ComboSustainType.SetCurSel(d);
- s = mpt::cfmt::val(sample.nSustainStart);
- m_EditSustainStart.SetWindowText(s);
- s = mpt::cfmt::val(sample.nSustainEnd);
- m_EditSustainEnd.SetWindowText(s);
- // Disable certain buttons for OPL instruments
- BOOL b = isOPL ? FALSE : TRUE;
- static constexpr int sampleButtons[] =
- {
- IDC_SAMPLE_NORMALIZE, IDC_SAMPLE_AMPLIFY,
- IDC_SAMPLE_DCOFFSET, IDC_SAMPLE_STEREOSEPARATION,
- IDC_SAMPLE_RESAMPLE, IDC_SAMPLE_REVERSE,
- IDC_SAMPLE_SILENCE, IDC_SAMPLE_INVERT,
- IDC_SAMPLE_SIGN_UNSIGN, IDC_SAMPLE_XFADE,
- };
- for(auto btn : sampleButtons)
- {
- m_ToolBar2.EnableButton(btn, b);
- }
- m_ComboLoopType.EnableWindow(b);
- m_SpinLoopStart.EnableWindow(b);
- m_SpinLoopEnd.EnableWindow(b);
- m_EditLoopStart.EnableWindow(b);
- m_EditLoopEnd.EnableWindow(b);
- const bool hasSustainLoop = !isOPL && ((m_sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (m_nSample <= m_sndFile.GetNumSamples() && m_sndFile.GetSample(m_nSample).uFlags[CHN_SUSTAINLOOP]));
- b = hasSustainLoop ? TRUE : FALSE;
- m_ComboSustainType.EnableWindow(b);
- m_SpinSustainStart.EnableWindow(b);
- m_SpinSustainEnd.EnableWindow(b);
- m_EditSustainStart.EnableWindow(b);
- m_EditSustainEnd.EnableWindow(b);
- }
- if(hintType[HINT_MODTYPE | HINT_SAMPLEINFO | HINT_SMPNAMES])
- {
- // Name
- SetDlgItemText(IDC_SAMPLE_NAME, mpt::ToWin(m_sndFile.GetCharsetInternal(), m_sndFile.m_szNames[m_nSample]).c_str());
- CheckDlgButton(IDC_CHECK2, m_sndFile.GetSample(m_nSample).uFlags[SMP_KEEPONDISK] ? BST_CHECKED : BST_UNCHECKED);
- GetDlgItem(IDC_CHECK2)->EnableWindow((m_sndFile.SampleHasPath(m_nSample) && m_sndFile.GetType() == MOD_TYPE_MPT) ? TRUE : FALSE);
- }
- if (!m_bInitialized)
- {
- // First update
- m_bInitialized = TRUE;
- UnlockControls();
- }
- m_ComboLoopType.Invalidate(FALSE);
- m_ComboSustainType.Invalidate(FALSE);
- m_ComboAutoVib.Invalidate(FALSE);
- UnlockControls();
- }
- // updateAll: Update all views including this one. Otherwise, only update update other views.
- void CCtrlSamples::SetModified(SampleHint hint, bool updateAll, bool waveformModified)
- {
- m_modDoc.SetModified();
- if(waveformModified)
- {
- // Update on-disk sample status in tree
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(sample.uFlags[SMP_KEEPONDISK] && !sample.uFlags[SMP_MODIFIED]) hint.Names();
- sample.uFlags.set(SMP_MODIFIED);
- }
- m_modDoc.UpdateAllViews(nullptr, hint.SetData(m_nSample), updateAll ? nullptr : this);
- }
- void CCtrlSamples::PrepareUndo(const char *description, sampleUndoTypes type, SmpLength start, SmpLength end)
- {
- m_startedEdit = true;
- m_modDoc.GetSampleUndo().PrepareUndo(m_nSample, type, description, start, end);
- }
- bool CCtrlSamples::OpenSample(const mpt::PathString &fileName, FlagSet<OpenSampleTypes> types)
- {
- BeginWaitCursor();
- InputFile f(fileName, TrackerSettings::Instance().MiscCacheCompleteFileBeforeLoading);
- if(!f.IsValid())
- {
- EndWaitCursor();
- return false;
- }
- FileReader file = GetFileReader(f);
- if(!file.IsValid())
- {
- EndWaitCursor();
- return false;
- }
- PrepareUndo("Replace", sundo_replace);
- const auto parentIns = GetParentInstrumentWithSameName();
- bool bOk = false;
- if(types[OpenSampleKnown])
- {
- bOk = m_sndFile.ReadSampleFromFile(m_nSample, file, TrackerSettings::Instance().m_MayNormalizeSamplesOnLoad);
- if(!bOk)
- {
- // Try loading as module
- bOk = CMainFrame::GetMainFrame()->SetTreeSoundfile(file);
- if(bOk)
- {
- m_modDoc.GetSampleUndo().RemoveLastUndoStep(m_nSample);
- return true;
- }
- }
- }
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(!bOk && types[OpenSampleRaw])
- {
- CRawSampleDlg dlg(file, this);
- EndWaitCursor();
- if(m_rememberRawFormat || dlg.DoModal() == IDOK)
- {
- SampleIO sampleIO = dlg.GetSampleFormat();
- m_rememberRawFormat = m_rememberRawFormat || dlg.GetRemeberFormat();
- BeginWaitCursor();
- file.Seek(dlg.GetOffset());
- m_sndFile.DestroySampleThreadsafe(m_nSample);
- const auto bytesPerSample = sampleIO.GetNumChannels() * sampleIO.GetBitDepth() / 8u;
- sample.nLength = mpt::saturate_cast<SmpLength>(file.BytesLeft() / bytesPerSample);
- if(TrackerSettings::Instance().m_MayNormalizeSamplesOnLoad)
- {
- sampleIO.MayNormalize();
- }
- if(sampleIO.ReadSample(sample, file))
- {
- bOk = true;
- sample.nGlobalVol = 64;
- sample.nVolume = 256;
- sample.nPan = 128;
- sample.uFlags.reset(CHN_LOOP | CHN_SUSTAINLOOP | SMP_MODIFIED);
- sample.filename = "";
- m_sndFile.m_szNames[m_nSample] = "";
- if(!sample.nC5Speed) sample.nC5Speed = 22050;
- sample.PrecomputeLoops(m_sndFile, false);
- } else
- {
- m_modDoc.GetSampleUndo().Undo(m_nSample);
- }
- } else
- {
- m_modDoc.GetSampleUndo().RemoveLastUndoStep(m_nSample);
- }
- } else
- {
- m_sndFile.SetSamplePath(m_nSample, fileName);
- }
- EndWaitCursor();
- if (bOk)
- {
- TrackerSettings::Instance().PathSamples.SetWorkingDir(fileName, true);
- if(sample.filename.empty())
- {
- mpt::PathString name, ext;
- fileName.SplitPath(nullptr, nullptr, &name, &ext);
- if(m_sndFile.m_szNames[m_nSample].empty()) m_sndFile.m_szNames[m_nSample] = name.ToLocale();
- if(name.AsNative().length() < 9) name += ext;
- sample.filename = name.ToLocale();
- }
- if ((m_sndFile.GetType() & MOD_TYPE_XM) && !sample.uFlags[CHN_PANNING])
- {
- sample.nPan = 128;
- sample.uFlags.set(CHN_PANNING);
- }
- SetModified(SampleHint().Info().Data().Names(), true, false);
- sample.uFlags.reset(SMP_KEEPONDISK);
- if(parentIns <= m_sndFile.GetNumInstruments())
- {
- if(auto instr = m_sndFile.Instruments[parentIns]; instr != nullptr)
- {
- m_modDoc.GetInstrumentUndo().PrepareUndo(parentIns, "Set Name");
- instr->name = m_sndFile.m_szNames[m_nSample];
- m_modDoc.UpdateAllViews(nullptr, InstrumentHint(parentIns).Names(), this);
- }
- }
- }
- return true;
- }
- bool CCtrlSamples::OpenSample(const CSoundFile &sndFile, SAMPLEINDEX nSample)
- {
- if(!nSample || nSample > sndFile.GetNumSamples()) return false;
- BeginWaitCursor();
- PrepareUndo("Replace", sundo_replace);
- const auto parentIns = GetParentInstrumentWithSameName();
- if(m_sndFile.ReadSampleFromSong(m_nSample, sndFile, nSample))
- {
- SetModified(SampleHint().Info().Data().Names(), true, false);
- if(parentIns <= m_sndFile.GetNumInstruments())
- {
- if(auto instr = m_sndFile.Instruments[parentIns]; instr != nullptr)
- {
- m_modDoc.GetInstrumentUndo().PrepareUndo(parentIns, "Set Name");
- instr->name = m_sndFile.m_szNames[m_nSample];
- m_modDoc.UpdateAllViews(nullptr, InstrumentHint(parentIns).Names(), this);
- }
- }
- } else
- {
- m_modDoc.GetSampleUndo().RemoveLastUndoStep(m_nSample);
- }
- EndWaitCursor();
- return true;
- }
- //////////////////////////////////////////////////////////////////////////////////
- // CCtrlSamples messages
- void CCtrlSamples::OnSampleChanged()
- {
- if(!IsLocked())
- {
- SAMPLEINDEX n = (SAMPLEINDEX)GetDlgItemInt(IDC_EDIT_SAMPLE);
- if ((n > 0) && (n <= m_sndFile.GetNumSamples()) && (n != m_nSample))
- {
- SetCurrentSample(n, -1, FALSE);
- m_parent.SampleChanged(m_nSample);
- }
- }
- }
- void CCtrlSamples::OnZoomChanged()
- {
- if (!IsLocked()) SetCurrentSample(m_nSample);
- SwitchToView();
- }
- void CCtrlSamples::OnTbnDropDownToolBar(NMHDR *pNMHDR, LRESULT *pResult)
- {
- CInputHandler *ih = CMainFrame::GetInputHandler();
- NMTOOLBAR *pToolBar = reinterpret_cast<NMTOOLBAR *>(pNMHDR);
- ClientToScreen(&(pToolBar->rcButton)); // TrackPopupMenu uses screen coords
- const int offset = Util::ScalePixels(4, m_hWnd); // Compared to the main toolbar, the offset seems to be a bit wrong here...?
- int x = pToolBar->rcButton.left + offset, y = pToolBar->rcButton.bottom + offset;
- CMenu menu;
- switch(pToolBar->iItem)
- {
- case IDC_SAMPLE_NEW:
- {
- menu.CreatePopupMenu();
- menu.AppendMenu(MF_STRING, IDC_SAMPLE_DUPLICATE, ih->GetKeyTextFromCommand(kcSampleDuplicate, m_sndFile.GetSample(m_nSample).uFlags[CHN_ADLIB] ? _T("&Duplicate Instrument") : _T("&Duplicate Sample")));
- menu.AppendMenu(MF_STRING | (m_sndFile.SupportsOPL() ? 0 : MF_DISABLED), IDC_SAMPLE_INITOPL, ih->GetKeyTextFromCommand(kcSampleInitializeOPL, _T("Initialize &OPL Instrument")));
- menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, this);
- menu.DestroyMenu();
- }
- break;
- case IDC_SAMPLE_OPEN:
- {
- menu.CreatePopupMenu();
- menu.AppendMenu(MF_STRING, IDC_SAMPLE_OPENKNOWN, ih->GetKeyTextFromCommand(kcSampleLoad, _T("Import &Sample...")));
- menu.AppendMenu(MF_STRING, IDC_SAMPLE_OPENRAW, ih->GetKeyTextFromCommand(kcSampleLoadRaw, _T("Import &Raw Sample...")));
- menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, this);
- menu.DestroyMenu();
- }
- break;
- case IDC_SAMPLE_SAVEAS:
- {
- menu.CreatePopupMenu();
- menu.AppendMenu(MF_STRING, IDC_SAVE_ALL, _T("Save &All..."));
- menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, this);
- menu.DestroyMenu();
- }
- break;
- }
- *pResult = 0;
- }
- void CCtrlSamples::OnSampleNew()
- {
- InsertSample(CMainFrame::GetInputHandler()->ShiftPressed());
- SwitchToView();
- }
- bool CCtrlSamples::InsertSample(bool duplicate, int8 *confirm)
- {
- const SAMPLEINDEX smp = m_modDoc.InsertSample();
- if(smp != SAMPLEINDEX_INVALID)
- {
- const SAMPLEINDEX oldSmp = m_nSample;
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- SetCurrentSample(smp);
- if(duplicate && oldSmp >= 1 && oldSmp <= sndFile.GetNumSamples())
- {
- m_modDoc.GetSampleUndo().PrepareUndo(smp, sundo_replace, "Duplicate");
- sndFile.ReadSampleFromSong(smp, sndFile, oldSmp);
- }
- m_modDoc.UpdateAllViews(nullptr, SampleHint(smp).Info().Data().Names());
- if(m_modDoc.GetNumInstruments() > 0 && m_modDoc.FindSampleParent(smp) == INSTRUMENTINDEX_INVALID)
- {
- bool insertInstrument;
- if(confirm == nullptr || *confirm == -1)
- {
- insertInstrument = Reporting::Confirm("This sample is not used by any instrument. Do you want to create a new instrument using this sample?") == cnfYes;
- if(confirm != nullptr) *confirm = insertInstrument;
- } else
- {
- insertInstrument = (*confirm) != 0;
- }
- if(insertInstrument)
- {
- INSTRUMENTINDEX nins = m_modDoc.InsertInstrument(smp);
- m_modDoc.UpdateAllViews(nullptr, InstrumentHint(nins).Info().Envelope().Names());
- m_parent.InstrumentChanged(nins);
- }
- }
- }
- return (smp != SAMPLEINDEX_INVALID);
- }
- static constexpr std::pair<const mpt::uchar *, const mpt::uchar *> SampleFormats[]
- {
- { UL_("Wave Files (*.wav)"), UL_("*.wav") },
- #ifdef MPT_WITH_FLAC
- { UL_("FLAC Files (*.flac,*.oga)"), UL_("*.flac;*.oga") },
- #endif // MPT_WITH_FLAC
- #if defined(MPT_WITH_OPUSFILE)
- { UL_("Opus Files (*.opus,*.oga)"), UL_("*.opus;*.oga") },
- #endif // MPT_WITH_OPUSFILE
- #if defined(MPT_WITH_VORBISFILE) || defined(MPT_WITH_STBVORBIS)
- { UL_("Ogg Vorbis Files (*.ogg,*.oga)"), UL_("*.ogg;*.oga") },
- #endif // VORBIS
- #if defined(MPT_ENABLE_MP3_SAMPLES)
- { UL_("MPEG Files (*.mp1,*.mp2,*.mp3)"), UL_("*.mp1;*.mp2;*.mp3") },
- #endif // MPT_ENABLE_MP3_SAMPLES
- { UL_("XI Samples (*.xi)"), UL_("*.xi") },
- { UL_("Impulse Tracker Samples (*.its)"), UL_("*.its") },
- { UL_("Scream Tracker Samples (*.s3i,*.smp)"), UL_("*.s3i;*.smp") },
- { UL_("OPL Instruments (*.sb0,*.sb2,*.sbi)"), UL_("*.sb0;*.sb2;*.sbi") },
- { UL_("GF1 Patches (*.pat)"), UL_("*.pat") },
- { UL_("Wave64 Files (*.w64)"), UL_("*.w64") },
- { UL_("CAF Files (*.wav)"), UL_("*.caf") },
- { UL_("AIFF Files (*.aiff,*.8svx)"), UL_("*.aif;*.aiff;*.iff;*.8sv;*.8svx;*.svx") },
- { UL_("Sun Audio (*.au,*.snd)"), UL_("*.au;*.snd") },
- { UL_("SNES BRR Files (*.brr)"), UL_("*.brr") },
- };
- static mpt::ustring ConstructFileFilter(bool includeRaw)
- {
- mpt::ustring s = U_("All Samples (*.wav,*.flac,*.xi,*.its,*.s3i,*.sbi,...)|");
- bool first = true;
- for(const auto &[name, exts] : SampleFormats)
- {
- if(!first)
- s += U_(";");
- else
- first = false;
- s += exts;
- }
- #if defined(MPT_WITH_MEDIAFOUNDATION)
- std::vector<FileType> mediaFoundationTypes = CSoundFile::GetMediaFoundationFileTypes();
- s += ToFilterOnlyString(mediaFoundationTypes, true).ToUnicode();
- #endif
- if(includeRaw)
- {
- s += U_(";*.raw;*.snd;*.pcm;*.sam");
- }
- s += U_("|");
- for(const auto &[name, exts] : SampleFormats)
- {
- s += name + U_("|");
- s += exts + U_("|");
- }
- #if defined(MPT_WITH_MEDIAFOUNDATION)
- s += ToFilterString(mediaFoundationTypes, FileTypeFormatShowExtensions).ToUnicode();
- #endif
- if(includeRaw)
- {
- s += U_("Raw Samples (*.raw,*.snd,*.pcm,*.sam)|*.raw;*.snd;*.pcm;*.sam|");
- }
- s += U_("All Files (*.*)|*.*||");
- return s;
- }
- void CCtrlSamples::OnSampleOpen()
- {
- static int nLastIndex = 0;
- std::vector<FileType> mediaFoundationTypes = CSoundFile::GetMediaFoundationFileTypes();
- FileDialog dlg = OpenFileDialog()
- .AllowMultiSelect()
- .EnableAudioPreview()
- .ExtensionFilter(ConstructFileFilter(true))
- .WorkingDirectory(TrackerSettings::Instance().PathSamples.GetWorkingDir())
- .FilterIndex(&nLastIndex);
- if(!dlg.Show(this)) return;
- TrackerSettings::Instance().PathSamples.SetWorkingDir(dlg.GetWorkingDirectory());
- OpenSamples(dlg.GetFilenames(), OpenSampleKnown | OpenSampleRaw);
- SwitchToView();
- }
- void CCtrlSamples::OnSampleOpenKnown()
- {
- static int nLastIndex = 0;
- std::vector<FileType> mediaFoundationTypes = CSoundFile::GetMediaFoundationFileTypes();
- FileDialog dlg = OpenFileDialog()
- .AllowMultiSelect()
- .EnableAudioPreview()
- .ExtensionFilter(ConstructFileFilter(false))
- .WorkingDirectory(TrackerSettings::Instance().PathSamples.GetWorkingDir())
- .FilterIndex(&nLastIndex);
- if(!dlg.Show(this)) return;
- TrackerSettings::Instance().PathSamples.SetWorkingDir(dlg.GetWorkingDirectory());
- OpenSamples(dlg.GetFilenames(), OpenSampleKnown);
- }
- void CCtrlSamples::OnSampleOpenRaw()
- {
- static int nLastIndex = 0;
- FileDialog dlg = OpenFileDialog()
- .AllowMultiSelect()
- .EnableAudioPreview()
- .ExtensionFilter("Raw Samples (*.raw,*.snd,*.pcm,*.sam)|*.raw;*.snd;*.pcm;*.sam|"
- "All Files (*.*)|*.*||")
- .WorkingDirectory(TrackerSettings::Instance().PathSamples.GetWorkingDir())
- .FilterIndex(&nLastIndex);
- if(!dlg.Show(this)) return;
- TrackerSettings::Instance().PathSamples.SetWorkingDir(dlg.GetWorkingDirectory());
- OpenSamples(dlg.GetFilenames(), OpenSampleRaw);
- }
- void CCtrlSamples::OpenSamples(const std::vector<mpt::PathString> &files, FlagSet<OpenSampleTypes> types)
- {
- int8 confirm = -1;
- bool first = true;
- for(const auto &file : files)
- {
- // If loading multiple samples, create new slots for them
- if(!first)
- {
- if(!InsertSample(false, &confirm))
- break;
- }
- if(OpenSample(file, types))
- first = false;
- else
- ErrorBox(IDS_ERR_FILEOPEN, this);
- }
- SwitchToView();
- }
- void CCtrlSamples::OnSampleSave()
- {
- SaveSample(CMainFrame::GetInputHandler()->ShiftPressed());
- }
- void CCtrlSamples::SaveSample(bool doBatchSave)
- {
- mpt::PathString fileName, defaultPath = TrackerSettings::Instance().PathSamples.GetWorkingDir();
- SampleEditorDefaultFormat defaultFormat = TrackerSettings::Instance().m_defaultSampleFormat;
- bool hasAdlib = false;
- if(!doBatchSave)
- {
- // Save this sample
- const ModSample &sample = m_sndFile.GetSample(m_nSample);
- if((!m_nSample) || (!sample.HasSampleData()))
- {
- SwitchToView();
- return;
- }
- if(m_sndFile.SampleHasPath(m_nSample))
- {
- // For on-disk samples, propose their original filename and location
- auto path = m_sndFile.GetSamplePath(m_nSample);
- fileName = path.GetFullFileName();
- defaultPath = path.GetPath();
- }
- if(fileName.empty()) fileName = mpt::PathString::FromLocale(sample.filename);
- if(fileName.empty()) fileName = mpt::PathString::FromLocale(m_sndFile.m_szNames[m_nSample]);
- if(fileName.empty()) fileName = P_("untitled");
- const mpt::PathString ext = fileName.GetFileExt();
- if(!mpt::PathString::CompareNoCase(ext, P_(".flac"))) defaultFormat = dfFLAC;
- else if(!mpt::PathString::CompareNoCase(ext, P_(".wav"))) defaultFormat = dfWAV;
- else if(!mpt::PathString::CompareNoCase(ext, P_(".s3i"))) defaultFormat = dfS3I;
- hasAdlib = sample.uFlags[CHN_ADLIB];
- } else
- {
- // Save all samples
- fileName = m_sndFile.GetpModDoc()->GetPathNameMpt().GetFileName();
- if(fileName.empty()) fileName = P_("untitled");
- fileName += P_(" - %sample_number% - ");
- if(m_sndFile.GetModSpecifications().sampleFilenameLengthMax == 0)
- fileName += P_("%sample_name%");
- else
- fileName += P_("%sample_filename%");
- }
- SanitizeFilename(fileName);
- int filter;
- switch(defaultFormat)
- {
- case dfWAV:
- filter = 1;
- break;
- case dfFLAC:
- default:
- filter = 2;
- break;
- case dfS3I:
- filter = 3;
- break;
- case dfRAW:
- filter = 4;
- break;
- }
- // Do we have to use a format that can save OPL instruments?
- if(hasAdlib)
- filter = 3;
- FileDialog dlg = SaveFileDialog()
- .DefaultExtension(ToSettingValue(defaultFormat).as<mpt::ustring>())
- .DefaultFilename(fileName)
- .ExtensionFilter("Wave File (*.wav)|*.wav|"
- "FLAC File (*.flac)|*.flac|"
- "S3I Scream Tracker 3 Instrument (*.s3i)|*.s3i|"
- "RAW Audio (*.raw)|*.raw||")
- .WorkingDirectory(defaultPath)
- .FilterIndex(&filter);
- if(!dlg.Show(this)) return;
- BeginWaitCursor();
- const auto saveFormat = FromSettingValue<SampleEditorDefaultFormat>(dlg.GetExtension().ToUnicode());
- SAMPLEINDEX minSmp = m_nSample, maxSmp = m_nSample;
- if(doBatchSave)
- {
- minSmp = 1;
- maxSmp = m_sndFile.GetNumSamples();
- }
- const auto numberFmt = mpt::FormatSpec().Dec().FillNul().Width(1 + static_cast<int>(std::log10(maxSmp)));
- bool ok = false;
- CString sampleName, sampleFilename;
- for(SAMPLEINDEX smp = minSmp; smp <= maxSmp; smp++)
- {
- ModSample &sample = m_sndFile.GetSample(smp);
- if(sample.HasSampleData())
- {
- const bool isAdlib = sample.uFlags[CHN_ADLIB];
- fileName = dlg.GetFirstFile();
- if(doBatchSave)
- {
- sampleName = mpt::ToCString(m_sndFile.GetCharsetInternal(), (!m_sndFile.m_szNames[smp].empty()) ? std::string(m_sndFile.m_szNames[smp]) : "untitled");
- sampleFilename = mpt::ToCString(m_sndFile.GetCharsetInternal(), (!sample.filename.empty()) ? sample.GetFilename() : m_sndFile.m_szNames[smp]);
- SanitizeFilename(sampleName);
- SanitizeFilename(sampleFilename);
- mpt::ustring fileNameU = fileName.ToUnicode();
- fileNameU = mpt::String::Replace(fileNameU, U_("%sample_number%"), mpt::ufmt::fmt(smp, numberFmt));
- fileNameU = mpt::String::Replace(fileNameU, U_("%sample_filename%"), mpt::ToUnicode(sampleFilename));
- fileNameU = mpt::String::Replace(fileNameU, U_("%sample_name%"), mpt::ToUnicode(sampleName));
- fileName = mpt::PathString::FromUnicode(fileNameU);
- // Need to enforce S3I for Adlib samples
- if(isAdlib && saveFormat != dfS3I)
- fileName = fileName.ReplaceExt(P_(".s3i"));
- }
- try
- {
- mpt::SafeOutputFile sf(fileName, std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave));
- mpt::ofstream &f = sf;
- if(!f)
- {
- ok = false;
- continue;
- }
- f.exceptions(f.exceptions() | std::ios::badbit | std::ios::failbit);
- // Need to enforce S3I for Adlib samples
- const auto thisFormat = isAdlib ? dfS3I : saveFormat;
- if(thisFormat == dfRAW)
- ok = m_sndFile.SaveRAWSample(smp, f);
- else if(thisFormat == dfFLAC)
- ok = m_sndFile.SaveFLACSample(smp, f);
- else if(thisFormat == dfS3I)
- ok = m_sndFile.SaveS3ISample(smp, f);
- else
- ok = m_sndFile.SaveWAVSample(smp, f);
- } catch(const std::exception &)
- {
- ok = false;
- }
- if(ok)
- {
- m_sndFile.SetSamplePath(smp, fileName);
- sample.uFlags.reset(SMP_MODIFIED);
- UpdateView(SampleHint().Info());
- // Check if any other samples refer to the same file - that would be dangerous.
- if(sample.uFlags[SMP_KEEPONDISK])
- {
- for(SAMPLEINDEX i = 1; i <= m_sndFile.GetNumSamples(); i++)
- {
- if(i != smp && m_sndFile.GetSample(i).uFlags[SMP_KEEPONDISK] && m_sndFile.GetSamplePath(i) == m_sndFile.GetSamplePath(smp))
- {
- m_sndFile.GetSample(i).uFlags.reset(SMP_KEEPONDISK);
- m_modDoc.UpdateAllViews(nullptr, SampleHint(i).Names().Info(), this);
- }
- }
- }
- }
- }
- }
- EndWaitCursor();
- if (!ok)
- {
- ErrorBox(IDS_ERR_SAVESMP, this);
- } else
- {
- TrackerSettings::Instance().PathSamples.SetWorkingDir(dlg.GetWorkingDirectory());
- }
- SwitchToView();
- }
- void CCtrlSamples::OnSamplePlay()
- {
- if (m_modDoc.IsNotePlaying(NOTE_NONE, m_nSample, 0))
- {
- m_modDoc.NoteOff(0, true);
- } else
- {
- m_modDoc.PlayNote(PlayNoteParam(NOTE_MIDDLEC).Sample(m_nSample));
- }
- SwitchToView();
- }
- template<typename T>
- static bool DoNormalize(T *p, SmpLength selStart, SmpLength selEnd)
- {
- auto [min, max] = CViewSample::FindMinMax(p + selStart, selEnd - selStart, 1);
- max = std::max(-min, max);
- if(max < std::numeric_limits<T>::max())
- {
- max++;
- for(SmpLength i = selStart; i < selEnd; i++)
- {
- p[i] = static_cast<T>((static_cast<int>(p[i]) << (sizeof(T) * 8 - 1)) / max);
- }
- return true;
- }
- return false;
- }
- void CCtrlSamples::Normalize(bool allSamples)
- {
- //Default case: Normalize current sample
- SAMPLEINDEX minSample = m_nSample, maxSample = m_nSample;
- //If only one sample is selected, parts of it may be amplified
- SmpLength selStart = 0, selEnd = 0;
- if(allSamples)
- {
- if(Reporting::Confirm(_T("This will normalize all samples independently. Continue?"), _T("Normalize")) == cnfNo)
- return;
- minSample = 1;
- maxSample = m_sndFile.GetNumSamples();
- } else
- {
- SampleSelectionPoints selection = GetSelectionPoints();
- selStart = selection.nStart;
- selEnd = selection.nEnd;
- }
- BeginWaitCursor();
- bool modified = false;
- for(SAMPLEINDEX smp = minSample; smp <= maxSample; smp++)
- {
- if(m_sndFile.GetSample(smp).HasSampleData())
- {
- ModSample &sample = m_sndFile.GetSample(smp);
- if(minSample != maxSample)
- {
- // If more than one sample is selected, always amplify the whole sample.
- selStart = 0;
- selEnd = sample.nLength;
- } else
- {
- // One sample: correct the boundaries, if needed
- LimitMax(selEnd, sample.nLength);
- LimitMax(selStart, selEnd);
- if(selStart == selEnd)
- {
- selStart = 0;
- selEnd = sample.nLength;
- }
- }
- m_modDoc.GetSampleUndo().PrepareUndo(smp, sundo_update, "Normalize", selStart, selEnd);
- selStart *= sample.GetNumChannels();
- selEnd *= sample.GetNumChannels();
- if(sample.uFlags[CHN_16BIT])
- {
- modified |= DoNormalize(sample.sample16(), selStart, selEnd);
- } else
- {
- modified |= DoNormalize(sample.sample8(), selStart, selEnd);
- }
- if(modified)
- {
- sample.PrecomputeLoops(m_sndFile, false);
- m_modDoc.UpdateAllViews(nullptr, SampleHint(smp).Data());
- }
- }
- }
- if(modified)
- {
- SetModified(SampleHint().Data(), false, true);
- }
- EndWaitCursor();
- SwitchToView();
- }
- void CCtrlSamples::OnNormalize()
- {
- Normalize(CMainFrame::GetInputHandler()->ShiftPressed());
- }
- void CCtrlSamples::RemoveDCOffset(bool allSamples)
- {
- SAMPLEINDEX minSample = m_nSample, maxSample = m_nSample;
- //Shift -> Process all samples
- if(allSamples)
- {
- if(Reporting::Confirm(_T("This will process all samples independently. Continue?"), _T("DC Offset Removal")) == cnfNo)
- return;
- minSample = 1;
- maxSample = m_sndFile.GetNumSamples();
- }
- BeginWaitCursor();
- // for report / SetModified
- SAMPLEINDEX numModified = 0;
- double reportOffset = 0;
- for(SAMPLEINDEX smp = minSample; smp <= maxSample; smp++)
- {
- SmpLength selStart, selEnd;
- if(!m_sndFile.GetSample(smp).HasSampleData())
- continue;
- if (minSample != maxSample)
- {
- selStart = 0;
- selEnd = m_sndFile.GetSample(smp).nLength;
- } else
- {
- SampleSelectionPoints selection = GetSelectionPoints();
- selStart = selection.nStart;
- selEnd = selection.nEnd;
- }
- m_modDoc.GetSampleUndo().PrepareUndo(smp, sundo_update, "Remove DC Offset", selStart, selEnd);
- const double offset = SampleEdit::RemoveDCOffset(m_sndFile.GetSample(smp), selStart, selEnd, m_sndFile);
- if(offset == 0.0f) // No offset removed.
- continue;
- reportOffset += offset;
- numModified++;
- m_modDoc.UpdateAllViews(nullptr, SampleHint(smp).Info().Data());
- }
- EndWaitCursor();
- SwitchToView();
- // fill the statusbar with some nice information
- CString dcInfo;
- if(numModified)
- {
- SetModified(SampleHint().Info().Data(), true, true);
- if(numModified == 1)
- {
- dcInfo.Format(_T("Removed DC offset (%.1f%%)"), reportOffset * 100);
- } else
- {
- dcInfo.Format(_T("Removed DC offset from %u samples (avg %0.1f%%)"), numModified, reportOffset / numModified * 100);
- }
- } else
- {
- dcInfo.SetString(_T("No DC offset found"));
- }
- CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
- pMainFrm->SetXInfoText(dcInfo);
- }
- void CCtrlSamples::OnRemoveDCOffset()
- {
- RemoveDCOffset(CMainFrame::GetInputHandler()->ShiftPressed());
- }
- void CCtrlSamples::ApplyAmplify(const double amp, const double fadeInStart, const double fadeOutEnd, const bool fadeIn, const bool fadeOut, const Fade::Law fadeLaw)
- {
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(!sample.HasSampleData() || sample.uFlags[CHN_ADLIB]) return;
- BeginWaitCursor();
- SampleSelectionPoints selection = GetSelectionPoints();
- const auto start = selection.nStart, end = selection.nEnd, mid = (start + end) / 2;
- PrepareUndo("Amplify", sundo_update, start, end);
- if(fadeIn && fadeOut)
- {
- SampleEdit::AmplifySample(sample, start, mid, fadeInStart, amp, true, fadeLaw, m_sndFile);
- SampleEdit::AmplifySample(sample, mid, end, amp, fadeOutEnd, false, fadeLaw, m_sndFile);
- } else if(fadeIn)
- {
- SampleEdit::AmplifySample(sample, start, end, fadeInStart, amp, true, fadeLaw, m_sndFile);
- } else if(fadeOut)
- {
- SampleEdit::AmplifySample(sample, start, end, amp, fadeOutEnd, false, fadeLaw, m_sndFile);
- } else
- {
- SampleEdit::AmplifySample(sample, start, end, amp, amp, true, Fade::kLinear, m_sndFile);
- }
- sample.PrecomputeLoops(m_sndFile, false);
- SetModified(SampleHint().Data(), false, true);
- EndWaitCursor();
- SwitchToView();
- }
- void CCtrlSamples::OnAmplify()
- {
- static CAmpDlg::AmpSettings settings { Fade::kLinear, 0, 0, 100, false, false };
- CAmpDlg dlg(this, settings);
- if (dlg.DoModal() != IDOK) return;
- ApplyAmplify(settings.factor / 100.0, settings.fadeInStart / 100.0, settings.fadeOutEnd / 100.0, settings.fadeIn, settings.fadeOut, settings.fadeLaw);
- }
- // Quickly fade the selection in/out without asking the user.
- // Fade-In is applied if the selection starts at the beginning of the sample.
- // Fade-Out is applied if the selection ends and the end of the sample.
- void CCtrlSamples::OnQuickFade()
- {
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(!sample.HasSampleData() || sample.uFlags[CHN_ADLIB]) return;
- SampleSelectionPoints sel = GetSelectionPoints();
- if(sel.selectionActive && (sel.nStart == 0 || sel.nEnd == sample.nLength))
- {
- ApplyAmplify(1.0, (sel.nStart == 0) ? 0.0 : 1.0, (sel.nEnd == sample.nLength) ? 0.0 : 1.0, sel.nStart == 0, sel.nEnd == sample.nLength, Fade::kLinear);
- } else
- {
- // Can't apply quick fade as no appropriate selection has been made, so ask the user to amplify the whole sample instead.
- OnAmplify();
- }
- }
- void CCtrlSamples::OnResample()
- {
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(!sample.HasSampleData() || sample.uFlags[CHN_ADLIB])
- return;
- SAMPLEINDEX first = m_nSample, last = m_nSample;
- if(CMainFrame::GetInputHandler()->ShiftPressed())
- {
- first = 1;
- last = m_sndFile.GetNumSamples();
- }
-
- const uint32 oldRate = sample.GetSampleRate(m_sndFile.GetType());
- CResamplingDlg dlg(this, oldRate, TrackerSettings::Instance().sampleEditorDefaultResampler, first != last);
- if(dlg.DoModal() != IDOK)
- return;
-
- TrackerSettings::Instance().sampleEditorDefaultResampler = dlg.GetFilter();
- for(SAMPLEINDEX smp = first; smp <= last; smp++)
- {
- const uint32 sampleFreq = m_sndFile.GetSample(smp).GetSampleRate(m_sndFile.GetType());
- uint32 newFreq = dlg.GetFrequency();
- if(dlg.GetResamplingOption() == CResamplingDlg::Upsample)
- newFreq = sampleFreq * 2;
- else if(dlg.GetResamplingOption() == CResamplingDlg::Downsample)
- newFreq = sampleFreq / 2;
- else if(newFreq == sampleFreq)
- continue;
- ApplyResample(smp, newFreq, dlg.GetFilter(), first != last, dlg.UpdatePatternCommands());
- }
- }
- void CCtrlSamples::ApplyResample(SAMPLEINDEX smp, uint32 newRate, ResamplingMode mode, bool ignoreSelection, bool updatePatternCommands)
- {
- BeginWaitCursor();
- ModSample &sample = m_sndFile.GetSample(smp);
- if(!sample.HasSampleData() || sample.uFlags[CHN_ADLIB])
- {
- EndWaitCursor();
- return;
- }
- SampleSelectionPoints selection = GetSelectionPoints();
- LimitMax(selection.nEnd, sample.nLength);
- if(selection.nStart >= selection.nEnd || ignoreSelection)
- {
- selection.nStart = 0;
- selection.nEnd = sample.nLength;
- }
- const uint32 oldRate = sample.GetSampleRate(m_sndFile.GetType());
- if(newRate < 1 || oldRate < 1)
- {
- MessageBeep(MB_ICONWARNING);
- EndWaitCursor();
- return;
- }
- const SmpLength oldLength = sample.nLength;
- const SmpLength selLength = (selection.nEnd - selection.nStart);
- const SmpLength newSelLength = Util::muldivr_unsigned(selLength, newRate, oldRate);
- const SmpLength newSelEnd = selection.nStart + newSelLength;
- const SmpLength newTotalLength = sample.nLength - selLength + newSelLength;
- const uint8 numChannels = sample.GetNumChannels();
- if(newTotalLength <= 1)
- {
- MessageBeep(MB_ICONWARNING);
- EndWaitCursor();
- return;
- }
- void *newSample = ModSample::AllocateSample(newTotalLength, sample.GetBytesPerSample());
- if(newSample != nullptr)
- {
- // First, copy parts of the sample that are not affected by partial upsampling
- const SmpLength bps = sample.GetBytesPerSample();
- std::memcpy(newSample, sample.sampleb(), selection.nStart * bps);
- std::memcpy(static_cast<char *>(newSample) + newSelEnd * bps, sample.sampleb() + selection.nEnd * bps, (sample.nLength - selection.nEnd) * bps);
- if(mode == SRCMODE_DEFAULT)
- {
- // Resample using r8brain
- const SmpLength bufferSize = std::min(std::max(selLength, SmpLength(oldRate)), SmpLength(1024 * 1024));
- std::vector<double> convBuffer(bufferSize);
- r8b::CDSPResampler16 resampler(oldRate, newRate, bufferSize);
- for(uint8 chn = 0; chn < numChannels; chn++)
- {
- if(chn != 0) resampler.clear();
- SmpLength readCount = selLength, writeCount = newSelLength;
- SmpLength readOffset = selection.nStart * numChannels + chn, writeOffset = readOffset;
- SmpLength outLatency = newRate;
- double *outBuffer, lastVal = 0.0;
- {
- // Pre-fill the resampler with the first sampling point.
- // Otherwise, it will assume that all samples before the first sampling point are 0,
- // which can lead to unwanted artefacts (ripples) if the sample doesn't start with a zero crossing.
- double firstVal = 0.0;
- switch(sample.GetElementarySampleSize())
- {
- case 1:
- firstVal = SC::Convert<double, int8>()(sample.sample8()[readOffset]);
- lastVal = SC::Convert<double, int8>()(sample.sample8()[readOffset + selLength - numChannels]);
- break;
- case 2:
- firstVal = SC::Convert<double, int16>()(sample.sample16()[readOffset]);
- lastVal = SC::Convert<double, int16>()(sample.sample16()[readOffset + selLength - numChannels]);
- break;
- default:
- // When higher bit depth is added, feel free to also replace CDSPResampler16 by CDSPResampler24 above.
- MPT_ASSERT_MSG(false, "Bit depth not implemented");
- }
- // 10ms or less would probably be enough, but we will pre-fill the buffer with exactly "oldRate" samples
- // to prevent any further rounding errors when using smaller buffers or when dividing oldRate or newRate.
- uint32 remain = oldRate;
- for(SmpLength i = 0; i < bufferSize; i++) convBuffer[i] = firstVal;
- while(remain > 0)
- {
- uint32 procIn = std::min(remain, mpt::saturate_cast<uint32>(bufferSize));
- SmpLength procCount = resampler.process(convBuffer.data(), procIn, outBuffer);
- MPT_ASSERT(procCount <= outLatency);
- LimitMax(procCount, outLatency);
- outLatency -= procCount;
- remain -= procIn;
- }
- }
- // Now we can start with the actual resampling work...
- while(writeCount > 0)
- {
- SmpLength smpCount = (SmpLength)convBuffer.size();
- if(readCount != 0)
- {
- LimitMax(smpCount, readCount);
- switch(sample.GetElementarySampleSize())
- {
- case 1:
- CopySample<SC::ConversionChain<SC::Convert<double, int8>, SC::DecodeIdentity<int8> > >(convBuffer.data(), smpCount, 1, sample.sample8() + readOffset, sample.GetSampleSizeInBytes(), sample.GetNumChannels());
- break;
- case 2:
- CopySample<SC::ConversionChain<SC::Convert<double, int16>, SC::DecodeIdentity<int16> > >(convBuffer.data(), smpCount, 1, sample.sample16() + readOffset, sample.GetSampleSizeInBytes(), sample.GetNumChannels());
- break;
- }
- readOffset += smpCount * numChannels;
- readCount -= smpCount;
- } else
- {
- // Nothing to read, but still to write (compensate for r8brain's output latency)
- for(SmpLength i = 0; i < smpCount; i++) convBuffer[i] = lastVal;
- }
- SmpLength procCount = resampler.process(convBuffer.data(), smpCount, outBuffer);
- const SmpLength procLatency = std::min(outLatency, procCount);
- procCount = std::min(procCount- procLatency, writeCount);
- switch(sample.GetElementarySampleSize())
- {
- case 1:
- CopySample<SC::ConversionChain<SC::Convert<int8, double>, SC::DecodeIdentity<double> > >(static_cast<int8 *>(newSample) + writeOffset, procCount, sample.GetNumChannels(), outBuffer + procLatency, procCount * sizeof(double), 1);
- break;
- case 2:
- CopySample<SC::ConversionChain<SC::Convert<int16, double>, SC::DecodeIdentity<double> > >(static_cast<int16 *>(newSample) + writeOffset, procCount, sample.GetNumChannels(), outBuffer + procLatency, procCount * sizeof(double), 1);
- break;
- }
- writeOffset += procCount * numChannels;
- writeCount -= procCount;
- outLatency -= procLatency;
- }
- }
- } else
- {
- // Resample using built-in filters
- uint32 functionNdx = MixFuncTable::ResamplingModeToMixFlags(mode);
- if(sample.uFlags[CHN_16BIT]) functionNdx |= MixFuncTable::ndx16Bit;
- if(sample.uFlags[CHN_STEREO]) functionNdx |= MixFuncTable::ndxStereo;
- ModChannel chn{};
- chn.pCurrentSample = sample.samplev();
- chn.increment = SamplePosition::Ratio(oldRate, newRate);
- chn.position.Set(selection.nStart);
- chn.leftVol = chn.rightVol = (1 << 8);
- chn.nLength = sample.nLength;
- SmpLength writeCount = newSelLength;
- SmpLength writeOffset = selection.nStart * sample.GetNumChannels();
- while(writeCount > 0)
- {
- SmpLength procCount = std::min(static_cast<SmpLength>(MIXBUFFERSIZE), writeCount);
- mixsample_t buffer[MIXBUFFERSIZE * 2];
- MemsetZero(buffer);
- MixFuncTable::Functions[functionNdx](chn, m_sndFile.m_Resampler, buffer, procCount);
- for(uint8 c = 0; c < numChannels; c++)
- {
- switch(sample.GetElementarySampleSize())
- {
- case 1:
- CopySample<SC::ConversionChain<SC::ConvertFixedPoint<int8, mixsample_t, 23>, SC::DecodeIdentity<mixsample_t> > >(static_cast<int8 *>(newSample) + writeOffset + c, procCount, sample.GetNumChannels(), buffer + c, sizeof(buffer), 2);
- break;
- case 2:
- CopySample<SC::ConversionChain<SC::ConvertFixedPoint<int16, mixsample_t, 23>, SC::DecodeIdentity<mixsample_t> > >(static_cast<int16 *>(newSample) + writeOffset + c, procCount, sample.GetNumChannels(), buffer + c, sizeof(buffer), 2);
- break;
- }
- }
- writeCount -= procCount;
- writeOffset += procCount * sample.GetNumChannels();
- }
- }
- m_modDoc.GetSampleUndo().PrepareUndo(smp, sundo_replace, (newRate > oldRate) ? "Upsample" : "Downsample");
- // Adjust loops and cues
- const auto oldCues = sample.cues;
- for(SmpLength &point : SampleEdit::GetCuesAndLoops(sample))
- {
- if(point >= oldLength)
- point = newTotalLength;
- else if(point >= selection.nEnd)
- point += newSelLength - selLength;
- else if(point > selection.nStart)
- point = selection.nStart + Util::muldivr_unsigned(point - selection.nStart, newRate, oldRate);
- LimitMax(point, newTotalLength);
- }
- if(updatePatternCommands)
- {
- bool patternUndoCreated = false;
- m_sndFile.Patterns.ForEachModCommand([&](ModCommand &m)
- {
- if(m.command != CMD_OFFSET && m.command != CMD_REVERSEOFFSET && m.command != CMD_OFFSETPERCENTAGE)
- return;
- if(m_sndFile.GetSampleIndex(m.note, m.instr) != smp)
- return;
- SmpLength point = m.param * 256u;
-
- if(m.command == CMD_OFFSETPERCENTAGE || (m.volcmd == VOLCMD_OFFSET && m.vol == 0))
- point = Util::muldivr_unsigned(point, oldLength, 65536);
- else if(m.volcmd == VOLCMD_OFFSET && m.vol <= std::size(oldCues))
- point += oldCues[m.vol - 1];
- if(point >= oldLength)
- point = newTotalLength;
- else if (point >= selection.nEnd)
- point += newSelLength - selLength;
- else if (point > selection.nStart)
- point = selection.nStart + Util::muldivr_unsigned(point - selection.nStart, newRate, oldRate);
- LimitMax(point, newTotalLength);
- if(m.command == CMD_OFFSETPERCENTAGE || (m.volcmd == VOLCMD_OFFSET && m.vol == 0))
- point = Util::muldivr_unsigned(point, 65536, newTotalLength);
- else if(m.volcmd == VOLCMD_OFFSET && m.vol <= std::size(sample.cues))
- point -= sample.cues[m.vol - 1];
- if(!patternUndoCreated)
- {
- patternUndoCreated = true;
- m_modDoc.PrepareUndoForAllPatterns(false, "Resample (Adjust Offsets)");
- }
- m.param = mpt::saturate_cast<ModCommand::PARAM>(point / 256u);
- });
- }
- if(!selection.selectionActive)
- {
- if(m_sndFile.GetType() != MOD_TYPE_MOD)
- {
- sample.nC5Speed = newRate;
- sample.FrequencyToTranspose();
- }
- }
- ctrlSmp::ReplaceSample(sample, newSample, newTotalLength, m_sndFile);
- // Update loop wrap-around buffer
- sample.PrecomputeLoops(m_sndFile);
- auto updateHint = SampleHint(smp).Info().Data();
- if(sample.uFlags[SMP_KEEPONDISK] && !sample.uFlags[SMP_MODIFIED])
- updateHint.Names();
- sample.uFlags.set(SMP_MODIFIED);
- m_modDoc.SetModified();
- m_modDoc.UpdateAllViews(nullptr, updateHint, nullptr);
- if(selection.selectionActive && !ignoreSelection)
- {
- SetSelectionPoints(selection.nStart, newSelEnd);
- }
- }
- EndWaitCursor();
- SwitchToView();
- }
- void CCtrlSamples::ReadTimeStretchParameters()
- {
- m_nSequenceMs = GetDlgItemInt(IDC_EDIT10);
- m_nSeekWindowMs = GetDlgItemInt(IDC_EDIT11);
- m_nOverlapMs = GetDlgItemInt(IDC_EDIT12);
- }
- void CCtrlSamples::UpdateTimeStretchParameters()
- {
- GetDlgItem(IDC_EDIT10)->SetWindowText(((m_nSequenceMs <= 0) ? _T("auto") : MPT_TFORMAT("{}ms")(m_nSequenceMs)).c_str());
- GetDlgItem(IDC_EDIT11)->SetWindowText(((m_nSeekWindowMs <= 0) ? _T("auto") : MPT_TFORMAT("{}ms")(m_nSeekWindowMs)).c_str());
- GetDlgItem(IDC_EDIT12)->SetWindowText(((m_nOverlapMs <= 0) ? _T("auto") : MPT_TFORMAT("{}ms")(m_nOverlapMs)).c_str());
- }
- void CCtrlSamples::OnEnableStretchToSize()
- {
- // Enable time-stretching / disable unused pitch-shifting UI elements
- bool timeStretch = IsDlgButtonChecked(IDC_CHECK3) != BST_UNCHECKED;
- if(!timeStretch) ReadTimeStretchParameters();
- ((CComboBox *)GetDlgItem(IDC_COMBO4))->EnableWindow(timeStretch ? FALSE : TRUE);
- ((CEdit *)GetDlgItem(IDC_EDIT6))->EnableWindow(timeStretch ? TRUE : FALSE);
- ((CButton *)GetDlgItem(IDC_BUTTON2))->EnableWindow(timeStretch ? TRUE : FALSE);
- GetDlgItem(IDC_TEXT_PITCH)->SetWindowText(timeStretch ? _T("Sequence") : _T("Pitch"));
- GetDlgItem(IDC_TEXT_QUALITY)->SetWindowText(timeStretch ? _T("Seek Window") : _T("Quality"));
- GetDlgItem(IDC_TEXT_FFT)->SetWindowText(timeStretch ? _T("Overlap") : _T("FFT Size"));
- GetDlgItem(IDC_EDIT10)->ShowWindow(timeStretch ? SW_SHOW : SW_HIDE);
- GetDlgItem(IDC_EDIT11)->ShowWindow(timeStretch ? SW_SHOW : SW_HIDE);
- GetDlgItem(IDC_EDIT12)->ShowWindow(timeStretch ? SW_SHOW : SW_HIDE);
- GetDlgItem(IDC_SPIN10)->ShowWindow(timeStretch ? SW_SHOW : SW_HIDE);
- GetDlgItem(IDC_SPIN14)->ShowWindow(timeStretch ? SW_SHOW : SW_HIDE);
- GetDlgItem(IDC_SPIN15)->ShowWindow(timeStretch ? SW_SHOW : SW_HIDE);
-
- GetDlgItem(IDC_COMBO4)->ShowWindow(timeStretch ? SW_HIDE : SW_SHOW);
- GetDlgItem(IDC_COMBO5)->ShowWindow(timeStretch ? SW_HIDE : SW_SHOW);
- GetDlgItem(IDC_COMBO6)->ShowWindow(timeStretch ? SW_HIDE : SW_SHOW);
- SetDlgItemText(IDC_BUTTON1, timeStretch ? _T("Time Stretch") : _T("Pitch Shift"));
- if(timeStretch)
- UpdateTimeStretchParameters();
- }
- void CCtrlSamples::OnEstimateSampleSize()
- {
- if(!m_sndFile.GetSample(m_nSample).HasSampleData())
- return;
- //Ensure m_dTimeStretchRatio is up-to-date with textbox content
- UpdateData(TRUE);
- //Open dialog
- CPSRatioCalc dlg(m_sndFile, m_nSample, m_dTimeStretchRatio, this);
- if (dlg.DoModal() != IDOK) return;
- //Update ratio value&textbox
- m_dTimeStretchRatio = dlg.m_dRatio;
- UpdateData(FALSE);
- }
- enum TimeStretchPitchShiftResult
- {
- kUnknown,
- kOK,
- kAbort,
- kInvalidRatio,
- kStretchTooShort,
- kStretchTooLong,
- kOutOfMemory,
- kSampleTooShort,
- kStretchInvalidSampleRate,
- };
- class DoPitchShiftTimeStretch : public CProgressDialog
- {
- public:
- CCtrlSamples &m_parent;
- CModDoc &m_modDoc;
- const float m_ratio;
- TimeStretchPitchShiftResult m_result = kUnknown;
- uint32 m_updateInterval;
- const SAMPLEINDEX m_sample;
- const bool m_pitchShift;
- DoPitchShiftTimeStretch(CCtrlSamples &parent, CModDoc &modDoc, SAMPLEINDEX sample, float ratio, bool pitchShift)
- : CProgressDialog(&parent)
- , m_parent(parent)
- , m_modDoc(modDoc)
- , m_ratio(ratio)
- , m_sample(sample)
- , m_pitchShift(pitchShift)
- {
- m_updateInterval = TrackerSettings::Instance().GUIUpdateInterval;
- if(m_updateInterval < 15) m_updateInterval = 15;
- }
- void Run() override
- {
- SetTitle(m_pitchShift ? _T("Pitch Shift") : _T("Time Stretch"));
- SetRange(0, 100);
- if(m_pitchShift)
- m_result = PitchShift();
- else
- m_result = TimeStretch();
- EndDialog((m_result == kOK) ? IDOK : IDCANCEL);
- }
- TimeStretchPitchShiftResult TimeStretch()
- {
- ModSample &sample = m_modDoc.GetSoundFile().GetSample(m_sample);
- const uint32 sampleRate = sample.GetSampleRate(m_modDoc.GetModType());
- if(!sample.HasSampleData()) return kAbort;
- if(m_ratio == 1.0) return kAbort;
- if(m_ratio < 0.5f) return kStretchTooShort;
- if(m_ratio > 2.0f) return kStretchTooLong;
- if(sampleRate > 192000) return kStretchInvalidSampleRate;
- HANDLE handleSt = soundtouch_createInstance();
- if(handleSt == NULL)
- {
- mpt::throw_out_of_memory();
- }
- const uint8 smpSize = sample.GetElementarySampleSize();
- const uint8 numChannels = sample.GetNumChannels();
- // Initialize soundtouch object.
- soundtouch_setSampleRate(handleSt, sampleRate);
- soundtouch_setChannels(handleSt, numChannels);
- // Given ratio is time stretch ratio, and must be converted to
- // tempo change ratio: for example time stretch ratio 2 means
- // tempo change ratio 0.5.
- soundtouch_setTempoChange(handleSt, (1.0f / m_ratio - 1.0f) * 100.0f);
- // Read settings from GUI.
- m_parent.ReadTimeStretchParameters();
- // Set settings to soundtouch. Zero value means 'use default', and
- // setting value is read back after setting because not all settings are accepted.
- soundtouch_setSetting(handleSt, SETTING_SEQUENCE_MS, m_parent.m_nSequenceMs);
- m_parent.m_nSequenceMs = soundtouch_getSetting(handleSt, SETTING_SEQUENCE_MS);
- soundtouch_setSetting(handleSt, SETTING_SEEKWINDOW_MS, m_parent.m_nSeekWindowMs);
- m_parent.m_nSeekWindowMs = soundtouch_getSetting(handleSt, SETTING_SEEKWINDOW_MS);
- soundtouch_setSetting(handleSt, SETTING_OVERLAP_MS, m_parent.m_nOverlapMs);
- m_parent.m_nOverlapMs = soundtouch_getSetting(handleSt, SETTING_OVERLAP_MS);
- // Update GUI with the actual SoundTouch parameters in effect.
- m_parent.UpdateTimeStretchParameters();
- const SmpLength inBatchSize = soundtouch_getSetting(handleSt, SETTING_NOMINAL_INPUT_SEQUENCE) + 1; // approximate value, add 1 to play safe
- const SmpLength outBatchSize = soundtouch_getSetting(handleSt, SETTING_NOMINAL_OUTPUT_SEQUENCE) + 1; // approximate value, add 1 to play safe
- const auto selection = m_parent.GetSelectionPoints();
- const SmpLength selLength = selection.selectionActive ? selection.nEnd - selection.nStart : sample.nLength;
- const SmpLength remainLength = sample.nLength - selLength;
- if(selLength < inBatchSize)
- {
- soundtouch_destroyInstance(handleSt);
- return kSampleTooShort;
- }
- if(static_cast<SmpLength>(std::ceil(static_cast<double>(m_ratio) * selLength)) < outBatchSize)
- {
- soundtouch_destroyInstance(handleSt);
- return kSampleTooShort;
- }
- const SmpLength stretchLength = mpt::saturate_round<SmpLength>(m_ratio * selLength);
- const SmpLength stretchEnd = selection.nStart + stretchLength;
- const SmpLength newSampleLength = remainLength + stretchLength;
- void *pNewSample = nullptr;
- if(newSampleLength <= MAX_SAMPLE_LENGTH)
- {
- pNewSample = ModSample::AllocateSample(newSampleLength, sample.GetBytesPerSample());
- }
- if(pNewSample == nullptr)
- {
- soundtouch_destroyInstance(handleSt);
- return kOutOfMemory;
- }
- // Show wait mouse cursor
- BeginWaitCursor();
- memcpy(pNewSample, sample.sampleb(), selection.nStart * sample.GetBytesPerSample());
- memcpy(static_cast<std::byte *>(pNewSample) + stretchEnd * sample.GetBytesPerSample(), sample.sampleb() + selection.nEnd * sample.GetBytesPerSample(), (sample.nLength - selection.nEnd) * sample.GetBytesPerSample());
- constexpr SmpLength MaxInputChunkSize = 1024;
- std::vector<float> buffer(MaxInputChunkSize * numChannels);
- SmpLength inPos = selection.nStart;
- SmpLength outPos = selection.nStart; // Keeps count of the sample length received from stretching process.
- DWORD timeLast = 0;
- // Process sample in steps.
- while(inPos < selection.nEnd)
- {
- // Current chunk size limit test
- const SmpLength inChunkSize = std::min(MaxInputChunkSize, sample.nLength - inPos);
- DWORD timeNow = timeGetTime();
- if(timeNow - timeLast >= m_updateInterval)
- {
- // Show progress bar using process button painting & text label
- TCHAR progress[32];
- uint32 percent = static_cast<uint32>(100 * (inPos + inChunkSize) / sample.nLength);
- wsprintf(progress, _T("Time Stretch... %u%%"), percent);
- SetText(progress);
- SetProgress(percent);
- ProcessMessages();
- if(m_abort)
- break;
- timeLast = timeNow;
- }
- // Send sampledata for processing.
- switch(smpSize)
- {
- case 1:
- CopyAudioChannelsInterleaved(buffer.data(), sample.sample8() + inPos * numChannels, numChannels, inChunkSize);
- break;
- case 2:
- CopyAudioChannelsInterleaved(buffer.data(), sample.sample16() + inPos * numChannels, numChannels, inChunkSize);
- break;
- }
- soundtouch_putSamples(handleSt, buffer.data(), inChunkSize);
- // Receive some processed samples (it's not guaranteed that there is any available).
- {
- SmpLength outChunkSize = std::min(static_cast<SmpLength>(soundtouch_numSamples(handleSt)), stretchLength - outPos);
- if(outChunkSize > 0)
- {
- buffer.resize(outChunkSize * numChannels);
- soundtouch_receiveSamples(handleSt, buffer.data(), outChunkSize);
- switch(smpSize)
- {
- case 1:
- CopyAudioChannelsInterleaved(static_cast<int8 *>(pNewSample) + numChannels * outPos, buffer.data(), numChannels, outChunkSize);
- break;
- case 2:
- CopyAudioChannelsInterleaved(static_cast<int16 *>(pNewSample) + numChannels * outPos, buffer.data(), numChannels, outChunkSize);
- break;
- }
- outPos += outChunkSize;
- }
- }
- // Next buffer chunk
- inPos += inChunkSize;
- }
- if(!m_abort)
- {
- // The input sample should now be processed. Receive remaining samples.
- soundtouch_flush(handleSt);
- SmpLength outChunkSize = std::min(static_cast<SmpLength>(soundtouch_numSamples(handleSt)), stretchLength - (outPos - selection.nStart));
- if(outChunkSize > 0)
- {
- buffer.resize(outChunkSize * numChannels);
- soundtouch_receiveSamples(handleSt, buffer.data(), outChunkSize);
- switch(smpSize)
- {
- case 1:
- CopyAudioChannelsInterleaved(static_cast<int8 *>(pNewSample) + numChannels * outPos, buffer.data(), numChannels, outChunkSize);
- break;
- case 2:
- CopyAudioChannelsInterleaved(static_cast<int16 *>(pNewSample) + numChannels * outPos, buffer.data(), numChannels, outChunkSize);
- break;
- }
- outPos += outChunkSize;
- }
- soundtouch_clear(handleSt);
- MPT_ASSERT(soundtouch_isEmpty(handleSt) != 0);
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- m_parent.PrepareUndo("Time Stretch", sundo_replace);
- // Swap sample buffer pointer to new buffer, update song + sample data & free old sample buffer
- ctrlSmp::ReplaceSample(sample, pNewSample, std::min(outPos + remainLength, newSampleLength), sndFile);
- // Update loops and wrap-around buffer
- sample.SetLoop(
- mpt::saturate_round<SmpLength>(sample.nLoopStart * m_ratio),
- mpt::saturate_round<SmpLength>(sample.nLoopEnd * m_ratio),
- sample.uFlags[CHN_LOOP],
- sample.uFlags[CHN_PINGPONGLOOP],
- sndFile);
- sample.SetSustainLoop(
- mpt::saturate_round<SmpLength>(sample.nSustainStart * m_ratio),
- mpt::saturate_round<SmpLength>(sample.nSustainEnd * m_ratio),
- sample.uFlags[CHN_SUSTAINLOOP],
- sample.uFlags[CHN_PINGPONGSUSTAIN],
- sndFile);
- } else
- {
- ModSample::FreeSample(pNewSample);
- }
- soundtouch_destroyInstance(handleSt);
- // Restore mouse cursor
- EndWaitCursor();
- if(selection.selectionActive)
- m_parent.SetSelectionPoints(selection.nStart, selection.nStart + stretchLength);
- return m_abort ? kAbort : kOK;
- }
- TimeStretchPitchShiftResult PitchShift()
- {
- static constexpr SmpLength MAX_BUFFER_LENGTH = 8192;
- ModSample &sample = m_modDoc.GetSoundFile().GetSample(m_sample);
- if(!sample.HasSampleData() || m_ratio < 0.5f || m_ratio > 2.0f)
- {
- return kAbort;
- }
- // Get selected oversampling - quality - (also refered as FFT overlapping) factor
- CComboBox *combo = (CComboBox *)m_parent.GetDlgItem(IDC_COMBO5);
- long ovs = combo->GetCurSel() + 4;
- // Get selected FFT size (power of 2; should not exceed MAX_BUFFER_LENGTH - see smbPitchShift.h)
- combo = (CComboBox *)m_parent.GetDlgItem(IDC_COMBO6);
- UINT fft = 1 << (combo->GetCurSel() + 8);
- while(fft > MAX_BUFFER_LENGTH) fft >>= 1;
- // Show wait mouse cursor
- BeginWaitCursor();
- // Get original sample rate
- const float sampleRate = static_cast<float>(sample.GetSampleRate(m_modDoc.GetModType()));
- // Allocate working buffer
- const size_t bufferSize = MAX_BUFFER_LENGTH + fft;
- std::vector<float> buffer;
- try
- {
- buffer.resize(bufferSize);
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- return kOutOfMemory;
- }
- const auto smpSize = sample.GetElementarySampleSize();
- const auto numChans = sample.GetNumChannels();
- const auto bps = sample.GetBytesPerSample();
- int8 *pNewSample = static_cast<int8 *>(ModSample::AllocateSample(sample.nLength, bps));
- if(pNewSample == nullptr)
- return kOutOfMemory;
- DWORD timeLast = 0;
- const auto selection = m_parent.GetSelectionPoints();
- // Process each channel separately
- for(uint8 chn = 0; chn < numChans; chn++)
- {
- // Process sample buffer using MAX_BUFFER_LENGTH (max) sized chunk steps (in order to allow
- // the processing of BIG samples...)
- for(SmpLength pos = selection.nStart; pos < selection.nEnd;)
- {
- DWORD timeNow = timeGetTime();
- if(timeNow - timeLast >= m_updateInterval)
- {
- TCHAR progress[32];
- uint32 percent = static_cast<uint32>(chn * 50.0 + (100.0 / numChans) * (pos - selection.nStart) / (selection.nEnd - selection.nStart));
- wsprintf(progress, _T("Pitch Shift... %u%%"), percent);
- SetText(progress);
- SetProgress(percent);
- ProcessMessages();
- if(m_abort)
- break;
- timeLast = timeNow;
- }
- // TRICK : output buffer offset management
- // as the pitch-shifter adds some blank signal in head of output buffer (matching FFT
- // length - in short it needs a certain amount of data before being able to output some
- // meaningful processed samples) , in order to avoid this behaviour , we will ignore
- // the first FFT_length samples and process the same amount of extra blank samples
- // (all 0.0f) at the end of the buffer (those extra samples will benefit from internal
- // FFT data computed during the previous steps resulting in a correct and consistent
- // signal output).
- const SmpLength processLen = (pos + MAX_BUFFER_LENGTH <= selection.nEnd) ? MAX_BUFFER_LENGTH : (selection.nEnd - pos);
- const bool bufStart = (pos == selection.nStart);
- const bool bufEnd = (pos + processLen >= selection.nEnd);
- const SmpLength startOffset = (bufStart ? fft : 0);
- const SmpLength innerOffset = (bufStart ? 0 : fft);
- const SmpLength finalOffset = (bufEnd ? fft : 0);
- // Re-initialize pitch-shifter with blank FFT before processing 1st chunk of current channel
- if(bufStart)
- {
- std::fill(buffer.begin(), buffer.begin() + fft, 0.0f);
- smbPitchShift(m_ratio, fft, fft, ovs, sampleRate, buffer.data(), buffer.data());
- }
- // Convert current channel's data chunk to float
- SmpLength offset = pos * numChans + chn;
- switch(smpSize)
- {
- case 1:
- CopySample<SC::ConversionChain<SC::Convert<float, int8>, SC::DecodeIdentity<int8>>>(buffer.data(), processLen, 1, sample.sample8() + offset, sizeof(int8) * processLen * numChans, numChans);
- break;
- case 2:
- CopySample<SC::ConversionChain<SC::Convert<float, int16>, SC::DecodeIdentity<int16>>>(buffer.data(), processLen, 1, sample.sample16() + offset, sizeof(int16) * processLen * numChans, numChans);
- break;
- }
- // Fills extra blank samples (read TRICK description comment above)
- if(bufEnd)
- std::fill(buffer.begin() + processLen, buffer.begin() + processLen + finalOffset, 0.0f);
- // Apply pitch shifting
- smbPitchShift(m_ratio, static_cast<long>(processLen + finalOffset), fft, ovs, sampleRate, buffer.data(), buffer.data());
- // Restore pitched-shifted float sample into original sample buffer
- void *ptr = pNewSample + (pos - innerOffset) * smpSize * numChans + chn * smpSize;
- const SmpLength copyLength = processLen + finalOffset - startOffset + 1;
- switch(smpSize)
- {
- case 1:
- CopySample<SC::ConversionChain<SC::Convert<int8, float>, SC::DecodeIdentity<float>>>(static_cast<int8 *>(ptr), copyLength, numChans, buffer.data() + startOffset, sizeof(float) * bufferSize, 1);
- break;
- case 2:
- CopySample<SC::ConversionChain<SC::Convert<int16, float>, SC::DecodeIdentity<float>>>(static_cast<int16 *>(ptr), copyLength, numChans, buffer.data() + startOffset, sizeof(float) * bufferSize, 1);
- break;
- }
- // Next buffer chunk
- pos += processLen;
- }
- }
- if(!m_abort)
- {
- m_parent.PrepareUndo("Pitch Shift", sundo_replace);
- memcpy(pNewSample, sample.sampleb(), selection.nStart * bps);
- memcpy(pNewSample + selection.nEnd * bps, sample.sampleb() + selection.nEnd * bps, (sample.nLength - selection.nEnd) * bps);
- ctrlSmp::ReplaceSample(sample, pNewSample, sample.nLength, m_modDoc.GetSoundFile());
- } else
- {
- ModSample::FreeSample(pNewSample);
- }
- // Restore mouse cursor
- EndWaitCursor();
- return m_abort ? kAbort : kOK;
- }
- };
- void CCtrlSamples::OnPitchShiftTimeStretch()
- {
- TimeStretchPitchShiftResult errorcode = kAbort;
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(!sample.HasSampleData()) goto error;
- if(IsDlgButtonChecked(IDC_CHECK3))
- {
- // Time stretching
- UpdateData(TRUE); //Ensure m_dTimeStretchRatio is up-to-date with textbox content
- DoPitchShiftTimeStretch timeStretch(*this, m_modDoc, m_nSample, static_cast<float>(m_dTimeStretchRatio / 100.0), false);
- timeStretch.DoModal();
- errorcode = timeStretch.m_result;
- } else
- {
- // Pitch shifting
- // Get selected pitch modifier [-12,+12]
- CString text;
- static_cast<CComboBox *>(GetDlgItem(IDC_COMBO4))->GetWindowText(text);
- float pm = ConvertStrTo<float>(text);
- if(pm == 0.0f) goto error;
- // Compute pitch ratio in range [0.5f ; 2.0f] (1.0f means output == input)
- // * pitch up -> 1.0f + n / 12.0f -> (12.0f + n) / 12.0f , considering n : pitch modifier > 0
- // * pitch dn -> 1.0f - n / 24.0f -> (24.0f - n) / 24.0f , considering n : pitch modifier > 0
- float pitch = pm < 0 ? ((24.0f + pm) / 24.0f) : ((12.0f + pm) / 12.0f);
- // Apply pitch modifier
- DoPitchShiftTimeStretch pitchShift(*this, m_modDoc, m_nSample, pitch, true);
- pitchShift.DoModal();
- errorcode = pitchShift.m_result;
- }
- if(errorcode == kOK)
- {
- // Update sample view
- SetModified(SampleHint().Info().Data(), true, true);
- return;
- }
- // Error management
- error:
- if(errorcode != kAbort)
- {
- CString str;
- switch(errorcode)
- {
- case kInvalidRatio:
- str = _T("Invalid stretch ratio!");
- break;
- case kStretchTooShort:
- case kStretchTooLong:
- str = MPT_CFORMAT("Stretch ratio is too {}. Must be between 50% and 200%.")((errorcode == kStretchTooShort) ? CString(_T("low")) : CString(_T("high")));
- break;
- case kOutOfMemory:
- str = _T("Out of memory.");
- break;
- case kSampleTooShort:
- str = _T("Sample too short.");
- break;
- case kStretchInvalidSampleRate:
- str = _T("Sample rate must be 192,000 Hz or lower.");
- break;
- default:
- str = _T("Unknown Error.");
- break;
- }
- Reporting::Error(str);
- }
- }
- void CCtrlSamples::OnReverse()
- {
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- SampleSelectionPoints selection = GetSelectionPoints();
- PrepareUndo("Reverse", sundo_reverse, selection.nStart, selection.nEnd);
- if(SampleEdit::ReverseSample(sample, selection.nStart, selection.nEnd, m_sndFile))
- {
- SetModified(SampleHint().Data(), false, true);
- } else
- {
- m_modDoc.GetSampleUndo().RemoveLastUndoStep(m_nSample);
- }
- EndWaitCursor();
- SwitchToView();
- }
- void CCtrlSamples::OnInvert()
- {
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- SampleSelectionPoints selection = GetSelectionPoints();
- PrepareUndo("Invert", sundo_invert, selection.nStart, selection.nEnd);
- if(SampleEdit::InvertSample(sample, selection.nStart, selection.nEnd, m_sndFile) == true)
- {
- SetModified(SampleHint().Data(), false, true);
- } else
- {
- m_modDoc.GetSampleUndo().RemoveLastUndoStep(m_nSample);
- }
- EndWaitCursor();
- SwitchToView();
- }
- void CCtrlSamples::OnSignUnSign()
- {
- if(!m_sndFile.GetSample(m_nSample).HasSampleData()) return;
- if(m_modDoc.IsNotePlaying(0, m_nSample, 0))
- MsgBoxHidable(ConfirmSignUnsignWhenPlaying);
- BeginWaitCursor();
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- SampleSelectionPoints selection = GetSelectionPoints();
- PrepareUndo("Unsign", sundo_unsign, selection.nStart, selection.nEnd);
- if(SampleEdit::UnsignSample(sample, selection.nStart, selection.nEnd, m_sndFile) == true)
- {
- SetModified(SampleHint().Data(), false, true);
- } else
- {
- m_modDoc.GetSampleUndo().RemoveLastUndoStep(m_nSample);
- }
- EndWaitCursor();
- SwitchToView();
- }
- void CCtrlSamples::OnSilence()
- {
- if(!m_sndFile.GetSample(m_nSample).HasSampleData()) return;
- BeginWaitCursor();
- SampleSelectionPoints selection = GetSelectionPoints();
- // never apply silence to a sample that has no selection
- const SmpLength len = selection.nEnd - selection.nStart;
- if(selection.selectionActive && len > 1)
- {
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- PrepareUndo("Silence", sundo_update, selection.nStart, selection.nEnd);
- if(SampleEdit::SilenceSample(sample, selection.nStart, selection.nEnd, m_sndFile))
- {
- SetModified(SampleHint().Data(), false, true);
- }
- }
- EndWaitCursor();
- SwitchToView();
- }
- void CCtrlSamples::OnPrevInstrument()
- {
- if (m_nSample > 1)
- SetCurrentSample(m_nSample - 1);
- else
- SetCurrentSample(m_sndFile.GetNumSamples());
- }
- void CCtrlSamples::OnNextInstrument()
- {
- if (m_nSample < m_sndFile.GetNumSamples())
- SetCurrentSample(m_nSample + 1);
- else
- SetCurrentSample(1);
- }
- void CCtrlSamples::OnNameChanged()
- {
- if(IsLocked() || !m_nSample) return;
- CString tmp;
- m_EditName.GetWindowText(tmp);
- const std::string s = mpt::ToCharset(m_sndFile.GetCharsetInternal(), tmp);
- if(s != m_sndFile.m_szNames[m_nSample])
- {
- if(!m_startedEdit)
- {
- PrepareUndo("Set Name");
- m_editInstrumentName = GetParentInstrumentWithSameName();
- if(m_editInstrumentName != INSTRUMENTINDEX_INVALID)
- m_modDoc.GetInstrumentUndo().PrepareUndo(m_editInstrumentName, "Set Name");
- }
- if(m_editInstrumentName <= m_sndFile.GetNumInstruments())
- {
- if(auto instr = m_sndFile.Instruments[m_editInstrumentName]; instr != nullptr)
- {
- instr->name = s;
- m_modDoc.UpdateAllViews(nullptr, InstrumentHint(m_editInstrumentName).Names(), this);
- }
- }
- m_sndFile.m_szNames[m_nSample] = s;
- SetModified(SampleHint().Names(), false, false);
- }
- }
- void CCtrlSamples::OnFileNameChanged()
- {
- if(IsLocked()) return;
- CString tmp;
- m_EditFileName.GetWindowText(tmp);
- const std::string s = mpt::ToCharset(m_sndFile.GetCharsetInternal(), tmp);
- if(s != m_sndFile.GetSample(m_nSample).filename)
- {
- if(!m_startedEdit) PrepareUndo("Set Filename");
- m_sndFile.GetSample(m_nSample).filename = s;
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnVolumeChanged()
- {
- if (IsLocked()) return;
- int nVol = GetDlgItemInt(IDC_EDIT7);
- Limit(nVol, 0, 64);
- nVol *= 4;
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if (nVol != sample.nVolume)
- {
- if(!m_startedEdit) PrepareUndo("Set Default Volume");
- sample.nVolume = static_cast<uint16>(nVol);
- sample.uFlags.reset(SMP_NODEFAULTVOLUME);
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnGlobalVolChanged()
- {
- if (IsLocked()) return;
- int nVol = GetDlgItemInt(IDC_EDIT8);
- Limit(nVol, 0, 64);
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if (nVol != sample.nGlobalVol)
- {
- if(!m_startedEdit) PrepareUndo("Set Global Volume");
- sample.nGlobalVol = static_cast<uint16>(nVol);
- // Live-adjust volume
- for(auto &chn : m_sndFile.m_PlayState.Chn)
- {
- if(chn.pModSample == &sample)
- {
- chn.UpdateInstrumentVolume(chn.pModSample, chn.pModInstrument);
- }
- }
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnSetPanningChanged()
- {
- if (IsLocked()) return;
- bool b = false;
- if (m_sndFile.GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))
- {
- b = IsDlgButtonChecked(IDC_CHECK1) != FALSE;
- }
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(b != sample.uFlags[CHN_PANNING])
- {
- PrepareUndo("Toggle Panning");
- sample.uFlags.set(CHN_PANNING, b);
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnPanningChanged()
- {
- if (IsLocked()) return;
- int nPan = GetDlgItemInt(IDC_EDIT9);
- if (nPan < 0) nPan = 0;
- if (m_sndFile.GetType() == MOD_TYPE_XM)
- {
- if (nPan > 255) nPan = 255; // displayed panning will be 0-255 with XM
- } else
- {
- if (nPan > 64) nPan = 64; // displayed panning will be 0-64 with anything but XM.
- nPan = nPan * 4; // so we x4 to get MPT's internal 0-256 range.
- }
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if (nPan != sample.nPan)
- {
- if(!m_startedEdit) PrepareUndo("Set Panning");
- sample.nPan = static_cast<uint16>(nPan);
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnFineTuneChanged()
- {
- if (IsLocked()) return;
- int n = GetDlgItemInt(IDC_EDIT5);
- if(!m_startedEdit)
- PrepareUndo("Finetune");
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if (!m_sndFile.UseFinetuneAndTranspose())
- {
- if ((n > 0) && (n <= (m_sndFile.GetType() == MOD_TYPE_S3M ? 65535 : 9999999)) && (n != (int)m_sndFile.GetSample(m_nSample).nC5Speed))
- {
- sample.nC5Speed = n;
- int transp = ModSample::FrequencyToTranspose(n).first;
- int basenote = (NOTE_MIDDLEC - NOTE_MIN) + transp;
- Clamp(basenote, BASENOTE_MIN, BASENOTE_MAX);
- basenote -= BASENOTE_MIN;
- if (basenote != m_CbnBaseNote.GetCurSel())
- {
- LockControls();
- m_CbnBaseNote.SetCurSel(basenote);
- UnlockControls();
- }
- SetModified(SampleHint().Info(), false, false);
- }
- } else
- {
- if(m_sndFile.GetType() & MOD_TYPE_MOD)
- n = MOD2XMFineTune(n);
- if((n >= -128) && (n <= 127))
- {
- sample.nFineTune = static_cast<int8>(n);
- SetModified(SampleHint().Info(), false, false);
- }
- }
- }
- void CCtrlSamples::OnFineTuneChangedDone()
- {
- // Update all playing channels
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- for(auto &chn : m_sndFile.m_PlayState.Chn)
- {
- if(chn.pModSample == &sample)
- {
- chn.nTranspose = sample.RelativeTone;
- chn.nFineTune = sample.nFineTune;
- if(chn.nC5Speed != 0 && sample.nC5Speed != 0)
- {
- if(m_sndFile.PeriodsAreFrequencies())
- chn.nPeriod = Util::muldivr(chn.nPeriod, sample.nC5Speed, chn.nC5Speed);
- else if(!m_sndFile.m_SongFlags[SONG_LINEARSLIDES])
- chn.nPeriod = Util::muldivr(chn.nPeriod, chn.nC5Speed, sample.nC5Speed);
- }
- chn.nC5Speed = sample.nC5Speed;
- }
- }
- }
- void CCtrlSamples::OnBaseNoteChanged()
- {
- if (IsLocked()) return;
- int n = static_cast<int>(m_CbnBaseNote.GetItemData(m_CbnBaseNote.GetCurSel()));
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- PrepareUndo("Transpose");
- if(!m_sndFile.UseFinetuneAndTranspose())
- {
- const int oldTransp = ModSample::FrequencyToTranspose(sample.nC5Speed).first;
- const uint32 newFreq = mpt::saturate_round<uint32>(sample.nC5Speed * std::pow(2.0, (n - oldTransp) / 12.0));
- if (newFreq > 0 && newFreq <= (m_sndFile.GetType() == MOD_TYPE_S3M ? 65535u : 9999999u) && newFreq != sample.nC5Speed)
- {
- sample.nC5Speed = newFreq;
- LockControls();
- SetDlgItemInt(IDC_EDIT5, newFreq, FALSE);
- // Due to rounding imprecisions if the base note is below 0, we recalculate it here to make sure that the value stays consistent.
- int basenote = (NOTE_MIDDLEC - NOTE_MIN) + ModSample::FrequencyToTranspose(newFreq).first;
- Limit(basenote, BASENOTE_MIN, BASENOTE_MAX);
- basenote -= BASENOTE_MIN;
- if(basenote != m_CbnBaseNote.GetCurSel())
- m_CbnBaseNote.SetCurSel(basenote);
- OnFineTuneChangedDone();
- UnlockControls();
- SetModified(SampleHint().Info(), false, false);
- }
- } else
- {
- if ((n >= -128) && (n < 128))
- {
- sample.RelativeTone = (int8)n;
- OnFineTuneChangedDone();
- SetModified(SampleHint().Info(), false, false);
- }
- }
- }
- void CCtrlSamples::OnVibTypeChanged()
- {
- if (IsLocked()) return;
- int n = m_ComboAutoVib.GetCurSel();
- if (n >= 0)
- {
- PrepareUndo("Set Vibrato Type");
- m_sndFile.GetSample(m_nSample).nVibType = static_cast<VibratoType>(m_ComboAutoVib.GetItemData(n));
- PropagateAutoVibratoChanges();
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnVibDepthChanged()
- {
- if (IsLocked()) return;
- int lmin = 0, lmax = 0;
- m_SpinVibDepth.GetRange(lmin, lmax);
- int n = GetDlgItemInt(IDC_EDIT15);
- if ((n >= lmin) && (n <= lmax))
- {
- if(!m_startedEdit) PrepareUndo("Set Vibrato Depth");
- m_sndFile.GetSample(m_nSample).nVibDepth = static_cast<uint8>(n);
- PropagateAutoVibratoChanges();
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnVibSweepChanged()
- {
- if (IsLocked()) return;
- int lmin = 0, lmax = 0;
- m_SpinVibSweep.GetRange(lmin, lmax);
- int n = GetDlgItemInt(IDC_EDIT14);
- if ((n >= lmin) && (n <= lmax))
- {
- if(!m_startedEdit) PrepareUndo("Set Vibrato Sweep");
- m_sndFile.GetSample(m_nSample).nVibSweep = static_cast<uint8>(n);
- PropagateAutoVibratoChanges();
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnVibRateChanged()
- {
- if (IsLocked()) return;
- int lmin = 0, lmax = 0;
- m_SpinVibRate.GetRange(lmin, lmax);
- int n = GetDlgItemInt(IDC_EDIT16);
- if ((n >= lmin) && (n <= lmax))
- {
- if(!m_startedEdit) PrepareUndo("Set Vibrato Rate");
- m_sndFile.GetSample(m_nSample).nVibRate = static_cast<uint8>(n);
- PropagateAutoVibratoChanges();
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnLoopTypeChanged()
- {
- if(IsLocked()) return;
- const int n = m_ComboLoopType.GetCurSel();
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- bool wasDisabled = !sample.uFlags[CHN_LOOP];
- PrepareUndo("Set Loop Type");
- // Loop type index: 0: Off, 1: On, 2: PingPong
- sample.uFlags.set(CHN_LOOP, n > 0);
- sample.uFlags.set(CHN_PINGPONGLOOP, n == 2);
- // set loop points if theren't any
- if(wasDisabled && sample.uFlags[CHN_LOOP] && sample.nLoopStart == sample.nLoopEnd)
- {
- SampleSelectionPoints selection = GetSelectionPoints();
- if(selection.selectionActive)
- {
- sample.SetLoop(selection.nStart, selection.nEnd, true, n == 2, m_sndFile);
- } else
- {
- sample.SetLoop(0, sample.nLength, true, n == 2, m_sndFile);
- }
- m_modDoc.UpdateAllViews(NULL, SampleHint(m_nSample).Info());
- } else
- {
- sample.PrecomputeLoops(m_sndFile);
- }
- ctrlSmp::UpdateLoopPoints(sample, m_sndFile);
- SetModified(SampleHint().Info(), false, false);
- }
- void CCtrlSamples::OnLoopPointsChanged()
- {
- if(IsLocked()) return;
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- SmpLength start = GetDlgItemInt(IDC_EDIT1, NULL, FALSE), end = GetDlgItemInt(IDC_EDIT2, NULL, FALSE);
- if(start < end || !sample.uFlags[CHN_LOOP])
- {
- if(!m_startedEdit) PrepareUndo("Set Loop");
- const int n = m_ComboLoopType.GetCurSel();
- sample.SetLoop(start, end, n > 0, n == 2, m_sndFile);
- SetModified(SampleHint().Info(), false, false);
- }
- }
- void CCtrlSamples::OnSustainTypeChanged()
- {
- if(IsLocked()) return;
- const int n = m_ComboSustainType.GetCurSel();
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- bool wasDisabled = !sample.uFlags[CHN_SUSTAINLOOP];
- PrepareUndo("Set Sustain Loop Type");
- // Loop type index: 0: Off, 1: On, 2: PingPong
- sample.uFlags.set(CHN_SUSTAINLOOP, n > 0);
- sample.uFlags.set(CHN_PINGPONGSUSTAIN, n == 2);
- // set sustain loop points if theren't any
- if(wasDisabled && sample.uFlags[CHN_SUSTAINLOOP] && sample.nSustainStart == sample.nSustainEnd)
- {
- SampleSelectionPoints selection = GetSelectionPoints();
- if(selection.selectionActive)
- {
- sample.SetSustainLoop(selection.nStart, selection.nEnd, true, n == 2, m_sndFile);
- } else
- {
- sample.SetSustainLoop(0, sample.nLength, true, n == 2, m_sndFile);
- }
- m_modDoc.UpdateAllViews(NULL, SampleHint(m_nSample).Info());
- } else
- {
- sample.PrecomputeLoops(m_sndFile);
- }
- ctrlSmp::UpdateLoopPoints(sample, m_sndFile);
- SetModified(SampleHint().Info(), false, false);
- }
- void CCtrlSamples::OnSustainPointsChanged()
- {
- if(IsLocked()) return;
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- SmpLength start = GetDlgItemInt(IDC_EDIT3, NULL, FALSE), end = GetDlgItemInt(IDC_EDIT4, NULL, FALSE);
- if(start < end || !sample.uFlags[CHN_SUSTAINLOOP])
- {
- if(!m_startedEdit) PrepareUndo("Set Sustain Loop");
- const int n = m_ComboSustainType.GetCurSel();
- sample.SetSustainLoop(start, end, n > 0, n == 2, m_sndFile);
- SetModified(SampleHint().Info(), false, false);
- }
- }
- #define SMPLOOP_ACCURACY 7 // 5%
- #define BIDILOOP_ACCURACY 2 // 5%
- bool MPT_LoopCheck(int sstart0, int sstart1, int send0, int send1)
- {
- int dse0 = send0 - sstart0;
- if ((dse0 < -SMPLOOP_ACCURACY) || (dse0 > SMPLOOP_ACCURACY)) return false;
- int dse1 = send1 - sstart1;
- if ((dse1 < -SMPLOOP_ACCURACY) || (dse1 > SMPLOOP_ACCURACY)) return false;
- int dstart = sstart1 - sstart0;
- int dend = send1 - send0;
- if (!dstart) dstart = dend >> 7;
- if (!dend) dend = dstart >> 7;
- if ((dstart ^ dend) < 0) return false;
- int delta = dend - dstart;
- return ((delta > -SMPLOOP_ACCURACY) && (delta < SMPLOOP_ACCURACY));
- }
- bool MPT_BidiEndCheck(int spos0, int spos1, int spos2)
- {
- int delta0 = spos1 - spos0;
- int delta1 = spos2 - spos1;
- int delta2 = spos2 - spos0;
- if (!delta0) delta0 = delta1 >> 7;
- if (!delta1) delta1 = delta0 >> 7;
- if ((delta1 ^ delta0) < 0) return false;
- return ((delta0 >= -1) && (delta0 <= 0) && (delta1 >= -1) && (delta1 <= 0) && (delta2 >= -1) && (delta2 <= 0));
- }
- bool MPT_BidiStartCheck(int spos0, int spos1, int spos2)
- {
- int delta1 = spos1 - spos0;
- int delta0 = spos2 - spos1;
- int delta2 = spos2 - spos0;
- if (!delta0) delta0 = delta1 >> 7;
- if (!delta1) delta1 = delta0 >> 7;
- if ((delta1 ^ delta0) < 0) return false;
- return ((delta0 >= -1) && (delta0 <= 0) && (delta1 > -1) && (delta1 <= 0) && (delta2 >= -1) && (delta2 <= 0));
- }
- void CCtrlSamples::OnVScroll(UINT nCode, UINT, CScrollBar *scrollBar)
- {
- TCHAR s[256];
- if(IsLocked()) return;
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- const uint8 *pSample = mpt::byte_cast<const uint8 *>(sample.sampleb());
- const uint32 inc = sample.GetBytesPerSample();
- SmpLength i;
- int pos;
- bool redraw = false;
- static CScrollBar *lastScrollbar = nullptr;
- LockControls();
- if ((!sample.nLength) || (!pSample)) goto NoSample;
- if (sample.uFlags[CHN_16BIT])
- {
- pSample++;
- }
- // Loop Start
- if ((pos = m_SpinLoopStart.GetPos32()) != 0 && sample.nLoopEnd > 0)
- {
- bool bOk = false;
- const uint8 *p = pSample + sample.nLoopStart * inc;
- int find0 = (int)pSample[sample.nLoopEnd*inc-inc];
- int find1 = (int)pSample[sample.nLoopEnd*inc];
- // Find Next LoopStart Point
- if (pos > 0)
- {
- for (i = sample.nLoopStart + 1; i + 16 < sample.nLoopEnd; i++)
- {
- p += inc;
- bOk = sample.uFlags[CHN_PINGPONGLOOP] ? MPT_BidiStartCheck(p[0], p[inc], p[inc*2]) : MPT_LoopCheck(find0, find1, p[0], p[inc]);
- if (bOk) break;
- }
- } else
- // Find Prev LoopStart Point
- {
- for (i = sample.nLoopStart; i; )
- {
- i--;
- p -= inc;
- bOk = sample.uFlags[CHN_PINGPONGLOOP] ? MPT_BidiStartCheck(p[0], p[inc], p[inc*2]) : MPT_LoopCheck(find0, find1, p[0], p[inc]);
- if (bOk) break;
- }
- }
- if (bOk)
- {
- if(!m_startedEdit && lastScrollbar != scrollBar) PrepareUndo("Set Loop Start");
- sample.nLoopStart = i;
- wsprintf(s, _T("%u"), sample.nLoopStart);
- m_EditLoopStart.SetWindowText(s);
- redraw = true;
- sample.PrecomputeLoops(m_sndFile);
- }
- m_SpinLoopStart.SetPos(0);
- }
- // Loop End
- if ((pos = m_SpinLoopEnd.GetPos32()) != 0)
- {
- bool bOk = false;
- const uint8 *p = pSample + sample.nLoopEnd * inc;
- int find0 = (int)pSample[sample.nLoopStart*inc];
- int find1 = (int)pSample[sample.nLoopStart*inc+inc];
- // Find Next LoopEnd Point
- if (pos > 0)
- {
- for (i = sample.nLoopEnd + 1; i <= sample.nLength; i++, p += inc)
- {
- bOk = sample.uFlags[CHN_PINGPONGLOOP] ? MPT_BidiEndCheck(p[0], p[inc], p[inc*2]) : MPT_LoopCheck(find0, find1, p[0], p[inc]);
- if (bOk) break;
- }
- } else
- // Find Prev LoopEnd Point
- {
- for (i = sample.nLoopEnd; i > sample.nLoopStart + 16; )
- {
- i--;
- p -= inc;
- bOk = sample.uFlags[CHN_PINGPONGLOOP] ? MPT_BidiEndCheck(p[0], p[inc], p[inc*2]) : MPT_LoopCheck(find0, find1, p[0], p[inc]);
- if (bOk) break;
- }
- }
- if (bOk)
- {
- if(!m_startedEdit && lastScrollbar != scrollBar) PrepareUndo("Set Loop End");
- sample.nLoopEnd = i;
- wsprintf(s, _T("%u"), sample.nLoopEnd);
- m_EditLoopEnd.SetWindowText(s);
- redraw = true;
- sample.PrecomputeLoops(m_sndFile);
- }
- m_SpinLoopEnd.SetPos(0);
- }
- // Sustain Loop Start
- if ((pos = m_SpinSustainStart.GetPos32()) != 0 && sample.nSustainEnd > 0)
- {
- bool bOk = false;
- const uint8 *p = pSample + sample.nSustainStart * inc;
- int find0 = (int)pSample[sample.nSustainEnd*inc-inc];
- int find1 = (int)pSample[sample.nSustainEnd*inc];
- // Find Next Sustain LoopStart Point
- if (pos > 0)
- {
- for (i = sample.nSustainStart + 1; i + 16 < sample.nSustainEnd; i++)
- {
- p += inc;
- bOk = sample.uFlags[CHN_PINGPONGSUSTAIN] ? MPT_BidiStartCheck(p[0], p[inc], p[inc*2]) : MPT_LoopCheck(find0, find1, p[0], p[inc]);
- if (bOk) break;
- }
- } else
- // Find Prev Sustain LoopStart Point
- {
- for (i = sample.nSustainStart; i; )
- {
- i--;
- p -= inc;
- bOk = sample.uFlags[CHN_PINGPONGSUSTAIN] ? MPT_BidiStartCheck(p[0], p[inc], p[inc*2]) : MPT_LoopCheck(find0, find1, p[0], p[inc]);
- if (bOk) break;
- }
- }
- if (bOk)
- {
- if(!m_startedEdit && lastScrollbar != scrollBar) PrepareUndo("Set Sustain Loop Start");
- sample.nSustainStart = i;
- wsprintf(s, _T("%u"), sample.nSustainStart);
- m_EditSustainStart.SetWindowText(s);
- redraw = true;
- sample.PrecomputeLoops(m_sndFile);
- }
- m_SpinSustainStart.SetPos(0);
- }
- // Sustain Loop End
- if ((pos = m_SpinSustainEnd.GetPos32()) != 0)
- {
- bool bOk = false;
- const uint8 *p = pSample + sample.nSustainEnd * inc;
- int find0 = (int)pSample[sample.nSustainStart*inc];
- int find1 = (int)pSample[sample.nSustainStart*inc+inc];
- // Find Next LoopEnd Point
- if (pos > 0)
- {
- for (i = sample.nSustainEnd + 1; i + 1 < sample.nLength; i++, p += inc)
- {
- bOk = sample.uFlags[CHN_PINGPONGSUSTAIN] ? MPT_BidiEndCheck(p[0], p[inc], p[inc*2]) : MPT_LoopCheck(find0, find1, p[0], p[inc]);
- if (bOk) break;
- }
- } else
- // Find Prev LoopEnd Point
- {
- for (i = sample.nSustainEnd; i > sample.nSustainStart + 16; )
- {
- i--;
- p -= inc;
- bOk = sample.uFlags[CHN_PINGPONGSUSTAIN] ? MPT_BidiEndCheck(p[0], p[inc], p[inc*2]) : MPT_LoopCheck(find0, find1, p[0], p[inc]);
- if (bOk) break;
- }
- }
- if (bOk)
- {
- if(!m_startedEdit && lastScrollbar != scrollBar) PrepareUndo("Set Sustain Loop End");
- sample.nSustainEnd = i;
- wsprintf(s, _T("%u"), sample.nSustainEnd);
- m_EditSustainEnd.SetWindowText(s);
- redraw = true;
- sample.PrecomputeLoops(m_sndFile);
- }
- m_SpinSustainEnd.SetPos(0);
- }
- NoSample:
- // FineTune / C-5 Speed
- if ((pos = m_SpinFineTune.GetPos32()) != 0)
- {
- if (!m_sndFile.UseFinetuneAndTranspose())
- {
- if(!m_startedEdit && lastScrollbar != scrollBar) PrepareUndo("Finetune");
- if(sample.nC5Speed < 1)
- sample.nC5Speed = 8363;
- auto oldFreq = sample.nC5Speed;
- sample.Transpose((pos * TrackerSettings::Instance().m_nFinetuneStep) / 1200.0);
- if(sample.nC5Speed == oldFreq)
- sample.nC5Speed += pos;
- Limit(sample.nC5Speed, 1u, 9999999u); // 9999999 is max. in Impulse Tracker
- int transp = ModSample::FrequencyToTranspose(sample.nC5Speed).first;
- int basenote = (NOTE_MIDDLEC - NOTE_MIN) + transp;
- Clamp(basenote, BASENOTE_MIN, BASENOTE_MAX);
- basenote -= BASENOTE_MIN;
- if (basenote != m_CbnBaseNote.GetCurSel()) m_CbnBaseNote.SetCurSel(basenote);
- SetDlgItemInt(IDC_EDIT5, sample.nC5Speed, FALSE);
- } else
- {
- if(!m_startedEdit && lastScrollbar != scrollBar) PrepareUndo("Finetune");
- int ftune = (int)sample.nFineTune;
- // MOD finetune range -8 to 7 translates to -128 to 112
- if(m_sndFile.GetType() & MOD_TYPE_MOD)
- {
- ftune = Clamp((ftune >> 4) + pos, -8, 7);
- sample.nFineTune = MOD2XMFineTune((signed char)ftune);
- } else
- {
- ftune = Clamp(ftune + pos, -128, 127);
- sample.nFineTune = (signed char)ftune;
- }
- SetDlgItemInt(IDC_EDIT5, ftune, TRUE);
- }
- redraw = true;
- m_SpinFineTune.SetPos(0);
- OnFineTuneChangedDone();
- }
- if(scrollBar->m_hWnd == m_SpinSequenceMs.m_hWnd || scrollBar->m_hWnd == m_SpinSeekWindowMs.m_hWnd || scrollBar->m_hWnd == m_SpinOverlap.m_hWnd)
- {
- ReadTimeStretchParameters();
- UpdateTimeStretchParameters();
- }
- if(nCode == SB_ENDSCROLL) SwitchToView();
- if(redraw)
- {
- SetModified(SampleHint().Info().Data(), false, false);
- }
- lastScrollbar = scrollBar;
- UnlockControls();
- }
- BOOL CCtrlSamples::PreTranslateMessage(MSG *pMsg)
- {
- if (pMsg)
- {
- //We handle keypresses before Windows has a chance to handle them (for alt etc..)
- if ((pMsg->message == WM_SYSKEYUP) || (pMsg->message == WM_KEYUP) ||
- (pMsg->message == WM_SYSKEYDOWN) || (pMsg->message == WM_KEYDOWN))
- {
- CInputHandler* ih = CMainFrame::GetInputHandler();
- //Translate message manually
- UINT nChar = (UINT)pMsg->wParam;
- UINT nRepCnt = LOWORD(pMsg->lParam);
- UINT nFlags = HIWORD(pMsg->lParam);
- KeyEventType kT = ih->GetKeyEventType(nFlags);
- InputTargetContext ctx = (InputTargetContext)(kCtxViewSamples);
- if (ih->KeyEvent(ctx, nChar, nRepCnt, nFlags, kT) != kcNull)
- return true; // Mapped to a command, no need to pass message on.
- }
- }
- return CModControlDlg::PreTranslateMessage(pMsg);
- }
- LRESULT CCtrlSamples::OnCustomKeyMsg(WPARAM wParam, LPARAM /*lParam*/)
- {
- int transpose = 0;
- switch(wParam)
- {
- case kcSampleLoad: OnSampleOpen(); return wParam;
- case kcSampleLoadRaw: OnSampleOpenRaw(); return wParam;
- case kcSampleSave: OnSampleSaveOne(); return wParam;
- case kcSampleNew: InsertSample(false); return wParam;
- case kcSampleDuplicate: InsertSample(true); return wParam;
- case kcSampleTransposeUp: transpose = 1; break;
- case kcSampleTransposeDown: transpose = -1; break;
- case kcSampleTransposeOctUp: transpose = 12; break;
- case kcSampleTransposeOctDown: transpose = -12; break;
- case kcSampleUpsample:
- case kcSampleDownsample:
- {
- uint32 oldRate = m_sndFile.GetSample(m_nSample).GetSampleRate(m_sndFile.GetType());
- ApplyResample(m_nSample, wParam == kcSampleUpsample ? oldRate * 2 : oldRate / 2, TrackerSettings::Instance().sampleEditorDefaultResampler);
- }
- return wParam;
- case kcSampleResample:
- OnResample();
- return wParam;
- case kcSampleStereoSep:
- OnStereoSeparation();
- return wParam;
- case kcSampleInitializeOPL:
- OnInitOPLInstrument();
- return wParam;
- }
- if(transpose)
- {
- if(m_CbnBaseNote.IsWindowEnabled())
- {
- int sel = Clamp(m_CbnBaseNote.GetCurSel() + transpose, 0, m_CbnBaseNote.GetCount() - 1);
- if(sel != m_CbnBaseNote.GetCurSel())
- {
- m_CbnBaseNote.SetCurSel(sel);
- OnBaseNoteChanged();
- }
- }
- return wParam;
- }
- return kcNull;
- }
- // Return currently selected part of the sample.
- // The whole sample size will be returned if no part of the sample is selected.
- // However, point.bSelected indicates whether a sample selection exists or not.
- CCtrlSamples::SampleSelectionPoints CCtrlSamples::GetSelectionPoints()
- {
- SampleSelectionPoints points;
- SAMPLEVIEWSTATE viewstate;
- const ModSample &sample = m_sndFile.GetSample(m_nSample);
- Clear(viewstate);
- SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate);
- points.nStart = viewstate.dwBeginSel;
- points.nEnd = viewstate.dwEndSel;
- if(points.nEnd > sample.nLength) points.nEnd = sample.nLength;
- if(points.nStart > points.nEnd) points.nStart = points.nEnd;
- points.selectionActive = true;
- if(points.nStart >= points.nEnd)
- {
- points.nStart = 0;
- points.nEnd = sample.nLength;
- points.selectionActive = false;
- }
- return points;
- }
- // Set the currently selected part of the sample.
- // To reset the selection, use nStart = nEnd = 0.
- void CCtrlSamples::SetSelectionPoints(SmpLength nStart, SmpLength nEnd)
- {
- const ModSample &sample = m_sndFile.GetSample(m_nSample);
- Limit(nStart, SmpLength(0), sample.nLength);
- Limit(nEnd, SmpLength(0), sample.nLength);
- SAMPLEVIEWSTATE viewstate;
- Clear(viewstate);
- SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate);
- viewstate.dwBeginSel = nStart;
- viewstate.dwEndSel = nEnd;
- SendViewMessage(VIEWMSG_LOADSTATE, (LPARAM)&viewstate);
- }
- // Crossfade loop to create smooth loop transitions
- void CCtrlSamples::OnXFade()
- {
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(!sample.HasSampleData())
- {
- MessageBeep(MB_ICONWARNING);
- SwitchToView();
- return;
- }
- bool resetLoopOnCancel = false;
- if((sample.nLoopEnd <= sample.nLoopStart || sample.nLoopEnd > sample.nLength)
- && (sample.nSustainEnd <= sample.nSustainStart || sample.nSustainEnd > sample.nLength))
- {
- const auto selection = GetSelectionPoints();
- if(selection.nStart > 0 && selection.nEnd > selection.nStart)
- {
- sample.SetLoop(selection.nStart, selection.nEnd, true, false, m_sndFile);
- resetLoopOnCancel = true;
- } else
- {
- Reporting::Error("Crossfade requires a sample loop to work.", this);
- SwitchToView();
- return;
- }
- }
- if(sample.nLoopStart == 0 && sample.nSustainStart == 0)
- {
- Reporting::Error("Crossfade requires the sample to have data before the loop start.", this);
- SwitchToView();
- return;
- }
- CSampleXFadeDlg dlg(this, sample);
- if(dlg.DoModal() == IDOK)
- {
- const SmpLength loopStart = dlg.m_useSustainLoop ? sample.nSustainStart: sample.nLoopStart;
- const SmpLength loopEnd = dlg.m_useSustainLoop ? sample.nSustainEnd: sample.nLoopEnd;
- const SmpLength maxSamples = std::min({ sample.nLength, loopStart, loopEnd / 2 });
- SmpLength fadeSamples = dlg.PercentToSamples(dlg.m_fadeLength);
- LimitMax(fadeSamples, maxSamples);
- if(fadeSamples < 2) return;
- PrepareUndo("Crossfade", sundo_update,
- loopEnd - fadeSamples,
- loopEnd + (dlg.m_afterloopFade ? std::min(sample.nLength - loopEnd, fadeSamples) : 0));
- if(SampleEdit::XFadeSample(sample, fadeSamples, dlg.m_fadeLaw, dlg.m_afterloopFade, dlg.m_useSustainLoop, m_sndFile))
- {
- SetModified(SampleHint().Info().Data(), true, true);
- } else
- {
- m_modDoc.GetSampleUndo().RemoveLastUndoStep(m_nSample);
- }
- } else if(resetLoopOnCancel)
- {
- sample.SetLoop(0, 0, false, false, m_sndFile);
- }
- SwitchToView();
- }
- void CCtrlSamples::OnStereoSeparation()
- {
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- if(!sample.HasSampleData()
- || sample.GetNumChannels() != 2
- || sample.uFlags[CHN_ADLIB])
- {
- MessageBeep(MB_ICONWARNING);
- SwitchToView();
- return;
- }
- static double separation = 100.0;
- CInputDlg dlg(this, _T("Stereo separation amount\n0% = mono, 100% = no change, 200% = double separation\nNegative values swap channels"), -200.0, 200.0, separation);
- if(dlg.DoModal() == IDOK)
- {
- separation = dlg.resultAsDouble;
- SampleSelectionPoints selection = GetSelectionPoints();
- PrepareUndo("Stereo Separation", sundo_update,
- selection.nStart, selection.nEnd);
- if(SampleEdit::StereoSepSample(sample, selection.nStart, selection.nEnd, separation, m_sndFile))
- {
- SetModified(SampleHint().Info().Data(), true, true);
- } else
- {
- m_modDoc.GetSampleUndo().RemoveLastUndoStep(m_nSample);
- }
- }
- SwitchToView();
- }
- void CCtrlSamples::OnAutotune()
- {
- SampleSelectionPoints selection = GetSelectionPoints();
- if(!selection.selectionActive)
- {
- selection.nStart = selection.nEnd = 0;
- }
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- Autotune at(sample, m_sndFile.GetType(), selection.nStart, selection.nEnd);
- if(at.CanApply())
- {
- CAutotuneDlg dlg(this);
- if(dlg.DoModal() == IDOK)
- {
- BeginWaitCursor();
- PrepareUndo("Automatic Sample Tuning");
- bool modified = true;
- if(IsOPLInstrument())
- {
- const uint32 newFreq = mpt::saturate_round<uint32>(dlg.GetPitchReference() * (8363.0 / 440.0) * std::pow(2.0, dlg.GetTargetNote() / 12.0));
- modified = (newFreq != sample.nC5Speed);
- sample.nC5Speed = newFreq;
- } else
- {
- modified = at.Apply(static_cast<double>(dlg.GetPitchReference()), dlg.GetTargetNote());
- }
- OnFineTuneChangedDone();
- if(modified)
- SetModified(SampleHint().Info(), true, false);
- EndWaitCursor();
- }
- }
- SwitchToView();
- }
- void CCtrlSamples::OnKeepSampleOnDisk()
- {
- SAMPLEINDEX first = m_nSample, last = m_nSample;
- if(CMainFrame::GetInputHandler()->ShiftPressed())
- {
- first = 1;
- last = m_sndFile.GetNumSamples();
- }
- const bool enable = IsDlgButtonChecked(IDC_CHECK2) != BST_UNCHECKED;
- for(SAMPLEINDEX i = first; i <= last; i++)
- {
- if(bool newState = enable && m_sndFile.SampleHasPath(i); newState != m_sndFile.GetSample(i).uFlags[SMP_KEEPONDISK])
- {
- m_sndFile.GetSample(i).uFlags.set(SMP_KEEPONDISK, newState);
- m_modDoc.UpdateAllViews(nullptr, SampleHint(i).Info().Names(), this);
- }
- }
- m_modDoc.SetModified();
- }
- // When changing auto vibrato properties, propagate them to other samples of the same instrument in XM edit mode.
- void CCtrlSamples::PropagateAutoVibratoChanges()
- {
- if(!(m_sndFile.GetType() & MOD_TYPE_XM))
- {
- return;
- }
- for(INSTRUMENTINDEX i = 1; i <= m_sndFile.GetNumInstruments(); i++)
- {
- if(m_sndFile.IsSampleReferencedByInstrument(m_nSample, i))
- {
- const auto referencedSamples = m_sndFile.Instruments[i]->GetSamples();
- // Propagate changes to all samples that belong to this instrument.
- const ModSample &it = m_sndFile.GetSample(m_nSample);
- m_sndFile.PropagateXMAutoVibrato(i, it.nVibType, it.nVibSweep, it.nVibDepth, it.nVibRate);
- for(auto smp : referencedSamples)
- {
- m_modDoc.UpdateAllViews(nullptr, SampleHint(smp).Info(), this);
- }
- }
- }
- }
- void CCtrlSamples::OnXButtonUp(UINT nFlags, UINT nButton, CPoint point)
- {
- if(nButton == XBUTTON1) OnPrevInstrument();
- else if(nButton == XBUTTON2) OnNextInstrument();
- CModControlDlg::OnXButtonUp(nFlags, nButton, point);
- SwitchToView();
- }
- bool CCtrlSamples::IsOPLInstrument() const
- {
- return m_nSample >= 1 && m_nSample <= m_sndFile.GetNumSamples() && m_sndFile.GetSample(m_nSample).uFlags[CHN_ADLIB];
- }
- void CCtrlSamples::OnInitOPLInstrument()
- {
- if(m_sndFile.SupportsOPL())
- {
- CriticalSection cs;
- PrepareUndo("Initialize OPL Instrument", sundo_replace);
- m_sndFile.DestroySample(m_nSample);
- m_sndFile.InitOPL();
- ModSample &sample = m_sndFile.GetSample(m_nSample);
- sample.nC5Speed = 8363;
- // Initialize with instant attack, release and enabled sustain for carrier and instant attack for modulator
- sample.SetAdlib(true, { 0x00, 0x20, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00 });
- SetModified(SampleHint().Info().Data().Names(), true, true);
- SwitchToView();
- }
- }
- INSTRUMENTINDEX CCtrlSamples::GetParentInstrumentWithSameName() const
- {
- auto ins = m_modDoc.FindSampleParent(m_nSample);
- if(ins == INSTRUMENTINDEX_INVALID)
- return INSTRUMENTINDEX_INVALID;
- auto instr = m_sndFile.Instruments[ins];
- if(instr == nullptr)
- return INSTRUMENTINDEX_INVALID;
- if((!instr->name.empty() && instr->name != m_sndFile.m_szNames[m_nSample]) || instr->GetSamples().size() != 1)
- return INSTRUMENTINDEX_INVALID;
- return ins;
- }
- OPENMPT_NAMESPACE_END
|