gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Garbled Wav Sound Playing from Memcard(FAT)

#149311 - sowee - Fri Jan 18, 2008 7:10 am

I'm loading a small wav file from fat, mono, 11025 and 8bits, and the code for playing the sound through the IPC from arm9 to arm7 is structured similar to the command ipc structure discussed in the double.co.nz ds dev't tutorial part 6.

My problem is that when the data i got from the wav file is played, it comes out garbled, either that or theres just a huge amount of noise.

This is my method for loading wav files from the memory card and storing the data in a SoundData structure:
Code:
SoundData Content_LoadWav(char *fname)
{
   FILE *fp;
   fp = fopen(fname, "rb");
   if (fp)
   {
      WAV_HDR *wav;
      CHK_HDR *chk;
      char buffer[80];
      int i;
      int chkCounter;
      int dataLengthBuffer;
      short int channels;
      long int sample_rate;
      long int seekOffset;
      unsigned int fSuccess;

      wav = (WAV_HDR *) malloc (sizeof(WAV_HDR));      //allocate memory for wav header
      chk = (CHK_HDR *) malloc (sizeof(CHK_HDR));
      fSuccess = fread((void *)wav,sizeof(WAV_HDR),(size_t)1,fp);//get wav headers
      if (fSuccess != 1) { printf("\n\tcan't read wav header\n");}
      for(i=0;i<4;i++)buffer[i] = wav->riffID[i];      //check for RIFF
      buffer[4] = 0;
      if(strcmp(buffer,"RIFF")!=0){ printf("\n\tbad RIFF format\n");}
      printf("\n\tShouldBeRiff:%s\n", buffer);
      for(i=0;i<4;i++)buffer[i] = wav->waveID[i];      //check for WAVE
      buffer[4] = 0;
      if(strcmp(buffer,"WAVE")!=0){ printf("\n\tbad WAVE format\n");}
      printf("\n\tShouldBeWAVE:%s\n", buffer);
      for(i=0;i<3;i++)buffer[i] = wav->fmtID[i];      //check for fmt
      buffer[3]=0;
      if(strcmp(buffer,"fmt")!=0){ printf("\n\tbad fmt format\n");}
      printf("\n\tShouldBefmt:%s\n", buffer);
      if(wav->format_tag!=1){printf("\n\tbad wav format_tag\n");}

      sample_rate = wav->sample_rate;
      printf("\n\tsample_rate:%d\n", sample_rate);
      if( (wav->bits_per_sample != 16) && (wav->bits_per_sample != 8) ){ //check if bit rate is valid
         printf("\n\t%d is a bad wav bits_per_sample\n", wav->bits_per_sample);}else {
            printf("\n\tbits_per_sample:%d\n",wav->bits_per_sample);}
      
      //skip over remaining portion of wav header
      channels = wav->channels;
      printf("\n\tchannels:%d\n", channels);
      seekOffset = wav->format_len - (sizeof(WAV_HDR) - 20);
      fSuccess = fseek(fp, seekOffset, SEEK_CUR);
      if (fSuccess!=0){printf("\n\tcan't seek\n");}

      chkCounter = 1;
      
      while(1)
      {
         if (chkCounter > 10) {printf("\n\ttoo many chunks\n"); break;}   //check attempt counter
         fSuccess = fread( (void*)chk, sizeof(CHK_HDR), (size_t)1, fp);
         if (fSuccess != 1){ printf("\n\tcan't read chunk\n"); break;} //get chunk header
         for (i=0;i<4;i++)buffer[i] = chk->dataID[i];
         buffer[4] = 0;
         if (strcmp(buffer, "data") == 0){break;}
         
         //skip over chunk
         chkCounter++;
         fSuccess = fseek(fp, chk->dLen, SEEK_CUR);
         if (fSuccess!=0){printf("\n\tcan't seek\n"); break;}
      }
      for(i=0;i<4;i++)buffer[i] = chk->dataID[i];      //check for fmt
      buffer[4]=0;
      printf("\n\tShouldBedata:%s\n", buffer);
      dataLengthBuffer = chk->dLen;
      //char wbuff[dataLengthBuffer];
      //char *wbuff;
      wbuff = (char *) malloc (sizeof(char)*dataLengthBuffer);
      fSuccess = fread((void *)wbuff, dataLengthBuffer, (size_t)1, fp);
      if (fSuccess!=1){printf("\n\tcan't read data\n");}
      
      
      universalSoundData.frequency = sample_rate;
      universalSoundData.length = dataLengthBuffer;
      printf("\n\tfrequency:%d\n", universalSoundData.frequency);
      printf("\n\tlength:%d\n", universalSoundData.length);
      
      free(universalSoundData.data);
      universalSoundData.data = (char *) malloc (sizeof(char)*dataLengthBuffer);
      memcpy(universalSoundData.data, wbuff, (sizeof(char)*dataLengthBuffer));
      //universalSoundData.data = &wbuff;
      
      free(wbuff);
      free(wav);
      free(chk);
      fclose(fp);
      return universalSoundData;
   }
   return;
}


As you can see, my concern here is with the universalSoundData.data, which supposedly holds the actual data of the wave file (headers excluded). Now i don't know why the sound comes out the way it does, do i need to do something to the data before i pass it to the arm7 to be played? or is it that theres just a problem with how i store the data?

#149313 - eKid - Fri Jan 18, 2008 7:34 am

Does the sound output resemble the correct sound slightly or is it just bad data? Why not just store the wave data as RAW? And how are you setting the sound registers? Oh, and what does WAVE_HDR look like? :)

#149319 - sowee - Fri Jan 18, 2008 10:11 am

The sound output resembles the actual, at least I think it does because I can somewhat hear the words being spoken in the wav file if I concentrate...either that or I'm just imagining it hehe :P

WAV_HDR looks like this:
Code:
typedef struct{
   char riffID[4];
   long int rLen;
   char waveID[4];
   char fmtID[4];
   long int format_len;
   short int format_tag;
   short int channels;
   long int sample_rate;
   long int avg_bytes_sec;
   short int block_align;
   short int bits_per_sample;
}WAV_HDR;


It doesn't really contain the actual sound data, just to get the header information out of the way and as a means for me to check the wav info.

I play the sound using this arm7 method through the ipc
Code:
static void PlaySoundOnce(SoundData* sd) {
   int channel = sd->channel;
   SCHANNEL_CR(channel) = 0;
   SCHANNEL_TIMER(channel) = SOUND_FREQ(sd->frequency);
   SCHANNEL_SOURCE(channel) = (uint32)sd->data;
   SCHANNEL_LENGTH(channel) = sd->length >> 2; 
   SCHANNEL_CR(channel) =
      SCHANNEL_ENABLE |
      SOUND_ONE_SHOT |
      SOUND_8BIT |
      SOUND_VOL(sd->volume);
}


I basically left the template main method of arm7 alone, and just added a ProcessAllSound() method at the end of the VBlankHandler method of the template which looks like this:


Code:
void ProcessAllSound() {
  static int currentSoundCommand = -1;
  while(currentSoundCommand != sharedSoundControl->currentSoundCommand) {
    SoundCommand* soundCommand = &sharedSoundControl->sound[currentSoundCommand];
    switch(soundCommand->playType) {
    case PLAY_ONE_SHOT_SAMPLE:
      PlaySoundOnce(&soundCommand->soundData);
      break;     
    }
    currentSoundCommand++;
    currentSoundCommand &= MAX_SOUNDS-1;
  }
}


it's basically just the same as the tutorial at double.co.nz, with the sharedSoundControl as a define for the ipc shared area

By "store the wave data as RAW" do you mean that I should convert the wave file first using a program, like sox?, or is there a way to convert the data after I fread it from the wave file and before I place it in the struct?
I want to be able to play wave files stored on the memory card so the former really isn't an option for me hehe. Thanks for looking into it :D much appreciated

#149320 - Dark Knight ez - Fri Jan 18, 2008 10:31 am

You do realise that 8bit wavs are unsigned, right? And that the DS requires RAW samples to be signed?

Code:
void toggleSignBit(unsigned char *sample, unsigned int len) {
    for(; len > 0; --len, ++sample) {
        *sample ^= 0x80;
    }
}

_________________
AmplituDS website

#149321 - sowee - Fri Jan 18, 2008 12:01 pm

wow..i didn't notice that, sorry hehe, goes to show how much experience I have with this stuff hehe :P i tried it out and it works fine now :) thanks a million for the quick solution!

#149322 - eKid - Fri Jan 18, 2008 12:24 pm

Ah.. yeah, treating unsigned as signed data can result in some pretty hard distortion :P