Introduction
Une pause a une durée limitée. Votre bibliothèque musicale contient des enregistrements complets avec une note de préférence, une durée en secondes et un identifiant de piste stable. Vous allez créer un programme C++ réutilisable qui examine les pistes dans un ordre annoncé, conserve chaque enregistrement qui tient dans le temps disponible et enregistre une véritable liste de lecture.
Vous devez connaître la navigation élémentaire dans un terminal ainsi que les chaînes, les classes, vector, les flux de fichiers et les exceptions en C++. Nous présenterons les références const, la fonction de comparaison et les chemins relatifs employés ici. L'analyseur, les compositions synthétiques originales et le lecteur du navigateur sont prêts. Vous écrirez les fonctions de sélection et d'exportation, pas un convertisseur audio ni un algorithme d'optimisation du remplissage.
Utilisez l'onglet Terminal pour les commandes et Player pour écouter. Tous deux utilisent le même environnement. Modifiez uniquement selection.cpp et export.cpp et créez vos listes directement dans outputs/. Conservez le catalogue, le code fourni et les originaux de outputs/audio/ sans modification. Ces compositions synthétiques originales n'utilisent aucun échantillon audio externe.
Faire tenir des pistes complètes dans une pause
Dans cette étape, vous écouterez un exemple normal avant d'utiliser un compteur de temps restant pour sélectionner des enregistrements complets. Commencez dans Player. Sélectionnez example-120.m3u8, cliquez sur Load playlist, puis Play 1. La liste contient Morning Light (50 secondes) et Window Seat (70 secondes). Les commandes audio permettent de mettre en pause ou de déplacer la position de lecture. Sélectionnez ensuite example-90.m3u8, chargez-la et écoutez son unique piste. La pause plus courte conserve le premier enregistrement entier ; elle ne découpe pas le second.
Passez à Terminal. Si des caractères parasites ou une commande inachevée apparaissent à l'invite, appuyez sur Ctrl+C pour annuler cette saisie. cd change de répertoire. Ce chemin absolu permet aussi de corriger une simple erreur de répertoire :
cd /home/labex/project/short-break
cat affiche un fichier texte. sample.tsv est un catalogue de deux pistes ; TSV désigne des valeurs séparées par des tabulations. Les colonnes sont l'identifiant, le titre, la durée en secondes entières, la préférence et le chemin audio. Les préférences élevées sont examinées en premier. Un identifiant désigne un enregistrement ; les titres peuvent se répéter.
cat sample.tsv
playlist.hpp déclare l'objet Track et les interfaces des deux fonctions. Chaque Track regroupe les cinq champs. Un vector<Track> stocke plusieurs objets dans un certain ordre. Lisez cette courte définition :
cat playlist.hpp
Compilez le programme initial. g++ compile le C++ ; -std=c++17 choisit C++17, -Wall -Wextra active des avertissements utiles et -o playlist nomme l'exécutable. Les trois fichiers .cpp sont compilés ensemble. L'avertissement concernant le budget inutilisé du programme initial est normal.
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist lance l'exécutable du répertoire courant. Ses arguments sont le catalogue, un budget en secondes entières compris entre 1 et 86400 inclus, puis le chemin de sortie. L'analyseur fourni applique la même limite aux durées et préférences du catalogue ; vous n'avez pas à l'implémenter. Lancez la version incomplète sur l'exemple de 90 secondes :
./playlist sample.tsv 90 outputs/sample-90.m3u8
Elle affiche les deux enregistrements et Total: 120s / 90s. C'est le défaut à corriger. Le fichier initial ne contient encore que des titres : le charger dans Player produit donc une erreur d'en-tête #EXTM3U absent. Cette erreur de format est attendue et se distingue d'un fichier audio manquant. L'étape 3 expliquera l'exportation.
Donnez maintenant à chooseTracks un budget restant. Le passage de tracks par valeur crée une copie locale ; le tri ultérieur ne modifiera pas le catalogue. const auto& track lit chaque objet existant sans le copier ni le modifier. push_back ajoute l'objet choisi au résultat. <= accepte une piste qui tient exactement. Ensuite, -= soustrait sa durée. Une piste trop longue est ignorée et la boucle continue.
Remplacez le petit fichier par la fonction complète ci-dessous. > remplace ce fichier source. Le here-document <<'CPP' transmet à cat toutes les lignes jusqu'au CPP final ; les guillemets du délimiteur préservent les caractères C++ tels quels. Collez tout le bloc, y compris le délimiteur final seul sur sa ligne.
cat > selection.cpp <<'CPP'
#include "playlist.hpp"
#include <algorithm>
std::vector<Track> chooseTracks(std::vector<Track> tracks, int budget) {
std::vector<Track> selected;
for (const auto& track : tracks) {
if (track.seconds <= budget) {
selected.push_back(track);
budget -= track.seconds;
}
}
return selected;
}
CPP
Recompilez après la modification, puis relancez le même exemple :
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist sample.tsv 90 outputs/sample-90.m3u8
Seuls t01 | Morning Light | 50s et Total: 50s / 90s apparaissent maintenant. Il reste quarante secondes : l'enregistrement de 70 secondes ne tient pas. Cette version suit l'ordre d'entrée. Vous allez ensuite rendre le choix indépendant de l'ordre des lignes du catalogue.
Si une erreur de compilation indique une ligne, rouvrez selection.cpp dans l'éditeur ou répétez le bloc de remplacement complet, puis recompilez. Exécuter un ancien binaire après un échec de compilation ne vérifie pas votre modification.
Appliquer un ordre de préférence déterministe
Dans cette étape, vous trierez toute la bibliothèque avant de choisir les pistes. Commencez dans Terminal, dans /home/labex/project/short-break. Examinez le catalogue complet :
cat catalog.tsv
Les lignes sont volontairement mélangées. Examinez les préférences de la plus élevée à la plus faible, puis les identifiants par ordre alphabétique croissant en cas d'égalité. Ainsi, t02 précède t03, même si sa durée est supérieure. Il s'agit de décisions de préférence simples, pas d'une recherche de la durée totale maximale.
Avec un budget de 90 secondes, t01 en utilise 50. Ni t02 (95) ni t03 (70) ne tiennent dans les 40 restantes. Continuez jusqu'à t04 (30), qui laisse 10 secondes. t05 (20) ne tient pas. Le résultat est t01, t04, pour un total de 80 secondes. S'arrêter à la première piste trop longue ferait perdre un choix ultérieur valide.
std::sort utilise l'intervalle semi-ouvert allant de begin() à end(). La comparaison renvoie true si a doit précéder b. [] introduit une petite fonction anonyme, appelée lambda ; ses deux paramètres const Track& lisent les objets comparés. Le test != choisit la comparaison principale et le dernier return départage les égalités par identifiant. Remplacez selection.cpp par la version complète :
cat > selection.cpp <<'CPP'
#include "playlist.hpp"
#include <algorithm>
std::vector<Track> chooseTracks(std::vector<Track> tracks, int budget) {
std::sort(tracks.begin(), tracks.end(),
[](const Track& a, const Track& b) {
if (a.preference != b.preference)
return a.preference > b.preference;
return a.id < b.id;
});
std::vector<Track> selected;
for (const auto& track : tracks) {
if (track.seconds <= budget) {
selected.push_back(track);
budget -= track.seconds;
}
}
return selected;
}
CPP
Recompilez et essayez les deux budgets :
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist catalog.tsv 90 outputs/break-90.m3u8
./playlist catalog.tsv 120 outputs/break-120.m3u8
Pour 90 secondes, le résultat est t01, t04, soit 80 secondes. Pour 120 secondes, c'est t01, t03, exactement 120 : t02 est ignorée, puis la piste de 70 secondes remplit les 70 restantes. Les deux choix conservent les enregistrements entiers. La sortie reste une liste de titres jusqu'à l'étape suivante.
Exporter une liste réellement lisible
Dans cette étape, vous enregistrerez les objets choisis en M3U étendu UTF-8. Commencez dans Terminal. Le fichier .m3u8 est une liste texte, pas le son lui-même. Sa première ligne est #EXTM3U. Pour chaque piste sélectionnée, une ligne de métadonnées #EXTINF:seconds,title est suivie du chemin audio.
Un chemin relatif part du répertoire de la liste, pas de celui du catalogue ni du répertoire courant du processus. Enregistrez les listes directement dans outputs/ ; les originaux se trouvent dans outputs/audio/. Une entrée telle que audio/Morning Light.wav atteint donc l'enregistrement situé à côté de cette liste. Conservez cette relation de dossiers lors du téléchargement ou du partage. Les espaces et Unicode sont pris en charge ; gardez les chemins fournis, sans saut de ligne, # initial, chemin absolu, remontée au répertoire parent ou directive supplémentaire.
std::ofstream ouvre le fichier de sortie. << écrit du texte ou des nombres ; \n écrit un saut de ligne. La fonction reçoit le vector sélectionné et le nom de sortie par références const, sans les copier ni les modifier. Un échec de fichier lance une exception signalée par le programme principal fourni. L'analyseur valide le budget entier positif, le catalogue et tous les fichiers audio référencés avant que cette fonction ouvre la sortie, préservant ainsi une sortie existante lors de ces erreurs d'entrée.
Remplacez export.cpp :
cat > export.cpp <<'CPP'
#include "playlist.hpp"
#include <fstream>
#include <stdexcept>
void writePlaylist(const std::vector<Track>& tracks, const std::string& output) {
std::ofstream file(output);
if (!file) throw std::runtime_error("Cannot write playlist.");
file << "#EXTM3U\n";
for (const auto& track : tracks) {
file << "#EXTINF:" << track.seconds << ',' << track.title << '\n';
file << track.path << '\n';
}
if (!file) throw std::runtime_error("Playlist write failed.");
}
CPP
Recompilez et recréez les deux listes avec l'exportateur mis à jour :
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist catalog.tsv 90 outputs/break-90.m3u8
./playlist catalog.tsv 120 outputs/break-120.m3u8
Examinez le fichier enregistré pour 90 secondes :
cat outputs/break-90.m3u8
Le fichier UTF-8 enregistré contient :
#EXTM3U
#EXTINF:50,Morning Light
audio/Morning Light.wav
#EXTINF:30,夜の散歩
audio/夜の散歩.wav
Le titre et le nom de fichier japonais restent du texte UTF-8. Certaines sessions de terminal affichent des caractères de remplacement alors que les octets sont intacts. Ne renommez pas les originaux pour corriger cet affichage ; Player montre le titre et le chemin d'origine. Les durées décrivent les fichiers entiers : modifier un nombre ici ne raccourcit pas le son.
Passez à Player. Cliquez sur Refresh files, sélectionnez break-90.m3u8, puis Load playlist. Deux pistes et 80 secondes déclarées doivent apparaître. Cliquez sur Play 1, déplacez la position et mettez en pause avec les contrôles audio, puis cliquez sur Play 2 pour écouter le second enregistrement. Le navigateur lit votre M3U8 enregistré et exactement les chemins qu'il contient. Une source absente produit Cannot play, sans piste de remplacement. Download playlist et Download current track téléchargent les vrais fichiers ; la liste seule ne contient pas l'audio.

Observez les deux lignes, leurs durées et les contrôles audio situés dessous. Après avoir vérifié la lecture, revenez à Terminal.
Réutiliser le programme et comprendre un refus
Dans cette étape, vous distinguerez un résultat court valide d'une entrée invalide. Commencez dans Terminal. Un budget positif inférieur à toutes les durées est valide : aucun enregistrement complet ne tient. Générez une liste pour une seconde :
./playlist catalog.tsv 1 outputs/tiny.m3u8
Le programme indique Total: 0s / 1s. Le fichier contient seulement #EXTM3U. Son chargement dans Player doit afficher zéro piste. C'est un résultat vide correct, pas un échec de connexion ou d'analyse.
Un budget comme 90s est invalide, car la CLI attend des chiffres exprimant des secondes entières. La commande suivante se termine volontairement avec le statut 1 et ne doit pas remplacer votre liste existante de 90 secondes. echo $? affiche immédiatement le statut de sortie de la commande précédente (0 signifie réussite). Exécutez ces deux commandes à la suite :
./playlist catalog.tsv 90s outputs/break-90.m3u8
echo $?
L'erreur précise que le budget et les durées doivent être des secondes entières positives, puis le statut 1 s'affiche. Ne réinitialisez pas l'environnement : retirez le suffixe et réessayez avec 90. Si vous faites une faute dans le chemin d'entrée, Cannot open catalog désigne l'argument du catalogue. Utilisez le cd absolu de l'étape 1 et le vrai nom catalog.tsv pour corriger. Missing audio signifie qu'un chemin du catalogue n'atteint aucun fichier ; conservez les originaux préparés et contrôlez le chemin avant de réessayer.
Confirmez que le fichier correct existant contient encore les deux paires de métadonnées et de chemins appropriées :
cat outputs/break-90.m3u8
Vous pouvez désormais utiliser le même programme avec différents budgets positifs valides, toujours en écrivant directement dans outputs/. Il trie par préférence et identifiant, ignore les candidats trop longs et ne découpe jamais un enregistrement. Le son sur des haut-parleurs physiques et le comportement de tous les lecteurs tiers sortent du cadre de cet exercice ; le navigateur fourni décode ces vrais fichiers WAV.
Résumé
Vous avez utilisé des objets C++ et un vector pour ordonner les enregistrements, choisir des pistes complètes dans un budget restant et écrire des fichiers M3U8 UTF-8. Vous avez écouté la véritable sortie, compris les chemins relatifs et distingué une liste vide valide d'une entrée invalide. Le défi suivant change la bibliothèque en conservant ces règles.



