instruments part 1
This commit is contained in:
1 parent
f50dd86e75
commit
417362b87f
1 file changed
+118
-16
@@ -4,46 +4,134 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
void WriteWAVHeader(std::ofstream &file, DWORD length) {
|
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 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;
|
DWORD sampleRate = 44100;
|
||||||
WORD bitsPerSample = 16;
|
WORD bitsPerSample = 16;
|
||||||
WORD channels = 2;
|
|
||||||
|
// Calculate byte rate and block align
|
||||||
DWORD byteRate = sampleRate * channels * bitsPerSample / 8;
|
DWORD byteRate = sampleRate * channels * bitsPerSample / 8;
|
||||||
WORD blockAlign = 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);
|
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(reinterpret_cast<const char*>(&totalSize), 4);
|
||||||
file.write("WAVE", 4);
|
file.write("WAVE", 4);
|
||||||
|
|
||||||
// fmt chunk
|
// fmt chunk
|
||||||
file.write("fmt ", 4);
|
file.write("fmt ", 4); // Note the trailing space
|
||||||
file.write(reinterpret_cast<const char*>(&fmtChunkSize), 4);
|
file.write(reinterpret_cast<const char*>(&fmtChunkSize), 4); // Size of the fmt chunk (16 for PCM)
|
||||||
WORD formatTag = 1; // PCM
|
file.write(reinterpret_cast<const char*>(&formatTag), 2); // Audio format (1 for PCM)
|
||||||
file.write(reinterpret_cast<const char*>(&formatTag), 2);
|
file.write(reinterpret_cast<const char*>(&channels), 2); // Number of channels
|
||||||
file.write(reinterpret_cast<const char*>(&channels), 2);
|
file.write(reinterpret_cast<const char*>(&sampleRate), 4); // Sample rate
|
||||||
file.write(reinterpret_cast<const char*>(&sampleRate), 4);
|
file.write(reinterpret_cast<const char*>(&byteRate), 4); // Byte rate
|
||||||
file.write(reinterpret_cast<const char*>(&byteRate), 4);
|
file.write(reinterpret_cast<const char*>(&blockAlign), 2); // Block align
|
||||||
file.write(reinterpret_cast<const char*>(&blockAlign), 2);
|
file.write(reinterpret_cast<const char*>(&bitsPerSample), 2); // Bits per sample
|
||||||
file.write(reinterpret_cast<const char*>(&bitsPerSample), 2);
|
|
||||||
|
|
||||||
// data chunk
|
// data chunk
|
||||||
file.write("data", 4);
|
file.write("data", 4); // Note the trailing space is NOT present for "data"
|
||||||
file.write(reinterpret_cast<const char*>(&dataChunkSize), 4);
|
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) {
|
int main(int argc, char** argv) {
|
||||||
std::cout << "Program start\n";
|
std::cout << "Program start\n";
|
||||||
if (argc != 4) {
|
if (argc < 3 || argc > 4) {
|
||||||
std::cerr << "Wrong arguments!\n";
|
std::cerr << "Wrong arguments!\n";
|
||||||
return -1;
|
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
|
// 1) Initialize BASS: default device, 44100 Hz, no flags
|
||||||
if (!BASS_Init(-1, 44100, 0, NULL, NULL)) {
|
if (!BASS_Init(-1, 44100, 0, NULL, NULL)) {
|
||||||
std::cerr << "BASS_Init error: " << BASS_ErrorGetCode() << std::endl;
|
std::cerr << "BASS_Init error: " << BASS_ErrorGetCode() << std::endl;
|
||||||
@@ -83,6 +171,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
if (!synthToFile) {
|
if (!synthToFile) {
|
||||||
std::cout << "Playing live...\n";
|
std::cout << "Playing live...\n";
|
||||||
|
int loopCounter = 0;
|
||||||
if (!BASS_ChannelPlay(midiStream, FALSE)) {
|
if (!BASS_ChannelPlay(midiStream, FALSE)) {
|
||||||
std::cerr << "BASS_ChannelPlay error: " << BASS_ErrorGetCode() << std::endl;
|
std::cerr << "BASS_ChannelPlay error: " << BASS_ErrorGetCode() << std::endl;
|
||||||
BASS_StreamFree(midiStream);
|
BASS_StreamFree(midiStream);
|
||||||
@@ -90,9 +179,22 @@ int main(int argc, char** argv) {
|
|||||||
BASS_Free();
|
BASS_Free();
|
||||||
return 1;
|
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::cout << "Press Enter to stop...\n";
|
||||||
std::cin.get(); // wait for user input
|
std::cin.get(); // wait for user input
|
||||||
|
monitorThread.join();
|
||||||
} else {
|
} else {
|
||||||
std::ofstream wav("output.wav", std::ios::binary);
|
std::ofstream wav("output.wav", std::ios::binary);
|
||||||
if (!wav) {
|
if (!wav) {
|
||||||
|
|||||||
Reference in new issue
Block a user