如何制作midi文件?(100分)

  • 主题发起人 主题发起人 number1
  • 开始时间 开始时间
它用了自己定义的格式来保存的,播放的时候再用自己定义的格式来读取!
 
http://www.cfan.com.cn/qikan/cxg/0212dzq.zip
终于找到了
 
爱元哥哥,请看一看这个文件
 
>??是吧,至少按键盘,可以出声音吧
 
出声音和输出midi文件是两码事
 
可以出声,然后保存不是就可以了吗?
 
听说midi文件格式还有好多分类??
 
up,请大家在讨论一下吧
谁有midi格式介绍啊?
 
Delphi的我有啊
MIDI的格式也不复杂的.下面是一个播放资源文件里面的MIDI的部分代码
直接播放,不用保存为文件
procedure TForm1.Button1Click(Sender: TObject);
var
MyRes:TResourceStream;
MyStream:TMemoryStream;
P:DWORD;
dwHeaderSize:DWORD;
hdr:^MIDIFILEHDR;
m_dwFormat:DWORD;
m_dwTrackCount:DWORD;
m_dwTimeDivision:DWORD;
m_Tracks:array of TRACK;
i:DWORD;
begin


MyRes:=TResourceStream.Create(HInstance,'MyMidi','MIDI');
MyStream:=TMemoryStream.Create;
MyRes.SaveToStream(MyStream);
MyRes.Free;
MyStream.Position:=0;

MyStream.Read(P,Sizeof(DWORD));
if P<>MThd then

begin

Application.MessageBox('头格式不对!','',0);
Exit;
end;


MyStream.Read(P,Sizeof(DWORD));
dwHeaderSize:=DWORDSWAP(P);
if dwHeaderSize<>Sizeof(MIDIFILEHDR) then

begin

Application.MessageBox('头格式不对!','',0);
Showmessage(inttostr(dwHeaderSize));
Showmessage(inttostr(Sizeof(MIDIFILEHDR)));
Exit;
end;


MyStream.Read(P,Sizeof(DWORD));
new(hdr);
CopyMemory(hdr,@P,dwHeaderSize);
m_dwFormat:=WORDSWAP(hdr^.wFormat);
m_dwTrackCount:=WORDSWAP(hdr^.wTrackCount);
m_dwTimeDivision:=WORDSWAP(hdr^.wTimeDivision);
SetLength(m_Tracks,m_dwTrackCount);

MyStream.Seek(dwHeaderSize-Sizeof(DWORD),soFromCurrent);
MyStream.Read(P,Sizeof(DWORD));
for i:=0 to m_dwTrackCount-1do

begin

if P<>MTrk then

begin

Application.MessageBox('头格式不对!','',0);
Exit;
end;

MyStream.Read(P,Sizeof(DWORD));
m_Tracks.dwTrackLength := DWORDSWAP(P);
MyStream.Read(P,Sizeof(DWORD));
m_Tracks.pTrackCurrent := p;
m_Tracks.pTrackStart := m_Tracks.pTrackCurrent;
Showmessage(inttostr(m_Tracks.dwTrackLength));
MyStream.Seek(m_Tracks.dwTrackLength-Sizeof(DWORD),soFromCurrent);
MyStream.Read(P,Sizeof(DWORD));
// p += m_Tracks.dwTrackLength;

// Handle bozo MIDI files which contain empty track chunks
if( m_Tracks.dwTrackLength =0) then

begin

m_Tracks.fdwTrack := ITS_F_ENDOFTRK or m_Tracks.fdwTrack;
continue;
end;


// We always preread the time from each track so the mixer code can
// determine which track has the next event with a minimum of work
if(not GetTrackVDWord(@m_Tracks,@m_Tracks.tkNextEventDue))then

begin

Application.MessageBox('Error in MIDI data!','',0);
showmessage(inttostr(i));
//Exit;
end;

end;

end;


...
type
MIDIFILEHDR = record
wFormat: WORD;
// Format (hi-lo)
wTrackCount: WORD;
// # tracks (hi-lo)
wTimeDivision: WORD;
// Time division (hi-lo)
end;

type
PTRACK=^TRACK;
TRACK = record
fdwTrack: DWORD;
// Track's flags
dwTrackLength: DWORD;
// Total bytes in track
pTrackStart:DWORD;
// PBYTE;
// -> start of track data buffer
pTrackCurrent:DWORD;
// PBYTE;
// -> next byte to read in buffer
tkNextEventDue: DWORD;
// Absolute time of next event in track
byRunningStatus: BYTE;
// Running status from last channel msg
end;
 
那么如何可以自己制作midi文件呢?
 
老外的
unit cmpMidiData;

interface

uses
Windows, Messages, SysUtils, Classes, unitMidiGlobals, unitMidiTrackStream;

type
//---------------------------------------------------------------------------
// Type for 'FindEvent'
// feFirst = find first event at position
// feLast = find last event at position
// feAny = find any event at position

SwappedWord = word;

//---------------------------------------------------------------------------
// Midi Data component.
TMidiData = class(TComponent)
private
fTrackList : TList;
fFileName : string;
// File (full path) name.
fHeaderExtra : PChar;
// Extra bytes from midi file header
fHeaderExtraSize : LongInt;
fChanges : boolean;
// 'Data has changed' flag
fActive : boolean;
// True if the data is valid.

fHeader : packed record // Midi file header
format : SwappedWord;
nTracks : SwappedWord;
ppqn : SwappedWord;
end;


fRiffFlag : boolean;

// Helper functions for properties...

procedure SetActive (value : boolean);

function GetPPQN : Integer;
// Unscrambles it from the header
procedure SetPPQN (value : Integer);

function GetNoTracks : Integer;
// " " " " "

function GetFileType : Integer;
// Unscrambles it from the 'format' field of the header

function GetTrack (i : Integer) : TMidiTrackStream;
function GetShortFileName : string;
function GetChanges : boolean;
procedure ClearChanges;


protected
procedure ReadHeader (f : TStream);
procedure ReadTracks (f : TStream);
procedure WriteHeader (f : TStream);
procedure WriteTracks ( f : TStream);
procedure Close;
procedure Open;

public
constructor Create (AOwner : TComponent);
override;
destructor Destroy;
override;

procedure New;
procedure Save;

property HeaderExtra : PChar read fHeaderExtra;
property HeaderExtraSize : LongInt read fHeaderExtraSize;

procedure SetHeaderExtra (value : PChar;
size : LongInt);
procedure RemoveTrack (idx : Integer);
procedure EraseTrack (idx : Integer);
function AddNewTrack (i : Integer) : boolean;

procedure LoadFromStream (data : TStream);

property NoTracks : Integer read GetNoTracks;
property FileType : Integer read GetFileType;
property Changes : boolean read GetChanges;
property Tracks [index : Integer] : TMidiTrackStream read GetTrack;
property ShortFileName : string read GetShortFileName;

published
property PPQN : Integer read GetPPQN write SetPPQN;
property FileName : string read fFileName write fFileName;
property Active : boolean read fActive write SetActive;
end;


EMidiData = class (Exception);

implementation

uses cmpriffStream, mmsystem;

(*---------------------------------------------------------------------*
| constructor TMidiData.Create ();
|
| |
| Create a MidiData component. |
| |
| Parameters: |
| AOwner : TComponent // Component's owner |
*---------------------------------------------------------------------*)
constructor TMidiData.Create (AOwner : TComponent);
begin

inherited Create (AOwner);
fTrackList := TList.Create;
fHeader.ppqn := swap (180);
// Set default header record.
fHeader.format := swap (1);
end;


(*---------------------------------------------------------------------*
| destructor TMidiData.Destroy;
|
| |
| Delete the data first. |
*---------------------------------------------------------------------*)
destructor TMidiData.Destroy;
begin

Close;
fTrackList.Free;
inherited Destroy;
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.SetActive ();
| |
| Activate or deactivate the data. Load the file if it's being |
| activated. Destroy the data if it's being deactivated. |
| |
| Parameters: |
| value : boolean // New 'active' state. |
*---------------------------------------------------------------------*)
procedure TMidiData.SetActive (value : boolean);
begin

if value <> fActive then

begin

case value of
True :
if FileName = '' then

New // Initialise new data
else

Open;
// Load the data
False :
Close;
// Delete the data
end
end
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.SetHeaderExtra ();
|
| |
| Set extra data in MIDI header rec. |
| |
| Parameters: |
| value : PChar;
// The extra data |
| size : LongInt // Size of extra data. |
*---------------------------------------------------------------------*)
procedure TMidiData.SetHeaderExtra (value : PChar;
size : LongInt);
begin

if fHeaderExtra <> Nil then
// Free the old extra data
FreeMem (fHeaderExtra);

if (value <> Nil) and (size > 0) then

begin

fHeaderExtraSize := size;
// Save the data.
GetMem (fHeaderExtra, size);
Move (value^, fHeaderExtra^, size);
end
else

begin

fHeaderExtra := Nil;
// No data to save
fHeaderExtraSize := 0
end;

fChanges := True;
end;


(*---------------------------------------------------------------------*
| function TMidiData.GetPPQN : Integer;
|
| |
| Get resolution of data in PPQN (ticks per crochet) |
| |
| The function returns the resolution. |
*---------------------------------------------------------------------*)
function TMidiData.GetPPQN : Integer;
begin

result := Swap (fHeader.ppqn);
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.SetPPQN ();
|
| |
| Set the resolution of the data in PPQN (ticks per crochet) |
| |
| Parameters: |
| value : Integer // The new resoltuion |
*---------------------------------------------------------------------*)
procedure TMidiData.SetPPQN (value : Integer);
begin

if value <> Swap (fHeader.ppqn) then

begin

fHeader.ppqn := Swap (value);
fChanges := True
end
end;


(*---------------------------------------------------------------------*
| function TMidiData.GetNoTracks : Integer;
|
| |
| Get the number of tracks |
| |
| The function returns the number of tracks. |
*---------------------------------------------------------------------*)
function TMidiData.GetNoTracks : Integer;
begin

result := fTrackList.Count
end;


(*---------------------------------------------------------------------*
| function TMidiData.GetFileType : Integer;
|
| |
| Get the file type only file type 1 is supported. |
| |
| The function returns the file type. |
*---------------------------------------------------------------------*)
function TMidiData.GetFileType : Integer;
begin

result := Swap (fHeader.format);
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.Close;
|
| |
| Deletes the midi data. This can cause data logss. Check the |
| Changes property before calling... |
*---------------------------------------------------------------------*)
procedure TMidiData.Close;
var i : Integer;
begin

if fHeaderExtra <> Nil then
// Free the extra header info.
begin

FreeMem (fHeaderExtra);
fheaderExtra := Nil
end;


for i := 0 to fTrackList.Count - 1do

TObject (fTrackList ).Free;

fTrackList.Clear;
// Free the tracks.

// Re-initialise the MIDI header.
fHeader.ppqn := swap (180);
fHeader.format := swap (1);
fHeader.nTracks := swap (0);
ClearChanges;
// Clear the changes flag, and make the
fActive := False // data inactive.
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.Open;
|
| |
| Load the MIDI data. |
*---------------------------------------------------------------------*)
procedure TMidiData.Open;
var
f : TStream;
ext : string;
p : Integer;
begin

Close;
ext := UpperCase (ExtractFileExt (FileName));
fRiffFlag := (ext = '.RMI') or (ext = '.RIFF');
if fRiffFlag then

begin

f := TRiffFileStream.Create (FileName, fmOpenRead or fmShareDenyWrite);
with TRiffStream (f)do

begin

Descend ('RMID', MMIO_FINDRIFF);
Descend ('data', MMIO_FINDCHUNK);
end
end
else

f := TFileStream.Create (FileName, fmOpenRead or fmShareDenyWrite);
try
ReadHeader (f);
// Read the track header
try
ReadTracks (f);
fActive := True;

if fRiffFlag then
// Always convert to MIDI file for now...
begin

p := Pos ('.', FileName);
if p > 0 then
fFileName [p] := #0;
FileName := PChar (FileName);
FileName := FileName + '.MID'
end;



except // We may have allocated memory. Free it
Close;
// with Close
raise // Re-raise the exception
end
finally
f.Free;
// Get rid of the stream
ClearChanges
end
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.Save;
|
| |
| Save the MIDI data. |
*---------------------------------------------------------------------*)
procedure TMidiData.Save;
var f : TFileStream;
begin

f := TFileStream.Create (FileName, fmOpenWrite or fmShareExclusive or fmCreate);
try
WriteHeader (f);
// Write the header.
WriteTracks (f);
// Write the tracks.
ClearChanges
finally
f.Free;
end
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.New;
|
| |
| Initialise new data |
*---------------------------------------------------------------------*)
procedure TMidiData.New;
begin

Close;
// Delete existing data
FileName := '';
// Clear the file name
fActive := True
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.ReadHeader ();
|
| |
| Read the midi header from the stream. |
| |
| Parameters: |
| f : TStream // The stream to read from. |
*---------------------------------------------------------------------*)
procedure TMidiData.ReadHeader (f : TStream);
var
hdr : array [0..3] of char;
hSize : LongInt;
begin

f.ReadBuffer (hdr, sizeof (hdr));
// Read the MIDI file signature.
if StrLComp (hdr, 'MThd', 4) <> 0 then

raise EMidiData.Create ('Invalid MIDI file ID');

f.ReadBuffer (hSize, sizeof (hSize));
// Read the header size
hSize := SwapLong (hSize);
if hSize < sizeof (fHeader) then

raise EMidiData.Create ('Invalid MIDI header size');

// Read the MIDI header
f.ReadBuffer (fHeader, sizeof (fHeader));

if hSize > sizeof (fHeader) then

begin
// Read the extra headr bytes.
fHeaderExtraSize := hSize - sizeof (fHeader);
GetMem (fHeaderExtra, fHeaderExtraSize);
f.ReadBuffer (fHeaderExtra^, fHeaderExtraSize);
end
else

begin
// No extra header bytes.
fHeaderExtraSize := 0;
fHeaderExtra := Nil
end
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.WriteHeader ();
|
| |
| Write the MIDI header record. |
| |
| Parameters: |
| f : TStream // The stream to write to. |
*---------------------------------------------------------------------*)
procedure TMidiData.WriteHeader;
var
hSize : LongInt;
begin

f.WriteBuffer ('MThd', 4);
// Write MIDI ID
hSize := SwapLong (fHeaderExtraSize + sizeof (fHeader));
// Write the header size
f.WriteBuffer (hSize, sizeof (hSize));
// Write the header data
f.WriteBuffer (fHeader, sizeof (fHeader));
if fHeaderExtraSize > 0 then

// Write thea header extra data.
f.WriteBuffer (fHeaderExtra^, fHeaderExtraSize);
end;



(*---------------------------------------------------------------------*
| procedure TMidiData.ReadTracks ();
|
| |
| Read the tracks data. Parse it into our own internal format. |
| |
| Parameters: |
| f : TStream // The stream to read from. |
*---------------------------------------------------------------------*)
procedure TMidiData.ReadTracks (f : TStream);
var
nTracks, i : Integer;
track : TMidiTrackStream;
begin

nTracks := Swap (fHeader.nTracks);
for i := 0 to nTracks - 1do

begin

track := TMidiTrackStream.Create (100000);
// Reserve 1100000 bytes
try
track.LoadFromSMFStream (f);
fTrackList.Add (track);
except
track.Free;
raise
end
end
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.WriteTracks ();
|
| |
| Write data for all tracks |
| |
| Parameters: |
| f : TStream // The stream to write to |
*---------------------------------------------------------------------*)
procedure TMidiData.WriteTracks (f : TStream);
var
i : Integer;
begin

for i := 0 to NoTracks - 1do

Tracks .SaveToSMFStream (f);
end;


(*---------------------------------------------------------------------*
| function TMidiData.GetTrack () : TMidiTrack;
|
| |
| Get track 'n' |
| |
| Parameters: |
| i : Integer // The track to get |
| |
| The function returns the specified track |
*---------------------------------------------------------------------*)
function TMidiData.GetTrack (i : Integer) : TMidiTrackStream;
begin

if i < fTrackList.Count then

result := TMidiTrackStream (fTrackList )
else

result := Nil
end;


function TMidiData.AddNewTrack (i : Integer) : boolean;
var
Track : TMidiTrackStream;
begin

result := False;
if (i > 0) and (NoTracks = 0) then

begin

AddNewTrack (0);
result := True
end;


track := TMidiTrackStream.Create (100000);
track.Init;
fTrackList.Add (track);
fHeader.nTracks := Swap (NoTracks);
fChanges := True
end;


(*---------------------------------------------------------------------*
| function TMidiData.GetShortFileName : string;
|
| |
| Gets the short file name for display purposes. |
| |
| The function returns the short file name. |
*---------------------------------------------------------------------*)
function TMidiData.GetShortFileName : string;
begin

if FileName = '' then

result := '<Untitled>'
else

result := ExtractFileName (FileName)
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.RemoveTrack ();
|
| |
| Completely remove a specified track |
| |
| Parameters: |
| idx : Integer // The track to remove. |
*---------------------------------------------------------------------*)
procedure TMidiData.RemoveTrack (idx : Integer);
var track : TMidiTrackStream;
begin

track := Tracks [idx];
fChanges := True;
if idx < NoTracks - 1 then

begin

track.Free;
fTrackList [idx] := TMidiTrackStream.Create (100000);
track := Tracks [idx];
track.Init;
end
else

begin

if Assigned (track) then

begin

track.Free;
fTrackList.Delete (idx)
end;

fHeader.nTracks := Swap (NoTracks)
end
end;


(*---------------------------------------------------------------------*
| procedure TMidiData.EraseTrack ();
|
| |
| Erase all events from a track except for meta events. |
| |
| Parameters: |
| idx : Integer // The track to remove events from |
*---------------------------------------------------------------------*)
procedure TMidiData.EraseTrack (idx : Integer);
begin

if Assigned (Tracks [idx]) then

Tracks [idx].EraseNonMetaEvents
end;


function TMidiData.GetChanges : boolean;
var
i : Integer;
begin

result := fChanges;
if not result then

for i := 0 to NoTracks - 1do

if Tracks .Changes then

begin

result := True;
break
end
end;


procedure TMidiData.ClearChanges;
var
i : Integer;
begin

fChanges := False;
for i := 0 to NoTracks -1do

Tracks .Changes := False
end;


procedure TMidiData.LoadFromStream(data: TStream);
var
f : TRiffMemoryStream;
s : TMemoryStream;
id : array [0..4] of char;
begin

Active := False;
if data.Size > 4 then

begin

data.Read (id, 4);
id [4] := #0;
data.Seek (0, soFrombegin
ning);
if CompareText (id, 'RIFF') = 0 then

begin

f := Nil;
s := TMemoryStream.Create;
try
s.CopyFrom (data, 0);
f := TRiffMemoryStream.Create (s.Memory, s.Size);
f.Descend ('RMID', MMIO_FINDRIFF);
f.Descend ('data', MMIO_FINDCHUNK);
ReadHeader (f);
// Read the track header
try
ReadTracks (f);
fActive := True;
except
Close;
raise
end
finally
s.Free;
f.Free
end
end
else

begin

try
ReadHeader (data);
ReadTracks (data);
fActive := True
except
Close;
raise
end
end
end
end;


initialization
end.

 
unit unitMidiGlobals;

interface

const
midiNoteOff = $80;
midiNoteOn = $90;
midiKeyAftertouch = $a0;
midiController = $b0;
midiProgramChange = $c0;
midiChannelAftertouch = $d0;
midiPitchBend = $e0;
midiSysex = $f0;
midiSysexCont = $f7;
midiMeta = $ff;

midiStatusMask = $f0;
midiStatus = $80;
midiChannelMask = $0f;

metaSeqno = $00;
metaText = $01;
metaCopyright = $02;
metaTrackName = $03;
metaInstrumentName = $04;
metaLyric = $05;
metaMarker = $06;
metaCuePoint = $07;
metaMiscText0 = $08;
metaMiscText1 = $09;
metaMiscText2 = $0a;
metaMiscText3 = $0b;
metaMiscText4 = $0c;
metaMiscText5 = $0d;
metaMiscText6 = $0e;
metaMiscText7 = $0f;
metaTrackStart = $21;
metaTrackEnd = $2f;
metaTempoChange = $51;
metaSMPTE = $54;
metaTimeSig = $58;
metaKeySig = $59;
metaSequencer = $7f;



type
TTrack = 0..255;
TChannel = 0..15;
TNote = 0..127;
TController = 0..127;
TPatchNo = 0..127;
TBankNo = 0..127;
TControllerValue = 0..127;

//---------------------------------------------------------------------------
// Four byte MIDI message. (No running status, but Note off may be Note on
// with zero velocity )

TEventData = packed record // ** nb takes 5 bytes
case status : byte of
0 : (b2, b3 : byte);
1 : (sysex : PChar)
end;

PEventData = ^TEventData;

//---------------------------------------------------------------------------
// Midi event
PMidiEventData = ^TMidiEventData;
TMidiEventData = packed record // ** nb takes 11 bytes
pos : LongInt;
// Position in ticks from start of song.
sysexSize : word;
// Size of sysex or meta message
data : TEventData;
// Event data
OnOffEvent : PMidiEventData;
end;


TMidiEventClipboardHeader = packed record
noEvents, startPosn : Integer;
end;

PMidiEventClipboardHeader = ^TMidiEventClipboardHeader;

const
ControllerNames : array [TController] of string [20] = (
'', 'Mod. Wheel', 'Breath Controller', '',
'Foot Control', 'Portamento Time', 'Data Entry Slider', 'Volume',
'Balance', '', 'Pan', 'Expression',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'Sustain', 'Portamento', 'Sostenuto', 'Soft Pedal',
'General 4', 'Hold 2', '', '',
'', '', '', '',
'', '', '', '',
'General 5', 'General 6', 'General 7', 'General 8',
'', '', '', '',
'', '', '', '',
'Tremolo Depth', 'Chorus Depth', 'Detune', 'Phaser Depth',
'Data Entry +1', 'Data Entry -1', 'Non reg lsb', 'Non reg msb',
'Reg msb', 'Reg lsb', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', '', '', '',
'', 'Reset Controllers', 'Local Mode', 'All Notes Off',
'Omni Mode Off', 'Omni Mode On', 'Mono Mode On', '');

function AdjustForTimesig (n, beatDiv : Integer) : Integer;
function UnadjustForTimesig (n, beatDiv : Integer) : Integer;
function GetBPM (tempo, beatDiv : Integer) : Integer;
function SwapLong (value : LongInt) : LongInt;
function GetNoteName (note : Integer) : string;

implementation

uses sysutils;

(*---------------------------------------------------------------------*
| function SwapLong () : LongInt;
|
| |
| Byte swaps a four-byte integer eg: $01020304 becomes $04030201 |
| |
| Parameters: |
| value : LongInt The integer to swap |
| |
| The function returns the swapped integer |
*---------------------------------------------------------------------*)
function SwapLong (value : LongInt) : LongInt;
var
r : packed record case Integer of
1 : (a : byte;
b : word;
c : byte);
2 : (l : longint);
end;

t : byte;

begin

r.l := value;
r.b := swap (r.b);
t := r.a;
r.a := r.c;
r.c := t;
result := r.l;
end;



function AdjustForTimesig (n, beatDiv : Integer) : Integer;
begin

if BeatDiv > 2 then

result := n shr (BeatDiv - 2)
else

if BeatDiv < 2 then

result := n shl (2 - BeatDiv)
else

result := n;
end;




function UnAdjustForTimesig (n, beatDiv : Integer) : Integer;
begin

if BeatDiv > 2 then

result := n shl (BeatDiv - 2)
else

if BeatDiv < 2 then

result := n shr (2 - BeatDiv)
else

result := n;
end;


function GetBPM (tempo, beatDiv : Integer) : Integer;
begin

result := UnAdjustForTimesig (60000 div tempo, beatDiv);
end;


function GetNoteName (note : Integer) : string;
var
ch : char;
Octave : Integer;
begin

Octave := note div 12;
Note := Note mod 12;
case note of
0, 1 : ch := 'C';
2, 3 : ch := 'D';
4 : ch := 'E';
5, 6 : ch := 'F';
7, 8 : ch := 'G';
9, 10 : ch := 'A';
11 : ch := 'B';
else
ch := '?'
end;


if note in [1, 3, 6, 8, 10] then

result := ch + '#' + IntToStr (Octave)
else

result := ch + IntToStr (Octave);
end;


end.

 
用键盘模拟使发声,怎么做啊?
 
后退
顶部