Forum: PC-Programmierung Probleme bei schwieriger c++ Programmieraufgabe


von Kurt T. (kurtisblow)


Lesenswert?

Hallo, ich bin noch ein ziemlicher Neuling in Sachen programmieren und
muss nun, zumindest für mich, schwierige Programmieraufgabe lösen:

Es geht darum WAV Dateien in c++ zu manipulieren. Wir haben einen 
framework zur Verfügung gestellt bekommen, wo schon wesentliche 
Funktionen enthalten sind, müssen aber noch fertig erstellt werden.

-Die erste Aufgabe besteht darin, die "readWaveHeader" und 
printWaveheader"  Funktionen so zu implementieren, dass sie 
Informationen zu einem WAV-File nach folgendem Schema ausgeben:

Codec: PCM, 44100Hz, 16bitm 1 channel: Length 9.81 sec

-In Aufgabe zwei soll die "readWaveHeader" so umprogrammiert werden, 
dass sie eine Fehlermeldung ausgibt, wenn eine WAV-Datei nicht die 
Eigenschaften:
"Codec: PCM, 44100Hz, 16bit, 1 channel" erfüllt.

-Dann soll als drittens die "writeWaveHeader" so implementiert werden, 
dass sie ein Waveheader in einen Output-Stream schreibt.

-Dann ist noch angegeben, dass das Programm folgendes Unterstüzen 
sollte:
 1.) Wenn das Programm mit dem -totext Parameter aufgerufen wird, soll 
eine Textdatei (out.text) erzeugt werden, welche die einzelnen 
Samples(short) als ASCII Zahl auflistet.
 2.) Wenn das Programm mit dem -volume Parameter aufgerufen wird, soll 
das Programm die Lautstärke der Eingabedatei ändern. Ein float Parameter 
soll den Faktor angeben.
 3.) Wenn das Programm mit dem -mix Parameter aufgerufen wird, kann es 
zwei Audiodateien mischen.
 4.) Wenn das Programm mit dem -echo Parameter aufgerufen wird, soll ein 
Echoeffekt erzeugt werden. Der erste Parameter soll die Verzögerung, der 
zweite die Lautstärke des Echos angeben.

Hier ist das noch das vorgegebene Framework:

[cpp][code]

/********************************************************************
  purpose:  A simple PCM Wave File Editing TOOL

  created:  2010/11/08
  filename:   audio_tmpl.cpp
  author:

*********************************************************************/

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstring>
#include <limits.h>
#include <stdlib.h>

using namespace std;

struct WAVEHEADER {
  char  ChunkID[4];  //          Contains the letters "RIFF" in ASCII 
form
  int    ChunkSize;
  int    Format;
  char  Subchunk1ID[4];
  int    Subchunk1Size;
  short  AudioFormat;
  short  NumChannels;
  int    SampleRate;
  int    ByteRate;
  short  BlockAlign;
  short  BitsPerSample;
  char  Subchunk2ID[4];
  int    Subchunk2Size;
};

//---------------------------------------------------------
// helper functions
//---------------------------------------------------------

/*
* Gibt wave file header Informationen in der Console aus;
* \param hdr: wavefile hdr
*/
void printWaveHeaderInfos( WAVEHEADER *hdr) {
  if(NULL == hdr) {
    cerr << "Keine Waveheaderinformationen gefunden" << endl;
  }



  // outpout
  // codec, sampling rate, sampling precision, channels, length

  /* TODO */
}

/*
 * Liest den Waveheader und checkt ob das Format unterstuetzt wird;
 * \param fin: ifstream.
 * \return: NULL in case of error, waveheader else
 */
WAVEHEADER *readWaveHeader(ifstream *fin) {
  WAVEHEADER *hdr = NULL;

  //benutze die read Funktion um binaere Daten zu lesen
  hdr = new WAVEHEADER;

  fin.read((char*)&hdr, sizeof(WAVEHEADER))
  /* TODO */

  printWaveHeaderInfos(hdr);
  return hdr;
}

/*
 * Schreibt den Waveheader
 * \param fin: ifstream.
 * \param hdr: wavefile hdr
 * \return: false in case of an write error
 */
bool writeWaveHeader(ofstream *fout, WAVEHEADER *hdr) {

  if(NULL == hdr)
    return false;

  // save hdr into file

  /* TODO */

  return true;
}


//---------------------------------------------------------
// Commands
//---------------------------------------------------------

/*
 * Fuer den Commando -info
 * \param fin: ifstream.
 */
void waveFileInfo( char * filename ) {

  //oeffne die Datei filename als binaere Datei (fin)
  ifstream fin(filename, ios::in | ios::binary);

  if( !fin.is_open() ) {
    cerr << filename << " not found." << endl;
  } else {
    WAVEHEADER *hdr = readWaveHeader(&fin);
    delete hdr;
  }
}


void convertWaveFile( char infile[], char outfile[] )
{
  cout << "Trying to convert wavefile to text:" << endl;

  /* TODO */

  cout << "done" << endl;
}



void volumeWaveFile( char infile[], char outfile[], double gain )
{
  cout << "Changing Volume:" << endl;

  /* TODO */

  cout << "done" << endl;
}

void mixWaveFiles( char infile1[], char infile2[], char outfile[] )
{
  cout << "Mixing 2 audio files:" << endl;

  /* TODO */

  cout << "done" << endl;
}


void echoWaveFile( char infile[], char outfile[], double delay, double 
echo_gain )
{
  cout << "Adding echo:" << endl;

  /* TODO */

  cout << "done" << endl;
}

//---------------------------------------------------------
// main
//---------------------------------------------------------
int main(int argc, char *argv[])
{
  readWaveHeader();

  char *command = "-info";
  char **parms = &argv[1];
  int parms_count = argc - 1;

  if(argc > 1 && argv[1][0] == '-') {
    command = argv[1];
    parms = &argv[2];
    parms_count--;
  }



  // -info command standard case prints info of the given file
  if(!strcmp(command, "-info")) {
    if(parms_count >= 1) {
      waveFileInfo(parms[0]);
      return 0;
    } else {
      cerr << "Call " << endl;
      cerr << argv[0] << " [-info] wavefile" << endl;
      cerr << argv[0] << " -help : \tfor more help" << endl;
      return 1;
    }
  }

  // -help command
  if(!strcmp(command, "-help")) {
    cout << "Help:" << endl;
    cout << argv[0] << " -totext wavfile_in  textfile_out" << endl;
    cout << argv[0] << " -volume gain wavfile_in  wavfile_out" << endl;
    cout << argv[0] << " -mix    wavfile1_in wavfile2_in  wavfile_out" 
<< endl;
    cout << argv[0] << " -echo   delay strength wavfile_in wavfile_out" 
<< endl;

    return 0;
  }

  // -totext command
  if(!strcmp(command, "-totext")) {
    if(parms_count >= 2) {
      convertWaveFile( parms[0], parms[1]);
      return 0;
    } else {
      cerr << "Not enough parameters" << endl;
      return 1;
    }

  }

  // -vol command
  if(!strcmp(command, "-volume")) {
    if(parms_count >= 3) {
      volumeWaveFile( parms[1], parms[2], atof(parms[0]));
      return 0;
    } else {
      cerr << "Not enough parameters" << endl;
      return 1;
    }

  }

  // -mix command
  if(!strcmp(command, "-mix")) {
    if(parms_count >= 3) {
      mixWaveFiles( parms[0], parms[1], parms[2]);
      return 0;
    } else {
      cerr << "Not enough parameters" << endl;
      return 1;
    }

  }

  // -echo command
  if(!strcmp(command, "-echo")) {
    if(parms_count >= 4) {
      echoWaveFile( parms[2], parms[3], atof(parms[0]), atof(parms[1]));
      return 0;
    } else {
      cerr << "Not enough parameters" << endl;
      return 1;
    }

  }

  return 0;
}

[/cpp]

Die Funktion "readWaveHeader();" in der Main habe ich bereits eingefügt,
ebenfalls habe ich bereits die Zeile "fin.read((char*)&hdr, 
sizeof(WAVEHEADER))" in WAVEHEADER *readWaveHeader(ifstream *fin) {..}
eingeführt, jedoch kommt schon da die erste Fehlermedlung.
Ich hoffe mir kann jemand ein paar Tipps zu den einzelnen Aufgaben 
geben.

mfg Hans

von D. I. (Gast)


Lesenswert?

Hans Lüthi schrieb:
> Ich hoffe mir kann jemand ein paar Tipps zu den einzelnen Aufgaben
> geben.

Mach deine Hausaufgaben selbst?

von Udo S. (urschmitt)


Lesenswert?

Was hast du schon selbst gemacht und wo liegt ein konkretes Problem?

von Karl H. (kbuchegg)


Lesenswert?

Hans Lüthi schrieb:

> eingeführt, jedoch kommt schon da die erste Fehlermedlung.

Dann lies die Fehlermeldung. Meistens (nicht immer) ist die ein guter 
Hinweis darauf, wo du die Regeln der Sprache verletzt hast.
Schau dir den Code an und versuche die Fehlermeldung mit dem Code in 
Übereinstimmung zu bringen und dann korrigiere den Fehler.

> Ich hoffe mir kann jemand ein paar Tipps zu den einzelnen Aufgaben
> geben.

Fang bei der ersten Nummer an und arbeite dich sukzessive durch die 
Aufgabe durch.

Deine Aufgabe ist nicht schwer und wenn du in deinem Kurs aufgepasst 
hast, ist das ziemlich einfach zu lösen. Da werden Konstrukte verwendet, 
die ganz sicher nicht in der ersten Stunde Programmieren benutzt werden. 
Von daher kann man mit Sichereheit sagen: Wenn dich da bei einer Zeile 
einfügen eine Fehlermeldung soweit aus dem Konzept wirft, dass du noch 
nicht mal den Text der Fehlermeldung weißt bzw. dir bewusst ist, dass 
dieser Text wichtig ist, dann hast du in der Vergangenheit in deinem 
Kurs, tja, geschlafen oder Karten gespielt oder was weiß ich. Auf jeden 
Fall kannst du nicht viele Hausaufgaben selbst gemacht haben. Und das 
rächt sich jetzt.

von Kurt T. (kurtisblow)


Lesenswert?

So, ich glaube Aufgabe 1 habe ich soweit gelöst:

void printWaveHeaderInfos( WAVEHEADER *hdr) {
  if(NULL == hdr) {
    cerr << "Keine Waveheaderinformationen gefunden" << endl;
  }
  else {
    cout << "Codec: ";
    cout << "PCM, ";
    cout << hdr->SampleRate << " Hz, ";
    cout << hdr->BitsPerSample << "bit, ";
    cout << hdr->NumChannels << " channel, ";
    cout << "Length " << hdr->ByteRate << " sec" << endl;
  }

  // outpout
  // codec, sampling rate, sampling precision, channels, length

  /* TODO */
}

/*
 * Liest den Waveheader und checkt ob das Format unterstuetzt wird;
 * \param fin: ifstream.
 * \return: NULL in case of error, waveheader else
 */
WAVEHEADER *readWaveHeader(ifstream *fin) {
  WAVEHEADER *hdr = NULL;

  //benutze die read Funktion um binaere Daten zu lesen
  hdr = new WAVEHEADER;

  fin->read((char*)&hdr, sizeof(WAVEHEADER));
  /* TODO */

  printWaveHeaderInfos(hdr);
  return hdr;
}

Nun habe ich aber folgenden Fehler, aber erst beim Ausführen:

"a.exe": "C:\Users\Hans\Desktop\Aufgaben c++\a\Debug\a.exe" geladen, 
Symbole wurden geladen.
"a.exe": "C:\Windows\SysWOW64\ntdll.dll" geladen, Cannot find or open 
the PDB file
"a.exe": "C:\Windows\SysWOW64\kernel32.dll" geladen, Cannot find or open 
the PDB file
"a.exe": "C:\Windows\SysWOW64\KernelBase.dll" geladen, Cannot find or 
open the PDB file
"a.exe": "C:\Windows\SysWOW64\msvcp100d.dll" geladen, Symbole wurden 
geladen.
"a.exe": "C:\Windows\SysWOW64\msvcr100d.dll" geladen, Symbole wurden 
geladen.
Das Programm "[4196] a.exe: Systemeigen" wurde mit Code 1 (0x1) beendet.

Die WAV Daten habe ich aber im gleichen File wie die .exe oder die .cpp
Als Programmierumgebung habe ich VS2008

von Karl H. (kbuchegg)


Lesenswert?

Hans Lüthi schrieb:


> Nun habe ich aber folgenden Fehler, aber erst beim Ausführen:
>
> "a.exe": "C:\Users\Hans\Desktop\Aufgaben c++\a\Debug\a.exe" geladen,
> Symbole wurden geladen.
> "a.exe": "C:\Windows\SysWOW64\ntdll.dll" geladen, Cannot find or open
> the PDB file
> "a.exe": "C:\Windows\SysWOW64\kernel32.dll" geladen, Cannot find or open
> the PDB file
> "a.exe": "C:\Windows\SysWOW64\KernelBase.dll" geladen, Cannot find or
> open the PDB file
> "a.exe": "C:\Windows\SysWOW64\msvcp100d.dll" geladen, Symbole wurden
> geladen.
> "a.exe": "C:\Windows\SysWOW64\msvcr100d.dll" geladen, Symbole wurden
> geladen.
> Das Programm "[4196] a.exe: Systemeigen" wurde mit Code 1 (0x1) beendet.

Kannst du alle ignorieren.
Das sind Meldungen vom Debugger, dass er keine Debuginfo für die Teile 
deines Programmes gefunden hat, die eigentlich WIndows Funktionen sind. 
Du wirst daher nicht in Windows-Funktionen reinsteppen können, was du 
aber sowieso nicht vor hast.


>     cout << "Codec: ";
>    cout << "PCM, ";

Echt? Dein Codec ist immer PCM?

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

>>     cout << "Codec: ";
>>    cout << "PCM, ";
>
> Echt? Dein Codec ist immer PCM?


Schau mal hier rein
http://www.sonicspot.com/guide/wavefiles.html

Das was bei dir "AudioFormat" heißt, heißt in dieser Beschreibung 
"Compression Code" und da gibt es eine schöne Tabelle über mögliche 
Werte.

von Kurt T. (kurtisblow)


Lesenswert?

Ok, dass mit dem PCM muss ich noch ändern:

                if (hdr->AudioFormat == 1) {
      cout << "PCM, ";
    }
    else {
      cout << "This is not a PCM file! ";
    }

Ich bin mir unschlüssig wie ich die fin.open Argumente von der 
Mainfunktion
nach WAVEHEADER *readWaveHeader(ifstream *fin) {...} übergeben kann.
Ich habe es bisher soweit gebracht:

                ifstream fin;
          fin.open("audio1.wav");
          readWaveHeader();
          fin.close;

Ich weiss aber nicht was für ein Argument ich in die Klammern von 
"readWaveHeader()" tu soll.

von Karl H. (kbuchegg)


Lesenswert?

Hans Lüthi schrieb:


> Ich bin mir unschlüssig wie ich die fin.open Argumente von der
> Mainfunktion
> nach WAVEHEADER *readWaveHeader(ifstream *fin) {...} übergeben kann.

Die Funktion sagt es dir doch. Sie will einen Pointer auf ein ifstream 
Objekt.

> Ich habe es bisher soweit gebracht:
>
>                 ifstream fin;
>           fin.open("audio1.wav");
>           readWaveHeader();

dein ifstream Objekt heißt fin. Und readWaveHeader will einen Pointer 
darauf. Also:

            readWaveHeader( &fin );

von Kurt T. (kurtisblow)


Lesenswert?

Ok, danke
Kompilieren tut es ohne probleme, jedoch kommt ein Fehler:

Durch einen Pufferüberlauf in a.exe wurde der interne Programmzustand 
beschädigt. Klicken Sie auf "Unterbrechen", um das Programm zu debuggen, 
oder auf "Weiter", um es zu beenden.

Weitere Informationen finden Sie im Hilfethema "Gewusst wie: Debugging 
von Pufferüberlaufproblemen".

Meine Funktionen sehen nun so aus:

void printWaveHeaderInfos( WAVEHEADER *hdr) {
  if(NULL == hdr) {
    cerr << "Keine Waveheaderinformationen gefunden" << endl;
  }
  if(hdr->AudioFormat == 1 && hdr->SampleRate == 44100 && 
hdr->BitsPerSample == 16 && hdr->NumChannels ==1 )  {
    cout << "Codec: ";
    if (hdr->AudioFormat == 1) {
      cout << "PCM, ";
    }
    else {
      cout << "This is not a PCM file! ";
    }
    cout << hdr->SampleRate << " Hz, ";
    cout << hdr->BitsPerSample << "bit, ";
    cout << hdr->NumChannels << " channel, ";
    cout << "Length " << ((hdr->Subchunk2Size * 8.0) / 
(hdr->BitsPerSample) / (hdr->NumChannels) / (hdr->SampleRate)) << " sec" 
<< endl;
  } else {
    cout << "Keine Waveheaderinformationen gefunden";
  }


  // outpout
  // codec, sampling rate, sampling precision, channels, length

}




WAVEHEADER *readWaveHeader(ifstream *fin) {
  WAVEHEADER *hdr = NULL;

  //benutze die read Funktion um binaere Daten zu lesen
  hdr = new WAVEHEADER;

  fin->read((char*)&hdr, sizeof(WAVEHEADER));
  /* TODO */

  printWaveHeaderInfos(hdr);
  return hdr;
}




int main(int argc, char *argv[]){

  ifstream fin;
  fin.open("audio1.wav");
  readWaveHeader(&fin);
  fin.close();
}

von Dirk B. (garag)


Lesenswert?

Überleg mal was der new operator zurück gibt und danach schau dir 
nochmal genau die Zeile mit dem read() an.

Speicher der mit new alloziert wurde sollte auch irgendwann mit delete 
wieder freigegeben werden. Ansonsten kommt es zu häßlichen Memoryleaks.

von Karl H. (kbuchegg)


Lesenswert?

Willkommen in der Programmierung.

Jetzt machst du das erste mal Bekanntschaft mit deinem Debugger und 
seinen Möglichkeiten.

Du startest dein Programm jetzt nicht mehr mit F5, sondern mittels F10.

F10 führt genau einen Programmschritt aus. Danach kannst du dir zb 
Variableninhalte ansehen: einfach mit der Maus drauf zeigen.

F11 macht etwas ähnliches: es führt einen Programmschritt aus. Der 
Unterschied zu F10: F10 fasst einen Funktionsaufruf als einen Schritt 
auf und macht den Schritt über die Funktion hinweg. D.h. F10 meldet sich 
erst wieder, wenn die Funktion retourniert. F11 hingegen macht einen 
Programmschritt in die Funktion hinein, so dass du auch dort wieder in 
Einzelschritten dein Programm durchgehen kannst.

Und damit findest du erst mal heraus, WO (bei welcher Anweisung) das 
Problem entsteht.

von Kurt T. (kurtisblow)


Lesenswert?

So, habe es mir nochmal angeschaut und ein bisschen den Debugger 
angeschaut, komme aber leider mit dem Debbuger noch nicht zurecht, werde 
ich noch besser anschauen müssen.
Habe jetzt bei noch folgendes verändert:

WAVEHEADER *readWaveHeader(ifstream *fin) {
  WAVEHEADER *hdr = NULL;

  //benutze die read Funktion um binaere Daten zu lesen
  hdr = new WAVEHEADER;

  fin->read((char*)hdr, sizeof(WAVEHEADER));
  /* TODO */
  if (fin->eof() || fin->fail() || fin->bad()){
    WAVEHEADER *hdr = NULL;
  }

  printWaveHeaderInfos(hdr);
  return hdr;
  delete [] hdr;

Bei Read hdr habe ich den Adressoperator weggelassen, am Schluss delete 
ich nun noch die hdr.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Hans Lüthi schrieb:
> am Schluss delete
> ich nun noch die hdr.

Nö... du löscht die niemals da der Programmfluss dort nie ankommt, 
sollte dir der Compiler auch anmeckern...

Außerdem ist es extrem unklug den Speicher zu löschen wenn du ihn 
zurückgibst. Hier würde ich lieber einen Autopointer zurückgeben. 
Außerdem solltest du den Inputstream lieber als Referenz übergeben (wozu 
der Pointer?) oder wenigsten auf != 0 prüfen...

von Suri (Gast)


Lesenswert?

Habe auch bei Cellier Vorlesung, der TODO Teil den du noch nicht hast 
bei readWaveHeader sollte so aussehen:

if (hdr -> Subchunk1Size != 16 || hdr -> SampleRate != 44100 || hdr -> 
BitsPerSample != 16 || hdr -> NumChannels !=1)
  {
                    cout <<"Wrong format!"<< endl;
                    return NULL;
    }

dann else if (fin-> eof( etc. ; return NULL;}

und dann else
{printWaveHeaderInfos(hdr);
return hdr;
delete hdr;}

Die -totext Option sollte in etwa so aussehen:

void convertWaveFile( char infile[], char outfile[] )
{
  cout << "Trying to convert wavefile to text:" << endl;

    ifstream fin(infile, ios::in | ios::binary);
    ofstream fout(outfile);

    WAVEHEADER *hdr = readWaveHeader(&fin);

    int samples = hdr -> Subchunk2Size * 8 / hdr ->BitsPerSample / hdr 
-> NumChannels;

    for(int i = 0; i<samples ;i++)
    {
            short s;
            fin.read((char*)&s, sizeof(s));
            fout <<s<< endl;
}
  cout << "done" << endl;
  delete hdr;
}


dann kannst du das erstellte out.txt öffnen, diese Werte in eine 
Exceltabelle kopieren und als Diagramm anzeigen um zu kontrollieren obs 
stimmt...
Weiter bin ich selbst auch noch nicht, aber schau dir mal die 
Präsentationen der Assistenten an, hat z.T. gute Hilfe dabei:

http://www.inf.ethz.ch/personal/fcellier/Lect/InfI/Ex/Inf1_ex_praes.html

von D. I. (Gast)


Lesenswert?

Ist das das was man an der ETH Programmiereinstieg im 1. Semester nennt?

WENN ja, da muss ich persönlich sagen, dass C++ und die dürftigen 
Erklärungen in den Folien didaktisch nicht grad die Höhe sind... da 
stelle ich mir die Lernkurve schon steil vor, wenn man vorher noch 
keinen Dunst hatte.

von Udo S. (urschmitt)


Lesenswert?

Suri schrieb:
> Habe auch bei Cellier Vorlesung, der TODO Teil den du noch nicht hast
> bei readWaveHeader sollte so aussehen:

Prima, jetzt hat er endlich den Dummen gefunden der ihm die Hausaufgabe 
macht.
Wenn dann in der Klauser ne 5 rauskommt war natürlich der Prof schuld.

von Suri (Gast)


Lesenswert?

Ne 5 is in der Schweiz eine 2...
Im übrigen komme ich selbst auch nicht weiter und habe ihm geholfen, 
weil ich hoffe, dass er jetzt eine gute Idee hat und mir weiterhelfen 
kann.

Nur weil du hier im Internet anonym bist, sind solche Kommentare 
trotzdem unproduktiv und unerwünscht.

von Udo S. (urschmitt)


Lesenswert?

Suri schrieb:
> Nur weil du hier im Internet anonym bist, sind solche Kommentare
> trotzdem unproduktiv und unerwünscht.
1. Du bist anonym, ich nicht. ich bin nicht als Gast angemeldet.
2. gibt es hier ein von den meisten praktiziertes stilles Übereinkommen, 
daß man Hilfe zur Selbsthilfe gibt und nicht einfach Leuten die dreist 
hier ihre Hausaufgaben reinstellen diese macht.
3. Sieh dir an was er selbst gemacht hat. Ich glaube nicht daß du vom 
ihm irgendeinen Rücklauf bekommst, drücke dir trotzdem die Daumen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.