#include #include //for class ofstream #include //for the sin function #include //for the data types uint32_t, etc. #include //for the strncpy function #include //for class numeric_limits #include //for the accumulate algorithm using namespace std; /* Creates stars.wav with a cinematic, spacey, Interstellar-inspired vibe. Original melody (not a transcription). */ int main() { const double half {pow(2.0, 1.0/12.0)}; //half step const double whole {half * half}; //1 whole step = 2 half steps // Equal-tempered pitches from middle C upward const double C {261.63}; const double D {C * whole}; const double E {D * whole}; const double F {E * half}; const double G {F * whole}; const double A {G * whole}; const double B {A * whole}; struct note { double pitch; //in Hz; use 0.0 for a rest double length; //in seconds }; // ===== THEME (ORIGINAL) ===== // A minor ostinato + lifts. Quarter-ish notes around ~0.45s each. // Progression: Am (A C E) -> F (F A C) -> C (C E G) -> G (G B D) // Then a lift using higher octave tones, and resolve back to Am. const double q {0.45}; // "quarter note" length in seconds const note a[] { // Am arpeggios (A, E, A, C, E, A) x2 {A, q}, {E, q}, {A, q}, {C, q}, {E, q}, {A, q}, {A, q}, {E, q}, {A, q}, {C, q}, {E, q}, {A, q}, // F arpeggios (F, C, F, A, C, F) {F, q}, {C, q}, {F, q}, {A, q}, {C, q}, {F, q}, {F, q}, {C, q}, {F, q}, {A, q}, {C, q}, {F, q}, // C arpeggios (C, G, C, E, G, C) {C, q}, {G, q}, {C, q}, {E, q}, {G, q}, {C, q}, {C, q}, {G, q}, {C, q}, {E, q}, {G, q}, {C, q}, // G arpeggios (G, D, G, B, D, G) {G, q}, {D, q}, {G, q}, {B, q}, {D, q}, {G, q}, {G, q}, {D, q}, {G, q}, {B, q}, {D, q}, {G, q}, // Lift: sequence climbing, touches upper octave {A, q}, {C, q}, {E, q}, {A*2, q}, {E, q}, {C, q}, {A, q}, {D, q}, {F, q}, {A*2, q}, {F, q}, {D, q}, // Brief breath (rests) {0.0, q}, {0.0, q}, // Final swell and resolve on Am {A, q}, {E, q}, {A, q}, {C, q}, {E, q}, {A*2, q*2}, // hold {E, q}, {C, q}, {A, q}, {0.0, q*2} }; const size_t n {size(a)}; //how many notes //total number of seconds for entire melody const double duration {accumulate(begin(a), end(a), 0.0, [](double sum, const note& no) {return sum + no.length;})}; const uint32_t sampleRate {210 * 210}; //44100 Hz // Allocate audio buffer const int numSamples {static_cast(sampleRate * duration)}; int16_t *const data {new int16_t[numSamples]}; int d {0}; const int16_t maxheight {numeric_limits::max()}; const int16_t minheight {numeric_limits::min()}; // Synthesis helpers: soft attack & harmonics for organ-like body auto softAttack = [](int s, int attackSamples) -> double { if (s >= attackSamples) return 1.0; return static_cast(s) / static_cast(attackSamples); }; // Generate notes for (int i {0}; i < static_cast(n); ++i) { const int samplesThisNote {static_cast(sampleRate * a[i].length)}; const double pitch {a[i].pitch}; for (int s {0}; s < samplesThisNote; ++s) { double sample {0.0}; if (pitch > 0.0) { // Fundamental + gentle 2nd/3rd harmonics (organ-ish) const double t {static_cast(s)/sampleRate}; const double x1 {pitch * 2.0 * M_PI * s / sampleRate}; const double x2 {2.0 * x1}; const double x3 {3.0 * x1}; double y = 1.00 * sin(x1) + 0.40 * sin(x2) + 0.20 * sin(x3); // Very light tremolo (~0.35 Hz) for breath const double tremolo {0.95 + 0.05 * sin(2.0 * M_PI * 0.35 * t)}; // Soft attack, gentle natural release (we’ll also fade tail) const double envA {softAttack(s, 350)}; // ~8ms attack at 44.1kHz const double gain {0.55}; // keep headroom sample = y * tremolo * envA * gain; } // Write dry sample (we'll apply fade tail + echo after) data[d] = static_cast(max(-1.0, min(1.0, sample)) * maxheight); ++d; } // Fade last chunk of each note for click-free release int lastSamples {500}; for (int t {lastSamples}; t >= 1; --t) { int idx {d - t}; if (idx >= 0 && idx < numSamples) { double factor {static_cast(t) / lastSamples}; int32_t v {data[idx]}; data[idx] = static_cast(v * factor); } } } // Simple echo to add space (post-process) // ~350ms delay, 40% feedback (single tap for simplicity) const int delaySamples {static_cast(0.35 * sampleRate)}; const double decay {0.40}; for (int i {delaySamples}; i < numSamples; ++i) { int32_t wet {static_cast(data[i]) + static_cast(data[i - delaySamples] * decay)}; if (wet > maxheight) wet = maxheight; if (wet < minheight) wet = minheight; data[i] = static_cast(wet); } // WAV header struct WAVHeader { char chunkId[4]; // "RIFF" uint32_t chunkSize; char format[4]; // "WAVE" char subchunk1Id[4]; // "fmt " uint32_t subchunk1Size; // 16 for PCM uint16_t audioFormat; // 1 = PCM uint16_t numChannels; // 1 mono uint32_t sampleRate; // 44100 uint32_t byteRate; // sampleRate * channels * bits/8 uint16_t blockAlign; // channels * bits/8 uint16_t bitsPerSample; // 16 char subchunk2Id[4]; // "data" uint32_t subchunk2Size; // numSamples * channels * bits/8 }; WAVHeader header { {'R','I','F','F'}, 0, {'W','A','V','E'}, {'f','m','t',' '}, 16, 1, 1, sampleRate, sampleRate * 1 * 16 / 8, 1 * 16 / 8, 16, {'d','a','t','a'}, 0 }; header.subchunk2Size = numSamples * header.numChannels * header.bitsPerSample / 8; header.chunkSize = 36 + header.subchunk2Size; ofstream file("stars.wav", ios::binary); if (!file) { cerr << "Couldn't open output file.\n"; delete[] data; return EXIT_FAILURE; } file.write(reinterpret_cast(&header), sizeof header); file.write(reinterpret_cast(data), header.subchunk2Size); file.close(); delete[] data; return EXIT_SUCCESS; }