#include #include #include #include "song.h" #include "cd.h" using namespace std; Cd::Cd () // default constructor - empty CD { // default constructor for list has already run for allSongs // we need do no more } // get contents of CD from user or file // input stream defaults to cin // This doesn't currently work with files because mygetline actually uses // getchar which doesn't handle files Cd::Cd (bool fromUser, istream & strIn) { string cdTitle; string cdArtist; char cont; if (fromUser) { clog << "Enter the CD title: " << flush; mygetline(strIn,cdTitle,'\n'); // echo for trace cout << "Enter the CD title: " << cdTitle << endl; clog << "Enter the CD artist ('Various' for Compilations): " << flush; mygetline(strIn,cdArtist,'\n'); // echo for trace cout << "Enter the CD artist: " << cdArtist << endl; } else { // getting from file, no need to ask mygetline(strIn,cdTitle,'\n'); mygetline(strIn,cdArtist,'\n'); } SetTitle(cdTitle); SetArtist(cdArtist); // loop as long as there are more songs do { // need to request info instead of just getting it string newTitle; string newArtist; int newMins; int newSecs; if (fromUser) { clog << "Enter the song title: " << flush; mygetline(strIn,newTitle,'\n'); // echo for trace cout << "Enter the song title: " << newTitle << endl; // avoid repetively asking user for artist for all songs on album if (cdArtist == "Various") { clog << "Enter the artist: " << flush; mygetline(strIn,newArtist,'\n'); // echo for trace cout << "Enter the artist: " << newArtist << endl; } else { newArtist = cdArtist; } clog << "Enter the song time in whole minutes " << flush; strIn >> newMins; // echo for trace cout << "Enter the song time in whole minutes " << newMins << endl; clog << "Now enter seconds " << flush; newSecs = GetValid(0,59,"Now enter seconds"); // uses cin - but that's // consistent with asking users //strIn >> newSecs; } else { // getting from file - no need to ask mygetline(strIn,newTitle,'\n'); mygetline(strIn,newArtist,'\n'); strIn >> newMins; strIn >> newSecs; } Song newSong(newTitle,newArtist,newMins,newSecs); AddSong(newSong); cont = askYN("are there more songs to add?"); } while (cont == 'y'); } Cd::Cd (const Cd & toCopy) { // copy constructor allSongs = toCopy.allSongs; // list assignment does the job } Song Cd::ReturnNthSong(int n) { list::iterator songItr = allSongs.begin(); // loop to bump up to position we want // dont go past it - user thinks of songs 1-> // so n acts like number of songs for (int cnt = 0; cnt < n - 1; cnt++ ) { songItr++; } return *songItr; } Song Cd::ReturnRandomSong() { int numSongs = allSongs.size(); unsigned int ran = rand(); int choice = (ran % numSongs) + 1; // count starting with 1 return ReturnNthSong(choice); } // play songs in order // wasn't always in cd class, was in main file // would have written it using iterator if it had been here // this way is grossly inefficient void Cd::PlayInOrder() { int numSongs = GetNumSongs(); int sum = 0; // char stop; for (int cnt = 0; cnt < numSongs; cnt++) { Song currSong = ReturnNthSong(cnt+1); cout << endl << "Playing song: " << cnt+1 << " " << currSong //<< "> " << flush; << "> " << endl; // cin >> stop; sum += currSong.GetLength(); } cout << "Played: " << sum / 60 << " minutes and " << sum % 60 << " seconds " << endl; return; } // play songs in random order // avoid songs repeating void Cd::PlayRandomOrder() { Cd songsToPlay(*this); // keep track of songs remaining to play starting with // all of them int numSongs = songsToPlay.GetNumSongs(); int sum = 0; while (numSongs > 0) { // char stop; Song currSong = songsToPlay.ReturnRandomSong(); cout << endl << "Playing song: " << " " << currSong << "> " << endl; sum += currSong.GetLength(); //allSongs.remove(currSong); songsToPlay.allSongs.remove(currSong); // remove song played from list numSongs = songsToPlay.GetNumSongs(); // get new count } cout << "Played: " << sum / 60 << " minutes and " << sum % 60 << " seconds " << endl; return; } /* void Cd::PlayRandomOrder() { int numSongs = GetNumSongs(); int sum = 0; // dont worry about songs repeating (first generation CD players) char stop; do { Song currSong = ReturnRandomSong(); cout << endl << "Playing song: " << " " << currSong << "> " << endl; sum += currSong.GetLength(); stop = askYN("Heard enough of this CD?"); } while (stop == 'n'); cout << "Played: " << sum / 60 << " minutes and " << sum % 60 << " seconds " << endl; return; } */ // play songs in order chosen by user (repeats allowed) void Cd::PlayUserOrder() { int numSongs = GetNumSongs(); int sum = 0; vector songsToPlay; char cont; char stop; // have user program the player cout << "you will program the CD player by specifying the song numbers to play in the order desired (repeats acceptable) " << endl; cout << *this; do { int next = GetValid(1,numSongs,"Enter Song Number"); songsToPlay.push_back(next); // add to songs to play cont = askYN("Do you want to play any more songs from this CD?"); } while (cont == 'y'); // play the songs int numToPlay = songsToPlay.size(); for (int cnt = 0; cnt < numToPlay; cnt++) { int toPlay = songsToPlay[cnt]; Song curr = ReturnNthSong(toPlay); cout << endl << "Playing song: " << toPlay << " " << curr << "> " << flush; sum += curr.GetLength(); cin >> stop; } cout << "Played: " << sum / 60 << " minutes and " << sum % 60 << " seconds " << endl; return; } int Cd::GetTotalTime() { // total time for all songs on the CD int sum = 0; // initialize total time to zero list::iterator songItr; // loop through all songs on CD adding length to total for (songItr = allSongs.begin(); songItr != allSongs.end(); songItr++ ) { int currSecs = songItr->GetLength(); sum += currSecs; } return sum; } void Cd::AddSong(const Song & newOne) { // used by constructor allSongs.push_back(newOne); return; } ostream & operator << (ostream & strOut, Cd & current) { strOut << endl << "CD: " << current.GetTitle() << " by " << current.GetArtist() << endl << " Songs: " << endl; // outside class so cannot just loop through allSongs int numSongs = current.GetNumSongs(); for (int cnt = 0; cnt < numSongs; cnt++ ){ strOut << " " << cnt+1 << " " << current.ReturnNthSong(cnt+1) << endl; } int totalSecs = current.GetTotalTime(); strOut << "Total Time: " << totalSecs / 60 << " minutes and " << totalSecs % 60 << " seconds " << endl; return strOut; } // gets a line of input up to toStop (skipping leading whitespace // toStop defaults to \n istream& mygetline(istream &strIn, string &newVal, char toStop) { char curr; newVal = ""; curr = getchar(); // skip leading "white space" - new line blank while ((curr == ' ') || (curr == '\n')) { curr = getchar(); } // now get real chars while (curr != toStop) { newVal += curr; curr = getchar(); } return strIn; } // ask yes no question - not allowing to continue until y/Y/n/N entered // returns a lower case y or n (always! nothing else) char askYN (const string & prompt) { char answ; // (loop until they give us a valid answer to the question) do { // ask for and get answer cout << prompt << " (y or n): " << flush; cin >> answ; // force all to lower case to simplify while test below if (answ == 'Y') answ = 'y'; if (answ == 'N') answ = 'n'; } while ((answ != 'n') && (answ != 'y')); return answ; } // ask for a number within a range // not allowing an exit until a valid number is entered // Given: // the lowest valid value // the highest valid value // the prompt to ask the user (a string) double GetValid(double low, double high, string prompt) { cout << prompt << " (between " << low << " and " << high << ") " << flush; double res; cin >> res; // keep on asking until they give a valid value while ((res < low) || (res > high)) { cout << "That number was invalid " << endl; cout << prompt << " (between " << low << " and " << high << ") " << flush; cin >> res; } return res; } // ask for a number within a range // not allowing an exit until a valid number is entered // Given: // the lowest valid value // the highest valid value // the prompt to ask the user (a string) int GetValid(int low, int high, string prompt) { cout << prompt << " (between " << low << " and " << high << ") " << flush; int res; cin >> res; // keep on asking until they give a valid value while ((res < low) || (res > high)) { cout << "That number was invalid " << endl; cout << prompt << " (between " << low << " and " << high << ") " << flush; cin >> res; } return res; }