#include "AudioEmitter.hpp" #include #include #include #include #include void ERRCHECK(FMOD_RESULT result) { if (result != FMOD_OK) { std::cout << "FMOD error: " << FMOD_ErrorString(result) << std::endl; exit(-1); } } auto fmodSoundDeleter = [](FMOD::Sound *sound) { if (sound) { sound->release(); } }; AudioEmitter::AudioEmitter() { FMOD::System *rawSystem = nullptr; ERRCHECK(FMOD::System_Create(&rawSystem)); system.reset(rawSystem); ERRCHECK(system->init(512, FMOD_INIT_NORMAL, nullptr)); std::vector rawChords(7); ERRCHECK(system->createSound("media/chords/variation1/C.mp3", FMOD_LOOP_OFF, nullptr, &rawChords[0])); ERRCHECK(system->createSound("media/chords/variation1/D.mp3", FMOD_LOOP_OFF, nullptr, &rawChords[1])); ERRCHECK(system->createSound("media/chords/variation1/E.mp3", FMOD_LOOP_OFF, nullptr, &rawChords[2])); ERRCHECK(system->createSound("media/chords/variation1/F.mp3", FMOD_LOOP_OFF, nullptr, &rawChords[3])); ERRCHECK(system->createSound("media/chords/variation1/G.mp3", FMOD_LOOP_OFF, nullptr, &rawChords[4])); ERRCHECK(system->createSound("media/chords/variation1/A.mp3", FMOD_LOOP_OFF, nullptr, &rawChords[5])); ERRCHECK(system->createSound("media/chords/variation1/B.mp3", FMOD_LOOP_OFF, nullptr, &rawChords[6])); std::vector rawChords2(7); ERRCHECK(system->createSound("media/chords/variation2/C.mp3", FMOD_LOOP_OFF, nullptr, &rawChords2[0])); ERRCHECK(system->createSound("media/chords/variation2/D.mp3", FMOD_LOOP_OFF, nullptr, &rawChords2[1])); ERRCHECK(system->createSound("media/chords/variation2/E.mp3", FMOD_LOOP_OFF, nullptr, &rawChords2[2])); ERRCHECK(system->createSound("media/chords/variation2/F.mp3", FMOD_LOOP_OFF, nullptr, &rawChords2[3])); ERRCHECK(system->createSound("media/chords/variation2/G.mp3", FMOD_LOOP_OFF, nullptr, &rawChords2[4])); ERRCHECK(system->createSound("media/chords/variation2/A.mp3", FMOD_LOOP_OFF, nullptr, &rawChords2[5])); ERRCHECK(system->createSound("media/chords/variation2/B.mp3", FMOD_LOOP_OFF, nullptr, &rawChords2[6])); for (int i = 0; i < 7; i += 1) { chords.push_back(std::unique_ptr(rawChords[i])); } for (int i = 0; i < 7; i += 1) { chords.push_back(std::unique_ptr(rawChords2[i])); } std::vector rawNotes(15); ERRCHECK(system->createSound("media/notes/A1.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[0])); ERRCHECK(system->createSound("media/notes/B1.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[1])); ERRCHECK(system->createSound("media/notes/C1.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[2])); ERRCHECK(system->createSound("media/notes/D1.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[3])); ERRCHECK(system->createSound("media/notes/E1.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[4])); ERRCHECK(system->createSound("media/notes/F1.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[5])); ERRCHECK(system->createSound("media/notes/G1.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[6])); ERRCHECK(system->createSound("media/notes/A2.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[7])); ERRCHECK(system->createSound("media/notes/B2.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[8])); ERRCHECK(system->createSound("media/notes/C2.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[9])); ERRCHECK(system->createSound("media/notes/D2.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[10])); ERRCHECK(system->createSound("media/notes/E2.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[11])); ERRCHECK(system->createSound("media/notes/F2.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[12])); ERRCHECK(system->createSound("media/notes/G2.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[13])); ERRCHECK(system->createSound("media/notes/A3.mp3", FMOD_LOOP_OFF, nullptr, &rawNotes[14])); for (int i = 0; i < 15; i += 1) { notes.push_back(std::unique_ptr(rawNotes[i])); } index_note = firstNote(); rythmes = { {0, 4}, {0, 2, 4, 6}, {0, 2, 3, 4, 6}, {0, 2, 4, 5, 6}, {0, 1, 2, 3, 4, 5, 6, 7}, {0, 0.5, 2, 2.5, 4, 4.5, 6, 6.5}, {0, 2, 2.5, 4, 6, 6.5}, {0, 1.5, 3, 4.5, 6}, {0, 1, 2.5, 4, 5.5, 7}, {0, 1.5, 3, 4.5, 6}, {0, 1, 3, 4.5, 6}, {0, 0.5, 1.5, 2, 3.5, 4, 5.5, 6}, {0, 1.5, 3, 4, 5.5, 7}, {0, 2, 3.5, 5, 6.5}, {0, 0.5, 2, 2.5, 4, 4.5, 6, 6.5}, {0, 1.5, 3, 4.5, 6, 7.5}, {0, 0.5, 1.5, 3, 3.5, 5, 6}, {0, 0.75, 1.5, 3, 4, 5.5, 7}, {0, 0.5, 1.5, 2.5, 4, 4.5, 6, 7}, {0, 0.5, 1.5, 2, 3.5, 4.5, 6, 7}, {0, 1.5, 3, 4, 5.5, 7}, {0, 1.5, 3, 3.5, 5, 6.5, 7.5}, {0.5, 1.5, 3, 4.5, 6.5, 7}, {0, 0.5, 1.5, 2.5, 3.5, 4.5, 6}, {0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7}, {0,1,1.5,2,2.5,3,3.5,4,5,6,7} }; markov_matrix_chords = {{0, 0.10, 0.00, 0.40, 0.35, 0.15, 0.00}, // Depuis ii (1) {0.10, 0, 0.00, 0.25, 0.65, 0.00, 0.00}, // Depuis iii (2) {0.00, 0.00, 0, 0.25, 0.15, 0.50, 0.10}, // Depuis IV (3) {0.45, 0.15, 0.00, 0, 0.25, 0.15, 0.00}, // Depuis V (4) {0.70, 0.00, 0.00, 0.10, 0, 0.20, 0.00}, // Depuis vi (5) {0.15, 0.15, 0.00, 0.45, 0.25, 0, 0.00}, // Depuis vii° (6) {0.80, 0.00, 0.00, 0.00, 0.20, 0.00, 0.00}}; markov_matrix_melody = {// A1 (0) {0.05, 0.30, 0.25, 0.15, 0.10, 0.05, 0.03, 0.02, 0.02, 0.01, 0.01, 0.00, 0.00, 0.00, 0.00}, // B1 (1) {0.20, 0.05, 0.30, 0.20, 0.10, 0.05, 0.03, 0.02, 0.02, 0.01, 0.01, 0.00, 0.00, 0.00, 0.00}, // C2 (2) {0.10, 0.20, 0.05, 0.30, 0.15, 0.10, 0.05, 0.02, 0.02, 0.01, 0.00, 0.00, 0.00, 0.00, 0.00}, // D2 (3) {0.05, 0.15, 0.20, 0.05, 0.30, 0.15, 0.05, 0.03, 0.02, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00}, // E2 (4) {0.03, 0.07, 0.15, 0.20, 0.05, 0.25, 0.15, 0.07, 0.03, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00}, // F2 (5) {0.02, 0.05, 0.10, 0.15, 0.20, 0.05, 0.25, 0.15, 0.05, 0.02, 0.01, 0.00, 0.00, 0.00, 0.00}, // G2 (6) {0.01, 0.03, 0.07, 0.10, 0.15, 0.20, 0.05, 0.25, 0.10, 0.03, 0.01, 0.00, 0.00, 0.00, 0.00}, // A2 (7) - Centre tonal {0.00, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.05, 0.15, 0.05, 0.02, 0.01, 0.00, 0.00, 0.00}, // B2 (8) {0.00, 0.00, 0.01, 0.03, 0.07, 0.15, 0.20, 0.25, 0.05, 0.20, 0.10, 0.05, 0.03, 0.01, 0.00}, // C3 (9) {0.00, 0.00, 0.00, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.05, 0.20, 0.15, 0.07, 0.03, 0.01}, // D3 (10) {0.00, 0.00, 0.00, 0.00, 0.03, 0.07, 0.10, 0.15, 0.20, 0.25, 0.05, 0.25, 0.15, 0.07, 0.03}, // E3 (11) {0.00, 0.00, 0.00, 0.00, 0.00, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.05, 0.20, 0.15, 0.08}, // F3 (12) {0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.03, 0.07, 0.10, 0.15, 0.20, 0.25, 0.05, 0.25, 0.15}, // G3 (13) {0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.02, 0.05, 0.10, 0.15, 0.20, 0.25, 0.05, 0.28}, // A3 (14) {0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.03, 0.07, 0.10, 0.15, 0.20, 0.25, 0.05}}; chordProgression.resize(4); int chord = firstChord(); chordProgression[0] = chord; for (int i = 1; i < 4; i += 1) { chord = nextChord(chord); chordProgression[i] = chord; } FMOD::Sound *metronome_Sound; ERRCHECK(system->createSound("media/percussions/drums1.wav", FMOD_DEFAULT, nullptr, &metronome_Sound)); ERRCHECK(system->playSound(metronome_Sound, nullptr, true, &timer)); ERRCHECK(timer->setVolume(0)); ERRCHECK(timer->setPaused(false)); } int AudioEmitter::firstChord() { std::vector possibleChords = {1, 5, 6}; return possibleChords[rand() % possibleChords.size()] - 1; } int AudioEmitter::firstNote() { std::vector possibleNotes = {4, 5, 6, 7, 8, 9, 10, 11}; return possibleNotes[rand() % possibleNotes.size()]; } int sampleIndex(const std::vector &probabilities) { // Créer un générateur aléatoire static std::random_device rd; static std::mt19937 gen(rd()); // Créer une distribution discrète avec les probabilités données std::discrete_distribution<> dist(probabilities.begin(), probabilities.end()); // Tirer un échantillon return dist(gen); } int randomWeightedChoice(const std::vector &values, const std::vector &weights) { if (values.size() != weights.size() || values.empty()) { throw std::invalid_argument( "Les tableaux doivent être de même taille et non vides."); } // Calcul de la somme totale des poids double totalWeight = std::accumulate(weights.begin(), weights.end(), 0.0); if (totalWeight <= 0) { throw std::invalid_argument("La somme des poids doit être positive."); } // Générateur aléatoire std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<> dis(0.0, totalWeight); double r = dis(gen); double cumulative = 0.0; for (size_t i = 0; i < values.size(); ++i) { cumulative += weights[i]; if (r < cumulative) { return values[i]; } } // Fallback - devrait normalement ne jamais être atteint return values.back(); } int AudioEmitter::nextChord(int currentChord) { return sampleIndex(markov_matrix_chords[currentChord]); } int AudioEmitter::nextNote(int currentNote) { return sampleIndex(markov_matrix_melody[currentNote]); } /** * generate music * * @return vector of the notes generated. first is when the note will be played * in seconds, second is the note */ std::vector> AudioEmitter::generateMusic() { std::vector> result; result.reserve(16 * nbr_melo_max); float beatDuration = tempo / 60.f; unsigned int sampleRate = 48000; int maxsize = 400; int variation = 0; if (((current_beat / nbr_melo_max) % 4) < 2) { //On change la manière dont sont joués les accords toutes les 2*nbr_melo_max mélodies générées variation = 0; } else { variation = 1; } int nbrChords = 7; if (activeChannels.size() > maxsize) { for (int i = 0; i < maxsize / 2; i += 1) { if (activeChannels[i]) { activeChannels[i]->stop(); } } } chordProgression[0] = nextChord(chordProgression[3]); for (int i = 1; i < 4; i += 1) { chordProgression[i] = nextChord(chordProgression[i - 1]); } for (int i = current_beat; i < current_beat + nbr_melo_max; i += 1) { // Chords FMOD::Channel *channelChords = nullptr; int index_chord = chordProgression[i % 4]; ERRCHECK(system->playSound(chords[index_chord + variation*nbrChords].get(), nullptr, true, &channelChords)); unsigned long long delay = (unsigned long long)(i * beatDuration * sampleRate); ERRCHECK(channelChords->setDelay(delay, 0, true)); ERRCHECK(channelChords->setPaused(false)); activeChannels.push_back(channelChords); // Mélodie if (i >= 4) { int index_rythme = floor(((i - 4) * 1.f / nbr_melo_total) * (rythmes.size() - 1)) + ( rand() % nbr_melo_max ); //Les rythmes deviennent de plus en plus complexe, plus on avance dans le temps, plus le rythme est tiré de la fin du vecteur index_rythme = (int)fmin(index_rythme, rythmes.size() - 1); std::vector rythme_melodie = rythmes[index_rythme]; for (float time : rythme_melodie) { FMOD::Channel* channelNote = nullptr; ERRCHECK(system->playSound(notes[index_note].get(), nullptr, true, &channelNote)); float note_start = (i + time / 8.f) * beatDuration; unsigned long long delayNote = (unsigned long long)(note_start * sampleRate); ERRCHECK(channelNote->setDelay(delayNote, 0, true)); ERRCHECK(channelNote->setPaused(false)); result.push_back(std::pair(note_start, index_note)); index_note = nextNote(index_note); activeChannels.push_back(channelNote); } } } current_beat += nbr_melo_max; return result; } void AudioEmitter::audioUpdate() { system->update(); } void AudioEmitter::audioEnd() { for (FMOD::Channel *c : activeChannels) { delete c; } timer->stop(); system->close(); system->release(); } float AudioEmitter::getTimeTempo() const { float beatDuration = tempo / 60.f / 8.f; unsigned long long dspClock = 0; ERRCHECK(timer->getDSPClock(&dspClock, nullptr)); return dspClock / 48000.f / beatDuration; } float AudioEmitter::getTime() const { unsigned long long dspClock = 0; ERRCHECK(timer->getDSPClock(&dspClock, nullptr)); return dspClock / 48000.f; }