instruments part 1

This commit is contained in:
Maksymilian Wilczyński committed 2025-05-13 10:23:08 +02:00
1 parent f50dd86e75
commit 417362b87f
1 file changed
+118 -16
+118 -16
View File
@@ -4,46 +4,134 @@
#include <fstream>
#include <cstring>
#include <iomanip>
#include <thread>
#include <chrono>
void WriteWAVHeader(std::ofstream &file, DWORD length) {
// Ensure the file is open and valid before writing
if (!file.is_open()) {
// Handle error: file is not open
std::cerr << "Error: Output file is not open." << std::endl;
return;
}
DWORD dataChunkSize = length;
DWORD fmtChunkSize = 16;
DWORD fmtChunkSize = 16; // Size of the fmt chunk for PCM
// Audio format properties (PCM, Stereo, 44100 Hz, 16 bits per sample)
WORD formatTag = 1; // PCM = 1
WORD channels = 2;
DWORD sampleRate = 44100;
WORD bitsPerSample = 16;
WORD channels = 2;
// Calculate byte rate and block align
DWORD byteRate = sampleRate * channels * bitsPerSample / 8;
WORD blockAlign = channels * bitsPerSample / 8;
DWORD totalSize = 4 + (8 + fmtChunkSize) + (8 + dataChunkSize);
// Calculate the size of the data in the RIFF chunk following the initial 8 bytes.
// This is the size of the "WAVE" identifier (4 bytes) + the size of the "fmt " chunk
// (8 bytes header + fmtChunkSize) + the size of the "data" chunk (8 bytes header + dataChunkSize).
// Total size = 4 (for "WAVE") + (8 + fmtChunkSize) + (8 + dataChunkSize)
// Since fmtChunkSize is 16 for PCM:
// Total size = 4 + (8 + 16) + (8 + dataChunkSize)
// Total size = 4 + 24 + 8 + dataChunkSize
// Total size = 36 + dataChunkSize
DWORD totalSize = 36 + dataChunkSize;
// RIFF chunk
file.write("RIFF", 4);
// Write the total size of the file minus the first 8 bytes ("RIFF" and this size field)
file.write(reinterpret_cast<const char*>(&totalSize), 4);
file.write("WAVE", 4);
// fmt chunk
file.write("fmt ", 4);
file.write(reinterpret_cast<const char*>(&fmtChunkSize), 4);
WORD formatTag = 1; // PCM
file.write(reinterpret_cast<const char*>(&formatTag), 2);
file.write(reinterpret_cast<const char*>(&channels), 2);
file.write(reinterpret_cast<const char*>(&sampleRate), 4);
file.write(reinterpret_cast<const char*>(&byteRate), 4);
file.write(reinterpret_cast<const char*>(&blockAlign), 2);
file.write(reinterpret_cast<const char*>(&bitsPerSample), 2);
file.write("fmt ", 4); // Note the trailing space
file.write(reinterpret_cast<const char*>(&fmtChunkSize), 4); // Size of the fmt chunk (16 for PCM)
file.write(reinterpret_cast<const char*>(&formatTag), 2); // Audio format (1 for PCM)
file.write(reinterpret_cast<const char*>(&channels), 2); // Number of channels
file.write(reinterpret_cast<const char*>(&sampleRate), 4); // Sample rate
file.write(reinterpret_cast<const char*>(&byteRate), 4); // Byte rate
file.write(reinterpret_cast<const char*>(&blockAlign), 2); // Block align
file.write(reinterpret_cast<const char*>(&bitsPerSample), 2); // Bits per sample
// data chunk
file.write("data", 4);
file.write(reinterpret_cast<const char*>(&dataChunkSize), 4);
file.write("data", 4); // Note the trailing space is NOT present for "data"
file.write(reinterpret_cast<const char*>(&dataChunkSize), 4); // Size of the data chunk
// Note: For strict WAV compliance, if dataChunkSize is odd, a padding byte
// should be added here after the data chunk. This function only writes the header.
}
const char* gm_instruments[128] = {
"Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano",
"Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet",
"Celesta", "Glockenspiel", "Music Box", "Vibraphone",
"Marimba", "Xylophone", "Tubular Bells", "Dulcimer",
"Drawbar Organ", "Percussive Organ", "Rock Organ", "Church Organ",
"Reed Organ", "Accordion", "Harmonica", "Tango Accordion",
"Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", "Electric Guitar (clean)",
"Electric Guitar (muted)", "Overdriven Guitar", "Distortion Guitar", "Guitar Harmonics",
"Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass",
"Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2",
"Violin", "Viola", "Cello", "Contrabass",
"Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", "Timpani",
"String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2",
"Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit",
"Trumpet", "Trombone", "Tuba", "Muted Trumpet",
"French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2",
"Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax",
"Oboe", "English Horn", "Bassoon", "Clarinet",
"Piccolo", "Flute", "Recorder", "Pan Flute",
"Blown Bottle", "Shakuhachi", "Whistle", "Ocarina",
"Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)",
"Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass + lead)",
"Pad 1 (new age)", "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)",
"Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", "Pad 8 (sweep)",
"FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", "FX 4 (atmosphere)",
"FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)",
"Sitar", "Banjo", "Shamisen", "Koto",
"Kalimba", "Bagpipe", "Fiddle", "Shanai",
"Tinkle Bell", "Agogo", "Steel Drums", "Woodblock",
"Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal",
"Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet",
"Telephone Ring", "Helicopter", "Applause", "Gunshot"
};
void ShowActiveInstruments(HSTREAM stream) {
for (int ch = 0; ch < 16; ++ch) {
DWORD prog = BASS_MIDI_StreamGetEvent(stream, ch, MIDI_EVENT_PROGRAM);
DWORD bank = BASS_MIDI_StreamGetEvent(stream, ch, MIDI_EVENT_BANK);
if (prog == -1 || prog >= 128) continue;
const char* name = gm_instruments[prog];
std::cout << "> [Ch " << std::setw(2) << std::setfill(' ') << (ch + 1)
<< "] 🎶 " << name << " (Bank " << bank << ", Prog " << prog << ")\n";
}
}
void ClearLines(int count) {
for (int i = 0; i < count; ++i) {
std::cout << "\x1B[2K" << "\x1B[1A" << "\r"; // erase line + move up
}
std::cout << "\x1B[2K\r"; // erase current line
}
int main(int argc, char** argv) {
std::cout << "Program start\n";
if (argc != 4) {
if (argc < 3 || argc > 4) {
std::cerr << "Wrong arguments!\n";
return -1;
}
bool synthToFile = std::strcmp(argv[3], "true") == 0;
bool synthToFile = false;
if (argc == 4) {
if (std::strcmp(argv[3], "synth") == 0) {
synthToFile = true;
}
}
// 1) Initialize BASS: default device, 44100 Hz, no flags
if (!BASS_Init(-1, 44100, 0, NULL, NULL)) {
std::cerr << "BASS_Init error: " << BASS_ErrorGetCode() << std::endl;
@@ -83,6 +171,7 @@ int main(int argc, char** argv) {
if (!synthToFile) {
std::cout << "Playing live...\n";
int loopCounter = 0;
if (!BASS_ChannelPlay(midiStream, FALSE)) {
std::cerr << "BASS_ChannelPlay error: " << BASS_ErrorGetCode() << std::endl;
BASS_StreamFree(midiStream);
@@ -90,9 +179,22 @@ int main(int argc, char** argv) {
BASS_Free();
return 1;
}
std::thread monitorThread([&]() {
const int linesPerUpdate = 18; // 16 channels + header/footer
while (BASS_ChannelIsActive(midiStream) == BASS_ACTIVE_PLAYING) {
std::this_thread::sleep_for(std::chrono::seconds(1));
ClearLines(linesPerUpdate);
std::cout << "--- Active Instruments ---\n";
ShowActiveInstruments(midiStream);
std::cout << "--------------------------\n";
std::cout.flush();
}
});
std::cout << "Press Enter to stop...\n";
std::cin.get(); // wait for user input
monitorThread.join();
} else {
std::ofstream wav("output.wav", std::ios::binary);
if (!wav) {