Playing mp3s with libmad: no sound
March 29, 2009 11:33PM
I'm attempting to play mp3s with libmad included with libogc, and asndlib. But, no sound is played, although it displays "playing play.mp3...". This is based on the libmad example.(from the libmad src)
I tried mp3s with samples rates 44100 and 48000, and 16-bit/32-bit float, but neither worked.

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SAMPLE_RATE 44100
#define FRAME_SIZE MAD_BUFFER_MDLEN //1024
#define RINGBUF_SIZE ((SAMPLE_RATE * 4) * 5)

struct mad_decoder decoder;

static void *xfb = NULL;
static GXRModeObj *rmode = NULL;

FILE *fmp3 = NULL;
bool play_mp3 = 1;
u8 *frame = NULL;
u16 *samples_ringbuffer;
u16 *samples_playbuf;
int mad_ringbuffer_pos, aud_ringbuffer_pos;
bool allow_play = 0;

/*struct buffer {
  unsigned char const *start;
  unsigned long length;
};*/

void voicecallback(s32 voice)
{
    memcpy(samples_playbuf, &samples_ringbuffer[aud_ringbuffer_pos], (44100 * 4));
    aud_ringbuffer_pos+=(SAMPLE_RATE * 4);
    ASND_AddVoice(voice, samples_playbuf, (SAMPLE_RATE * 4) + 16);
    if(aud_ringbuffer_pos > RINGBUF_SIZE * 10)aud_ringbuffer_pos = 0;
}

enum mad_flow input(void *data,
		    struct mad_stream *stream)
{
    int read_framelen = 0;
  //struct buffer *buffer = data;

  //if (!buffer->length)
    //return MAD_FLOW_STOP;

  //mad_stream_buffer(stream, buffer->start, buffer->length);

  //buffer->length = 0;

  if(!play_mp3 || fmp3==NULL)return MAD_FLOW_STOP;
  if(feof(fmp3)!=0)
  {
      ASND_StopVoice(8);
      return MAD_FLOW_STOP;
  }

  memset(frame, 0, FRAME_SIZE);
  read_framelen = fread(frame, 1, FRAME_SIZE, fmp3);
  mad_stream_buffer(stream, frame, read_framelen);

  return MAD_FLOW_CONTINUE;
}

/*
 * The following utility routine performs simple rounding, clipping, and
 * scaling of MAD's high-resolution samples down to 16 bits. It does not
 * perform any dithering or noise shaping, which would be recommended to
 * obtain any exceptional audio quality. It is therefore not recommended to
 * use this routine if high-quality output is desired.
 */
inline
signed int scale(mad_fixed_t sample)
{
  /* round */
  sample += (1L << (MAD_F_FRACBITS - 16));

  /* clip */
  if (sample >= MAD_F_ONE)
    sample = MAD_F_ONE - 1;
  else if (sample < -MAD_F_ONE)
    sample = -MAD_F_ONE;

  /* quantize */
  return sample >> (MAD_F_FRACBITS + 1 - 16);
}

/*
 * This is the output callback function. It is called after each frame of
 * MPEG audio data has been completely decoded. The purpose of this callback
 * is to output (or play) the decoded PCM audio.
 */

enum mad_flow output(void *data,
		     struct mad_header const *header,
		     struct mad_pcm *pcm)
{
  unsigned int nchannels, nsamples;
  mad_fixed_t const *left_ch, *right_ch;

  /* pcm->samplerate contains the sampling frequency */

  nchannels = pcm->channels;
  nsamples  = pcm->length;
  left_ch   = pcm->samples[0];
  right_ch  = pcm->samples[1];

  while (nsamples--) {
    signed int sample;

    /* output sample(s) in 16-bit signed little-endian PCM */

    sample = scale(*left_ch++);
    //putchar((sample >> 0) & 0xff);
    //putchar((sample >> 8) & 0xff);
    samples_ringbuffer[mad_ringbuffer_pos] = sample;
    mad_ringbuffer_pos++;
    if(mad_ringbuffer_pos * 2 > RINGBUF_SIZE)mad_ringbuffer_pos = 0;

    if (nchannels == 2) {
      sample = scale(*right_ch++);
      //putchar((sample >> 0) & 0xff);
      //putchar((sample >> 8) & 0xff);
      samples_ringbuffer[mad_ringbuffer_pos] = sample;
      mad_ringbuffer_pos++;
      if(mad_ringbuffer_pos * 2 > RINGBUF_SIZE)mad_ringbuffer_pos = 0;
    }
  }

  if(!allow_play)
  {
    allow_play = 1;
    memcpy(samples_playbuf, &samples_ringbuffer[aud_ringbuffer_pos], (SAMPLE_RATE * 4));
    aud_ringbuffer_pos+=(44100 * 4);
    if(ASND_SetVoice(8, VOICE_STEREO_16BIT, SAMPLE_RATE, 0, samples_playbuf, (SAMPLE_RATE * 4) + 16, 175, 175, voicecallback)==SND_INVALID)printf("ASND_SetVoice returned SND_INVALID.\n");
    if(aud_ringbuffer_pos > RINGBUF_SIZE)aud_ringbuffer_pos = 0;
  }

  return MAD_FLOW_CONTINUE;
}

/*
 * This is the error callback function. It is called whenever a decoding
 * error occurs. The error is indicated by stream->error; the list of
 * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
 * header file.
 */

enum mad_flow error(void *data,
		    struct mad_stream *stream,
		    struct mad_frame *frame)
{
  //struct buffer *buffer = data;

    fprintf(stderr, "decoding error.\n");

  /*fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
	  stream->error, mad_stream_errorstr(stream),
	  stream->this_frame - buffer->start);*/


  /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */

  return MAD_FLOW_CONTINUE;
}

int main(int argc, char **argv)
{
    // Initialise the video system
	VIDEO_Init();

	// This function initialises the attached controllers
    WPAD_Init();

	// Obtain the preferred video mode from the system
	// This will correspond to the settings in the Wii menu
	rmode = VIDEO_GetPreferredMode(NULL);

	// Allocate memory for the display in the uncached region
	xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));

	// Initialise the console, required for printf
	console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);

	// Set up the video registers with the chosen mode
	VIDEO_Configure(rmode);

	// Tell the video hardware where our display memory is
	VIDEO_SetNextFramebuffer(xfb);

	// Make the display visible
	VIDEO_SetBlack(FALSE);

	// Flush the video register changes to the hardware
	VIDEO_Flush();

	// Wait for Video setup to complete
	VIDEO_WaitVSync();
	if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();

    iprintf("\x1b[2;2HMadPlayMp3 - test playing mp3 from /play.mp3 from SD.\n");
    if(!fatInitDefault())
    {
        printf("Failed to init FAT.\n");
        while(1)
        {
            WPAD_ScanPads();
            if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) exit(0);
        }
    }

    printf("Old frame size %d %x\n", MAD_BUFFER_MDLEN, MAD_BUFFER_MDLEN);
    samples_ringbuffer = (u16*)malloc(RINGBUF_SIZE);
    if(samples_ringbuffer==NULL)
    {
        printf("Samples ring buffer memory allocation error.\n");
        return 1;
    }
    memset(samples_ringbuffer, 0, RINGBUF_SIZE);
    frame = (u8*)malloc(FRAME_SIZE);
    if(frame==NULL)
    {
        printf("Frame memory allocation error.(%d %x)\n", FRAME_SIZE, FRAME_SIZE);
        return 1;
    }
    memset(frame, 0, FRAME_SIZE);
    samples_playbuf = (u16*)memalign(32, (SAMPLE_RATE * 4) + 16);
    if(samples_playbuf==NULL)
    {
        printf("Samples play buf memory allocation error.\n");
        return 1;
    }
    memset(samples_playbuf, 0, (SAMPLE_RATE * 4) + 16);
    mad_ringbuffer_pos = 0;
    aud_ringbuffer_pos = 0;
    allow_play = 0;
    play_mp3 = 1;
    fmp3 = fopen("/play.mp3", "rb");
    if(fmp3==NULL)
    {
        printf("Failed to open /play.mp3\n");
        free(frame);
        while(1)
        {
            WPAD_ScanPads();
            if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) exit(0);
        }
    }
    printf("Playing /play.mp3...\n");

    /* configure input, output, and error functions */

    ASND_Init();
    ASND_Pause(0);

  mad_decoder_init(&decoder, 0/*&buffer*/,
		   input, 0 /* header */, 0 /* filter */, output,
		   error, 0 /* message */);
    mad_decoder_run(&decoder, MAD_DECODER_MODE_ASYNC);

	while(1)
	{
		WPAD_ScanPads();
		if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
		{
		    mad_decoder_finish(&decoder);
		    free(frame);
		    free(samples_ringbuffer);
		    free(samples_playbuf);
		    fclose(fmp3);
		    exit(0);
		}
		if (WPAD_ButtonsDown(0) & WPAD_BUTTON_B)play_mp3 = !play_mp3;
		if (WPAD_ButtonsDown(0) & WPAD_BUTTON_MINUS)play_mp3 = 0;
		if (WPAD_ButtonsDown(0) & WPAD_BUTTON_PLUS)play_mp3 = 1;
	}
	return 0;
}
Re: Playing mp3s with libmad: no sound
March 30, 2009 06:47AM
If you're just trying to play a plain old mp3. There are wrapper functions that do this for you.

They are in mp3player.h

void MP3Player_Init();
void MP3Player_Stop();
BOOL MP3Player_IsPlaying();
void MP3Player_Volume(u32 volume);
s32 MP3Player_PlayBuffer(const void *buffer,s32 len,void (*filterfunc)(struct mad_stream *,struct mad_frame *));
s32 MP3Player_PlayFile(void *cb_data,s32 (*reader)(void *,void *,s32),void (*filterfunc)(struct mad_stream *,struct mad_frame *));

Re: Playing mp3s with libmad: no sound
March 31, 2009 05:02AM
The last time I tried using that libmad wrapper with short sound effects, that wrapper wouldn't play the whole mp3. Also, mp3player was never updated to use asndlib.
Re: Playing mp3s with libmad: no sound
March 31, 2009 08:40AM
Ok I get you.

So first of all is the buffer supposed to be commented out of mad_decoder_init?

mad_decoder_init(&decoder, 0/*&buffer*/,
		   input, 0 /* header */, 0 /* filter */, output,
		   error, 0 /* message */);

Re: Playing mp3s with libmad: no sound
March 31, 2009 06:20PM
Yes, mp3player has been updated to use ASND, just initialize ASND first. Though not with enough flexibility to use it for sound effects. For those I'm using ASND directly, with raw PCM files.
Re: Playing mp3s with libmad: no sound
April 01, 2009 02:24AM
I'm attempting to play music, though the last time I tried playing mp3s was with libogc Mp3player and sound effects. I don't see anything for asndlib in mp3player.c, though I'll still try it sometime. I tried using a buffer, but that didn't help. Apparently the output callback is never called; I un-commented the putchar code and nothing displayed...
Re: Playing mp3s with libmad: no sound
April 01, 2009 09:57PM
This only plays about a second of the mp3, then there's clicking for a while, then nothing.
s32 mp3player_input(void* cb_data, void* ReadStart, s32 ReadLen)
{
    int read_framelen = 0;
  //struct buffer *buffer = data;

  //if (!buffer->length)
    //return MAD_FLOW_STOP;

  //mad_stream_buffer(stream, buffer->start, buffer->length);

  //buffer->length = 0;

  if(!play_mp3 || fmp3==NULL)return 0;
  if(feof(fmp3)!=0)
  {
      //ASND_StopVoice(8);
      return 0;
  }

  memset(frame, 0, ReadLen);
  read_framelen = fread(ReadStart, 1, ReadLen, fmp3);
  //read_framelen = fread(frame, 1, FRAME_SIZE, fmp3);
  //mad_stream_buffer(stream, frame, read_framelen);
  /*if(feof(fmp3)!=0)
  {
      //ASND_StopVoice(8);
      return 0;
  }*/
  return read_framelen;
}

int main(int argc, char **argv)
{
...
//ASND_Init();//Adding these don't help.
    //ASND_Pause(0);
    MP3Player_Init();
    MP3Player_PlayFile(&buffer, mp3player_input, 0);
...
}
Re: Playing mp3s with libmad: no sound
April 02, 2009 01:03AM
Read the whole file into a buffer first and use PlayBuffer.



Edited 1 time(s). Last edit at 04/02/2009 01:04AM by Tantric.
Re: Playing mp3s with libmad: no sound
April 04, 2009 07:55PM
This produced silence for a while, then noise, then nothing.
unsigned int mp3buffer_len = GetFileLength(fmp3);
    unsigned char *mp3buffer = (unsigned char*)malloc(mp3buffer_len);
    fread(mp3buffer, 1, mp3buffer_len, fmp3);
    MP3Player_PlayBuffer(mp3buffer, mp3buffer_len, NULL);
    fclose(fmp3);
Re: Playing mp3s with libmad: no sound
April 05, 2009 05:58PM
MP3 does work but it's a pain in the ass. I'm using ogg now. Small files, and seems to work better. You can check out libwiigui or Snes9x GX to see how it was done.
Re: Playing mp3s with libmad: no sound
April 07, 2009 07:38PM
Thanks Tantric, your libtremor port and your oggplayer.c code works perfectly with this test ogg.
Re: Playing mp3s with libmad: no sound
August 27, 2009 07:38PM
"small" bump lol

im not sure if tantric's ogg loading works but i need mp3 for my project

however, same thing happens here
it says its playing the mp3


but it just outputs nothing. i hear nothing at all (while it should load an epic mp3!)

anything anyone knows by now? >______________>
Re: Playing mp3s with libmad: no sound
August 28, 2009 01:31PM
Here's the complete Main.c from a simple app I wrote. I had exactly the issue you are describing, and fixed it eventually. Note that I did not know any C before beginning this, so please forgive any howlers and faux pas you encounter. Also, I included the file I wanted to play in the binary, but I think it should be quite simple to change it to play from SD.



#include 
#include 
#include 
#include 
#include 
#include 
#include  //needed for mp3
#include 
#include  //not used here
#include  //mp3
#include "grrlib/GRRLIB.h" //graphics drawing lib

#include "test_mp3.h"
#include "gfx/bg.h"
#include "gfx/point.h"
#include "gfx/font.h"
#include "gfx/button.h"
#include "gfx/message.h"

//Needed by GRRLIB
Mtx GXmodelView2D;

//video
static u32 *xfb;

static GXRModeObj *rmode;

//ir variable
ir_t ir;
	


// function for initialising video, called at start of main()
void Initialise() {
	
	VIDEO_Init();
		
	rmode = VIDEO_GetPreferredMode(NULL);
	
	xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
	console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
	
	VIDEO_Configure(rmode);
	VIDEO_SetNextFramebuffer(xfb);
	VIDEO_SetBlack(FALSE);
	VIDEO_Flush();
	VIDEO_WaitVSync();
	if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
	
	//used to have these in Initialise, but wouldn't allow turning on and off in while 
	//loop from main (), for some reason
	//ASND_Init();
	//MP3Player_Init();
	//if (!MP3Player_IsPlaying()) MP3Player_PlayBuffer(test_mp3,test_mp3_size,NULL);
	//MP3Player_Stop();
	
	//Wiimote activation
	WPAD_Init();
	WPAD_SetVRes(0, 640, 480);
	WPAD_SetDataFormat(WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR);
	
	
	
	
	
	
	//Initialiases GRRLIB, needed to begin loading and drawing 
	GRRLIB_Init();
	
}




int main() {
	//calls Initialise function, no reason really.
	Initialise();
	
	//dunno what this does
	printf("\x1b[2;OH");
	
	
	
	//loads bg texture and assigns it to tex_background
	GRRLIB_texImg tex_background = GRRLIB_LoadTexture(bg);
	
	//Draws background, probably not needed here
	GRRLIB_DrawImg(0, 0, tex_background, 0, 1, 1, 0xffffffff);
	
	//loads other textures
	GRRLIB_texImg tex_point = GRRLIB_LoadTexture(point);
	GRRLIB_texImg tex_button = GRRLIB_LoadTexture(button);
	GRRLIB_texImg tex_message = GRRLIB_LoadTexture(message);
	
	//makes graphics appear
	GRRLIB_Render();
	
	//variables used to track ir movement, positioned to keep cursor off screen before
	//wiimote is synched.  Poss leaving these undefined would stop issue with it appearing
	//slightly on screen still.
	int cursor_x = 0;
	int cursor_y = 0;
	
	//Initialises sound and mp3 things
	ASND_Init();
	MP3Player_Init();
	
	//If no music playing, play test.mp3 (underworld)
	if (!MP3Player_IsPlaying()) MP3Player_PlayBuffer(test_mp3,test_mp3_size,NULL);
	//then immediately stop. Loading in the while loop wasn't working, so done here then stopped.
	MP3Player_Stop();	

	//main loop for tracking input
	while(1) {
		
		//Checks wiimote button input
		WPAD_ScanPads();
		
		//assigns the variable pressed to record when a button is held
		u32 pressed = WPAD_ButtonsDown(0);
		
		
		
		//checks wiimote ir input
		WPAD_IR(0, &ir);
		
		//sets these variables to match ir position
		cursor_x = ir.x;
		cursor_y = ir.y;
		
		 //We fill the screen in white(RGBA format)
        GRRLIB_FillScreen(0xFFFFFFFF);
		//draw images (in correct order!)
		GRRLIB_DrawImg(0, 0, tex_background, 0, 1, 1, 0xffffffff);
		GRRLIB_DrawImg(445, 55, tex_button, 0, 1, 1, 0xffffffff);
		GRRLIB_DrawImg (176, 367, tex_message, 0, 1, 1, 0xffffffff);
		//Cursor offset as seems to set ir at 0,0 of the png
		GRRLIB_DrawImg((cursor_x - 40), (cursor_y - 40), tex_point, 0, 1, 1, 0xffffffff);
		
		//makes images appear
		GRRLIB_Render();
	
		
		//handles turning music on and off
		if (pressed & WPAD_BUTTON_A) {
				//if a is pressed, and the cursor is over the button, and music is not playing, play music
				if ((ir.x >= 449 && ir.x <= 495 && ir.y >=59 && ir.y <= 75) & (!MP3Player_IsPlaying())) MP3Player_PlayBuffer(test_mp3, test_mp3_size, NULL);
				//otherwise, if it is playing, stop it
				else if ((ir.x >= 449 && ir.x <= 495 && ir.y >=59 && ir.y <= 75) & (MP3Player_IsPlaying())) MP3Player_Stop();
			
			}
			
		//return to loader
		if (pressed & WPAD_BUTTON_HOME) {
			if (MP3Player_IsPlaying()) {
				MP3Player_Stop();
				exit(0);
				
			}
	
			exit(0);
		
		}
		
		
	}
	//Good practice, apparently.
	GRRLIB_Exit();
	return 0;
}

Re: Playing mp3s with libmad: no sound
August 28, 2009 06:01PM
ye i got it to work.
thx
forgot a few things
however, where did you get that mp3 from in that buffer? from an include i can't see? :/
Re: Playing mp3s with libmad: no sound
August 28, 2009 06:10PM
The raw mp3 is in a folder called "Data" in the root if the project folder. The include is just test_mp3.h, which is built from the data file when compiling - it doesn't exist anywhere in the source.

This is what's needed in the makefile:

#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).dol: $(OUTPUT).elf
$(OUTPUT).elf: $(OFILES)

#---------------------------------------------------------------------------------
# This rule links in binary data with the .mp3 extension
#---------------------------------------------------------------------------------
%.mp3.o	:	%.mp3
#---------------------------------------------------------------------------------
	@echo $(notdir $<)
	$(bin2o)

-include $(DEPENDS)

#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------

I left in irrelevant parts so you know where it goes.



Edited 2 time(s). Last edit at 08/28/2009 06:17PM by alainvey.
Sorry, only registered users may post in this forum.

Click here to login