20071228

OpenSuSE 10.2: Πρόβλημα με Ελληνικά και τόνους;; Αφαίρεσε το SCIM.

Κατά καιρούς στο openSuSE 10.2 είχα πρόβλημα με του τόνους στα Ελληνικά. Το πρόβλημα αυτό δημιουργείται και από την εγκατάσταση του πακέτου SCIM -> Smart Chinese / Common Input Method platform. Μετά την αφαίρεση του πακέτου αυτού από το yast, τα ελληνικά + τόνοι λειτουργούν κανονικά.

20071227

OpenSuSE 10.2: Winodws Media support plug in firefox.

For Winodws Media support plug in firefox install mplayer and mplayerplug-in for firefox from http://packman.unixheads.com/suse/10.2.

20071204

OpenSuSE 10.2: mounting for write NTFS partiotion.

Για να μπορούμε να γράψουμε σε NTFS partiotion στο Open SuSE χρησιμοποιούμε τα πακέτα fuse, ntfs-3g από το http://download.opensuse.org/repositories/filesystems/openSUSE_10.2/
(όχι το ntfsprogs-fuse από το package repositories του yast!).

"the ntfs-fuse uses ntfsmount and is vastly inferior compared to ntfs-3g; there is a creation limit of 10 files per directory. If you want to use a ntfs partitions for more serious work, you should consider using a more appropriate ntfs driver such as ntfs-3g."

Απο το link που δίνεται παρακάτω.

Κατόπιν ρυθμίζουμε το /etc/fstab:

#Device    Mountpoint  Filesystem  Parameters
/dev/sda1 /windows/C ntfs-3g user,users,gid=users,umask=0002 0 0
Link: http://en.opensuse.org/NTFS

Unix: tar archiving / multi-volume archives / gzipped.

Έστω ότι θέλουμε να αποθηκεύσουμε σε ένα αρχείο tar όλα τα αρχεία που βρίσκονται στο /home/ggia μαζί με τα subdirs, files etc. Τρέχουμε:

tar cvf ggia_backup.tar /home/ggia

Παράμετροι που χρησιμοποιήσαμε είναι:

c - create (δημιουργία αρχείου tar)

v - verbose (να μας δείχνει ποια αρχεία επεξεργάζεται στην οθόνη)

f - filename (το όνομα του αρχείου tar που θα δημιουργήσουμε)

Αυτή η εντολή θα αποθηκεύση ΟΛΟΚΛΗΡΟ το path + subdirs.. Μπορούμε επίσης να προσθέσουμε το -p για να διατηρηθούν τα file permissions:

tar cpvf ggia_backup.tar /home/ggia

p - keep file permissions in tar

Μεταφέρουμε το αρχείο ydol_backup.tar σε ένα άλλο dir, π.χ. στο /tmp/ Με την εντολή

tar xf ggia_backup.tar

Σου ξαναβγάζει το directory με όλα τα αρχεία και subdirectories που έχεις βάλει μέσα στο tar (σχετικά όπως έδωσες στην αρχή.. πχ.. το /home/ggia θα δώσεις /tmp/home/ggia/..).

Αν θέλουμε μπορούμε το αρχείο να το συμπιέσουμε με το gzip ggia_backup.tar / αποσυμπιέζεται με το gunzip ggia_backup.tar.gz.

Αν θέλουμε το αρχείο να χωρέσει μέσα σε cd/dvd μπορούμε να δώσουμε εντολή στο tar να το χωρίσει σε ξεχωριστά αρχεία.

tar cvf ggia_backup.tar -L1000000 /home/ggia

L - ή αλλιώς --tape-length=<nr> (nr * 1024).

Για παράδειγμα το 1000000 * 1024 = 1 Gbyte περίππου.

Βέβαια ο παραπάνω τρόπος θέλει χειροκίνητα να δώσεις το όνομα του επόμενου αρχείου tar το οποίο γίνεται με την εντολή n ggia_backup1.tar κλπ κλπ.

Link: Archives Longer than One Tape or Disk

Στο παραπάνω link υπάρχει και script που μπορεί να περαστεί με την παράμετρο -F στο tar ώστε αυτόματα το tar να δημιουργήσει splitted tar archives.. αλλά δεν κατάφερα να το δουλέψω.

20071127

Newline terminator in Unix/Linux, Windows and Mac (+convertion methods).

Windows Newline: Carriage Return char(13) ή 0Dh + Line Feed char(10) ή 0Ah.
Unix Newline: Line Feed char(10) ή 0Ah.
Mac OS X Newline: Line Feed char(10) ή 0Ah.
Mac OS up to ver9: Carriage Return char(13) ή 0Dh.

Σε unix ή Windows με cygwin με χρήση sed/perl μπορούμε να μετατρέψουμε txt αρχείο:

sed -e 's/$/\r/' inputfile > outputfile # UNIX to DOS (adding CRs)
sed -e 's/\r$//' inputfile > outputfile # DOS to UNIX (removing CRs)
perl -p -e 's/(\r\n|\n|\r)/\r\n/g' inputfile > outputfile # Convert to DOS
perl -p -e 's/(\r\n|\n|\r)/\n/g' inputfile > outputfile # Convert to UNIX
perl -p -e 's/(\r\n|\n|\r)/\r/g' inputfile > outputfile # Convert to old Mac

Link: en.wikipedia article about Newline.

20071126

Convert a tab delimited file to CSV format using Regular Expressions in vi substitution.

Open the tab delimited file with vi.

a) At the end of each line delete all the whitespaces (one or more):

:1,$s/\s\+$//g

b) Substitute all the whitespaces (one or more) with comma ',' :

:1,$s/\s\+/,/g

Remove all "^M" characters from a file using vi.

To remove all "^M" characters from a file, open it in vi and use this command:
:%s/^M//g

The "^M" in the above line has to be typed in by pressing CTRL+v and then CTRL+M.

Also by :%s/\r\(\n\)/\1/g from here.

Convert all new lines to tab delimited using regex in vi.

1,$s/\n/\t/

For example file f.txt:

a1
a2
a3
..

will be converted to:

a1 a2 a3 ..

Post about Regular Expressions.

20071123

Regular expressions links/briefly.

Regular expressions in Perl.

Perl Regular Expression Quick Reference 1.05.

Perl Regular Expression Tutorial.

C++ Boost.Regex library for Perl Regular Expression Syntax.

Perl Regular Expressions briefly:

\ Quote the next metacharacter
^ Match the beginning of the line
. Match any character (except newline)
$ Match the end of the line (or before newline at the end)
| Alternation
() Grouping
[] Character class

* Match 0 or more times
+ Match 1 or more times
? Match 1 or 0 times
{n} Match exactly n times
{n,} Match at least n times
{n,m} Match at least n but not more than m times

* Match 0 or more times
+ Match 1 or more times
? Match 1 or 0 times
{n} Match exactly n times
{n,} Match at least n times
{n,m} Match at least n but not more than m times

*? Match 0 or more times
+? Match 1 or more times
?? Match 0 or 1 time
{n}? Match exactly n times
{n,}? Match at least n times
{n,m}? Match at least n but not more than m times

\t tab (HT, TAB)
\n newline (LF, NL)
\r return (CR)
\f form feed (FF)
\a alarm (bell) (BEL)
\e escape (think troff) (ESC)
\033 octal char (think of a PDP-11)
\x1B hex char
\c[ control char
\l lowercase next char (think vi)
\u uppercase next char (think vi)
\L lowercase till \E (think vi)
\U uppercase till \E (think vi)
\E end case modification (think vi)
\Q quote (disable) pattern metacharacters till \E

\w Match a "word" character (alphanumeric plus "_")
\W Match a non-word character
\s Match a whitespace character
\S Match a non-whitespace character
\d Match a digit character
\D Match a non-digit character

\b Match a word boundary
\B Match a non-(word boundary)
\A Match only at beginning of string
\Z Match only at end of string, or before newline at the end
\z Match only at end of string
\G Match only where previous m//g left off (works only with /g)

Decoding Regular Expressions

SymbolWhat It MeansExampleWhat It Might Find
+find 1 or more of preceding itemta+ta, taa, taaa
*find 0 or more of preceding itemba*b, ba, baa, baaa
?preceding item is optionalab?cabc, ac
.match any character (except return)1.21 2, 122, 1d2, 1$2
[^x]match any character except xus[^a]usb, usc, usd, use
[0-9]match any digit9021[0-9]90210 through 90219
^match start of line^hellomatches hello only at beginning of line
$match end of lineend$matches end only at end of line
( )group items together(ab)+ab, abab, ababab


20071120

Λεξικά για Firefox/Thunderbird, Open Office για Ελληνικά και Αγγλικά μαζί.

http://daremon.gr/tools.php

C++: Defining/initializing static member(s) within a class.

// file: obj.h
class obj {
public:
obj(void) { }; // ctor
~obj(void) { }; // dtor
private:
static int nr;
static bool initialized;
}

// file: obj.cpp
#include "obj.h"

// Waring: Static members need external definition!
int obj::nr;
// default value of static member:
bool obj::initialized=false;
Στον παραπάνω κώδικα βλέπουμε ένα παράδειγμα χρήσης στατικών μεταβλητών σε C++ class. Οι μεταβλητές nr, και initialized είναι ίδιες σε όλες τα στιγμιότυπα (instances) αντικειμένων από το class obj. Μάλιστα η μεταβλητή έχει αρχική μεταβλητή false, η οποία δεν ορίζεται μέσα στον ctor αλλά εξωτερικά.

20071116

C++: Example of class with ctor, increment operator (prefix and suffix) and ostream overloading.

// simple Int class with ctor, increment operator and ostream overload 
class Int{
public:
Int& operator++();
const Int operator++( int n );

Int(int i): _i(i) { };

friend ostream& operator <<(ostream& os, const Int &i);
private:
int _i;
};

// look difference between return values: Int& (in prefix)
Int& Int::operator++() {
_i++; // Handle case where no argument is passed.
return *this;
}

// and const Int (in postfix)
const Int Int::operator++( int n ) {
Int tmp(*this);

if( n != 0 )
for (int nr=0; nr<n; ++nr, ++(*this));
else
++(*this);

return tmp;
}

// overloading << operator for Int class
ostream& operator <<(ostream& os, const Int &i) {
os << i._i;
return os;
}

void main() {

Int gg(0);
cout << gg.operator++(25) << endl ; // Increment by 25 but shown 0
cout << gg << endl; // in next reference is shown as 25

// gg = 25
cout << gg++ << endl; // gg is 25. Only in the next reference will it be 26
cout << gg << endl; // gg is 26.
// gg = 26
cout << ++gg << endl; // ggia is immidiately 27
cout << gg << endl; // ggia is 27

return;
}
Link: Περισσότερα για την διαφορά prefix/suffix.

C++: difference between prefix and suffix increment / decrement.

  • As a prefix : i.e. when the operator precedes the variable (as in ++ i, where i is the integer variable)

If we write:

i1 = 10;
i2 = ++ i1;

This is the same as:

i1 = 10;
i1 = i1 + 1;
i2 = i1;

At the end of these steps the value of both i1 and i2 will be 11. When used as a prefix, the variable is first incremented and later assigned.

  • As a suffix: i.e. when the variable precedes the operator (as in i ++).

If we write:

i1 = 10;
i2 = i1 ++ ;

This is the same as writing:

i1 = 10;
i2 = i1;
i1 = i1 + 1;

At the end of these steps the value of i2 will be 10 and that of i1 will be 11. When used as a postfix, the variable value is first assigned and later incremented.

// suffix
int sum=5; // sum is 5
cout << sum++; // sum is 5. Only in the next reference to sum will it be 6
cout << " " << sum << endl; // sum is now 6

// prefix
sum=5; // sum is 5
cout<<++sum; // sum is immediately 6
cout<<" "<<sum; // sum is 6

Remember: When used as a prefix ( ++i ) compiler will first increment and then assign. When used as a suffix, assignment is done first and then incrementing is performed.

Link: C++ Operators - IV.

20071109

Visual Studio .NET 2005: Static library creation/usage.

Από το σύνδεσμο είναι οι παρακάτω πληροφορίες:

//---------------------------------------------------//

Walkthrough: Creating and Using a Static Library

In this walkthrough, you will create a static library (LIB) containing useful routines that can be used by other applications. Using static libraries is a great way to reuse code. Rather than re-implementing these routines in every program you create, you write them once and reference them from applications that need the functionality.

This walkthrough uses native C++. For a walkthrough using native C++ to create a dynamic link library (DLL), see Walkthrough: Creating and Using a Dynamic Link Library. For a walkthrough using Visual C++ that targets the Common Language Runtime, see Walkthrough: Creating and Using a Managed Assembly.

This walkthrough covers the following:

  • Creating a new static library project

  • Adding a class to the static library

  • Creating an application that references the static library

  • Using the functionality from the static library in the console application

  • Running the application

PrerequisitesPrerequisites

This topic assumes you understand the fundamentals of the C++ language.

To create a new static library project
  1. From the File menu, select New and then Project….

  2. From the Project types pane, under Visual C++, select Win32.

  3. From the Templates pane, select Win32 Console Application.

  4. Choose a name for the project, such as MathFuncsLib, and enter it in the Name field. Choose a name for the solution, such as StaticLibrary, and enter it in the Solution Name field.

  5. Press OK to start the Win32 application wizard. From the Overview page of the Win32 Application Wizard dialog, press Next.

  6. From the Application Settings page of the Win32 Application Wizard, under Application type, select Static library.

  7. From the Application Settings page of the Win32 Application Wizard, under Additional options, deselect Precompiled header.

  8. Press Finish to create the project.

To add a class to the static library
  1. To create a header file for a new class, from the Project menu, select Add New Item…. The Add New Item dialog will be displayed. From the Categories pane, under Visual C++, select Code. From the Templates pane, select Header File (.h). Choose a name for the header file, such as MathFuncsLib.h, and press Add. A blank file will be displayed.

  2. Add a simple class named MyMathFuncs to do common mathematical operations, such as addition, subtraction, multiplication, and division. The code should resemble the following:

    // MathFuncsLib.h

    namespace MathFuncs
    {
    class MyMathFuncs
    {
    public:
    // Returns a + b
    static double Add(double a, double b);

    // Returns a - b
    static double Subtract(double a, double b);

    // Returns a * b
    static double Multiply(double a, double b);

    // Returns a / b
    // Throws DivideByZeroException if b is 0
    static double Divide(double a, double b);
    };
    }

  3. To create a source file for a new class, from the Project menu, select Add New Item…. The Add New Item dialog will be displayed. From the Categories pane, under Visual C++, select Code. From the Templates pane, select C++ File (.cpp). Choose a name for the source file, such as MathFuncsLib.cpp, and press Add. A blank file will be displayed.


  4. Implement the functionality for MyMathFuncs in the source file. The code should resemble the following:

    // MathFuncsLib.cpp
    // compile with: /c /EHsc
    // post-build command: lib MathFuncsLib.obj

    #include "MathFuncsLib.h"

    #include <stdexcept>

    using namespace std;

    namespace MathFuncs
    {
    double MyMathFuncs::Add(double a, double b)
    {
    return a + b;
    }

    double MyMathFuncs::Subtract(double a, double b)
    {
    return a - b;
    }

    double MyMathFuncs::Multiply(double a, double b)
    {
    return a * b;
    }

    double MyMathFuncs::Divide(double a, double b)
    {
    if (b == 0)
    {
    throw new invalid_argument("b cannot be zero!");
    }

    return a / b;
    }
    }

  5. To build the project into a static library, from the Project menu, select MathFuncsLib Properties…. From the left pane, under Configuration Properties, select General. From the right pane, change the Configuration Type to Static Library (.lib). Press OK to save the changes.

    NoteNote

    If building from the command line, you must build the program in two steps. First, compile the code using Cl.exe with the /c compiler option (cl /c /EHsc MathFuncsLib.cpp). This will create an object file named MathFuncsLib.obj. For more information, see /c (Compile Without Linking). Second, link the code using the Library Manager Lib.exe (lib MathFuncsLib.obj). This will create the static library MathFuncsLib.lib. For more information on the Library Manager, see LIB Reference.


  6. Compile the static library by selecting Build Solution from the Build menu. This creates a static library that can be used by other programs.

To create an application that references the static library

  1. To create an application that will reference and use the static library that was just created, from the File menu, select New and then Project….


  2. From the Project types pane, under Visual C++, select Win32.


  3. From the Templates pane, select Win32 Console Application.


  4. Choose a name for the project, such as MyExecRefsLib, and enter it in the Name field. Next to Solution, select Add to Solution from the drop down list. This will add the new project to the same solution as the static library.


  5. Press OK to start the Win32 Application Wizard. From the Overview page of the Win32 Application Wizard dialog, press Next.


  6. From the Application Settings page of the Win32 Application Wizard, under Application type, select Console application.


  7. From the Application Settings page of the Win32 Application Wizard, under Additional options, deselect Precompiled header.


  8. Press Finish to create the project.

To use the functionality from the static library in the console application

  1. After you create a new Console Application, an empty program is created for you. The name for the source file will be the same as the name you chose for the project above. In this example, it is named MyExecRefsLib.cpp.


  2. To use the math routines that were created in the static library, you must reference it. To do this, select References… from the Project menu. From the Property Pages dialog, expand the Common Properties node and select References. Then select the Add New Reference… button. For more information on the References… dialog, see References, Common Properties, <Projectname> Property Pages Dialog Box.


  3. The Add Reference dialog is displayed. This dialog lists all the libraries that you can reference. The Project tab lists all the projects in the current solution and any libraries they contain. From the Projects tab, select MathFuncsLib. Then select OK. For more information on the Add Reference dialog, see Add Reference Dialog Box.


  4. To reference the header files of the static library, you must modify the include directories path. To do this, from the Property Pages dialog, expand the Configuration Properties node, then the C/C++ node, and select General. Next to Additional Include Directories, type in the path to the location of the MathFuncsLib.h header file.


  5. You can now use the MyMathFuncs class in this application. Replace the contents of MyExecRefsLib.cpp with the following code:

    // MyExecRefsLib.cpp
    // compile with: /EHsc /link MathFuncsLib.lib

    #include <iostream>

    #include "MathFuncsLib.h"

    using namespace std;

    int main()
    {
    double a = 7.4;
    int b = 99;

    cout << "a + b = " <<
    MathFuncs::MyMathFuncs::Add(a, b) << endl;
    cout << "a - b = " <<
    MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
    cout << "a * b = " <<
    MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
    cout << "a / b = " <<
    MathFuncs::MyMathFuncs::Divide(a, b) << endl;

    return 0;
    }

  6. Build the executable by selecting Build Solution from the Build menu.

To run the application

  1. Make sure MyExecRefsLib is selected as the default project. From the Solution Explorer, select MyExecRefsLib, and then select Set As StartUp Project from the Project menu.


  2. To run the project, select Start Without Debugging from the Debug menu. The output should look like this:

    a + b = 106.4
    a - b = -91.6
    a * b = 732.6
    a / b = 0.0747475
//---------------------------------------------------//

Η παραπάνω μεθοδολογία μας προτείνει να χρησιμοποιήσουμε μέσω του "Αdd Reference" το project με την βιβλιοθήκη static lib. Αν θέλουμε κατευθείαν να χρησιμοποιήσουμε την βιβλιοθήκη (lib) χωρίς να χρησιμοποιήσουμε το project πρέπει να ορίσουμε το directory όπου βρίσκεται η βιβλιοθήκη που φτιάξαμε. Υπάρχουν 2 εκδόσεις, η debug και η release έκδοση.. και αντίστοιχα ορίζουμε χειροκίνητα στα project properties:

Release έκδοση:

Debug έκδοση:

20071102

C++ Boost: Reading all the text files of directory and apply a substitution using a regular expression.

#include <fstream>
#include <string>
#include <sstream>
#include <iostream>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/regex.hpp>

using namespace boost::filesystem;
using namespace std;

// Apply Regular Expression + substitution
string process_str(string str, string regex, string substitution) {

cout << "applying regular expression.. " << regex << endl;

string result=string();

boost::regex reg(regex);

result=boost::regex_replace(str,reg,substitution);

return result;
}

// Read file fname and return its contents in string format
string load_data_in_str(string fname) {
// ifstream fIn;
// Error 1 error C2872: 'ifstream' : ambiguous symbol
// Because ifstream is defined by 2 namespaces:
// namespace std and boost::filesystem (!)
// Use either std::ifstream fIn; either boost::filesystem::ifstream fIn;

// If I use boost::filesystem::ifstream
boost::filesystem::ifstream fIn;

// Obj fIn can has as argument string fname:
// fIn.open(fname,std::ios::in);
// either obj boost::filesystem::path:
// fIn.open(path(fname),std::ios::in);
// But for comparibility with std::ifstream I use:
fIn.open(fname.c_str(),std::ios::in);

if (!fIn) {
cerr << "string load_data_in_str(string fname)" << endl;
cerr << "Error reading the file: " << fname << endl;
getchar();
exit(0);
}

string l;

stringstream ss;

ss << fIn.rdbuf();

return ss.str();
}

// Write to filename fname contents of string data (with default the overwrite: append=false)
void write_str_in_file(string fname, string data, bool append=false) {
// For the same reason as in function string load_data_in_str(string fname)
// I choose with lib I will use from namespaces: (boost::filesystem:: vs std::).
boost::filesystem::ofstream fOut;

if (append)
fOut.open(fname.c_str(),ios::app);
else
fOut.open(fname.c_str(),ios::out);

if (!fOut) {
cerr << "void write_str_in_file(string fname, string data, bool append)" << endl;
cerr << "Error writing to file: " << fname << endl;
getchar();
exit(0);
}

fOut << data;

fOut.close();
}

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

if (argc!=5) {
cerr << "Usage: " << argv[0]
<< " [ files dir ] [ reg ex ] [ substritution ] [ out file dir ]" << endl;
cerr << "Press <enter> to continue.." << endl;
getchar();
return (EXIT_FAILURE);
}

// template <class Path> Path system_complete(const Path& p);

// Effects: Composes a complete path from p, using the same rules
// used by the operating system to resolve a path passed as the
// filename argument to standard library open functions.

// Returns: The composed path.

// Postcondition: For the returned path, rp, rp.is_complete() is true.

// Throws: If p.empty().

// Returns: The composed path.
// [Note: For POSIX, system_complete(p) has the same semantics as complete(p, current_path()).

// For Windows, system_complete(p) has the same semantics as complete(ph, current_path())
// if p.is_complete() || !p.has_root_name() or p and base have the same root_name().
// Otherwise it acts like complete(p, kinky), where kinky is the current directory for
// the p.root_name() drive. This will be the current directory of that drive the last
// time it was set, and thus may be residue left over from a prior program run by the
// command processor! Although these semantics are often useful, they are also very error-prone.


path InputPath=system_complete(path(argv[1],native));

// in #include <stdlib.h> are defined EXIT_FAILURE and EXIT_SUCCESS

if (!exists(InputPath)) {
cerr << "Error: the directoty " << InputPath
<< " does not exist." << endl;
return (EXIT_FAILURE);
}

if (!is_directory(InputPath)) {
cout << InputPath << " is not a directory!" << endl;
return (EXIT_SUCCESS);
}

path OutputPath=system_complete(path(argv[4],native));

if (!exists(OutputPath)) {
cerr << "Error: the directoty " << OutputPath
<< " does not exist." << endl;
return (EXIT_FAILURE);
}

if (!is_directory(OutputPath)) {
cout << InputPath << " is not a directory!" << endl;
return (EXIT_SUCCESS);
}
directory_iterator end;
for (directory_iterator it(InputPath); it!= end; ++it) {
cout << it->leaf() << endl;
cout << it->string();
string load_txt;
if (is_directory(*it))
cout << " (dir)";
else {
// Read file into the string load_txt
load_txt=load_data_in_str(it->string());
// Substitute the match of regular expression argv[2]
// with the text of substitution argv[3].
load_txt=process_str(load_txt, argv[2], argv[3]);
/// write_str_in_file(OutputPath.directory_string()+(it->leaf()),load_txt);

// basic_path& operator/=(const basic_path& rhs);
// Effects: The path stored in rhs is appended to the stored path.

// The following function as we read from documenation @ www.boost.org
// http://www.boost.org/libs/filesystem/doc/tr2_proposal.html#basic_path-observers
// path(OutputPath)/=path(it->leaf())
// Gets OutputPath = OutputPath + / + path(it->leaf());
// It concatenates path + filename.
// At the end with .string() we convert to string in order to pass it as argument
write_str_in_file((path(OutputPath)/=path(it->leaf())).string(),load_txt);

}
cout << endl;
}

getchar();

return (EXIT_SUCCESS);
}

20071029

C++: STL vector<>::front/begin() and vector<>::back/end() methods.

// vector::front - Returns reference to first element of vector.
// vector::back - Returns reference to last element of vector.
// vector::push_back - Appends (inserts) an element to the end of a
// vector, allocating memory for it if necessary.
// vector::size - Returns number of elements in the vector.
// vector::begin - Returns an iterator to start traversal of the vector.
// vector::end - Returns an iterator for the last element of the vector.
// vector::erase - Deletes elements from a vector (single & range).

vector<int> vec;

// Intialize the array to contain the members [100, 200, 300, 400]
for (int i=0; i<4; i++)
vec.push_back((i + 1) * 100);

cout << "First element: " << vec.front() << endl;
cout << "Last element: " << vec.back() << endl;
cout << "Elements in vector: " << vec.size() << endl;

// Delete the last element of the vector. Remember that the vector
// is 0-based, so theVector.end() actually points 1 element beyond the end.
vec.erase(vec.end() - 1);

cout << endl << "After erasing last element, new last element is: " << vec.back() << endl;

// Delete the first element of the vector.
vec.erase(vec.begin());

cout << "After erasing first element, new first element is: "
<< vec.front() << endl;

cout << "Elements in vector: " << vec.size() << endl;
// Insert an element (number -11) in front of the vector.
vec.insert(vec.begin(),-11);
link: Visual C++ Developer Center / vector::front and vector::back

C++ Classes: Overloading ostream/istream operators, defining copy ctor, assignment operator, and operator +.

/*-------------------------------------------------------*/
file obj_test.h:
/*-------------------------------------------------------*/
#ifndef __OBJ_TEST_H
#define __OBJ_TEST_H

#include <vector>
#include <string>

using namespace std;

class obj_test {
public:
obj_test(void);
obj_test(vector<float> samples): _samples(samples) { };
~obj_test(void);

// copy constructor
obj_test(const obj_test& obj);
// assignment operator
obj_test& operator =(const obj_test& rhs);
// + operator
obj_test operator +(const obj_test& rhs);

friend ostream& operator <<(ostream& os, obj_test &test);
friend istream& operator >>(istream &is, obj_test &test);

private:
// temp samples
vector<float> _samples;

};

ostream& operator <<(ostream &os, obj_test &test);
istream& operator >>(istream &is, obj_test &test);

#endif
/* __OBJ_TEST_H */
/*-------------------------------------------------------*/
file obj_test.cpp:
/*-------------------------------------------------------*/
#include "test.h"

obj_test::obj_test(void) {
}


// copy constructor
obj_test::obj_test(const obj_test& obj): _samples(obj._samples) { }

// assignment operator
obj_test& obj_test::operator =(const obj_test& rhs) {

// guard against self-assignment Stanley B. Lippman C++ Primer p.729
if (this!=&rhs) {
_samples=rhs._samples;
}

return *this;
}

obj_test obj_test::operator +(const obj_test& rhs) {
obj_test test;

_samples.insert(_samples.end(),rhs._samples.begin(),rhs._samples.end());
test._samples=_samples;

return test;
}

obj_test::~obj_test(void) {
_samples.clear();
}


ostream& operator <<(ostream& os, obj_test &test) {

os << "samples: " << endl;

copy(test._samples.begin(),test._samples.end(),ostream_iterator<float>(os,"\n"));

return os;
}

istream& operator >>(istream &is, obj_test &test) {
string l;
getline(is,l);

float sample;
while (is >> sample)
test._samples.push_back(sample);

return is;
}
/*-------------------------------------------------------*/
file obj_test_driver.cpp:
/*-------------------------------------------------------*/
#include <iostream>
#include <sstream>
#include "test.h"

using namespace std;

void main() {

vector <float> samples;

samples.push_back(float(1.4));
samples.push_back(float(3));
samples.push_back(float(5.6));

cout << "test obj intialized with vector<float> samples:" << endl << endl;
obj_test test(samples);

cout << test << endl;

stringstream ss;

ss << test;

obj_test test_clone;

ss >> test_clone;

cout << "test clone should be the same with test:" << endl;
cout << test_clone;

getchar();

return;
}

20070722

Adobe Premiere Pro 2.0: αλλαγή του hadisk path σε όλα τα AVIs μέσα σε ένα project.

Ανοίγουμε με το vim το .prproj αρχείο του Adobe Premiere Pro 2.0 και εφαρμόζουμε την substitution command:

:1,$s/H:\\/I:\\/

Η οποία εδώ μετατρέπει όλα τα "Η:\" σε "I:\" θεωρώντας ότι το project βρισκόταν αρχικά στον δίσκο H:\ και κατόπιν μεταφέρθηκε στο δίσκο I:\.

20070717

C++ Πέρασμα αριθμητικών μεταβλητών στην main().

#include <iostream>                  // std::cerr
#include <string> // std::string
#include "boost/lexical_cast.hpp" // boost::lexical_cast, boost::bad_lexical_cast

using boost::lexical_cast;
using boost::bad_lexical_cast;

using namespace std;

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

cout << "Version: " << __DATE__ << '\t' << __TIME__ << endl << endl;

double d_nr;
if (argc==2) {

try {
d_nr=lexical_cast<double>(argv[1]);
}
catch (bad_lexical_cast &) {
cerr << "Error reading " << argv[1] << " into double d_nr!!" << endl;
cerr << "d_nr <- -1" << endl;
d_nr=-1;
}
}
else {
cerr << "Missing argument in command line mode!" << endl;
cerr << "Run: " << argv[0] << " <double parameter>" << endl;
}

return;
}
Στο παραπάνω κομμάτι κώδικα θεωρούμε ότι έχουμε περάσει στο command line μια αριθμητική μεταβλητή η οποία είναι double. Για να την περάσουμε στην μεταβλητή d_nr καλούμε το lexical_cast της βιβλιοθήκης boost όπως έχουμε δει και εδώ και εδώ.

Φυσικά υπάρχει και ο κλασικός τρόπος μετατροπής του char* argv[] με τις συναρτήσεις atoi, atof κλπ..

20070716

Διαμόρφωση σκληρού δίσκου ως FAT32 και προβλήματα με το format.exe των Windows.

Αν δοκιμάσεις να κάνεις format ένα δίσκο μεγαλύτερο από 32 gigabytes με FAT32 file system με το format.exe των windows θα δεις ότι δεν γίνεται.

Αγόρασα ένα εξωτερικό δίσκο Western Digital 500 gigabytes και παρατήρησα ότι είναι FAT32 formated! Κοιτάζοντας την knowledge base της Western Digital παρατήρησα ότι υπάρχουν utilities που μπορείς να κάνεις format σε FAT32 σύστημα αρχείων και ένα από αυτά τα δίνει η western digital από το παραπάνω link.

Θεωρητικά μπορείς να κάνεις format σε FAT32 σύστημα αρχείων μέχρι 2Terrabytes. Υπάρχει και το utility fat32format από την Ridgecrop Consulants που το αναφέρει αυτό.

Η χρήση ενός δίσκου σε fat32 bit συστήματος αρχείου είναι βολική όταν τον δίσκο αυτό θέλουμε να τον βλέπουμε κάτω από Windows, Linux, Mac κλπ. Για παράδειγμα το linux βλέπει ως read-only το σύστημα αρχείων NTFS.

20070715

Read two parallel C++/STL: vectors/containers within a single (for) loop.

vector<string> vec1;
vector<string> vec2;

vec1.push_back("1");
vec1.push_back("2");
vec1.push_back("3");

vec2.push_back("one");
vec2.push_back("two");
vec2.push_back("three");

if (vec1.size()!=vec2.size()) { // are they parallel vectors;
cerr << "Error size mismatch, vectors are not parallel!" << endl;
}
else {
vector<string>::iterator vec1_iter=vec1.begin();
for(vector<string>::iterator vec2_iter=vec2.begin();
vec2_iter!=vec2.end();
++vec2_iter, ++vec1_iter) {
cout << *vec1_iter << '\t' << *vec2_iter << endl;
}
}
Program execution:
1       one
2 two
3 three

20070712

Display Integer Type Information Standard C++ Compilers.

Το παρακάτω πρόγραμμα εμφανίζει στην οθόνη τα όρια των ακέραιων τύπων στην C++:
#include <iostream>
#include <climits>

using namespace std;

volatile int char_min = CHAR_MIN;

int main(void) {
cout << "Size of boolean type is " << sizeof(bool)
<< " byte(s)" << "\n\n";

cout << "Number of bits in a character: "
<< CHAR_BIT << '\n';
cout << "Size of character types is "
<< sizeof(char)
<< " byte" << '\n';
cout << "Signed char min: "
<< SCHAR_MIN << " max: "
<< SCHAR_MAX << '\n';
cout << "Unsigned char min: 0 max: "
<< UCHAR_MAX << '\n';

cout << "Default char is ";

if (char_min < 0)
cout << "signed";
else if (char_min == 0)
cout << "unsigned";
else
cout << "non-standard";
cout << "\n\n";

cout << "Size of short int types is "
<< sizeof(short) << " bytes"
<< '\n';
cout << "Signed short min: "
<< SHRT_MIN << " max: "
<< SHRT_MAX << '\n';
cout << "Unsigned short min: 0 max: "
<< USHRT_MAX << "\n\n";

cout << "Size of int types is "
<< sizeof(int) << " bytes"
<< '\n';
cout << "Signed int min: "
<< INT_MIN << " max: "
<< INT_MAX << '\n';
cout << "Unsigned int min: 0 max: "
<< UINT_MAX << "\n\n";

cout << "Size of long int types is "
<< sizeof(long) << " bytes"
<< '\n';
cout << "Signed long min: "
<< LONG_MIN << " max: "
<< LONG_MAX << '\n';
cout << "Unsigned long min: 0 max: "
<< ULONG_MAX << endl;

getchar();
return 0;
}
Η εκτέλεση του παραπάνω προγράμματος στο Visual Studio .NET 2005 βγάζει τα παρακάτω αποτελέσματα:

Size of boolean type is 1 byte(s)

Number of bits in a character: 8
Size of character types is 1 byte
Signed char min: -128 max: 127
Unsigned char min: 0 max: 255
Default char is signed

Size of short int types is 2 bytes
Signed short min: -32768 max: 32767
Unsigned short min: 0 max: 65535

Size of int types is 4 bytes
Signed int min: -2147483648 max: 2147483647
Unsigned int min: 0 max: 4294967295

Size of long int types is 4 bytes
Signed long min: -2147483648 max: 2147483647
Unsigned long min: 0 max: 4294967295

20070711

Visual Studio .NET 2005 project settings tricks.

Σε ορισμένες περιπτώσεις όταν έφτιαχνα projects στο Visual Studio .NET παρατηρούσα ότι δημιουργούσε τα compiled executables μέσα στο solution dir, δηλαδή: \<solution_dir>\debug και \<solution_dir>\release και όχι στα directories: \<solution_dir>\<project_dir>\debug και \<solution_dir>\<project_dir>\realease.

Για να το ρυθμίσεις αυτό, πήγαινε στα project settings και κάνε τις παρακάτω αλλαγές από:σε

20070710

C++ double to int convertion.

Δοκιμάζα να μετρεψω ένα αριθμό double τον οποίο διάβαζα από ένα stream κατευθεάν μέσω του >> operator, αλλά δεν δούλευε πάντα. Όταν είχα μεγάλους αριθμούς δεν έγραφε τίποτα στο sample.

int sample;
ss>>sample;
Δοκίμασα να το πάρω και να χρησιμοποιήσω το lexical_cast του boost και μου έριχνε exception!! Η λύση είναι πιό απλή. Το διαβάζω σε ένα double και κατόπιν (το κάνω casting σε double πχ χρησιμοποιώντας boost lexical cast) και στο τέλος χρησιμοποιώ casting με την int():
string str="31212.43343535";
double d_nr=lexical_cast<double>(str);
int nr=int(d_nr);
// nr = 31212

Copy all elements of an C++ STL container to a stream.

vector<string> vec_str;
copy(vec_str.begin(),vec_str.end(),ostream_iterator<string>(cout,"\n"));
Ο παραπάνω κώδικας αντιγράφει όλα τα δεδομένα του πίνακα vec_str στην οθόνη (μέσω του cout).
vector<string> vec_str;
string deliminer="\t";

stringstream ss;

copy(vec_str.begin(),vec_str.end(),ostream_iterator<string>(ss,deliminer.c_str()));

string str=ss.str();

// delete the last '\t' added in the end of str using copy(..) function
str.erase(str.length()-1);
Ο παραπάνω κώδικας παίρνει ένα vector<string> container και τον γράφει σε ένα string.

Iterating over an C++ STL container.

for (iter=container.begin(); iter!=container.end(); ++iter) {
do_something(*iter);
}

Pop values from a stl: C++ vector using reverse and pop_back() function.

vector<string> vvv;
vvv.push_back("1");
vvv.push_back("2");
vvv.push_back("3");
// vvv -> 1, 2 3
reverse(vvv.begin(),vvv.end());
// vvv -> 3, 2, 1
string front=vvv.back();
// front -> 1
vvv.pop_back();
// vvv -> 3, 2

Παράδειγμα διαβάσματος αρχείου WAV σε ansi C++.

Παραθέτω ένα παράδειγμα χρήσης της βιβλιοθήκης διαβάσματος/γραψίματος αρχείων WAV:

#include <string>
#include <fstream>
#include <iostream>
#include "WAVE.h"

using namespace std;

void main() {

WaveFile In;
In.OpenRead("input.wav");
In.ShowFormat();
WaveFile Out;

Out.OpenWrite("output.wav");
Out.ShowFormat();
Out.CopyFormatFrom(In);
Out.ShowFormat();

vector<float> v_samples;
for (size_t i=0; i < In.GetNumSamples(); i++) {
float sample;
In.ReadSample(sample);
v_samples.push_back(sample);
}
In.Close();

cout << "v_samples.size() = " << v_samples.size() << endl;
          // Γράψε τα δείγματα 229995 έως 232155 στο αρχείο output.wav
    for (int i=229995; i<232155; i++) {
Out.WriteSample(v_samples[i]);
}

cout << "Samples written: " << 232155 - 229995 << endl;
    // Αφού τα δείγματα τα έχουμε περάσει στον πίνακα v_samples
//
μπορούμε να εφραμόσουμε οποιαδήποτε περαιτέρω ψηφιακή επεξεργασία (dsp)
Out.ShowFormat();
Out.Close();
return;
}

Reading WAV files using ansi C++.

/* rifffile.h - Copyright (c) 1996, 1998 by Timothy J. Weber */

#ifndef __RIFFFILE_H
#define __RIFFFILE_H

#include <stack>
#include <string>
#include <vector>
#include <stdio.h>

#pragma warning(disable : 4996) // fopen warning problem Visual Studio .NET 2005

class RiffFile;

class RiffChunk {
public:
char name[5];
unsigned long size; // the length, read from the second chunk header entry
char subType[5]; // valid for RIFF and LIST chunks
long start; // the file offset in bytes of the chunk contents
long after; // the start of what comes after this chunk

// initialize at the file's current read position, and mark the file as bad
// if there's an error.
RiffChunk()
{};
RiffChunk(RiffFile& file);

bool operator < (const RiffChunk& other) const
{ return start < other.start; };
bool operator == (const RiffChunk& other) const
{ return strcmp(name, other.name) == 0
&& size == other.size
&& strcmp(subType, other.subType) == 0
&& start == other.start; };
};

class RiffFile {
FILE* fp;

unsigned long formSize;

std::stack<RiffChunk, std::vector<RiffChunk> > chunks;

public:
RiffFile(const char *name);
~RiffFile();

bool rewind();
bool push(const char* chunkType = 0);
bool pop();
long chunkSize() const;
const char* chunkName() const;
const char* subType() const;
bool getNextExtraItem(std::string& type, std::string& value);
FILE* filep()
{ return fp; };

protected:
bool readExtraItem(std::string& type, std::string& value);
};

#endif
/* __RIFFFILE_H */
// ---------------------------------------- EOF -----///
/* rifffile.cpp

Copyright (c) 1996, 1988 by Timothy J. Weber.

See rifffile.txt for documentation:

<rifffile.txt>
Documentation for module RiffFile.

Version 1.3.

Copyright (c) 1996, 1998 by Timothy J. Weber.
Contact: tjweber@lightlink.com, http://www.lightlink.com/tjweber

Requires:
Standard Template Library stacks and vectors
ANSI strings

Change history:
tjw 19 Aug 96: started
tjw 01 Sep 96: finished initial version
tjw 13 Mar 97: fixed bug that left file handle open upon destruction.
tjw 20 May 97: v. 1.2: added extra data methods
tjw 27 Sep 98: v. 1.3: updated to support current versions of STL.
tjw 23 Mar 01: made compatible with gcc.


-------------------------------------------------------------------------------
SUMMARY

A platform-independent way to read a RIFF file (e.g., WAVE).

-------------------------------------------------------------------------------
EXTERNAL INTERFACE


class RiffFile:

RiffFile(const char *name)
Constructor. Opens the named file, and calls rewind(); if rewind()
returns false, it sets the file pointer to null.
The file is closed when the instance is destroyed.

bool rewind()
Moves to the first chunk of the file.
Returns false if the file is not a RIFF file.

const char* formType() const
Returns the form type, or 0 if the file is not a RIFF file.

bool push(const char* chunkType = 0)
Descends from the current chunk to a subchunk with the specified type.
If the type is 0, descends into the next available chunk, starting at
the current file position.
Assumes chunkType is four characters long.
Leaves the get and put pointers pointing to the beginning of the
chunk's contents.
Returns false if the specified chunk type can't be found.

bool pop()
Pops up to the containing chunk, and positions after the end of of the
current subchunk. If the current chunk is a top-level chunk, returns
false.

long chunkSize() const
Returns the size of the current chunk, or 0 if there is none.

const char* chunkName() const
Returns the name of the current chunk, or 0 if there is none.

const char* subType() const
Returns the subtype of the current chunk, or 0 if there is none.
(E.g.: a LIST chunk might have a subtype of INFO, or a top-level
RIFF chunk a subtype of WAVE.)

bool getNextExtraItem(string& type, string& value)
Looks for the next "extra" data present in the file, from
LIST/INFO and DISP chunks. Sets the two arguments on success; returns
false on failure.

FILE* filep()
Returns a file pointer that can be used for reading. May return 0.


-------------------------------------------------------------------------------
PERMISSION

Copyright is retained by Timothy J. Weber. License is granted to use this
source code for any purpose.


-------------------------------------------------------------------------------
IMPLEMENTATION NOTES

-------------------------------------------------------------------------------
WISH LIST

Would be better to change the recursive algorithm in getNextExtraItem() to an
iterative one.

Should use Win32 Multimedia API calls if they're available, for complete
compatibility with other programs on those platforms.

Expand to handle RIFX transparently.
*/

#include "rifffile.h"

using namespace std;

/***************************************************************************
macros and constants
***************************************************************************/

// define REVERSE_ENDIANISM if the endianism of the host platform is not Intel
// (Intel is little-endian)
#ifdef REVERSE_ENDIANISM
#define SWAP_32(int32) ( \
((((DWORD) int32) & 0x000000FFL) << 24) + \
((((DWORD) int32) & 0x0000FF00L) << 8) + \
((((DWORD) int32) & 0x00FF0000L) >> 8) + \
((((DWORD) int32) & 0xFF000000L) >> 24))
#endif

struct
TypeRecord {
char* typeName; // four-letter name
char* realName; // English name
};

const int numExtraTypes = 24;
const TypeRecord extraTypes[numExtraTypes] = {
{ "DISP", "Display name" },
{ "IARL", "Archival location" },
{ "IART", "Artist" },
{ "ICMS", "Commissioned" },
{ "ICMT", "Comments" },
{ "ICOP", "Copyright" },
{ "ICRD", "Creation date" },
{ "ICRP", "Cropped" },
{ "IDIM", "Dimensions" },
{ "IDPI", "Dots Per Inch" },
{ "IENG", "Engineer" },
{ "IGNR", "Genre" },
{ "IKEY", "Keywords" },
{ "ILGT", "Lightness" },
{ "IMED", "Medium" },
{ "INAM", "Name" },
{ "IPLT", "Palette Setting" },
{ "IPRD", "Product" },
{ "ISBJ", "Subject" },
{ "ISFT", "Software" },
{ "ISHP", "Sharpness" },
{ "ISRC", "Source" },
{ "ISRF", "Source Form" },
{ "ITCH", "Technician" },
};


/***************************************************************************
member functions for RiffFile
***************************************************************************/

RiffFile::RiffFile(const char *name):
fp(fopen(name, "rb"))
{
if (fp && !rewind()) {
fclose(fp);
fp = 0;
}
}

RiffFile::~RiffFile()
{
if (fp)
fclose(fp);
}

bool RiffFile::rewind()
{
// clear the chunk stack
while (!chunks.empty())
chunks.pop();

// rewind to the start of the file
if (fseek(fp, 0, SEEK_SET))
return false;

// look for a valid RIFF header
RiffChunk topChunk(*this);

if (feof(fp) || strcmp(topChunk.name, "RIFF"))
return false;

// found; push it on the stack, and leave the put pointer in the same place
// as the get pointer.
formSize = topChunk.size;
chunks.push(topChunk);
return true;
}

bool RiffFile::push(const char* chunkType)
{
// can't descend if we haven't started out yet.
if (chunks.empty())
return false;

// first, go to the start of the current chunk, if we're looking for a named
// chunk.
if (chunkType)
if (fseek(fp, chunks.top().start, SEEK_SET))
return false;

// read chunks until one matches or we exhaust this chunk
while (!feof(fp) && ftell(fp) < chunks.top().after) {
RiffChunk chunk(*this);

if (!feof(fp)) {
// see if the subchunk type matches
if (!chunkType || strcmp(chunk.name, chunkType) == 0) {
// found; synchronize the put pointer, push the chunk, and succeed
chunks.push(chunk);
return true;
} else {
// not found; go to the next one.
if (fseek(fp, chunk.after, SEEK_SET))
return false;
}
}
}

// couldn't find it; synchronize the put pointer and return error.
fseek(fp, chunks.top().start, SEEK_SET);
return false;
}

bool RiffFile::pop()
{
// if we've only got the top level chunk (or not even that), then we can't
// go up.
if (chunks.size() < 2)
return false;

// Position the get and put pointers at the end of the current subchunk.
fseek(fp, chunks.top().after, SEEK_SET);

// Pop up the stack.
chunks.pop();
return true;
}

long RiffFile::chunkSize() const
{
if (!chunks.empty())
return chunks.top().size;
else
return 0;
}

const char* RiffFile::chunkName() const
{
if (!chunks.empty())
return chunks.top().name;
else
return 0;
}

const char* RiffFile::subType() const
{
if (!chunks.empty() && chunks.top().subType[0])
return chunks.top().subType;
else
return 0;
}

bool RiffFile::getNextExtraItem(string& type, string& value)
{
// if the current chunk is LIST/INFO, then try to read another subchunk.
if (strcmp(chunkName(), "LIST") == 0
&& strcmp(subType(), "INFO") == 0)
{
if (push()) {
if (readExtraItem(type, value))
return true;
else
// unrecognized type. Continue on.
return getNextExtraItem(type, value);
} else {
// got to the end of the LIST/INFO chunk. Pop back out and continue
// looking.
pop();
return getNextExtraItem(type, value);
}
// we're not in a LIST/INFO chunk, so look for the next DISP or LIST/INFO.
} else {
push();

if (strcmp(chunkName(), "DISP") == 0) {
// DISP chunk: read and pop back out.
return readExtraItem(type, value);
} else if (strcmp(chunkName(), "LIST") == 0
&& strcmp(subType(), "INFO") == 0)
{
// LIST/INFO chunk: read first element
return getNextExtraItem(type, value);
} else {
// Some other chunk. Pop back out and move on.
if (pop())
return getNextExtraItem(type, value);
else
return false;
}
}
}

// Reads extra data from the current chunk, and pops out of it.
bool RiffFile::readExtraItem(string& type, string& value)
{
// see if it's one we recognize
bool found = false;
for (int i = 0; i < numExtraTypes; i++) {
if (strcmp(chunkName(), extraTypes[i].typeName) == 0) {
type = extraTypes[i].realName;
found = true;
}
}

// DISP chunks skip four bytes before the display name starts.
if (strcmp(chunkName(), "DISP") == 0) {
fgetc(filep());
fgetc(filep());
fgetc(filep());
fgetc(filep());
}

// read the value, if we recognize the type
if (found) {
int c;
value = "";
while ((c = fgetc(filep())) != '\0' && c != EOF)
value += char(c);
}

// whether we recognize it or not, pop back out.
pop();
return found;
}

/***************************************************************************
member functions for RiffChunk
***************************************************************************/

RiffChunk::RiffChunk(RiffFile& parent)
{
// read the chunk name
fread(name, 1, 4, parent.filep());
name[4] = '\0';

// read the chunk size
fread(&size, 4, 1, parent.filep());

#ifdef REVERSE_ENDIANISM
// reverse the endianism of the chunk size.
size = SWAP_32(size);
#endif

// if this is a RIFF or LIST chunk, read its subtype.
if (strcmp(name, "RIFF") == 0
|| strcmp(name, "LIST") == 0)
{
fread(subType, 1, 4, parent.filep());
subType[4] = '\0';

// subtract the subtype from the size of the data.
size -= 4;
} else
*subType = '\0';

// the chunk starts after the name and size.
start = ftell(parent.filep());

// the next chunk starts after this one, but starts on a word boundary.
after = start + size;
if (after % 2)
after++;
}

// ---------------------------------------- EOF -----///

/* wave.h - Copyright (c) 1996-2002 by Timothy J. Weber */

#ifndef __WAVE_H
#define __WAVE_H

#include <stdio.h>
#include <iostream>
#include "rifffile.h"

#pragma warning(disable : 4996) // fopen warning problem

class WaveFile {
public:
WaveFile();
~WaveFile();

bool OpenRead(const char* name);
bool OpenWrite(const char* name);
bool ResetToStart();
bool Close();

unsigned short GetFormatType() const
{ return formatType; };
void SetFormatType(unsigned short type)
{ formatType = type; changed = true; };
bool IsCompressed() const
{ return formatType != 1; };

unsigned short GetNumChannels() const
{ return numChannels; };
void SetNumChannels(unsigned short num)
{ numChannels = num; changed = true; };

unsigned long GetSampleRate() const
{ return sampleRate; };
void SetSampleRate(unsigned long rate)
{ sampleRate = rate; changed = true; };

unsigned long GetBytesPerSecond() const
{ return bytesPerSecond; };
void SetBytesPerSecond(unsigned long bytes)
{ bytesPerSecond = bytes; changed = true; };

unsigned short GetBytesPerSample() const
{ return bytesPerSample; };
void SetBytesPerSample(unsigned short bytes)
{ bytesPerSample = bytes; changed = true; };

unsigned short GetBitsPerChannel() const
{ return bitsPerChannel; };
void SetBitsPerChannel(unsigned short bits)
{ bitsPerChannel = bits; changed = true; };

unsigned long GetNumSamples() const
{ return (GetBytesPerSample())?
GetDataLength() / GetBytesPerSample(): 0; };
void SetNumSamples(unsigned long num)
{ SetDataLength(num * GetBytesPerSample()); };

float GetNumSeconds() const
{ return GetBytesPerSecond()?
float(GetDataLength()) / GetBytesPerSecond(): 0; };

unsigned long GetDataLength() const
{ return dataLength; };
void SetDataLength(unsigned long numBytes)
{ dataLength = numBytes; changed = true; };

bool FormatMatches(const WaveFile& other);

void CopyFormatFrom(const WaveFile& other);

void SetupFormat(int sampleRate = 44100, short bitsPerChannel = 16, short channels = 1);

FILE* GetFile()
{ return readFile? readFile->filep(): writeFile; };

RiffFile* GetRiffFile()
{ return readFile? readFile : 0; };

bool WriteHeaderToFile(FILE* fp);

bool ReadSample(unsigned char& sample);
bool WriteSample(unsigned char sample);
bool ReadSample(short& sample);
bool WriteSample(short sample);
bool ReadSample(float& sample);
bool WriteSample(float sample);
bool ReadSample(double& sample);
bool WriteSample(double sample);

bool ReadSamples(unsigned char* samples, size_t count = 1);
bool WriteSamples(unsigned char* samples, size_t count = 1);
bool ReadSamples(short* samples, size_t count = 1);
bool WriteSamples(short* samples, size_t count = 1);

bool ReadRaw(char* buffer, size_t numBytes = 1);
bool WriteRaw(char* buffer, size_t numBytes = 1);

bool GetFirstExtraItem(std::string& type, std::string& value);
bool GetNextExtraItem(std::string& type, std::string& value);

bool CopyFrom(WaveFile& other);

const char* GetError() const
{ return error; };
void ClearError()
{ error = 0; };

void ShowFormat(bool details = true);

protected:
RiffFile* readFile;
FILE* writeFile;

unsigned short formatType;
unsigned short numChannels;
unsigned long sampleRate;
unsigned long bytesPerSecond;
unsigned short bytesPerSample;
unsigned short bitsPerChannel;
unsigned long dataLength;

const char* error;
bool changed; // true if any parameters changed since the header was last written
};

#endif

/* __WAVE_H */

// ---------------------------------------- EOF -----///


 

/* wave.cpp

Copyright (c) 1996-2002 by Timothy J. Weber.

See WAVE.txt for documentation:

<WAVE.txt>
Documentation for module WAVE.

Version 1.4.

Copyright (c) 1996-2002 by Timothy J. Weber.
Contact: tjweber@lightlink.com, http://www.lightlink.com/tjweber

Requires:
RiffFile (also by the author)
ANSI strings
Standard Template Library

Change history:
tjw 12 Nov 96: started
tjw 5 Feb 97: version 1.0, shipped with WavCat
tjw 20 May 97: added extra data reporting and RiffFile exporting, for use
in StripWav.
tjw 27 Sep 98: version 1.1, updated to support current versions of STL.
tjw 20 Dec 00: version 1.2, added more convenience functions and improved docs.
Tested with Borland C++Builder 3 and Visual C++ 6.
tjw 3 Feb 01: version 1.21, fixed a bug when using WriteSample() with
a stereo file.
tjw 15 Aug 01: Fixed a number of bugs with the sample-writing convenience functions.
tjw 28 Sep 02: Fixed a bug in ShowFormat() (affecting the test code only) that moved
the read pointer.


-------------------------------------------------------------------------------
SUMMARY

A platform-independent way to read and write RIFF WAVE files.

When a file is opened for reading, its attributes are set to correspond with
the specified file. Setting the attributes in this state has no effect.

When it's opened for writing, the file pointer is positioned to the approprate
point to start writing sample data. When it's closed, any attribute settings
that have been changed will be fixed in the file header.

For 8- and 16-bit PCM formats, you can use the supplied methods to read and
write samples. If you know the sample width you're expecting, use the
appropriate integral type (unsigned char or short) for speed; otherwise, use
floats, or doubles if you really want the extra precision (probably not helpful).

To read and write compressed formats (non-PCM), or other sample widths, you'll
have to implement your own compression/decompression and use the C-style I/O
functions (e.g., fopen, fread, fwrite) with the FILE* returned by GetFile().
If you write samples using this FILE*, you must update the data length by
calling SetDataLength(), or the file will not be written correctly.

-------------------------------------------------------------------------------
PERMISSION

Copyright is retained by Timothy J. Weber. License is granted to use this
source code for any purpose.

-------------------------------------------------------------------------------
EXTERNAL INTERFACE


class WaveFile:

bool OpenRead(const char* name)
Opens the named file for reading. If it doesn't exist, can't be opened,
or isn't a valid WAVE file, returns false. Otherwise, sets all the
attributes and positions the file at the start of the sample data chunk.

bool OpenWrite(const char* name)
Opens the named file for writing. If it already exists, overwrites it.
Returns false if it can't be opened.

bool Close()
Closes the file. If the file is open for output, also updates the
header.
The file is automatically closed when the object is destroyed.

High-level access methods:

bool GetFirstExtraItem(string& type, string& value)
bool GetNextExtraItem(string& type, string& value)
Iterates through the list of additional data present in the file, from
LIST/INFO and DISP chunks. Sets the two arguments on success;
returns false on failure. After GetFirstExtraItem() returns
true, ResetToStart() should be called before attempting to read sample
data.

bool CopyFrom(WaveFile& other)
Copies all the sample data from the other wave file to this one,
starting at the start of the data chunk in the other file and at the
current file position in this file. The DataLength attribute for this
file is incremented to reflect the bytes copied. No format checking is
done. Returns false on error, and sets the LastError flag if the
error was while writing to this file (as opposed to reading the other).
If either file pointer is invalid, returns false and sets LastError.

const char* GetError() const
void ClearError()
Inspects and clears the error description. If no error has occurred,
HadError() returns 0; otherwise, it returns a description of the error.

The following methods are for dealing with the format as an aggregate, instead
of manipulating individual elements directly:

bool FormatMatches(const WaveFile& other)
Returns true if all the format attributes of the other wave file are
identical to this one's.

void CopyFormatFrom(const WaveFile& other)
Copies all the format attributes from the specified WaveFile.

void SetupFormat(int sampleRate = 44100, short bitsPerChannel = 16, short channels = 1)
Sets up the format attributes for uncompressed (PCM) audio with the
given attributes. Computes the remaining attributes accordingly.

The following methods are for reading and writing samples in common formats.

bool ReadSample(T& sample)
bool WriteSample(T sample)
Overloads for reading/writing individual samples from numeric types,
where T is unsigned char (for 8-bit), short (for 16-bit), float, or
double.

For float and double, the assumed sample range is from -1.0 to +1.0, and
samples are converted to the format appropriate for the current sample
width.

Updates the file pointer and the data length.

Returns false if the argument size doesn't match the sample size, or
if there aren't as many samples left to read as requested.

Note that samples are interleaved for files with multiple channels.
I.e., the first call will read or write the left sample, the second
will read or write the right channel, etc.

bool ReadSamples(T* samples, size_t count = 1)
bool WriteSamples(T* samples, size_t count = 1)
Overloads for reading/writing your own provided buffers of samples,
where T is unsigned char (for 8-bit) or short (for 16-bit).

Note that count is the number of cross-channel samples. E.g., if you do:

ReadSamples(pShort, 1);

on a stereo file, pShort must be able to hold two integers, or four
bytes. In a stereo file, samples for the left channel come first.

Updates the file pointer and the data length.

Returns false if the buffer unit size doesn't match the sample size, or
if there aren't as many samples left to read as requested.

Note that samples are interleaved for files with multiple channels.
I.e., samples[0] contains the first left sample, samples[1] the first
right sample, samples[2] the second left sample, etc.

The following methods are for inspecting and changing individual aspects of the
data format. Use these if you're implementing a compressed format, or if you
need more control over individual values than SetupFormat() affords.

unsigned short GetFormatType() const
void SetFormatType(unsigned short type)
Inspect and set the format type.
This should be 1 for PCM.

bool IsCompressed() const
Returns true if the sample format uses compression, false if it's PCM.

unsigned short GetNumChannels() const
void SetNumChannels(unsigned short numChannels)
Inspect and set the number of channels (e.g., 2 for stereo, 1 for mono).

unsigned long GetSampleRate() const
void SetSampleRate(unsigned long sampleRate)
Inspect and set the sample rate, in Hertz.

unsigned long GetBytesPerSecond() const
void SetBytesPerSecond(unsigned long bytesPerSecond)
Inspect and set the bytes per second. For PCM formats, this is equal
to SampleRate * NumChannels * BytesPerSample; for compressed formats, it
may differ.

unsigned short GetBytesPerSample() const
void SetBytesPerSample(unsigned short bytesPerSample)
Inspect and set the bytes per sample. This will typically be 1 or 2 for
mono, 2 or 4 for stereo.

unsigned short GetBitsPerChannel() const
void SetBitsPerChannel(unsigned short bitsPerChannel)
Inspect and set the bits per channel. This is equal to BytesPerSample
* 8 / NumChannels. Typically 8 or 16.

unsigned long GetNumSamples() const
void SetNumSamples(unsigned long numSamples)
Inspect and set the number of samples. Note that this is really a
wrapper around the DataLength attribute, that factors in the sample
width. The samples reported here are cross-channel samples; i.e., a
sample in the left channel of a stereo file and its corresponding right-
channel sample are counted as a single sample.

float GetNumSeconds() const
Inspect the length in seconds. This is calculated from DataLength
and BytesPerSecond.

unsigned long GetDataLength() const
void SetDataLength(unsigned long numBytes)
Inspect and set the number of bytes in the data chunk.

void ShowFormat(bool details)
Using cout diplay wav format to the screen.

The following methods are for reading and writing to the underlying file:

bool ResetToStart()
Moves the file pointer to the start of the sample data. This is
done automatically on OpenRead() or OpenWrite().

FILE* GetFile()
Returns a file pointer that can be used for reading or writing. May
return 0.

RiffFile* GetRiffFile()
Returns a pointer to the currently opened RIFF file object.
Returns 0 if the file is not open for reading.

bool WriteHeaderToFile(FILE* fp)
Writes the RIFF/WAVE, fmt, and start of the data chunk in the canonical
format to the specified file.
You don't normally need to call this method, since it's automatically
called on close. You might want to call it explicitly in certain
situations to streamline writes for performance.

-------------------------------------------------------------------------------
EXAMPLE USAGE

Concatenating two WAVE files:

WaveFile in1;
in1.OpenRead("in1.wav");

WaveFile in2;
in2.OpenRead("in2.wav");

WaveFile out;
out.OpenWrite("out.wav");

out.CopyFormatFrom(in1);
out.CopyFrom(in1);
out.CopyFrom(in2);

Adjusting the volume of a WAVE file:

WaveFile in;
in.OpenRead("in.wav");

WaveFile out;
out.CopyFormatFrom(in);
out.OpenWrite("out.wav");

for (size_t i = 0; i < in.GetNumSamples(); i++) {
float sample;
in.ReadSample(sample);

out.WriteSample(sample * 0.6);
}

Mixing two WAVE files together to a third:

WaveFile in1;
in1.OpenRead("in1.wav");

WaveFile in2;
in2.OpenRead("in2.wav");

WaveFile out;
out.CopyFormatFrom(in1);
out.OpenWrite("out.wav");

for (size_t i = 0; i < in1.GetNumSamples() || i < in2.GetNumSamples(); i++) {
float sample1 = 0;
if (i < in1.GetNumSamples())
in1.ReadSample(sample1);

float sample2 = 0;
if (i < in2.GetNumSamples())
in2.ReadSample(sample2);

out.WriteSample(sample1 / 2 + sample2 / 2);
}


-------------------------------------------------------------------------------
IMPLEMENTATION NOTES

-------------------------------------------------------------------------------
WISH LIST
*/

#include "wave.h"

using namespace std;

/***************************************************************************
macros and constants
***************************************************************************/

// constants for the canonical WAVE format
const int fmtChunkLength = 16; // length of fmt contents
const int waveHeaderLength = 4 + 8 + fmtChunkLength + 8; // from "WAVE" to sample data

/***************************************************************************
member functions for WAVE
***************************************************************************/

WaveFile::WaveFile():
readFile(0),
writeFile(0),
formatType(0),
numChannels(0),
sampleRate(0),
bytesPerSecond(0),
bytesPerSample(0),
bitsPerChannel(0),
dataLength(0),
error(0),
changed(true)
{
}

WaveFile::~WaveFile()
{
Close();
}

bool WaveFile::OpenRead(const char* name)
{
if (readFile || writeFile)
Close();

try {
// open the RIFF file
readFile = new RiffFile(name);
if (!readFile->filep())
throw error = "Couldn't open file";

// read the header information
if (strcmp(readFile->chunkName(), "RIFF")
|| strcmp(readFile->subType(), "WAVE")
|| !readFile->push("fmt "))
throw error = "Couldn't find RIFF, WAVE, or fmt";

size_t dwFmtSize = size_t(readFile->chunkSize());
char* fmtChunk = new char[dwFmtSize];
try {
if (fread(fmtChunk, dwFmtSize, 1, readFile->filep()) != 1)
throw error = "Error reading format chunk";
readFile->pop();

// set the format attribute members
formatType = *((short*) fmtChunk);
numChannels = *((short*) (fmtChunk + 2));
sampleRate = *((long*) (fmtChunk + 4));
bytesPerSecond = *((long*) (fmtChunk + 8));
bytesPerSample = *((short*) (fmtChunk + 12));
bitsPerChannel = *((short*) (fmtChunk + 14));

// position at the data chunk
if (!readFile->push("data"))
throw error = "Couldn't find data chunk";

// get the size of the data chunk
dataLength = readFile->chunkSize();

delete[] fmtChunk;
} catch (...) {
delete[] fmtChunk;
throw error;
}
} catch (...) {
Close();
return false;
}
return true;
}

bool WaveFile::OpenWrite(const char* name)
{
if (readFile || writeFile)
Close();

// open the file
writeFile = fopen(name, "wb");
if (!writeFile) {
error = "Couldn't open output file";
return false;
}

dataLength = 0;

// write the header
return WriteHeaderToFile(writeFile);
}

bool WaveFile::ResetToStart()
{
if (readFile) {
// pop out of the data chunk
if (!readFile->rewind()
|| !readFile->push("data"))
{
error = "Couldn't find data chunk on reset";
return false;
} else
return true;
} else if (writeFile) {
return fseek(writeFile, waveHeaderLength, SEEK_SET) == 0;
} else
return false;
}

bool WaveFile::Close()
{
bool retval = true;

if (readFile) {
delete readFile; // closes the file before it's destroyed
readFile = 0;
} else if (writeFile) {
// write the header information at the start of the file, if necessary
if (changed) {
long currentSpot = ftell(writeFile); // save the position
retval = WriteHeaderToFile(writeFile);
fseek(writeFile, currentSpot, SEEK_SET); // restore the old position
// this is necessary so the file gets the right length--otherwise,
// all the data we wrote would be truncated.
}

// close the file
fclose(writeFile);
writeFile = 0;
}

return retval;
}

bool WaveFile::FormatMatches(const WaveFile& other)
{
return formatType == other.formatType
&& numChannels == other.numChannels
&& sampleRate == other.sampleRate
&& bytesPerSecond == other.bytesPerSecond
&& bytesPerSample == other.bytesPerSample
&& bitsPerChannel == other.bitsPerChannel;
}

void WaveFile::CopyFormatFrom(const WaveFile& other)
{
formatType = other.formatType;
numChannels = other.numChannels;
sampleRate = other.sampleRate;
bytesPerSecond = other.bytesPerSecond;
bytesPerSample = other.bytesPerSample;
bitsPerChannel = other.bitsPerChannel;
}

void WaveFile::SetupFormat(int sampleRate, short bitsPerChannel, short channels)
{
SetFormatType(1);
SetNumChannels(channels);
SetSampleRate(sampleRate);
SetBytesPerSample((unsigned short)((bitsPerChannel >> 3) * channels));
SetBytesPerSecond(sampleRate * GetBytesPerSample());
SetBitsPerChannel(bitsPerChannel);
SetNumSamples(0);
}

bool WaveFile::GetFirstExtraItem(string& type, string& value)
{
if (readFile)
return readFile->rewind() && readFile->getNextExtraItem(type, value);
else
return false;
}

bool WaveFile::GetNextExtraItem(string& type, string& value)
{
if (readFile)
return readFile->getNextExtraItem(type, value);
else
return false;
}

bool WaveFile::CopyFrom(WaveFile& other)
{
const size_t transferBufSize = 4096;

if (!writeFile) {
error = "Copy to an unopened file";
return false;
} else if (!other.readFile) {
error = "Copy from an unopened file";
return false;
}

try {
// allocate the transfer buffer
char* transferBuffer = new char[transferBufSize];
unsigned long bytesRead = 0;

try {
if (!other.ResetToStart())
throw error = "Couldn't reset input file to start";

while (bytesRead < other.dataLength) {
// calculate the size of the next buffer
size_t bytesToRead = (size_t) min(transferBufSize,
size_t(other.dataLength - bytesRead));

// read the buffer
if (fread(transferBuffer, 1, bytesToRead, other.readFile->filep())
!= bytesToRead)
throw error = "Error reading samples from input file";
bytesRead += unsigned long(bytesToRead);

// write the buffer
if (fwrite(transferBuffer, 1, bytesToRead, writeFile) != bytesToRead)
throw error = "Error writing samples to output file";
dataLength += unsigned long(bytesToRead);
changed = true;
}

// delete the transfer buffer
delete[] transferBuffer;
} catch (...) {
delete[] transferBuffer;
throw error;
}
} catch (...) {
return false;
}

return true;
}

bool WaveFile::WriteHeaderToFile(FILE* fp)
{
// seek to the start of the file
if (fseek(fp, 0, SEEK_SET) != 0)
return false;

// write the file header
unsigned long wholeLength = waveHeaderLength + dataLength;
unsigned long chunkLength = fmtChunkLength;

if (fputs("RIFF", fp) == EOF
|| fwrite(&wholeLength, sizeof(wholeLength), 1, fp) != 1
|| fputs("WAVE", fp) == EOF
|| fputs("fmt ", fp) == EOF
|| fwrite(&chunkLength, sizeof(chunkLength), 1, fp) != 1
|| fwrite(&formatType, sizeof(formatType), 1, fp) != 1
|| fwrite(&numChannels, sizeof(numChannels), 1, fp) != 1
|| fwrite(&sampleRate, sizeof(sampleRate), 1, fp) != 1
|| fwrite(&bytesPerSecond, sizeof(bytesPerSecond), 1, fp) != 1
|| fwrite(&bytesPerSample, sizeof(bytesPerSample), 1, fp) != 1
|| fwrite(&bitsPerChannel, sizeof(bitsPerChannel), 1, fp) != 1
|| fputs("data", fp) == EOF
|| fwrite(&dataLength, sizeof(dataLength), 1, fp) != 1)
{
error = "Error writing header";
return false;
}

// if it's the same file, now we don't have to write it again unless it's
// been changed.
if (fp == writeFile)
changed = false;

return true;
}

bool WaveFile::ReadSample(float& sample)
{
double fSample;

bool retval = ReadSample(fSample);

sample = float(fSample);

return retval;
}

bool WaveFile::WriteSample(float sample)
{
return WriteSample(double(sample));
}

bool WaveFile::ReadSample(double& sample)
{
bool retval = false;

if (GetBitsPerChannel() == 8) {
unsigned char cSample;
retval = ReadSample(cSample);
sample = double(cSample) / ((1 << (8 - 1)) - 1) - 1;
} else if (GetBitsPerChannel() == 16) {
short sSample;
retval = ReadSample(sSample);
sample = double(sSample) / ((1 << (16 - 1)) - 1);
} else
error = "Floats can be written only as 8 or 16-bit samples";

return retval;
}

bool WaveFile::WriteSample(double sample)
{
if (GetBitsPerChannel() == 8)
return WriteSample((unsigned char)((sample + 1) * ((1 << (8 - 1)) - 1)));
else if (GetBitsPerChannel() == 16)
return WriteSample(short(sample * ((1 << (16 - 1)) - 1)));
else {
error = "Floats can be written only as 8 or 16-bit samples";
return false;
}
}

bool WaveFile::ReadSample(unsigned char& sample)
{
if (GetBitsPerChannel() != 8) {
error = "Sample size mismatch";
return false;
}

return ReadRaw((char*) &sample);
};

bool WaveFile::WriteSample(unsigned char sample)
{
if (GetBitsPerChannel() != 8) {
error = "Sample size mismatch";
return false;
}

return WriteRaw((char*) &sample);
};

bool WaveFile::ReadSample(short& sample)
{
if (GetBitsPerChannel() != 16) {
error = "Sample size mismatch";
return false;
}

return ReadRaw((char*) &sample, 2);
};

bool WaveFile::WriteSample(short sample)
{
if (GetBitsPerChannel() != 16) {
error = "Sample size mismatch";
return false;
}

return WriteRaw((char*) &sample, 2);
};

bool WaveFile::ReadSamples(unsigned char* samples, size_t count)
{
if (GetBitsPerChannel() != 8) {
error = "Sample size mismatch";
return false;
}

return ReadRaw((char*) samples, GetNumChannels() * count);
}

bool WaveFile::WriteSamples(unsigned char* samples, size_t count)
{
if (GetBitsPerChannel() != 8) {
error = "Sample size mismatch";
return false;
}

return WriteRaw((char*) samples, GetNumChannels() * count);
}

bool WaveFile::ReadSamples(short* samples, size_t count)
{
if (GetBitsPerChannel() != 16) {
error = "Sample size mismatch";
return false;
}

return ReadRaw((char*) samples, 2 * GetNumChannels() * count);
}

bool WaveFile::WriteSamples(short* samples, size_t count)
{
if (GetBitsPerChannel() != 16) {
error = "Sample size mismatch";
return false;
}

return WriteRaw((char*) samples, 2 * GetNumChannels() * count);
}

bool WaveFile::ReadRaw(char* buffer, size_t numBytes)
{
if (fread(buffer, 1, numBytes, GetFile()) != numBytes) {
error = "Couldn't read samples";
return false;
}

return true;
}

bool WaveFile::WriteRaw(char* buffer, size_t numBytes)
{
if (fwrite(buffer, 1, numBytes, writeFile) != numBytes) {
error = "Couldn't write samples";
return false;
}

SetDataLength(GetDataLength() + numBytes);

return true;
}

void WaveFile::ShowFormat(bool details)
{
cout
<< "Format: " << GetFormatType()
<< (IsCompressed()? " (compressed)" : " (PCM)") << endl
<< "Channels: " << GetNumChannels() << endl
<< "Sample rate: " << GetSampleRate() << endl
<< "Bytes per second: " << GetBytesPerSecond() << endl
<< "Bytes per sample: " << GetBytesPerSample() << endl
<< "Bits per channel: " << GetBitsPerChannel() << endl
<< "Bytes: " << GetDataLength() << endl
<< "Samples: " << GetNumSamples() << endl
<< "Seconds: " << GetNumSeconds() << endl;

if(GetFile())
cout << "File pointer: " << ftell(GetFile()) << endl;
else
cout << "File pointer: null" << endl;

if (details) {
string type, value;
if (GetFirstExtraItem(type, value)) {
cout << "Extra data:" << endl;
do {
cout << " " << type << ": " << value << endl;
} while (GetNextExtraItem(type, value));
}
ResetToStart();
}
}

Αρχειοθήκη ιστολογίου

eggs.in.art (my non-technical blog)