-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
1,109 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
#include <cstdio> | ||
#include "Utils.hxx" | ||
#include "MIDI.hxx" | ||
using namespace NV; | ||
|
||
bool NVmidiFile::mid_open(const char *name) | ||
{ | ||
FILE *midifp = fopen(name, "rb"); | ||
u32_t tmp = 0, size = 0; | ||
|
||
if (!midifp) | ||
{ | ||
error("MIDI", "%s: 不存在或无法访问。 \n", name); | ||
return false; | ||
} | ||
|
||
fread(&tmp, 4, 1, midifp); | ||
|
||
if (tmp != 0x6468544Du) | ||
{ | ||
fclose(midifp); | ||
error("MIDI", "%s: 不是标准MIDI文件。 \n", name); | ||
return false; | ||
} | ||
|
||
fread(&size , 4, 1, midifp); | ||
fread(&type , 2, 1, midifp); | ||
fread(&tracks, 2, 1, midifp); | ||
fread(&ppnq , 2, 1, midifp); | ||
revU32(size ); revU16(type); | ||
revU16(tracks); revU16(ppnq); | ||
fseek(midifp, SEEK_SET, size + 8); | ||
|
||
trk_over = new bool [tracks]; | ||
trk_data = new nv_byte* [tracks]; | ||
trk_ptr = new nv_byte* [tracks]; | ||
grp_code = new nv_byte [tracks]; | ||
|
||
for (u16_t i = 0; i < tracks; ++i) | ||
{ | ||
fread(&tmp , 4, 1, midifp); | ||
fread(&size, 4, 1, midifp); | ||
|
||
if (tmp != 0x6B72544Du) | ||
{ | ||
tracks = i; mid_close(); | ||
fclose(midifp); | ||
error("MIDI", "异常数据!(track%hu)\n", i); | ||
return false; | ||
} | ||
|
||
revU32(size); tmp = 0; | ||
trk_over[i] = false; | ||
grp_code[i] = 0x0Fu; | ||
nv_byte *dat = new nv_byte [size]; | ||
fread(dat, size, 1, midifp); | ||
trk_data[i] = trk_ptr[i] = dat; | ||
} | ||
|
||
fclose(midifp); | ||
return true; | ||
} | ||
|
||
void NVmidiFile::mid_close() | ||
{ | ||
for (u16_t trk = 0; trk < tracks; ++trk) | ||
{ | ||
delete[] trk_data[trk]; | ||
} | ||
|
||
delete[] trk_data; delete[] trk_ptr; | ||
trk_data = nullptr; trk_ptr = nullptr; | ||
delete[] trk_over; delete[] grp_code; | ||
trk_over = nullptr; grp_code = nullptr; | ||
} | ||
|
||
static u32_t getVLi_U32(nv_byte **p) | ||
{ | ||
u32_t VLi = **p & 0x7Fu; | ||
|
||
while (*(*p)++ & 0x80u) | ||
{ | ||
(VLi <<= 7) |= **p & 0x7Fu; | ||
} | ||
|
||
return VLi; | ||
} | ||
|
||
bool NVmidiEvent::get(u16_t track, NVmidiFile &midi) | ||
{ | ||
if (midi.trk_over[track]) | ||
{ | ||
return false; | ||
} | ||
|
||
nv_byte code, **p = midi.trk_ptr + track; | ||
tick = getVLi_U32(p); | ||
|
||
if (**p & 0x80u) | ||
{ | ||
code = *(*p)++; | ||
midi.grp_code[track] = code; | ||
} | ||
else | ||
{ | ||
code = midi.grp_code[track]; | ||
} | ||
|
||
chan = code & 0x0Fu; | ||
|
||
switch (type = (NV_METYPE)(code & 0xF0u)) | ||
{ | ||
case (NV_METYPE::NOTEOFF): | ||
case (NV_METYPE::NOTEON): | ||
case (NV_METYPE::KEY_AT): | ||
case (NV_METYPE::CTROCH): | ||
|
||
num = *(*p)++; | ||
|
||
case (NV_METYPE::PROGCH): | ||
case (NV_METYPE::CHAN_AT): | ||
|
||
value = *(*p)++; | ||
break; | ||
|
||
case (NV_METYPE::PITCH): | ||
|
||
value = *(*p)++; | ||
value = value << 7 | *(*p)++; | ||
break; | ||
|
||
case (NV_METYPE::SYSCODE): | ||
|
||
if (code == 0xFFu) | ||
{ | ||
if ((num = *(*p)++) == 0x2Fu) | ||
{ | ||
midi.trk_over[track] = true; | ||
} | ||
|
||
type = NV_METYPE::OTHER; | ||
} | ||
else | ||
{ | ||
num = code & 0x0Fu; | ||
} | ||
|
||
datasz = getVLi_U32(p); data = *p; | ||
*p = *p + datasz; chan = (nv_byte)-1; | ||
break; | ||
|
||
default: | ||
|
||
warn("MIDI", "异常数据!(track=%hu) \n", track); | ||
return false; | ||
} | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// MIDI.hxx 2021-10-21 by 云中龙++ | ||
#ifndef __MIDI_H_ | ||
#define __MIDI_H_ | ||
|
||
enum class NV_METYPE // MIDI事件类型码 | ||
{ | ||
NOTEOFF = (nv_byte)0x80, // 音符停止发声 | ||
NOTEON = (nv_byte)0x90, // 音符开始发声 | ||
KEY_AT = (nv_byte)0xA0, // 触后音符(? | ||
CTROCH = (nv_byte)0xB0, // ControlChange | ||
PROGCH = (nv_byte)0xC0, // ProgramChange | ||
CHAN_AT = (nv_byte)0xD0, // 触后通道(? | ||
PITCH = (nv_byte)0xE0, // Pitchbend | ||
SYSCODE = (nv_byte)0xF0, // 系统码数据包 | ||
OTHER = (nv_byte)0xFF, // Meta-Event | ||
}; | ||
|
||
struct NVmidiFile // MIDI文件类 | ||
{ | ||
/* 类型、轨道数、分辨率 */ | ||
u16_t type, tracks, ppnq; | ||
|
||
bool *trk_over; // 轨道结束标志 | ||
nv_byte **trk_data; // 轨道数据 | ||
nv_byte **trk_ptr; // 读取位置 | ||
nv_byte *grp_code; // 事件组类型码 | ||
|
||
/* 打开MIDI文件 */ | ||
bool mid_open(const char *name); | ||
|
||
/* 关闭MIDI文件 */ | ||
void mid_close(); | ||
}; | ||
|
||
struct NVmidiEvent // MIDI事件类 | ||
{ | ||
u32_t tick, datasz; // tick与元数据长度 | ||
NV_METYPE type; // 事件类型码 | ||
nv_byte chan, num; // 通道号 编号 | ||
u16_t value; // 事件值 | ||
const nv_byte *data; // 元数据指针 | ||
|
||
/* 从指定文件的指定轨道中获取事件 */ | ||
bool get(u16_t track, NVmidiFile &midi); | ||
}; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
#include <cmath> | ||
#include "Utils.hxx" | ||
#include "MIDI.hxx" | ||
#include "Sequ.hxx" | ||
#include "Nlist.hxx" | ||
using namespace NV; | ||
using namespace std; | ||
|
||
NVnote::NVnote(double T, const NVseq_event &E): | ||
Tstart(T), Tend(114514191981.0), track(E.track), | ||
channel(E.chan), key(E.num), vel(E.value) { } | ||
|
||
NVnoteList::NVnoteList(const char *name): | ||
Tread(0.0), abstick(0), keys(nullptr) | ||
{ | ||
if ((err = !M.mid_open(name))) | ||
{ | ||
return; | ||
} | ||
|
||
print(name, "类型码: %4hu\n", M.type); | ||
print(name, "轨道数: %4hu\n", M.tracks); | ||
print(name, "分辨率: %4hu\n", M.ppnq); | ||
|
||
if ((err = M.type == 2)) | ||
{ | ||
error("Nlist", "%s: MIDI格式不支持!(type2)\n"); | ||
return; | ||
} | ||
|
||
S.seq_init(M); dT = 0.5 / M.ppnq; | ||
keys = new rmPR<decltype(keys)>::t [M.tracks]; | ||
} | ||
|
||
NVnoteList::~NVnoteList() | ||
{ | ||
delete[] keys; keys = nullptr; | ||
M.mid_close(); S.seq_destroy(); | ||
} | ||
|
||
void NVnoteList::update_to(double T) | ||
{ | ||
while (S.event().track < M.tracks && Tread < T) | ||
{ | ||
const NVseq_event &E = S.event(); | ||
|
||
if (E.abstick != abstick) | ||
{ | ||
Tread += dT * (E.abstick - abstick); | ||
abstick = E.abstick; | ||
} | ||
|
||
switch (E.type) | ||
{ | ||
case (NV_METYPE::OTHER): | ||
|
||
if (E.num == 0x51u) | ||
{ | ||
u32_t speed = *E.data; | ||
(speed <<= 8) |= *(E.data + 1); | ||
(speed <<= 8) |= *(E.data + 2); | ||
dT = 1e-6 * speed / M.ppnq; | ||
} | ||
|
||
break; | ||
|
||
case (NV_METYPE::NOTEON): | ||
|
||
if (E.value > 0) | ||
{ | ||
L[E.num].emplace_back(Tread, E); | ||
auto p = L[E.num].end(); | ||
keys[E.track][E.num].push(--p); | ||
break; | ||
} | ||
|
||
case (NV_METYPE::NOTEOFF): | ||
|
||
if (!keys[E.track][E.num].empty()) | ||
{ | ||
keys[E.track][E.num].top()->Tend = Tread; | ||
keys[E.track][E.num].pop(); | ||
} | ||
|
||
default: break; | ||
} | ||
|
||
S.seq_next(M); | ||
} | ||
} | ||
|
||
void NVnoteList::OR() | ||
{ | ||
for (int i = 0; i < 128; ++i) | ||
{ | ||
list<NVnote>::iterator p = L[i].end(); | ||
double T0 = 114514191981.0; | ||
double T1 = 114514191981.0; | ||
|
||
while (p-- != L[i].begin()) | ||
{ | ||
if (T0 < p->Tend && p->Tend < T1) | ||
{ | ||
p->Tend = T0, T0 = p->Tstart; | ||
|
||
if (p->Tend - p->Tstart < 1e-7) | ||
{ | ||
p = L[i].erase(p); | ||
} | ||
} | ||
else | ||
{ | ||
T0 = p->Tstart, T1 = p->Tend; | ||
} | ||
} | ||
} | ||
} | ||
|
||
void NVnoteList::remove_to(double T) | ||
{ | ||
for (int i = 0; i < 128; ++i) | ||
{ | ||
list<NVnote>::iterator p = L[i].begin(); | ||
|
||
while (p != L[i].end() && p->Tstart < T) | ||
{ | ||
p->Tend < T? (p = L[i].erase(p)) : ++p; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Nlist.hxx 2021-10-21 by 云中龙++ | ||
#ifndef __NLIST_H_ | ||
#define __NLIST_H_ | ||
|
||
#include <stack> | ||
#include <list> | ||
|
||
struct NVnote // 绘制用音符类 | ||
{ | ||
double Tstart, Tend; // 首尾时间 | ||
u16_t track; // 所属轨道 | ||
nv_byte channel, key, vel; // 音符属性 | ||
|
||
NVnote(double T, const NVseq_event &E); | ||
}; | ||
|
||
class NVnoteList // 音符队列 | ||
{ | ||
public: | ||
|
||
NVmidiFile M; // MIDI文件 | ||
bool err; // 错误标志 | ||
double Tread; // 当前读取位置(秒) | ||
std::list<NVnote> L[128]; // 音符列表 | ||
|
||
/* 打开一个MIDI文件 */ | ||
NVnoteList(const char *name); | ||
|
||
~NVnoteList(); | ||
|
||
/* 将第T秒前的音符放入列表 */ | ||
void update_to(double T); | ||
|
||
void OR(); // 懂BM的都懂,不懂的用不到 | ||
|
||
/* 移除列表中第T秒前的音符 */ | ||
void remove_to(double T); | ||
|
||
private: | ||
|
||
NVsequencer S; // 事件排列器 | ||
double dT; // 速率 | ||
u32_t abstick; // 当前读取位置(tick) | ||
std::stack<std::list<NVnote>::iterator> (*keys)[128]; | ||
}; | ||
|
||
#endif |
Oops, something went wrong.