Advertisement
Guest User

MIDI to mario paint composer v2

a guest
Apr 30th, 2012
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
MatLab 10.77 KB | None | 0 0
  1. %INPUT START
  2. %--------------------------------------------------------------------------
  3.  
  4. %input MIDI file
  5. %the script should work for any MIDI file
  6. infile='C:\Musik\NSF\Patashu in the Stars.mid';
  7. % infile='C:\Musik\Midi\Piano Collection\Final Fantasy 10\03_besaidisland.mid';
  8.  
  9. %path to the Prefs folder of MPC
  10. MPCfold='C:\Spel\mariopaintcomposerpc\Mario Paint Composer PC\Prefs\';
  11.  
  12.  
  13. %you probably don't want to touch the two lines below. you can play around
  14. %with it if you want to, it will change the MPC note each MIDI note is
  15. %assigned to
  16. note1=84; %use 72 or 84 depending on octave. 84 seems correct, though 72 was used before
  17. note2=note1-25;
  18.  
  19. %use bpm='auto' if you want the script to automatically find the bpm
  20. %(bpm = beats per minute), speed of the song
  21. %otherwise you can set this yourself to force a bpm on the song
  22. % bpm='auto';
  23.  
  24.  
  25. %sec_per_beat is the number of seconds between two beats
  26. %suggestion: find the second difference between two beats in the matrix N
  27. %and set sec_per_beat below to this difference
  28. sec_per_beat=0.1167;
  29.  
  30. %beats per minute in the song
  31. bpm=round(60/sec_per_beat);
  32.  
  33. %the sound channel indices you want. set to 'all' if you want to use all
  34. %channels in the MIDI file
  35. wantncs=[1 2 3]; %use [1 2 3] if MIDI is from NSF file, [1 2 3 4 5 6] for SNES
  36.  
  37. % wantncs='all';
  38.  
  39.  
  40. %replace with a number to cut off MIDI after this many seconds
  41. %good for very long MIDI files
  42. %have 'off' if you want to use entire MIDI file
  43. maxsec='off';
  44. % maxsec=30;
  45.  
  46.  
  47.  
  48. %instrument for each sound channel.
  49. %the value on index i is the instrument for channel number i
  50. %using p, star (piano), is usually a safe bet.
  51.  
  52. %example:
  53. %b = mushroom = drum
  54. %p = star = piano
  55. %l = airplane = guitar
  56. %e = flower = trumpet
  57. %f = game boy = blip blop sound
  58. instr={'p','p','b','l','b','f','p','p'};
  59.  
  60. %/INPUT END (you won't need to change anything below here)
  61. %--------------------------------------------------------------------------
  62.  
  63.  
  64.  
  65. %below the script extracts the name of the output file
  66. %the variable songname will be the name of the song
  67. q=find(infile=='\');
  68.  
  69. if numel(q)>0
  70.     songname=infile((q(end)+1):(end-4));
  71. else
  72.     songname=infile(1:(end-4));
  73. end
  74.  
  75.  
  76. %read MIDI file
  77. midi=readmidi(infile);
  78. N = midiInfo(midi,0);
  79. clc;
  80.  
  81. if not(strcmp(maxsec,'off'))
  82.     q=find(N(:,6)>maxsec,1,'first');
  83.     N=N(1:q,:);
  84. end
  85.  
  86. ns=size(N,1);
  87.  
  88. %get all channels
  89. C=unique(N(:,2));
  90.  
  91. if not(strcmp(wantncs,'all'))
  92.     %if wantncs is not 'all';
  93.     C=C(wantncs);
  94.    
  95.     q=false(ns,1);
  96.     for i=1:ns
  97.         if sum(C==N(i,2))>0
  98.             q(i)=true;
  99.         end
  100.     end
  101.     N=N(q,:);
  102. end
  103.  
  104. % error('stop');
  105.  
  106. ns=size(N,1);
  107.  
  108. if strcmp(bpm,'auto')
  109.     %automatic finding of bpm
  110.     %this algorithm seems to give a really high bpm for some songs.
  111.     %it works nice if the song has not arpeggios or anything like that and
  112.     %if the song has a clear beat time.
  113.    
  114.     %you can test this out, but you probably want to manually tweak this
  115.     %if you want your song to sound good
  116.    
  117.     %this algorithm basically computes the shortest time between any two
  118.     %consecutive notes and tests fractions of this as beat time and picks
  119.     %the best option
  120.    
  121.     r=10^6; %round to 4 decimals
  122.    
  123.     t1=N(2:end,5);
  124.     t2=N(1:(end-1),5);
  125.     tdiff=t1-t2;
  126.     tdiff=round(r*tdiff)/r;
  127.     tdiff=tdiff(tdiff>0);
  128.     % error('stop');
  129.     dt=min(tdiff); %beat time in seconds
  130.    
  131.    
  132.    
  133.     disp(['start beat time: ' num2str(60*dt)]);
  134.     k=1;
  135.     eps=10^-2;
  136.     bestval=Inf;
  137.     bestk=0;
  138.     for k=1:64
  139.         %     disp(['Trying k=' num2str(k)]);
  140.         dt0=dt/k;
  141.         u1=tdiff./dt0;
  142.         u2=round(u1);
  143.         u3=abs(u1-u2);
  144.         maxu3=max(u3);
  145.         %     disp(['max u3: ' num2str(maxu3)]);
  146.         if max(u3)<bestval
  147.             bestval=max(u3);
  148.             bestk=k;
  149.         end
  150.         %         break;
  151.         %     else
  152.         %         k=k+1;
  153.         %         if k>20
  154.         %             error('stop');
  155.         %         end
  156.     end
  157.     % disp(['Best k: ' num2str(bestk)]);
  158.     dt=dt./bestk;
  159.     % error('stop');
  160.    
  161.    
  162.     %find beat time...
  163.     % dt=1/k;
  164.     % if dt<10^-3
  165.     %     error('stop');
  166.     % end
  167.    
  168.    
  169.    
  170.     disp(['!!!!       Using k=' num2str(bestk)]);
  171.     disp(['!!!!       beat time in sec: ' num2str(dt)]);
  172.     disp(['!!!!       beat time in frames: ' num2str(60*dt)]);
  173.     disp(['!!!!       bpm: ' num2str(round(60/dt))]);
  174.     %beattime is number of frames between beats
  175.     %higher value = slower beat time
  176.     %this value can be converted to beats per minute by taking
  177.     %3600 and dividing it by beattime.
  178.     % beattime=2;
  179.    
  180.     % sec_per_beat=sec_per_beat;
  181.     %     1 track number
  182.     %     2 channel number
  183.     %     3 note number (midi encoding of pitch)
  184.     %     4 velocity
  185.     %     5 start time (seconds)
  186.     %     6 end time (seconds)
  187.     %     message number of note_on
  188.     %     message number of note_off
  189.    
  190.    
  191.     %clear command window if it's clottered by warning texts...
  192.    
  193.     disp(['Max note: ' num2str(note1)]);
  194.     disp(['Min note: ' num2str(note2)]);
  195.    
  196.     bpm=round(60/dt);
  197.     % bpm=round(60*60/beattime); %always this value for NES games, one per frame
  198.    
  199.  
  200.    
  201.     % bpm=60*tpqn;
  202.     %
  203.     % % bpm=bpm/10;
  204.     %
  205.    
  206.    
  207. else
  208.     dt=60/bpm;
  209. end
  210.  
  211. tvec=N(:,5); %vector with start times for each note
  212. F1=round(tvec/dt)+1; %beat assignment
  213. %fix octaves if necessary...
  214. nc=numel(C);
  215. for i=1:nc
  216.     q=(N(:,2)==C(i));
  217.     t=N(q,3);
  218.    
  219.     %     mindiff=min(t)-note2; %anything lower than 48 is not ok...
  220.     %     maxdiff=max(t)-note1; %anything above 72 is not ok...
  221.     %
  222.     disp(['Channel ' num2str(C(i)) ' has min ' num2str(min(t)) ' and max ' num2str(max(t))]);
  223.    
  224.     newmin=min(t)+12;
  225.     newmax=max(t)+12;
  226.    
  227.     if min(t)<note2 && newmin<=note1 && newmax<=note1
  228.         %         disp(['maxdiff: ' num2str(maxdiff)]);
  229.         %         disp(['mindiff: ' num2str(maxdiff)]);
  230.         disp(['raising channel ' num2str(C(i))]);
  231.         N(q,3)=N(q,3)+12;
  232.     end
  233.    
  234.     newmin=min(t)-12;
  235.     newmax=max(t)-12;
  236.    
  237.     if max(t)>note1 && newmin>=note2 && newmax>=note2
  238.         %         disp(['maxdiff: ' num2str(maxdiff)]);
  239.         %         disp(['mindiff: ' num2str(maxdiff)]);
  240.         disp(['lowering channel ' num2str(C(i))]);
  241.         N(q,3)=N(q,3)-12;
  242.     end
  243. end
  244.  
  245.  
  246. %number of channels
  247. % nc=numel(C);
  248.  
  249. %each row in F is one beat in MPC, each column is an instrument
  250. %the value of F is note number in MIDI terms
  251.  
  252. % F1=round(60*N(:,5)/beattime)+1; %the beat to place this on
  253.  
  254. %dim3: beat number
  255. %dim2: instrument of choise
  256. %dim3: what intrument plays this note
  257. %dim3: note number of choice -> each channel can play more than one note at
  258. %any time. maximum of 5, though
  259. numbeats=max(F1);
  260.  
  261. %F will contain the notes for the MPC file
  262. %each row is one beat
  263. %each column is one potential note on this beat (up to 5 notes on each
  264. %beat)
  265. %the first layer of the third dimension is the note number
  266. %the second layer is what instrument number plays this note
  267. F=NaN(numbeats,5,2);
  268.  
  269. Q=ones(numbeats,1); %what column we are on, on each beat
  270.  
  271.  
  272. %assign
  273. for i=1:ns
  274.     nowch=N(i,2);
  275.     nownote=N(i,3);
  276.    
  277.     q=find(C==nowch);
  278.     if numel(q)>0
  279.        
  280.         nownote=N(i,3);
  281.        
  282.         %find the beat to place this note on
  283.         f1=F1(i);
  284.         if Q(f1)<=5
  285.             F(f1,Q(f1),1)=nownote; %note
  286.             F(f1,Q(f1),2)=q; %instrument
  287.             Q(f1)=Q(f1)+1;
  288.             %             if Q(f1)>5
  289.             %                 disp(['Warning: more than 5 notes on beat ' num2str(i) '. Skipping any further notes on this beat. To solve, try decreasing "beattime".']);
  290.             %             end
  291.         end
  292.     end
  293. end
  294.  
  295. %prepare notes
  296.  
  297. L=cell(note1,1);
  298. L(note1)={'a'}; %C
  299. L(note1-1)={'b'}; %B
  300. L(note1-2)={'c#'}; %A#
  301. L(note1-3)={'c'}; %A
  302. L(note1-4)={'d#'}; %G#
  303. L(note1-5)={'d'}; %G
  304. L(note1-6)={'e#'}; %F#
  305. L(note1-7)={'e'}; %F
  306. L(note1-8)={'f'}; %E
  307.  
  308. L(note1-9)={'g#'}; %D#
  309. L(note1-10)={'g'}; %D
  310.  
  311. L(note1-11)={'h#'}; %C#
  312. L(note1-12)={'h'}; %C
  313.  
  314. L(note1-13)={'i'}; %B
  315.  
  316. L(note1-14)={'j#'}; %A#
  317. L(note1-15)={'j'}; %A
  318.  
  319. L(note1-16)={'k#'}; %G#
  320. L(note1-17)={'k'}; %G
  321.  
  322. L(note1-18)={'l#'}; %F#
  323. L(note1-19)={'l'}; %F
  324.  
  325. L(note1-20)={'m'}; %E
  326.  
  327. L(note1-21)={'n#'}; %D#
  328. L(note1-22)={'n'}; %D
  329.  
  330. L(note1-23)={'o#'}; %C#
  331. L(note1-24)={'o'}; %C
  332.  
  333. L(note1-25)={'p'}; %B
  334.  
  335.  
  336.  
  337. fidarr=fopen([MPCfold songname ']MarioPaint.txt'],'w');
  338.  
  339. %if necessary, add array to ArrList
  340. arrlist=[MPCfold 'MarioPaintArrList.txt'];
  341. fidnew=fopen(arrlist,'r');
  342. t=fgetl(fidnew);
  343. s0=strfind(t,songname);
  344. fclose(fidnew);
  345. if numel(s0)==0 %check if this song is already in array list
  346.     fid2=fopen(arrlist,'a');
  347.     fprintf(fid2,[songname ']MarioPaint*']);
  348.     fclose(fid2);
  349. end
  350.  
  351. %create array file and first output file
  352. arr_end=']MarioPaint';
  353.  
  354. nowfile_ind=1;
  355. if nowfile_ind<10 %if less than 10, add a 0 character in front
  356.     b='0';
  357. else
  358.     b='';
  359. end
  360. outfile=[MPCfold songname b num2str(nowfile_ind) arr_end '.txt'];
  361. fprintf(fidarr,[songname b num2str(nowfile_ind) '\r']);
  362. fid=fopen(outfile,'w');
  363. beatstr='4/4*';
  364.  
  365. fprintf(fid,beatstr);
  366.  
  367. totcols=0;
  368.  
  369. %dump to MPC file
  370. for i=1:numbeats %for all beats
  371.     k=0; %k counts number of notes played on this beat
  372.     for j=1:5 %for all 5 potential notes on this beat
  373.         v=F(i,j,1); %current note
  374.         w=F(i,j,2); %current instrument
  375.        
  376.         if v<note2 %if too low a note, increase its octave
  377.             v=v+12*ceil((note2-v)/12);
  378.         end
  379.        
  380.         if v>note1 %if too high a note, decrease its octave
  381.             v=v-12*ceil((v-note1)/12);
  382.         end
  383.        
  384.        
  385.         if isfinite(v) %if these is a note on this beat and on this column
  386.             now_note=char(L(v));
  387.             now_instr=char(instr(w));
  388.             fprintf(fid,[now_instr  now_note '+']); %print this note
  389.             k=k+1;
  390.         end
  391.     end
  392.    
  393.     %add additional + at end of beat
  394.     fprintf(fid,[repmat('+',1,6-k) 'q:']);
  395.     totcols=totcols+1;
  396.     if totcols==384
  397.         fprintf(fid,['"%%' num2str(bpm)]);
  398.         fclose(fid);
  399.         nowfile_ind=nowfile_ind+1;
  400.         if nowfile_ind<10
  401.             b='0';
  402.         else
  403.             b='';
  404.         end
  405.         outfile=['C:\Spel\mariopaintcomposerpc\Mario Paint Composer PC\Prefs\' songname b num2str(nowfile_ind) arr_end '.txt'];
  406.         fprintf(fidarr,[songname b num2str(nowfile_ind)  '\r']);
  407.         disp(['now on file ' outfile]);
  408.         fid=fopen(outfile,'w');
  409.         fprintf(fid,beatstr);
  410.         totcols=0;
  411.     end
  412. end
  413.  
  414. %add : characters so you get 384 of them. this seems necessary for some
  415. %reason
  416. fprintf(fid,repmat(':',1,384-totcols));
  417.  
  418. %print bpm at the end of the file
  419. fprintf(fid,['"%%' num2str(bpm)]);
  420. fclose(fid);
  421. fclose(fidarr);
  422. fclose all;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement