forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

streaming mp3 :)

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development
View previous topic :: View next topic  
Author Message
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Sat Dec 30, 2006 11:38 pm    Post subject: streaming mp3 :) Reply with quote

Hi folks,

i want to stream mp3 music files in my application.
The way i do it now is that the whole mp3 is loaded into memory and played. However since my game has expanded alot i only have like 3 to 6 mb of spare memory to play music. Since alot of player have larger files in there music dir, the game crashes when such a large file is loaded.

What is want it to stream the mp3 so that for example there is only 1 mb needed for playing every mp3 there is no matter how large it is.

is this possible? what does this do with speed? what libraries do i need? are there any examples?

hope to get some replies about this,

greets,

ghoti
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
TyRaNiD



Joined: 18 Jan 2004
Posts: 918

PostPosted: Sun Dec 31, 2006 1:58 am    Post subject: Reply with quote

I think libmad can consume a stream of mp3 on the fly so to speak, in fact it would be mostly useless if it didn't. Try doing some searchs for streaming with libmad as I am sure there will be samples
Back to top
View user's profile Send private message
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Sun Dec 31, 2006 7:28 am    Post subject: Reply with quote

Ah yes thanks, I have search and found out that it is called buffering :) and also some sources and examples so i'll try it out.

thanks and great work on the psplink app btw
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Mon Jan 08, 2007 8:53 pm    Post subject: Reply with quote

Hi me again,

i have looked at the source of the pspmediacenter but if i am correct it loads the entire mp3 file into memory :(
is there a quick way of modifying it? (my mp3 codec skills are non existing :S )

another question:
at this thread: http://forums.ps2dev.org/viewtopic.php?t=5089&highlight=mp3+buffered+playback PeterM has posted an example. in it i see this line:
Code:
pspAudioSetChannelCallback(0, fillOutputBuffer, 0);
does it set the callback so that you don't have to call it every loop iteration? or do i not understand correctly? and if so does it every callback check the fillOutputBuffer to see what has to be done?

also is it this way better then to do a custom callback every frameloop of the game?

hope to get some answer and thanks in advance
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
Raphael



Joined: 17 Jan 2006
Posts: 646
Location: Germany

PostPosted: Tue Jan 09, 2007 3:16 am    Post subject: Reply with quote

Ghoti wrote:
Hi me again,
another question:
at this thread: http://forums.ps2dev.org/viewtopic.php?t=5089&highlight=mp3+buffered+playback PeterM has posted an example. in it i see this line:
Code:
pspAudioSetChannelCallback(0, fillOutputBuffer, 0);
does it set the callback so that you don't have to call it every loop iteration? or do i not understand correctly? and if so does it every callback check the fillOutputBuffer to see what has to be done?

Code:

void pspAudioSetChannelCallback(int channel, pspAudioCallback_t callback, void *pdata)

This is the declaration of the function. As you see, the second parameter is a function, not a buffer. It is the function that actually fills the buffer with the new data every time it gets called. The pdata parameter is a pointer to data that is submitted to the callback function every time, so you could submit playback settings with it.
You need to create the callback function yourself, if you take a look at that source, you should find a function named fillOutputBuffer(...). Take a look at it to see how it should look and work.


Quote:

also is it this way better then to do a custom callback every frameloop of the game?

hope to get some answer and thanks in advance

Using the threaded callback method, you gain the advantage that the music playback is independent of your main loop, therefore you can easily pause your game and still have the music continue playing. Other than that, and saving you from having to manage the thread creation and management there is no advantage.
_________________
<Don't push the river, it flows.>
http://wordpress.fx-world.org - my devblog
http://wiki.fx-world.org - VFPU documentation wiki

Alexander Berl
Back to top
View user's profile Send private message Visit poster's website
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Fri Jan 12, 2007 9:26 pm    Post subject: Reply with quote

Hi i have the sample of PeterM almost working an i understand almost everything of the code. I have one problem though :s

Code:
Sample* destination = static_cast<Sample*> (buffer);
This function (static_cast) converts the Sample to a buffer right?

however i get this error when i try to compile it:
Code:
mp3player.c: In function 'fillOutputBuffer':
mp3player.c:789: error: 'static_cast' undeclared (first use in this function)
mp3player.c:789: error: (Each undeclared identifier is reported only once
mp3player.c:789: error: for each function it appears in.)
mp3player.c:789: error: syntax error before 'Sample'


any ideas why this is and what do i not get?


btw my code is in c not c++

greets ghoti
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
hlide



Joined: 10 Sep 2006
Posts: 750

PostPosted: Fri Jan 12, 2007 10:18 pm    Post subject: Reply with quote

Ghoti wrote:
Code:
Sample* destination = static_cast<Sample*> (buffer);


btw my code is in c not c++


this is C++ specific. In c, just do :
Code:
Sample* destination = (Sample*)buffer;
Back to top
View user's profile Send private message
PeterM



Joined: 31 Dec 2005
Posts: 125
Location: Edinburgh, UK

PostPosted: Sun Jan 14, 2007 5:01 am    Post subject: Reply with quote

Woops, shows how often I check this board :-)

If you need any more info about how the MP3 code works just reply. I've subscribed to this thread so I'll get an email.
Back to top
View user's profile Send private message Visit poster's website
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Mon Jan 15, 2007 6:45 am    Post subject: Reply with quote

Hi folks, i have the mp3 playing working but how do i stop the callback? and how do i pause it? and can i just change the file when a new song has to be loaded or do i have to do it some other way?

greets ghoti

PS to pause i have tried to make a boolean to just exit the callback function but then it gives a hanging sound as if the same part is playing in repeat. To stop it i have tried to shut down the audio and that works but there has to be a better way because that way i can't turn it back on. I have also tried to make a new callback to the 0 channel by passing 0 as a function and that stops the music but then if i try to load the fillbuffer into the callback then i get all craqcking and squeking noise...
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php


Last edited by Ghoti on Mon Jan 15, 2007 6:51 am; edited 1 time in total
Back to top
View user's profile Send private message
PeterM



Joined: 31 Dec 2005
Posts: 125
Location: Edinburgh, UK

PostPosted: Mon Jan 15, 2007 6:51 am    Post subject: Reply with quote

You probably don't want to stop the callback as such, but look into passing messages from your main thread to the callback thread.

Note that multi threaded programming is perhaps not for the uninitiated.

BTW, since the buffer will play continuously, you should fill it with zeroes if the player is paused or stopped.

Edit:

To clarify the message passing idea, investigate semaphores or mutexes. You probably want to set up a global queue.

The main thread could lock the queue, add a command message (play, pause, stop, skip, etc etc) and unlock the queue.

The callback thread could lock the queue, take the next command, unlock the queue and handle the command.

Note that it could hurt playback if opening files and stuff takes a long time and the decoded mp3 buffer can't be filled up in time before the buffer starts to play.

Clever buffering should fix this.
Back to top
View user's profile Send private message Visit poster's website
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Mon Jan 15, 2007 7:10 am    Post subject: Reply with quote

hi i'll look into the two things you have said, I'll learn alot from those things. But are those neccesary to just make the code pause, replay a song(isn't that just seek to the beginning of the file?) and load up a next song?

anyway, this will keep me occupite for a few days :) thanks and i'll let you know if i get it to work.

greets ghoti
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Tue Jan 16, 2007 10:24 pm    Post subject: Reply with quote

Hi I am trying to let the mp3 pause and It does not work like i want it:

Code:
      static void decode()
      {
         // While we need to fill the buffer...
         while (
            (mad_frame_decode(&frame, &stream) == -1) &&
            ((stream.error == MAD_ERROR_BUFLEN) || (stream.error == MAD_ERROR_BUFPTR))
            )
         {
           

            // Give new buffer to the stream.
            if (pausesong == 1) {
                  mad_stream_buffer(&stream, 0, 0);
                  //return;
            }
            else {
               // Fill up the remainder of the file buffer.
                  fillFileBuffer();
                  mad_stream_buffer(&stream, fileBuffer, sizeof(fileBuffer));
            }
         }

         // Synth the frame.
         mad_synth_frame(&synth, &frame);
      }
What Am i doing wrong here? because it does actually pause but when i unpause it, it crashes.
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
PeterM



Joined: 31 Dec 2005
Posts: 125
Location: Edinburgh, UK

PostPosted: Tue Jan 16, 2007 10:43 pm    Post subject: Reply with quote

For pausing, you should probably leave the decode() function as it is, and concentrate on the fillOutputBuffer() function.

You want to fill the output buffer with zeroes if the music is paused, instead of the whole mp3 decoding while loop.
Back to top
View user's profile Send private message Visit poster's website
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Tue Jan 16, 2007 11:51 pm    Post subject: Reply with quote

I'm sorry but i just don't get it :(
i have this:

Code:
     static void fillOutputBuffer(void* buffer, unsigned int samplesToWrite, void* userData)
      {
         // Where are we writing to?
      
       Sample* destination = (Sample*)buffer;
       if (pausesong == 1) {        
         //convertLeftSamples(destination, destination, NULL);
            //convertRightSamples(destination, destination, NULL);
//mad_synth_frame(NULL, NULL);

         return;
       }
         //Sample* destination = static_cast<Sample*> (buffer);
         
         // While we've got samples to write...

         while (samplesToWrite > 0)
         {
            // Enough samples available?
            const unsigned int samplesAvailable = synth.pcm.length - samplesRead;
         
            if (samplesAvailable > samplesToWrite)
            {
               // Write samplesToWrite samples.
               convertLeftSamples(destination, destination + samplesToWrite, &synth.pcm.samples[0][samplesRead]);
               convertRightSamples(destination, destination + samplesToWrite, &synth.pcm.samples[1][samplesRead]);

               // We're still using the same PCM data.
               samplesRead += samplesToWrite;

               // Done.
               samplesToWrite = 0;
            }
            else
            {
               // Write samplesAvailable samples.
               convertLeftSamples(destination, destination + samplesAvailable, &synth.pcm.samples[0][samplesRead]);
               convertRightSamples(destination, destination + samplesAvailable, &synth.pcm.samples[1][samplesRead]);

               // We need more PCM data.
               samplesRead = 0;
               decode();

               // We've still got more to write.
               destination += samplesAvailable;
               samplesToWrite -= samplesAvailable;
            }
         }
      }

this pauses the game but still plays one veryvery small part so it sounds like it hangs.

I just don't really understand the code :( can you explain the code for me line by line or is that to much to ask?

PS. using the convertleft and right in the pause piece of code does not make any difference, leaving them out results also into a hang...
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
PeterM



Joined: 31 Dec 2005
Posts: 125
Location: Edinburgh, UK

PostPosted: Wed Jan 17, 2007 4:37 am    Post subject: Reply with quote

Ghoti wrote:
I'm sorry but i just don't get it :(
i have this:

Code:
     static void fillOutputBuffer(void* buffer, unsigned int samplesToWrite, void* userData)
      {
         // Where are we writing to?
      
       Sample* destination = (Sample*)buffer;
       if (pausesong == 1) {        
         //convertLeftSamples(destination, destination, NULL);
            //convertRightSamples(destination, destination, NULL);
//mad_synth_frame(NULL, NULL);

         return;
       }



Try clearing the destination buffer inside the 'if' block. Use memset() or C++'s std::fill_n if you prefer.

Ghoti wrote:
I just don't really understand the code :( can you explain the code for me line by line or is that to much to ask?

I'm afraid so -- it would take a while.

It sounds like you are missing some regular C and C++ knowledge. Perhaps you should start with a simpler project, on Windows not PSP.

Pete
Back to top
View user's profile Send private message Visit poster's website
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Wed Jan 17, 2007 5:22 am    Post subject: Reply with quote

Hi,

i'll try to clear the buffer thanks for the tip.

I have done alot of C and C++ programming, I have made some software program for my company, also multi-user software. I have never come across these callback functions... :(

like i have never come across a function which you pass without parameters but the function itself has parameters which also already hold data. I did not understand that.

i understand almost everything from the code only the part why there is data in a function to which i give no parameters or data. That is what confusing me and what i don't get. :s

anyway thanks again for the tip try to find it out now.

greets ghoti
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
PeterM



Joined: 31 Dec 2005
Posts: 125
Location: Edinburgh, UK

PostPosted: Wed Jan 17, 2007 5:55 am    Post subject: Reply with quote

Ahh! It sounds like you know what you're talking about then. Sorry about that - I assumed you were new and out of your depth. Guess I made an ass out of myself there. Sorry again.

If I understand you correctly, you're having problems with function pointers, is that right? They're indeed one of the dark corners of C (and they get even uglier with C++ member function pointers), but well worth knowing.

This page ( http://www.newty.de/fpt/index.html ) is very good at teaching about function pointers.
Back to top
View user's profile Send private message Visit poster's website
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Wed Jan 17, 2007 6:54 am    Post subject: Reply with quote

Ah thank you very much for that link!
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Tue Mar 13, 2007 8:14 pm    Post subject: Reply with quote

Hi it's me again,

well the pause function works and i have put in a playnextsong button. the problem however is that I guess that the variable which hold the path to the song is being used by two thread :S
I have this char array, Song, this is used in fillFileBuffer() however this function is called as an callbackfunction (a different thread i guess)

the problem is that when i press the LBUTTON i call the function nextMusicNumber() which reads from a 2d char array which holds all songs(paths) but then i change the Song char. However since the song char can also be used by the thread, Now i have looked around and it looks like a dining philosophers problem. However how can i solve this on the PSP in C? I have not found any usable working articles about this in C. Is it possible in C? any pointers ??

thanks in advance also if it does not look like the problem i sketch or you have some other opinions please, please let me know it really gives me a headacke :S
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
PeterM



Joined: 31 Dec 2005
Posts: 125
Location: Edinburgh, UK

PostPosted: Tue Mar 13, 2007 8:24 pm    Post subject: Reply with quote

Hello,

Good to hear you've had some success in getting things working.

Sounds like you need to ensure that the two threads aren't reading/modifying the variables at the same time?

I don't think the PSP SDK has mutexes, but it does have semaphores. Hopefully a search on here for semaphore will turn something up.

Check here for some info on semaphores:
http://en.wikipedia.org/wiki/Semaphore_(programming)

Good luck!
Back to top
View user's profile Send private message Visit poster's website
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Wed Mar 14, 2007 9:36 pm    Post subject: Reply with quote

Hi thanks! nice to know someone with alot of knowledge answers this thread :) anyway i have another problem:S

I have 9 mp3 in a 2d char array. I loop through it to play them. after the last one has played the increment value is set to 0 so that it plays the first song again. This works fine for the first song of the array. I can loop 1 time through the song array but the second time it can only open the first song and after that it cannot open the mp3 file :S the mp3file however has not been changed so i guess the problem is that it is not closed properly and is still open. However i tried to solve this but it does not work. any ideas ?

how i change the song:
Code:
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

   ChangeSong: This function changes the song that
            the mp3 buffering will play.

   Param1:      A path to a musicsong
*/


   void ChangeSong(const char *Songtmp) {

      // wait for thread to be open
      sceKernelWaitSema(mutex, 1, 0);

      // Song is not used by another thread so we can change it
      sprintf(Song, "%s", Songtmp);

      // reset the file so it starts at the beginning
      //file = 0;

      // Mark status that the song is changed
      SongIsChanged = 1;

      // free the lock of this thread.
      sceKernelSignalSema(mutex, 1);
      
      return;
   }


and the fillbuffer function:

Code:
void fillFileBuffer()
{
   
   // wait for semaphore to be open
   sceKernelWaitSema(mutex, 1, 0);
 // Open the file if it's not open.
   if (SongIsChanged == 1) {
      //sceIoClose(file);
      SongIsChanged = 0;
      file = 0;
   }
    if (file <= 0)
    {
      char   cd[1024];
      memset(cd, 0, sizeof(cd));
      getcwd(cd, sizeof(cd) - 1);

      char   fileName[1024];
      memset(fileName, 0, sizeof(fileName));
      snprintf(fileName, sizeof(fileName) - 1, "%s", Song);

      //pspDebugScreenPrintf("Opening %s... ", fileName);
      file = sceIoOpen(fileName, PSP_O_RDONLY, 777);
      if (file <= 0)
      {
         pspDebugScreenPrintf("Failed (%s).\n", Song);
         sceKernelSignalSema(mutex, 1);
         MusicPlayerNextSong();
         return;
      }
      else
      {
         //pspDebugScreenPrintf("OK (%d).\n", file);
      }

      // Get the size.
      fileSize = sceIoLseek(file, 0, SEEK_END);
      sceIoLseek(file, 0, SEEK_SET);
    }

 // Find out how much to keep and how much to fill.
 const unsigned int   bytesToKeep   = stream.bufend - stream.next_frame;
 unsigned int      bytesToFill   = sizeof(fileBuffer) - bytesToKeep;
 //pspDebugScreenPrintf("bytesToFill = %u, bytesToKeep = %u.\n", bytesToFill, bytesToKeep);

 // Want to keep any bytes?
 if (bytesToKeep)
 {
    // Copy the tail to the head.
    memmove(fileBuffer, fileBuffer + sizeof(fileBuffer) - bytesToKeep, bytesToKeep);
 }

 // Read into the rest of the file buffer.
 unsigned char* bufferPos = fileBuffer + bytesToKeep;
 while (bytesToFill > 0)
 {
    // Read some.
    //pspDebugScreenPrintf("Reading %u bytes...\n", bytesToFill);
    const unsigned int bytesRead = sceIoRead(file, bufferPos, bytesToFill);

    // EOF?
    if (bytesRead == 0)
    {
       //pspDebugScreenPrintf("End of file.\n");
       //sceIoLseek(file, 0, SEEK_SET);
       //filePos = 0;
      MusicPlayerNextSong();
       break;
    }

    // Adjust where we're writing to.
    bytesToFill -= bytesRead;
    bufferPos += bytesRead;
    filePos += bytesRead;

    //pspDebugScreenPrintf("Read %u bytes from the file, %u left to fill.\n", bytesRead, bytesToFill);
    //pspDebugScreenPrintf("%u%%.\n", filePos * 100 / fileSize);
 }
 sceKernelSignalSema(mutex, 1);
}


any idea what is going wrong? is it really the opening and forgetting the closing of the file ?

greets ghoti
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
Raphael



Joined: 17 Jan 2006
Posts: 646
Location: Germany

PostPosted: Wed Mar 14, 2007 10:17 pm    Post subject: Reply with quote

Yes, that should be the problem. You should probably try to close the file as soon as the EOF is reached.

Code:

if (SongIsChanged == 1) {
      SongIsChanged = 0;
      if (file>0) sceIoClose(file); // If the file wasn't closed before (got to next song before reaching EOF), close it now
      file = 0;
   }


Code:

if (bytesRead == 0)
    {
       //pspDebugScreenPrintf("End of file.\n");
       sceIoClose(file);
       file = 0;
       //sceIoLseek(file, 0, SEEK_SET);
       //filePos = 0;
      MusicPlayerNextSong();
       break;
    }


Also, you probably should only call the open file function when a new song is selected, rather then every time the callback is entered without a file open, because the playback might just be stopped alltogether. Then change the fillbuffer to fill the buffer with zeros when file <= 0.
_________________
<Don't push the river, it flows.>
http://wordpress.fx-world.org - my devblog
http://wiki.fx-world.org - VFPU documentation wiki

Alexander Berl
Back to top
View user's profile Send private message Visit poster's website
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Wed Mar 14, 2007 11:28 pm    Post subject: Reply with quote

ah yes that fixed the problem :D mp3 buffering is working :) thank you all
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
Ghoti



Joined: 31 Dec 2005
Posts: 288

PostPosted: Sat Sep 08, 2007 11:13 pm    Post subject: Reply with quote

back again after quite a while :)

The buffering is working correctly and without any problems :D

The one big problem is that it is quite slow :(

Now I heard something about using the ME ? is there any progress made on this? example codes or something like that available ?

greets ghoti
_________________
My PSP games:

Boxy II: http://www.ghoti.nl/boxyii.php
Elementals: http://www.ghoti.nl/Elementals.php
Back to top
View user's profile Send private message
cooleyes



Joined: 18 May 2006
Posts: 125

PostPosted: Sun Sep 09, 2007 12:03 am    Post subject: Reply with quote

Ghoti wrote:
back again after quite a while :)

The buffering is working correctly and without any problems :D

The one big problem is that it is quite slow :(

Now I heard something about using the ME ? is there any progress made on this? example codes or something like that available ?

greets ghoti


en , you can use audiocodec.prx to decode mp3 frame

here is my demo code

http://forums.ps2dev.org/viewtopic.php?t=8469&highlight=
Back to top
View user's profile Send private message
Vincent_M



Joined: 03 Apr 2007
Posts: 73

PostPosted: Sun Sep 09, 2007 5:39 am    Post subject: Reply with quote

Does PSPMediaCenter handle code on the ME? Just curious. I'd check the code out for myself if I had the time, but I'll be gone for a while, and I won't be able to do much for the moment.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group