How to convert MIDI file into audio file and preserve tempo?

8 vues (au cours des 30 derniers jours)
mark palmer
mark palmer le 26 Déc 2023
Modifié(e) : Voss le 14 Jan 2024
This is a bit of a musical question.
First, I generate a click track in MIDI at 1/8 = .1250 and save the data in a MAT file. I don't need the whole MIDI header etc. since I just need to use the data to create an audio file. Since I don't specify a tempo in BPM, I assume it is 120.
Next I attempt to convert the MIDI info into an audio file, using Sine waves as the clicks. When I do this, the track is not at the same tempo as the MIDI file. Why is this?
(Excuse my clumsy programming.)
Fs = 96000; % SAMPLE RATE OF AUDIO FILE
% CREATE SINE WAVE PULSE
sine1 = dsp.SineWave();
sine1.SamplesPerFrame = Fs;
sine1.Frequency = 5;
fSine = sine1();
% CREATE AN EMPTY 1-SEC WAVE FILE TO HOLD OUTPUT
zOUT = zeros(Fs*1,1); % NUMBER OF SECONDS
% LOAD MIDI FILE INFO AND PUT IT INTO SEPARATE ARRAYS
zF = load("Click Track TEST.mat"); % LOAD MIDI FILE INFO
cMALL = zF.cZ;
cMTRACK = cMALL(:,1);
cMPITCH = cMALL(:,3);
cMVEL = cMALL(:,4);
cMDb = cMALL(:,5); % BEGIN DURATION FOR EACH PULSE
cMDe = cMALL(:,6); % END DURATION
cMsiz = size(cMTRACK,1);
aa = 0;
zz = 0;
cc = 1;
for ii = 1:cMsiz
% MIDI DURATION CALCULATE
bD = cMDb(ii); % MIDI DURATION BEGIN
eD = cMDe(ii); % MIDI DURATION END
cMDz = eD - bD; % DURATION OF THE PULSE
% CONVERT MIDI DURATION TO AUDIO FRAME # DURATION
cMDFsz = round(cMDz * Fs); % CONVERT MIDI TO SAMPLE DURATION
zzend = cMDFsz;
% ALTERNATE BETWEEN SINE WAVE AND SILENCE TO HEAR PULSE
if mod(ii,2) == 1
cMP = fSine(1:cMDFsz);
else
cMP = 0;
end
zOUT(zz+1:zz+zzend,1) = cMP;
zz = zz + zzend;
end
% WRITE FILE AS WAVE AUDIO
fileOUT = ' Click Test 96K 2023.wav';
audiowrite(fileOUT,zOUT, Fs);

Réponses (2)

Binaya
Binaya le 14 Jan 2024
Hi Mark
I understand that you would like to know why the audio file generated from MIDI file is not playing at the same tempo as the original MIDI track.
The main reason of the tempo mismatch can be:
  1. BPM: If not specified, the assumed BPM of 120 may differ from the track's actual tempo. With MIDI clicks at 0.1250s and assuming there are two notes played for each click, the resulting tempo is 240 BPM.
  2. Sampling rate: The sampling rate used in the code can be different from the original sampling rate.
I hope this helps.
Regards
Binaya

Voss
Voss le 14 Jan 2024
Modifié(e) : Voss le 14 Jan 2024
zF = load('Click Track TEST.mat'); % LOAD MIDI FILE INFO
cMALL = zF.cZ;
disp(cMALL)
1.0000 1.0000 75.0000 96.0000 0 0.1250 1.0000 1.0000 60.0000 96.0000 0.1250 0.2500 1.0000 1.0000 60.0000 96.0000 0.2500 0.3750 1.0000 1.0000 60.0000 96.0000 0.3750 0.5000 1.0000 1.0000 75.0000 96.0000 0.5000 0.6250 1.0000 1.0000 60.0000 96.0000 0.6250 0.7500 1.0000 1.0000 60.0000 96.0000 0.7500 0.8750 1.0000 1.0000 60.0000 96.0000 0.8750 1.0000 1.0000 1.0000 75.0000 96.0000 1.0000 1.1250 1.0000 1.0000 60.0000 96.0000 1.1250 1.2500 1.0000 1.0000 60.0000 96.0000 1.2500 1.3750 1.0000 1.0000 60.0000 96.0000 1.3750 1.5000 1.0000 1.0000 75.0000 96.0000 1.5000 1.6250 1.0000 1.0000 60.0000 96.0000 1.6250 1.7500 1.0000 1.0000 60.0000 96.0000 1.7500 1.8750 1.0000 1.0000 60.0000 96.0000 1.8750 2.0000 1.0000 1.0000 75.0000 96.0000 2.0000 2.1250 1.0000 1.0000 60.0000 96.0000 2.1250 2.2500 1.0000 1.0000 60.0000 96.0000 2.2500 2.3750 1.0000 1.0000 60.0000 96.0000 2.3750 2.5000 1.0000 1.0000 75.0000 96.0000 2.5000 2.6250 1.0000 1.0000 60.0000 96.0000 2.6250 2.7500 1.0000 1.0000 60.0000 96.0000 2.7500 2.8750 1.0000 1.0000 60.0000 96.0000 2.8750 3.0000 1.0000 1.0000 75.0000 96.0000 3.0000 3.1250 1.0000 1.0000 60.0000 96.0000 3.1250 3.2500 1.0000 1.0000 60.0000 96.0000 3.2500 3.3750 1.0000 1.0000 60.0000 96.0000 3.3750 3.5000 1.0000 1.0000 75.0000 96.0000 3.5000 3.6250 1.0000 1.0000 60.0000 96.0000 3.6250 3.7500 1.0000 1.0000 60.0000 96.0000 3.7500 3.8750 1.0000 1.0000 60.0000 96.0000 3.8750 4.0000
Assuming 120 BPM (i.e., 1 beat every 0.5 seconds), then each 4 rows is one beat.
A guess: You shouldn't be turning on the sine wave every alternate row:
if mod(ii,2) == 1
cMP = fSine(1:cMDFsz);
else
cMP = 0;
end
but every fourth row, based on where the 75's are in the third column
if cMPITCH(ii) == 75
cMP = fSine(1:cMDFsz);
else
cMP = 0;
end
Here's your code, incorporating that change (and re-written in a more concise style), including a plot that shows you get one duration-0.125-second click each 0.5 seconds (= 8 clicks in 4 seconds = 120 BPM).
Fs = 96000; % SAMPLE RATE OF AUDIO FILE
% CREATE SINE WAVE PULSE
sine1 = dsp.SineWave();
sine1.SamplesPerFrame = Fs;
sine1.Frequency = 5;
fSine = sine1();
zF = load('Click Track TEST.mat');
zOUT = zeros(Fs*zF.cZ(end,6),1);
zz = 0;
for ii = 1:size(zF.cZ,1)
N = round((zF.cZ(ii,6) - zF.cZ(ii,5)) * Fs);
if zF.cZ(ii,3) == 75
zOUT(zz+1:zz+N,1) = fSine(1:N);
end
zz = zz + N;
end
% WRITE FILE AS WAVE AUDIO
fileOUT = ' Click Test 96K 2023.wav';
audiowrite(fileOUT,zOUT, Fs);
plot((1:zz)/Fs,zOUT)

Catégories

En savoir plus sur Simulation, Tuning, and Visualization dans Help Center et File Exchange

Produits


Version

R2022a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by