Skip to content

Instantly share code, notes, and snippets.

@sfpgmr
Created March 23, 2023 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sfpgmr/50d6d7c6fed7ceac2330b1ac60237174 to your computer and use it in GitHub Desktop.
Save sfpgmr/50d6d7c6fed7ceac2330b1ac60237174 to your computer and use it in GitHub Desktop.
chatgptに作ってもらったピアノロールエディタのコード(動きません)
#include <imgui.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <vector>
struct NoteData {
int pitch;
float start;
float length;
};
class PianoRollEditor {
public:
PianoRollEditor() : gridDivision(16), gridSubdivision(4), noteBeingEdited(-1) {}
void Render() {
ImGui::Begin("Piano Roll Editor");
DrawGrid();
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
// 新しいノートを追加する
ImVec2 mousePos = ImGui::GetMousePos();
int pitch = (int)(127 - (mousePos.y - ImGui::GetCursorScreenPos().y) / gridYSize);
float start = (mousePos.x - ImGui::GetCursorScreenPos().x) / gridXSize;
noteBeingEdited = AddNote(pitch, start, 0.0f);
}
if (noteBeingEdited >= 0) {
// ノートを編集中の場合
bool noteDragged = ImGui::IsMouseDragging(ImGuiMouseButton_Left);
bool noteReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
if (noteDragged) {
// ノートをドラッグ中の場合
ImVec2 mousePos = ImGui::GetMousePos();
float start = (mousePos.x - ImGui::GetCursorScreenPos().x) / gridXSize;
float length = std::max < float>(start - notes[noteBeingEdited].start, 0.0f);
notes[noteBeingEdited].length = length;
}
else if (noteReleased) {
// ノートがリリースされた場合
ImVec2 mousePos = ImGui::GetMousePos();
float start = (mousePos.x - ImGui::GetCursorScreenPos().x) / gridXSize;
float length = std::max<float>(start - notes[noteBeingEdited].start, 0.0f);
notes[noteBeingEdited].length = length;
noteBeingEdited = -1;
}
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) {
// Deleteキーが押された場合
notes.erase(notes.begin() + noteBeingEdited);
noteBeingEdited = -1;
}
}
for (int i = 0; i < notes.size(); i++) {
// ノートを描画する
ImVec2 notePos = ImVec2(notes[i].start * gridXSize, (127 - notes[i].pitch) * gridYSize + ImGui::GetCursorScreenPos().y);
ImVec2 noteSize = ImVec2(notes[i].length * gridXSize, gridYSize);
ImU32 noteColor = (i == noteBeingEdited) ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 255, 255);
ImGui::GetWindowDrawList()->AddRectFilled(notePos, ImVec2(notePos.x + noteSize.x, notePos.y + noteSize.y), noteColor);
}
ImGui::End();
// MIDIファイルをエクスポートする
if (ImGui::Button("Export MIDI")) {
ExportMIDI("output.mid");
}
}
private:
int gridDivision;
int gridSubdivision;
std::vector<NoteData> notes;
int noteBeingEdited;
float gridXSize;
float gridYSize;
void DrawGrid() {
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 cursorPos = ImGui::GetCursorScreenPos();
ImVec2 windowPos = ImGui::GetWindowPos();
ImVec2 windowSize = ImGui::GetWindowSize();
float totalWidth = windowSize.x - 16.0f;
float totalHeight = windowSize.y - 16.0f;
// グリッドの大きさを計算する
gridXSize = totalWidth / (gridDivision * gridSubdivision);
gridYSize = totalHeight / 128.0f;
// グリッドを描画する
drawList->PushClipRect(ImVec2(windowPos.x + 8, windowPos.y + 8), ImVec2(windowPos.x + 8 + totalWidth, windowPos.y + 8 + totalHeight));
for (int i = 0; i <= gridDivision * gridSubdivision; i++) {
float x = i * gridXSize;
drawList->AddLine(ImVec2(cursorPos.x + x, cursorPos.y), ImVec2(cursorPos.x + x, cursorPos.y + totalHeight), IM_COL32(255, 255, 255, 255));
}
for (int i = 0; i <= 127; i++) {
float y = i * gridYSize;
drawList->AddLine(ImVec2(cursorPos.x, cursorPos.y + y), ImVec2(cursorPos.x + totalWidth, cursorPos.y + y), IM_COL32(255, 255, 255, 255));
}
drawList->PopClipRect();
}
int AddNote(int pitch, float start, float length) {
notes.push_back({ pitch, start, length });
return notes.size() - 1;
}
void ExportMIDI(const std::string& filename) {
std::ofstream file(filename, std::ios::binary);
if (!file.is_open()) {
std::cout << "Failed to open file for writing: " << filename << std::endl;
return;
}
// MIDIファイルヘッダーを書き込む
file.write("MThd", 4);
uint32_t headerLength = 6;
file.write((const char*)&headerLength, 4);
uint16_t format = 1;
file.write((const char*)&format, 2);
uint16_t numTracks = 1;
file.write((const char*)&numTracks, 2);
uint16_t timeDivision = 480;
file.write((const char*)&timeDivision, 2);
// MIDIトラックヘッダーを書き込む
file.write("MTrk", 4);
uint32_t trackLength = 0;
file.write((const char*)&trackLength, 4);
// MIDIイベントを書き込む
for (const NoteData& note : notes) {
// NoteData// 開始イベント
uint8_t statusByte = 0x90; // NoteData On
uint8_t pitch = note.pitch;
uint8_t velocity = 100;
uint8_t deltaTimeBytes[4];
uint32_t deltaTime = (uint32_t)(note.start * timeDivision);
EncodeDeltaTime(deltaTimeBytes, deltaTime);
file.write((const char*)deltaTimeBytes, 4);
file.write((const char*)&statusByte, 1);
file.write((const char*)&pitch, 1);
file.write((const char*)&velocity, 1);
// 終了イベント
statusByte = 0x80; // NoteData Off
deltaTime = (uint32_t)((note.start + note.length) * timeDivision) - deltaTime;
EncodeDeltaTime(deltaTimeBytes, deltaTime);
file.write((const char*)deltaTimeBytes, 4);
file.write((const char*)&statusByte, 1);
file.write((const char*)&pitch, 1);
file.write((const char*)&velocity, 1);
}
// MIDIトラックの長さを書き込む
trackLength = (uint32_t)file.tellp() - 8;
file.seekp(8);
file.write((const char*)&trackLength, 4);
std::cout << "MIDI file exported to " << filename << std::endl;
}
void EncodeDeltaTime(uint8_t* bytes, uint32_t deltaTime) {
int i = 3;
while (deltaTime > 0) {
bytes[i] = deltaTime & 0x7F;
deltaTime >>= 7;
if (i != 3) {
bytes[i] |= 0x80;
}
i--;
}
}
};
int main() {
PianoRollEditor editor;
editor.Run();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment