Go Down

Topic: Converting WAV to C - not working... (Read 14326 times) previous topic - next topic

WilliamK Govinda

Guys, I found some nice Wav to C tools around the net but none work correctly. (at least not 100% correctly)

I get a different size compared to the original WAV file, its like it cuts out something at the end. And in some situations, the start is all wrong.

Any good tools for converting regular 8-bit mono files to c code?

Best Regards, WilliamK

CrossRoads

What is WAV? You don't mean music files like songname.wav do you?
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

WilliamK Govinda

Sorry, yes, .wav musical files to c, for things like this:

http://www.arduino.cc/playground/Code/PCMAudio

http://github.com/olleolleolle/wav2c

Here's an example code:

Code: [Select]
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
printf("wav2c v1.00 by Dark Fader / BlackThunder\n");
if (argc < 2) { printf("Syntax: %s <input.wav>\n", argv[0]); return -1; }
char buf[1024], *s = buf;
strcpy(s, argv[1]);
char *p = strstr(s, ".wav");
if (!p) { printf("Please specify a 8KHz, 8-bit wav file!\n"); return -1; }

FILE *fi = fopen(s, "rb");
if (!fi) { printf("Could not open input file!\n"); return -1; }
strcpy(p, ".c");
FILE *fo = fopen(s, "wt");
if (!fo) { printf("Could not open output file!\n"); return -1; }

fseek(fi, 0x28, SEEK_SET);
p = strrchr(s, '\\'); if (p) s = p+1;
p = strrchr(s, '/'); if (p) s = p+1;
p = strrchr(s, '.'); if (p) *p = 0;
fprintf(fo, "const unsigned char %s[] =\n{", s);

int l=0;
while (1)
{
if (l++ % 100 == 0) { fprintf(fo, "\n\t"); }
int a = fgetc(fi); if (a == EOF) break;
fprintf(fo, "%d,", a);
}

fprintf(fo, "\n};\n");

fclose(fi);
fclose(fo);
return 0;
}

westfw

Quote
fseek(fi, 0x28, SEEK_SET);

That's ... really awful.  According to online sources, .wav files have a relatively complex internal format, possibly containing lots of random data even for any particular sample size and playback.

http://www.dreamincode.net/code/snippet433.htm  Looks more promising, or something called "wavosaur."  These seem to output raw binary; you'll probably need an additional step to convert to C code.

WilliamK Govinda

Yup, thanks for the heads up, I also found the code to be very badly written. I will find a way to do my own based on my own. ;-)

Wk

scjurgen


The question in this case is: do you need the header for what you are going to do?
The tool you were using saves only the raw binary data, and not the header (that is why it is shorter). It also doesn't check if the format you are reading is ok (i.e. bitsize, compression, stereo, samplerate, 2's compliment data etc.).

davekw7x


...

http://github.com/olleolleolle/wav2c


After modifying  wav2c to declare the array to be unsigned char rather than signed char, as indicated in the playground article, I tested wav2c with 8-bit mono wave files at 8000 samples per second.

The only thing that I see wrong is the following statement, on line 181, in wavdata.c:
Code: [Select]

    realLength = (s->dataLength / s->numChannels / s->bitsPerSample * 8);


If the data length is not an exact multiple of 8, a number of  bytes (as many as seven bytes) are truncated to make it a multiple of 8.

The exact expression should be
Code: [Select]

    realLength = (s->dataLength / s->numChannels / (s->bitsPerSample / 8));


Also, I'm not sure that it is necessary, but as I mentioned, I changed line 243 in wavdata.c from
Code: [Select]

fprintf(fpO, "const signed char %s_dataL[]= {", name);

to
Code: [Select]

fprintf(fpO, "const unsigned char %s_dataL[]= {", name);


Note that eight-bit .wav files have their samples stored as unsigned bytes, so declaring the array to be an array of unsigned chars seems to be logical, but it doesn't change the bit patterns of the samples, so you don't have to "convert" anything.

Why not make these two simple changes and try using wav2c? See Footnote.

Then study the code and see what the heck it is doing with the various headers, etc.  Note that some .wav files may include lots of extra stuff after the last data sample, and you only want the data sample bytes in the array that Arduino is going to use.


Note that for your use, wav2c requires an 8-bit mono file at 8000 pcm samples per second. It's possible to eliminate code for the other types of file from wavdata.c if you feel the need.

If wav2c doesn't work for you, then how about attaching a small .wav file to your post (one second of 8-bit mono at 8000 samples/sec is 8000 data samples)

Regards,

Dave

Footnote:
If you want to write your own conversion program as a learning exercise, then I think that's a Good Thing.  Furthermore, I think that having something that already works so that you can compare your output to some good stuff is a Really Good Thing.  At some point you will see things that you can improve on, and that means that You Have Arrived!

WilliamK Govinda

Dave, thank you so much, I couldn't look at the code, that's why I asked, I was just going nuts reading the PDF for the ATmega328. But at least now I understand better how Timers and PWM works. ;-)

Wk

davekw7x

#8
Feb 17, 2011, 05:18 pm Last Edit: Feb 18, 2011, 06:59 pm by davekw7x Reason: 1

...I couldn't look at the code...

We have a disconnect somewhere.  Your original post indicated that you were looking for tools to convert .wav files to something that could be used in the Arduino playground PCMAudio article.  The junky snippet that you posted (from Dark Fader / BlackThunder---gimme a break) is not from the wav2c program at the url that you gave as a reference.

I found Ino Schlaucher's code at  https://github.com/olleolleolle/wav2c to be a somewhat better than "junky."  Actually, a lot better than "junky" for your purpose.


[/Begin edited note] The wav2c from that url works if (and only if) the compiler uses 32-bit ints and 16-bit short ints.  This is the case for all of the workstation compilers to which I have access (GNU on 32-bit and 64-bit Linux and 32-bit Windows XP/cygwin, various Microsoft compilers and Borland compilers for 32-bit Windows XP).  If anyone has used workstation compilers for which these are not the same, I would like to know.  The "right" way is to put assertions in the program to inhibit execution for different sizes and let the user make typedefs appropriate for that particular setup.  I know that compilers for embedded systems (like avr-gcc) may have 16-bit ints rather than 32-bit ints, and, in my experience, porting code from one environment to another is somewhat easier if you use <stdint.h> designations instead of "plain vanilla" integer data types in places where size matters.  Of course I wouldn't be porting this particular code to an Arduino, but I thought I would point out a way of using size-dependent things in a way that is a little more robust.


I do something like the following in my programs.
Code: [Select]

/* GNU compilers have <stdint.h>, some others do not */
#ifdef __GNUC__
#define HAVE_STDINT
#endif

/*
   Maybe some other compilers, like recent Borland compilers, also have <stdint.h>
   so use their predefined macros here
*/

#ifdef HAVE_STDINT
#include <stdint.h>
#else
   /*
      If your compiler does not have stdint.h, and your
      compiler does not have 16-bit shorts and 32-bit ints,
      then redo the following typedefs to make them
      commensurate with your system. Runtime checks will
      make sure you did it right.
   */
   typedef unsigned char  uint8_t;
   typedef unsigned short uint16_t;
   typedef unsigned int   uint32_t;
#endif

#include <assert.h>
.
.
.
int main()
{
.
.
.
   assert(sizeof(uint16_t) == 2);
   assert(sizeof(uint32_t) == 4);


Then, when referring to header fields where size matters, I use uint16_t and uint32_t instead of short int and int designations.  See Footnote.
[/End edited note]


Regards,

Dave

Footnote:
Yes, it's true: Sometimes size matters!

WilliamK Govinda

Ah, yes, I mixed things a bit, sorry for that, doing too many things at once does this to your brain.  :smiley-eek:

But thanks for pointing that out, it sure looks like its a much better solution which I can keep and just add some extra layers to check for special wav headers. (later on)

Wk

westfw

Quote
I found Ino Schlaucher's code at  https://github.com/olleolleolle/wav2c to be a somewhat better than "junky."  Actually, a lot better than "junky" for your purpose.

Yes; that looks much better than the quoted snippet...

WilliamK Govinda

Thanks guys for all the input. I was able to create my own code instead, based on an old code I had. Its not perfect, but its much better. It reads 16 bit files, converts to mono, downsize to 8-bits and outputs the 8-bit unsigned char code for the arduino project.

I will post the code after I clean it up a little. ;-) (its a mess and has a few bugs I'm working on)

Wk

gluontronic


WilliamK Govinda

Yes, but I haven't got time to fix the bugs yet, will be able too in the other week. ;-)

Wk

gluontronic

ok :)
how about using real time sample reading from externa memory?
could it work?

Go Up