sending audio file to I2S (UDA1380)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

sending audio file to I2S (UDA1380)

8,136 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mrabbasi61 on Sun Sep 07 23:45:28 MST 2014
Hi,
My board is based on Keil_MCB_4357 , it has a UDA1380 audio codec,
LPCOpen sample for I2S peripheral samples the audio from Mic or Line-in and then sends the sampled data (32 bit) to I2S, this sample is fine on my board.

but I want to open a file, read bytes one by one and send bytes to the uda1380 codec, I can only hear some noise when I send the buffer to UDA1380,

any comment or sample for doing this is appreciated.
Labels (1)
0 Kudos
Reply
10 Replies

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Wed Sep 10 04:26:30 MST 2014
Nope. You're multiplying a bitrate with 8 and won't get another bitrate.
If you have 11kHz in 8-bit mono, then 11 kHz * 8 = 88 kbps.
...or if you have 88kbps in 8-bit mono; 88kbps / 8 = 11kHz.
...or 44.1kHz in 16-bit stereo: 44.1 kHz * 16 * 2 = 1.4Mbps.

Normally you don't use the bitrate when you speak audio. You just use the sample-frequency, the number of channels and the channel size.

I think you can take any audio-file (mp3, wav, aiff or whichever format your system can read) and convert into either wav, aiff or raw sample data in the number of channels and bitwidth that you require.
Try the application Audacity; it's a good and free converter and it has an option to convert to "Other uncompressed files : RAW (header-less) : Signed 8 bit PCM.

Try doing as follows...
[list=1]
  [*]Load your audio file into Audacity.
  [*]Choose Track : Resample ... (enter or choose 11025)
  [*]Choose Track : Stereo Track to Mono
  [*]Choose Save As, in the PopUp button, select "Other uncompressed files", click "Options...", choose Header: "Raw (header-less)", Encoding: "Signed 8 bit PCM". Click OK and save the file.
  [*] If you're asked for header-information, clear all data and click OK.
[/list]
0 Kudos
Reply

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mrabbasi61 on Tue Sep 09 23:10:19 MST 2014
to make it simple, I find a wav file with details: 8 bit, mono , audio sample rate 11kbps ,  bit rate =88kbps

so it would be enough to read one 8 bit data from wav file and send it to uda1380, right?

I think that 11 * 8 = 88kbps , right?
if so, in which frequency I should send data bytes to uda1380? in 11kbps or in 88kbps ?!
0 Kudos
Reply

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Tue Sep 09 19:34:16 MST 2014
A .wav file can be one of the following (but not limited to)...
8-bit stereo
16-bit stereo
24-bit stereo
32-bit stereo
8-bit mono
16-bit mono
24-bit mono
32-bit mono
(or something completely different).

You will need to make your code look at the file's header bytes, in order to determine what kind of format it is.
If, for example, the file is a 16-bit stereo, and you find the data in the file, you should be able to read them and directly feed them to the I2S system.
But if your I2S was set up to play 24-bit and you have a 16-bit wav file, you can convert it on-the-fly by using this simple algorithm:
const uint16_t *s;
uint8_t *d;
uint16_t left_input;
uint16_t right_input;

/* (set up s to point to the 16-bit source data) */
/* (set up d to point to the 24-bit destination buffer) */

while(d < e) /* as long as you're within the buffer limit... */
{
    left_input = *s++;    /* read a 16-bit word */
    right_input = *s++;  /* read another 16-bit word */

    *d++ = left_input >> 8;  /* write highbyte as Most Significant Byte */
    *d++ = left_input;          /* write lowbyte as mid-byte */
    *d++ = left_input >> 8;  /* 8-bit extend by duplicating highbyte as the Least Significant Byte */
    /* you may need to insert a zero-byte here */
    *d++ = right_input >> 8;  /* write highbyte as Most Significant Byte */
    *d++ = right_input;          /* write lowbyte as mid-byte */
    *d++ = right_input >> 8;  /* 8-bit extend by duplicating highbyte as the Least Significant Byte */
    /* you may need to insert a zero-byte here */
}


Regarding the wav file format, this site might contain useful information.

... If you're looking to simplify your playback handler, you could extract the raw sample data on your desktop computer, write them in a pre-defined format (such as 16-bit, stereo, 96kHz for instance). That would make it much easier for you to write the code that plays the audio data.
The drawback is that everyone that needs to feed audio-data to your microcontroller, will have to convert the data using your program first.

The solution to this depends on what your application needs. If it's an audio-file player, it's better to have the decoder / converter in the playback code itself.

You can configure your codec to accept mono input if you like. See the datasheet for different configuration options. They include the word length (that's how many bits each sample is), mono/stereo and some filter-settings.

If you do not set the codec to mono, and you have mono data, then yes, you would have to make a duplicate of the mono data in order to get some usable audio, otherwise it would be slowed to 50%.
On the other hand, if you have stereo data and you want mono-playback, and you've set the dac to stereo mode, you can read the left channel and output it twice; once for left, once for right.
But converting stereo to mono is better done by reading the left, then the right, adding those two and dividing them by two...
mono = (left + right) >> 1;


You'll also need to set the playback frequency so it matches the input-file.
0 Kudos
Reply

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mrabbasi61 on Tue Sep 09 14:58:59 MST 2014
again thank you for complete info.
some questions:
1- if I open a wav file, if I read one byte, I need to send it to I2S for "left" and then send it again for "right" ? or there are seperate bytes in the wav file for left and right, and so I just need to read the bytes and send them to I2S ?

2- consider if I use uda1380 with just one line-out left , in mono mode, So, do you think again I should send data for both right and left?

3- is question 1 related to the properties of the wav file which I'm using? if yes, which properties will affect which parts?


0 Kudos
Reply

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Tue Sep 09 09:02:43 MST 2014

Quote: mrabbasi61
I thought that UDA1380 itself will decode mp3 data or wav data ... what is my problem?



Uhm, when I read the datasheet for UDA1380, I do not see any such automatic feature.

I2S means 'audio data'.
One word for the left channel and one word for the right channel.
You decide the word length and tell the DAC what the word length is.
You only send raw audio data words to the chip, unless it can be configured to do something fancy.

Codec means (en)Coder/Decoder - correct - but here codec refers to that it has both a DAC and an ADC.
What it does, is that it encodes / decodes digital data to/from analogue signals.
It does not imply that there is a mp3 encoder/decoder on the codec.
The datasheet does tell what applications it can be used for, such as MiniDisc, CD and MP3 players (and more), but it does not promise an on-chip mp3-decoder.

If there had been an automatic detection of mp3 data, there would be a lot of music you would never be able to play, as the audio-data would be interpreted in an incorrect way.

There could have been a mp3-decoder / encoder on the chip, but if there was, you would have needed to configure the chip (via the I2C interface), in order to go into "mp3 mode".


Quote: mrabbasi61
if I read the file in 16 bit data and then send this 16 bit data to uda1380, is it OK?



You need to send data continuously like this...
{ left, right, left, right, left, right, left, right, ... }

See the 'left' and 'right' words as being speaker positions. Eg. the speaker unit's position, ranging from -32768 to +32767 for 16 bit mode for instance.
You'll then be sending 44100 speaker positions to each speaker per second.
That makes it 88200 speaker positions of each 16 bit, thus 176400 bytes in total.

...But you may choose another bit-depth and frequency if you like.

You can make the LPC43xx's DMA send the data to the audio codec. This will easy your job, because then you will not have to handle the timing yourself; the I2S interface will do all that for you.
-But correct configuration is necessary; see UM10503 for more information on the I2S interface.

As written in the previous post, I recommend double-buffering. Allocate two buffers, for instance 256 bytes each.
Create a LLI item that points to one buffer and send 128 words of 16 bits each. The first word would be the one for the left channel, the next for the right channel and they would be in that order for the rest of the buffer.
The LLI's nextLLI field should point to a similar LLI, which points to the second buffer.
That LLI's nextLLI should point back to the first LLI, so that the DMA would keep switching between the two buffers.

Remember to enable interrupt requests at the completion of each LLI; this will trigger an interrupt service routine, which can fill new data in the buffer that just finished playing.

The left word and right word are raw samples. You can get raw samples from WAV or AIFF files, but if you need to play mp3 files, you'll need to decode them into left/right words.

The easiest way to start is to generate a square-wave...
Fill the first buffer with 128 words of 16 bits each. Those words should have the value 0xf000. Fill the next buffer with 128 words of 16 bits each as well. Those words should have the value 0x0fff.
I've not chosen a full volume here, because if by accident you turn up the volume, you will get a shock and perhaps have trouble hearing for a while.
Create an empty interrupt service routine at first.

-Once you've configured the I2S peripheral, attached the DMA (remember to make sure it's synchronized to the I2S interface) and set up those buffers, start the DMA.

You should be able to measure (using an oscilloscope) if you get a square-wave.
0 Kudos
Reply

4,679 Views
otibouya
Contributor I

Hi, is the idea here using the dma in M2P(memory to peripherial) mode to send the wave data which is in the LLI construct pointing to two buffers  towards the i2s_txfifo, and leave it to the interface for sending this data towards the uda1380? Thanks i am having a little trouble with synchornising the timing.

How sure are you of the solution you proposed(any practical experience or is it all theory a good no less), how is the dma transfer size to be determined ?

0 Kudos
Reply

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mrabbasi61 on Tue Sep 09 06:25:57 MST 2014
thank you for your reply.

I have a UDA1380 codec which is connected to I2S, like Keil_mcb_4357_board.
I thought that UDA1380 itself will decode mp3 data or wav data,
I simply open a file, read a buffer of bytes (8-bits), and then send the buffer byte by byte to the UDA1380 through I2S.
what is my problem?
if I read the file in 16 bit data and then send this 16 bit data to uda1380, is it OK?
0 Kudos
Reply

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Tue Sep 09 05:19:36 MST 2014
I believe you may want to start with something like raw sample-data.

Your example above is missing a 'mp3 decoder'. The MP3 format is compressed and is why you would hear only noise.
WAV is not compressed, but there are some header data present, so you will probably not want to play every byte you receive.

Also: The data you send should probably be 16 bit or 24 bit data.

Usually you would set up a double-buffer and an interrupt. The interrupt should be triggered when you reach the end of the double-buffer.
You can create a double-buffer using Linked List Items.

Each time you get an interrupt, one buffer is empty, and you should then fill it with sample-data in the bit-format that you've chosen; for instance 16-bit.
The DMA would then take care of playing the sampled sounds.
0 Kudos
Reply

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mrabbasi61 on Mon Sep 08 23:27:45 MST 2014
I have this part of code,
I open the file (mp3 or wav) and read a buffer from file, then I send the buffer to I2S BUS , but I just hear a noisy sound from speaker!! can someone help me solving this issue please.



f_mount(0, &fatFS);/* Register volume work area (never fails) */

rc = f_open(&fileObj, "test.mp3", FA_READ);
if (rc) {
DEBUGOUT("Unable to open test.mp3 from USB Disk\r\n");
die(rc);
}
else {
DEBUGOUT("Opened file test.mp3 from USB Disk. reading contents...\r\n\r\n");
for (;; ) {
/* Read a chunk of file */
rc = f_read(&fileObj, buffer, sizeof buffer, &br);
if (rc || !br) {
break;/* Error or end of file */
}

ptr = (uint32_t *) buffer;


for (i = 0; i < br; i++) {/* Type the data */    //i+=4
DEBUGOUT("%c", ptr);
//for(int k=0; k<1800; k++){};
send_flag = 1;

if ((Chip_I2S_GetTxLevel(CODEC_I2S_BUS) < 4) && (send_flag == 1)) {

Chip_I2S_Send(CODEC_I2S_BUS, ptr ); 
send_flag = 0;
//for(int k=0; k<10000; k++){};

}
}


}
if (rc) {
die(rc);
}

DEBUGOUT("\r\n\r\nClose the file.\r\n");
rc = f_close(&fileObj);
if (rc) {
die(rc);
}
}
0 Kudos
Reply

4,679 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mrabbasi61 on Mon Sep 08 01:22:34 MST 2014
is it possible to open a file (*.mp3 or *.wav) and send bytes one after another to I2S ?
or I need to start from a specific position?
Or I cannot send 8_bit data?
0 Kudos
Reply