Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- %INPUT START
- %--------------------------------------------------------------------------
- %input MIDI file
- %the script should work for any MIDI file
- infile='C:\Musik\NSF\Uninvited01.mid';
- % infile='C:\Musik\Midi\Piano Collection\Final Fantasy 10\03_besaidisland.mid';
- %path to the Prefs folder of MPC
- MPCfold='C:\Spel\mariopaintcomposerpc\Mario Paint Composer PC\Prefs\';
- %file name of the text file containing all the arrays
- arrfile='MarioPaintArrList.txt';
- %you probably don't want to touch the two lines below. you can play around
- %with it if you want to, it will change the MPC note each MIDI note is
- %assigned to
- note1=84; %use 72 or 84 depending on octave. 84 seems correct, though 72 was used before
- note2=note1-25;
- %use bpm='auto' if you want the script to automatically find the bpm
- %(bpm = beats per minute), speed of the song
- %otherwise you can set this yourself to force a bpm on the song
- % bpm='auto';
- %sec_per_beat is the number of seconds between two beats
- %suggestion: find the second difference between two beats in the matrix N
- %and set sec_per_beat below to this difference
- sec_per_beat=0.1167;
- %beats per minute in the song
- bpm=round(60/sec_per_beat);
- %the sound channel indices you want. set to 'all' if you want to use all
- %channels in the MIDI file
- wantncs=[1 2 3]; %use [1 2 3] if MIDI is from NSF file, [1 2 3 4 5 6] for SNES
- % wantncs='all';
- %replace with a number to cut off MIDI after this many seconds
- %good for very long MIDI files
- %have 'off' if you want to use entire MIDI file
- maxsec='off';
- % maxsec=30;
- %instrument for each sound channel.
- %the value on index i is the instrument for channel number i
- %using p, star (piano), is usually a safe bet.
- %example:
- %b = mushroom = drum
- %p = star = piano
- %l = airplane = guitar
- %e = flower = trumpet
- %f = game boy = blip blop sound
- instr={'p','p','b','l','b','f','p','p'};
- %/INPUT END (you won't need to change anything below here)
- %--------------------------------------------------------------------------
- %below the script extracts the name of the output file
- %the variable songname will be the name of the song
- q=find(infile=='\');
- if numel(q)>0
- songname=infile((q(end)+1):(end-4));
- else
- songname=infile(1:(end-4));
- end
- %read MIDI file
- midi=readmidi(infile);
- N = midiInfo(midi,0);
- clc;
- if not(strcmp(maxsec,'off'))
- q=find(N(:,6)>maxsec,1,'first');
- N=N(1:q,:);
- end
- ns=size(N,1);
- %get all channels
- C=unique(N(:,2));
- if not(strcmp(wantncs,'all'))
- %if wantncs is not 'all';
- C=C(wantncs);
- q=false(ns,1);
- for i=1:ns
- if sum(C==N(i,2))>0
- q(i)=true;
- end
- end
- N=N(q,:);
- end
- % error('stop');
- ns=size(N,1);
- if strcmp(bpm,'auto')
- %automatic finding of bpm
- %this algorithm seems to give a really high bpm for some songs.
- %it works nice if the song has not arpeggios or anything like that and
- %if the song has a clear beat time.
- %you can test this out, but you probably want to manually tweak this
- %if you want your song to sound good
- %this algorithm basically computes the shortest time between any two
- %consecutive notes and tests fractions of this as beat time and picks
- %the best option
- r=10^6; %round to 4 decimals
- t1=N(2:end,5);
- t2=N(1:(end-1),5);
- tdiff=t1-t2;
- tdiff=round(r*tdiff)/r;
- tdiff=tdiff(tdiff>0);
- % error('stop');
- dt=min(tdiff); %beat time in seconds
- disp(['start beat time: ' num2str(60*dt)]);
- k=1;
- eps=10^-2;
- bestval=Inf;
- bestk=0;
- for k=1:64
- % disp(['Trying k=' num2str(k)]);
- dt0=dt/k;
- u1=tdiff./dt0;
- u2=round(u1);
- u3=abs(u1-u2);
- maxu3=max(u3);
- % disp(['max u3: ' num2str(maxu3)]);
- if max(u3)<bestval
- bestval=max(u3);
- bestk=k;
- end
- % break;
- % else
- % k=k+1;
- % if k>20
- % error('stop');
- % end
- end
- % disp(['Best k: ' num2str(bestk)]);
- dt=dt./bestk;
- % error('stop');
- %find beat time...
- % dt=1/k;
- % if dt<10^-3
- % error('stop');
- % end
- disp(['!!!! Using k=' num2str(bestk)]);
- disp(['!!!! beat time in sec: ' num2str(dt)]);
- disp(['!!!! beat time in frames: ' num2str(60*dt)]);
- disp(['!!!! bpm: ' num2str(round(60/dt))]);
- %beattime is number of frames between beats
- %higher value = slower beat time
- %this value can be converted to beats per minute by taking
- %3600 and dividing it by beattime.
- % beattime=2;
- % sec_per_beat=sec_per_beat;
- % 1 track number
- % 2 channel number
- % 3 note number (midi encoding of pitch)
- % 4 velocity
- % 5 start time (seconds)
- % 6 end time (seconds)
- % message number of note_on
- % message number of note_off
- %clear command window if it's clottered by warning texts...
- disp(['Max note: ' num2str(note1)]);
- disp(['Min note: ' num2str(note2)]);
- bpm=round(60/dt);
- % bpm=round(60*60/beattime); %always this value for NES games, one per frame
- % bpm=60*tpqn;
- %
- % % bpm=bpm/10;
- %
- else
- dt=60/bpm;
- end
- tvec=N(:,5); %vector with start times for each note
- F1=round(tvec/dt)+1; %beat assignment
- %fix octaves if necessary...
- nc=numel(C);
- for i=1:nc
- q=(N(:,2)==C(i));
- t=N(q,3);
- % mindiff=min(t)-note2; %anything lower than 48 is not ok...
- % maxdiff=max(t)-note1; %anything above 72 is not ok...
- %
- disp(['Channel ' num2str(C(i)) ' has min ' num2str(min(t)) ' and max ' num2str(max(t))]);
- newmin=min(t)+12;
- newmax=max(t)+12;
- if min(t)<note2 && newmin<=note1 && newmax<=note1
- % disp(['maxdiff: ' num2str(maxdiff)]);
- % disp(['mindiff: ' num2str(maxdiff)]);
- disp(['raising channel ' num2str(C(i))]);
- N(q,3)=N(q,3)+12;
- end
- newmin=min(t)-12;
- newmax=max(t)-12;
- if max(t)>note1 && newmin>=note2 && newmax>=note2
- % disp(['maxdiff: ' num2str(maxdiff)]);
- % disp(['mindiff: ' num2str(maxdiff)]);
- disp(['lowering channel ' num2str(C(i))]);
- N(q,3)=N(q,3)-12;
- end
- end
- %number of channels
- % nc=numel(C);
- %each row in F is one beat in MPC, each column is an instrument
- %the value of F is note number in MIDI terms
- % F1=round(60*N(:,5)/beattime)+1; %the beat to place this on
- %dim3: beat number
- %dim2: instrument of choise
- %dim3: what intrument plays this note
- %dim3: note number of choice -> each channel can play more than one note at
- %any time. maximum of 5, though
- numbeats=max(F1);
- %F will contain the notes for the MPC file
- %each row is one beat
- %each column is one potential note on this beat (up to 5 notes on each
- %beat)
- %the first layer of the third dimension is the note number
- %the second layer is what instrument number plays this note
- F=NaN(numbeats,5,2);
- Q=ones(numbeats,1); %what column we are on, on each beat
- %assign
- for i=1:ns
- nowch=N(i,2);
- nownote=N(i,3);
- q=find(C==nowch);
- if numel(q)>0
- nownote=N(i,3);
- %find the beat to place this note on
- f1=F1(i);
- if Q(f1)<=5
- F(f1,Q(f1),1)=nownote; %note
- F(f1,Q(f1),2)=q; %instrument
- Q(f1)=Q(f1)+1;
- % if Q(f1)>5
- % disp(['Warning: more than 5 notes on beat ' num2str(i) '. Skipping any further notes on this beat. To solve, try decreasing "beattime".']);
- % end
- end
- end
- end
- %prepare notes
- L=cell(note1,1);
- L(note1)={'a'}; %C
- L(note1-1)={'b'}; %B
- L(note1-2)={'c#'}; %A#
- L(note1-3)={'c'}; %A
- L(note1-4)={'d#'}; %G#
- L(note1-5)={'d'}; %G
- L(note1-6)={'e#'}; %F#
- L(note1-7)={'e'}; %F
- L(note1-8)={'f'}; %E
- L(note1-9)={'g#'}; %D#
- L(note1-10)={'g'}; %D
- L(note1-11)={'h#'}; %C#
- L(note1-12)={'h'}; %C
- L(note1-13)={'i'}; %B
- L(note1-14)={'j#'}; %A#
- L(note1-15)={'j'}; %A
- L(note1-16)={'k#'}; %G#
- L(note1-17)={'k'}; %G
- L(note1-18)={'l#'}; %F#
- L(note1-19)={'l'}; %F
- L(note1-20)={'m'}; %E
- L(note1-21)={'n#'}; %D#
- L(note1-22)={'n'}; %D
- L(note1-23)={'o#'}; %C#
- L(note1-24)={'o'}; %C
- L(note1-25)={'p'}; %B
- fidarr=fopen([MPCfold songname ']MarioPaint.txt'],'w');
- %if necessary, add array to ArrList
- arrlist=[MPCfold arrfile];
- fidnew=fopen(arrlist,'r');
- t=fgetl(fidnew);
- s0=strfind(t,songname);
- fclose(fidnew);
- if numel(s0)==0 %check if this song is already in array list
- fid2=fopen(arrlist,'a');
- fprintf(fid2,[songname ']MarioPaint*']);
- fclose(fid2);
- end
- %create array file and first output file
- arr_end=']MarioPaint';
- nowfile_ind=1;
- if nowfile_ind<10 %if less than 10, add a 0 character in front
- b='0';
- else
- b='';
- end
- outfile=[MPCfold songname b num2str(nowfile_ind) arr_end '.txt'];
- fprintf(fidarr,[songname b num2str(nowfile_ind) '\r']);
- fid=fopen(outfile,'w');
- beatstr='4/4*';
- fprintf(fid,beatstr);
- totcols=0;
- %dump to MPC file
- for i=1:numbeats %for all beats
- k=0; %k counts number of notes played on this beat
- for j=1:5 %for all 5 potential notes on this beat
- v=F(i,j,1); %current note
- w=F(i,j,2); %current instrument
- if v<note2 %if too low a note, increase its octave
- v=v+12*ceil((note2-v)/12);
- end
- if v>note1 %if too high a note, decrease its octave
- v=v-12*ceil((v-note1)/12);
- end
- if isfinite(v) %if these is a note on this beat and on this column
- now_note=char(L(v));
- now_instr=char(instr(w));
- fprintf(fid,[now_instr now_note '+']); %print this note
- k=k+1;
- end
- end
- %add additional + at end of beat
- fprintf(fid,[repmat('+',1,6-k) 'q:']);
- totcols=totcols+1;
- if totcols==384
- fprintf(fid,['"%%' num2str(bpm)]);
- fclose(fid);
- nowfile_ind=nowfile_ind+1;
- if nowfile_ind<10
- b='0';
- else
- b='';
- end
- outfile=[MPCfold songname b num2str(nowfile_ind) arr_end '.txt'];
- fprintf(fidarr,[songname b num2str(nowfile_ind) '\r']);
- disp(['now on file ' outfile]);
- fid=fopen(outfile,'w');
- fprintf(fid,beatstr);
- totcols=0;
- end
- end
- %add : characters so you get 384 of them. this seems necessary for some
- %reason
- fprintf(fid,repmat(':',1,384-totcols));
- %print bpm at the end of the file
- fprintf(fid,['"%%' num2str(bpm)]);
- fclose(fid);
- fclose(fidarr);
- fclose all;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement