×

Discussion Board

Results 1 to 7 of 7
  1. #1
    Registered User
    Join Date
    Jan 2005
    Posts
    48

    Unhappy How to repeat audio playback in audio streaming?

    Hi all,

    I'd really appreciate any help at this point of time, as I'm running out of
    time, and haven't a found a way to solve this problem.
    I've implemented a vorbis decoder for Symbian OS V7.0. Now I'm able to
    stream the device resident ogg files easily but
    when it comes to repeating the playback of the same file after its previous
    playback has got over, I've gone nuts.

    I tried simply calling PlayFunction of my audio engine within a loop, but it
    gives me a panic no 8, which means Audio stream is already open.
    Following is the code below of my audio engine.
    Code:
    #include "CVorbisEngine.h"
    #include <reent.h>
    #include <mda\common\audio.h>
    
    CVorbisEngine * CVorbisEngine::NewL(ETestType aType)
        {
        CVorbisEngine* self = new (ELeave) CVorbisEngine;
        CleanupStack::PushL( self );
        self->ConstructL ( aType );
        CleanupStack::Pop( self );
        return self;
        }
    
    void CVorbisEngine::ConstructL(ETestType aType)
        {
            iTestType = aType;
         if (aType == EStreamAudio)
            {
            iOutputStream =  CMdaAudioOutputStream::NewL(*this);
            iIsPlayBackOver=ETrue;
            iStatus=EEngineNotReady;
            }
        }
    
     //Actual decoding function
     void CVorbisEngine::OpenFileL(const TDesC8 &aFileName)
        {
       if(!iOutputStream)
        iOutputStream =  CMdaAudioOutputStream::NewL(*this);
    //    InitStream();
        FILE * aFile;
        char * filename ;
        filename = (char *) aFileName.Ptr();
        aFile = fopen((char *) aFileName.Ptr() , "r+");
    
        if(ov_open(aFile, &iVf, NULL, 0) < 0) {
             User::Leave(KErrNotSupported);
            }
    
        vorbis_info *vi=ov_info(&iVf,-1);
    
        switch(vi->rate)
            {
            case 8000:
                iRate = TMdaAudioDataSettings::ESampleRate8000Hz;
                break;
            case 11025:
                iRate = TMdaAudioDataSettings::ESampleRate11025Hz;
                break;
            case 16000:
                iRate = TMdaAudioDataSettings::ESampleRate16000Hz;
                break;
            case 22050:
                iRate = TMdaAudioDataSettings::ESampleRate22050Hz;
                break;
            case 32000:
                iRate = TMdaAudioDataSettings::ESampleRate32000Hz;
                break;
            case 44100:
                iRate = TMdaAudioDataSettings::ESampleRate44100Hz;
                break;
            case 48000:
                iRate = TMdaAudioDataSettings::ESampleRate48000Hz;
                break;
            default:
                User::Leave(KErrNotSupported);
            }
    
        switch(vi->channels)
            {
            case 1:
                iNbChannels =  TMdaAudioDataSettings::EChannelsMono;
                break;
            case 2:
                iNbChannels =  TMdaAudioDataSettings::EChannelsStereo;
                break;
            default:
                User::Leave(KErrNotSupported);
            }
        TMdaAudioDataSettings settings;
        settings.Query();
    
        // Initialize the Audio Stream
         settings.iSampleRate = iRate;
         settings.iChannels = iNbChannels;
         settings.iVolume = settings.iMaxVolume;
         if(iStatus!=EEnginePlaying)
          iOutputStream->Open(&settings);
      }
    
     int CVorbisEngine::FillSampleBufferL()
        {
    
        if(iStatus == EEngineReady)
            iStatus = EEnginePlaying;
        // Fill one buffer of PCM samples
        int current_section;
        TDes8 * bufPtr ;
    
        // Swap buffers
        // So that there is always one buffer ready waiting to
        // be streamed.
    
        if (iCurrentBuffer == 0)
            {
            bufPtr = &iPcmout1;
            iCurrentBuffer = 1;
            }
        else
            {
            bufPtr = &iPcmout2;
            iCurrentBuffer = 0;
            }
    
        // Decode the ogg file
        long ret=ov_read(&iVf,(char *)
    bufPtr->Ptr(),bufPtr->MaxLength(),&current_section); //Actual decoding
    function called from the Ogg library.
        if (ret == 0)
            {
            /* EOF */
            //EndOfFileL();
            }
        else if (ret < 0)
            {
            /* error in the stream.  Not a problem, just reporting it in
            case we (the app) cares.  In this case, we don't. */
            }
        else
            {
            /* we don't bother dealing with sample rate changes, etc, but
            you'll have to*/
            bufPtr->SetLength(ret);
            BufferFullL(*bufPtr);
            }
            return ret;
        }
    
    void CVorbisEngine::BufferFullL(TDes8 &aBuffer)
        {
         // Write the buffer to the audio stream
           iOutputStream->WriteL( aBuffer );
        }
    
    
    CVorbisEngine:: ~CVorbisEngine()
        {
    
        /* cleanup */
        ov_clear(&iVf);
        CloseSTDLIB();
        delete( iOutputStream );
        iOutputStream=NULL;
        }
    
    
    /////////////////////////////////////
    // Functions used for Audio Streaming
    /////////////////////////////////////
    
    
    void CVorbisEngine::MaoscPlayComplete(TInt aError)
        {
        if(aError==KErrUnderflow)
         {
          //ov_clear(&iVf);
          delete( iOutputStream );
          iOutputStream=NULL;
          //iIsPlayBackOver=EFalse;
          iStatus=EEngineReady;
         }
        }
    
    
    void CVorbisEngine::MaoscBufferCopied(TInt aError, const TDesC8&
    /*aBuffer*/)
        {
        if (aError)
            {
            aError = 0;
            }
        FillSampleBufferL();
        }
    
    
    void CVorbisEngine::MaoscOpenComplete(TInt aError)
        {
        if (aError)
         {
          aError = 0;
         }
        iStatus=EEngineReady;
        iOutputStream->SetVolume(iOutputStream->MaxVolume());
        iOutputStream->SetAudioPropertiesL(iRate,iNbChannels);
        iOutputStream->SetPriority(EMdaPriorityMax,
    EMdaPriorityPreferenceQuality);
        FillSampleBufferL();
        FillSampleBufferL();
        }
    This is how I'm trying to use it.
    Code:
    void CMyAppView::HandleControlEventL(CCoeControl* aControl, TCoeEvent
    aEventType)
     {
      if(aEventType==EEventPrepareFocusTransition)
      {
       if(Index(aControl)==1)
       {
        for (int i=0;i<iModel->GetAudioRep();i++) // In this loop gives me the
    panic no 8 error, so I remove this loop and it works fine.
         {
          PlayAudioFile(iTemp);
         }
       }
      }
     }
    
     void CMyAppView::PlayAudioFile(TInt aStrId)
     {
       TBufC8<80> audioFilePath;
       TPtr8 tmpPtr(audioFilePath.Des());
       iDBMSEngine->GetAudioFileNameL(tmpPtr,aStrId);
       iAudioEngine->OpenFileL(audioFilePath);
     }
    As you can see that I'm trying to call the same function say for X number of
    times within a loop but even before the Playback gets over, the
    PlayAudioFile method gets over and it tries to re-run the same function. As
    a result, I try to create a stream again over an already opened one.

    How can I implement a repeated playback of the audio within my view? Would I
    have to resort to active objects?
    If yes, would really appreciate if someone could provide a code snippet or a
    psuedo code achieving the same.
    Any idea, comment or suggestion would be extremely welcome as I'm not able
    to make any headway with this problem.

    Thanks & regards,
    Asheesh

  2. #2
    Regular Contributor
    Join Date
    May 2007
    Posts
    114

    Re: How to repeat audio playback in audio streaming?

    Quote Originally Posted by asheeshv
    Hi all,

    I'd really appreciate any help at this point of time, as I'm running out of
    time, and haven't a found a way to solve this problem.
    I've implemented a vorbis decoder for Symbian OS V7.0. Now I'm able to
    stream the device resident ogg files easily but
    when it comes to repeating the playback of the same file after its previous
    playback has got over, I've gone nuts.

    I tried simply calling PlayFunction of my audio engine within a loop, but it
    gives me a panic no 8, which means Audio stream is already open.
    Following is the code below of my audio engine.
    Code:
    #include "CVorbisEngine.h"
    #include <reent.h>
    #include <mda\common\audio.h>
    
    CVorbisEngine * CVorbisEngine::NewL(ETestType aType)
        {
        CVorbisEngine* self = new (ELeave) CVorbisEngine;
        CleanupStack::PushL( self );
        self->ConstructL ( aType );
        CleanupStack::Pop( self );
        return self;
        }
    
    void CVorbisEngine::ConstructL(ETestType aType)
        {
            iTestType = aType;
         if (aType == EStreamAudio)
            {
            iOutputStream =  CMdaAudioOutputStream::NewL(*this);
            iIsPlayBackOver=ETrue;
            iStatus=EEngineNotReady;
            }
        }
    
     //Actual decoding function
     void CVorbisEngine::OpenFileL(const TDesC8 &aFileName)
        {
       if(!iOutputStream)
        iOutputStream =  CMdaAudioOutputStream::NewL(*this);
    //    InitStream();
        FILE * aFile;
        char * filename ;
        filename = (char *) aFileName.Ptr();
        aFile = fopen((char *) aFileName.Ptr() , "r+");
    
        if(ov_open(aFile, &iVf, NULL, 0) < 0) {
             User::Leave(KErrNotSupported);
            }
    
        vorbis_info *vi=ov_info(&iVf,-1);
    
        switch(vi->rate)
            {
            case 8000:
                iRate = TMdaAudioDataSettings::ESampleRate8000Hz;
                break;
            case 11025:
                iRate = TMdaAudioDataSettings::ESampleRate11025Hz;
                break;
            case 16000:
                iRate = TMdaAudioDataSettings::ESampleRate16000Hz;
                break;
            case 22050:
                iRate = TMdaAudioDataSettings::ESampleRate22050Hz;
                break;
            case 32000:
                iRate = TMdaAudioDataSettings::ESampleRate32000Hz;
                break;
            case 44100:
                iRate = TMdaAudioDataSettings::ESampleRate44100Hz;
                break;
            case 48000:
                iRate = TMdaAudioDataSettings::ESampleRate48000Hz;
                break;
            default:
                User::Leave(KErrNotSupported);
            }
    
        switch(vi->channels)
            {
            case 1:
                iNbChannels =  TMdaAudioDataSettings::EChannelsMono;
                break;
            case 2:
                iNbChannels =  TMdaAudioDataSettings::EChannelsStereo;
                break;
            default:
                User::Leave(KErrNotSupported);
            }
        TMdaAudioDataSettings settings;
        settings.Query();
    
        // Initialize the Audio Stream
         settings.iSampleRate = iRate;
         settings.iChannels = iNbChannels;
         settings.iVolume = settings.iMaxVolume;
         if(iStatus!=EEnginePlaying)
          iOutputStream->Open(&settings);
      }
    
     int CVorbisEngine::FillSampleBufferL()
        {
    
        if(iStatus == EEngineReady)
            iStatus = EEnginePlaying;
        // Fill one buffer of PCM samples
        int current_section;
        TDes8 * bufPtr ;
    
        // Swap buffers
        // So that there is always one buffer ready waiting to
        // be streamed.
    
        if (iCurrentBuffer == 0)
            {
            bufPtr = &iPcmout1;
            iCurrentBuffer = 1;
            }
        else
            {
            bufPtr = &iPcmout2;
            iCurrentBuffer = 0;
            }
    
        // Decode the ogg file
        long ret=ov_read(&iVf,(char *)
    bufPtr->Ptr(),bufPtr->MaxLength(),&current_section); //Actual decoding
    function called from the Ogg library.
        if (ret == 0)
            {
            /* EOF */
            //EndOfFileL();
            }
        else if (ret < 0)
            {
            /* error in the stream.  Not a problem, just reporting it in
            case we (the app) cares.  In this case, we don't. */
            }
        else
            {
            /* we don't bother dealing with sample rate changes, etc, but
            you'll have to*/
            bufPtr->SetLength(ret);
            BufferFullL(*bufPtr);
            }
            return ret;
        }
    
    void CVorbisEngine::BufferFullL(TDes8 &aBuffer)
        {
         // Write the buffer to the audio stream
           iOutputStream->WriteL( aBuffer );
        }
    
    
    CVorbisEngine:: ~CVorbisEngine()
        {
    
        /* cleanup */
        ov_clear(&iVf);
        CloseSTDLIB();
        delete( iOutputStream );
        iOutputStream=NULL;
        }
    
    
    /////////////////////////////////////
    // Functions used for Audio Streaming
    /////////////////////////////////////
    
    
    void CVorbisEngine::MaoscPlayComplete(TInt aError)
        {
        if(aError==KErrUnderflow)
         {
          //ov_clear(&iVf);
          delete( iOutputStream );
          iOutputStream=NULL;
          //iIsPlayBackOver=EFalse;
          iStatus=EEngineReady;
         }
        }
    
    
    void CVorbisEngine::MaoscBufferCopied(TInt aError, const TDesC8&
    /*aBuffer*/)
        {
        if (aError)
            {
            aError = 0;
            }
        FillSampleBufferL();
        }
    
    
    void CVorbisEngine::MaoscOpenComplete(TInt aError)
        {
        if (aError)
         {
          aError = 0;
         }
        iStatus=EEngineReady;
        iOutputStream->SetVolume(iOutputStream->MaxVolume());
        iOutputStream->SetAudioPropertiesL(iRate,iNbChannels);
        iOutputStream->SetPriority(EMdaPriorityMax,
    EMdaPriorityPreferenceQuality);
        FillSampleBufferL();
        FillSampleBufferL();
        }
    This is how I'm trying to use it.
    Code:
    void CMyAppView::HandleControlEventL(CCoeControl* aControl, TCoeEvent
    aEventType)
     {
      if(aEventType==EEventPrepareFocusTransition)
      {
       if(Index(aControl)==1)
       {
        for (int i=0;i<iModel->GetAudioRep();i++) // In this loop gives me the
    panic no 8 error, so I remove this loop and it works fine.
         {
          PlayAudioFile(iTemp);
         }
       }
      }
     }
    
     void CMyAppView::PlayAudioFile(TInt aStrId)
     {
       TBufC8<80> audioFilePath;
       TPtr8 tmpPtr(audioFilePath.Des());
       iDBMSEngine->GetAudioFileNameL(tmpPtr,aStrId);
       iAudioEngine->OpenFileL(audioFilePath);
     }
    As you can see that I'm trying to call the same function say for X number of
    times within a loop but even before the Playback gets over, the
    PlayAudioFile method gets over and it tries to re-run the same function. As
    a result, I try to create a stream again over an already opened one.

    How can I implement a repeated playback of the audio within my view? Would I
    have to resort to active objects?
    If yes, would really appreciate if someone could provide a code snippet or a
    psuedo code achieving the same.
    Any idea, comment or suggestion would be extremely welcome as I'm not able
    to make any headway with this problem.

    Thanks & regards,
    Asheesh

    hi asheesh

    can u help me. i m very novice in this field .my problem is related with ur problem.
    in my application i m using a single buffer for passing the data to the player.
    due to this i m getting some delay like first time i got 4 seconds after 2 minutes itr becomes 6 secnds. i mean its not consistent.
    can u tell me how can i manage this with two buffer.coz i think this delay is due to find empty buffer.my voice quality is good there is no packet loosing.there is just delay.
    plz give me some guidlines.
    thanx in advance..
    Last edited by mobile_don; 2007-07-09 at 10:52.

  3. #3
    Super Contributor
    Join Date
    Mar 2004
    Location
    Singapore
    Posts
    9,968

    Re: How to repeat audio playback in audio streaming?

    Quote Originally Posted by mobile_don
    hi asheesh

    can u help me. i m very novice in this field .my problem is related with ur problem.
    in my application i m using a single buffer for passing the data to the player.
    due to thgis i m getting some delay like first time i got 4 seconds after 2 minutes itr becomes 6 secnds. i mean its not consistent.
    can u tell me how can i manage this with two buffer.coz i think this delay is due to find empty buffer.my voice quality is good there is no packet loosing.there is just delay.
    plz give me some guidlines.
    thanx in advance..
    I Suggest using a Queue to store the Audio.

    like A1 -> A2 -> A3-> A4 .... -> An
    so you play A1 then A2 . after play you delete that node.

    and you fill like An ->A(n+1)->A(n+2) .... etc...

    So there will not be any delay ..

    Also make a check so that under Flow does not occur..

  4. #4
    Registered User
    Join Date
    Feb 2005
    Posts
    3

    Re: How to repeat audio playback in audio streaming?

    Hi Mobile_don..
    I've posted the sample code.
    Hope this helps..
    int CVorbisEngine::FillSampleBufferL()
    {
    // Fill one buffer of PCM samples
    int current_section;
    TDes8 * bufPtr ;

    // Swap buffers
    // So that there is always one buffer ready waiting to
    // be streamed.

    if (iCurrentBuffer == 0)
    {
    bufPtr = &iPcmout1;
    iCurrentBuffer = 1;
    }
    else
    {
    bufPtr = &iPcmout2;
    iCurrentBuffer = 0;
    }

    // Decode the ogg file
    long ret=ov_read(&iVf,(char *) bufPtr->Ptr(),bufPtr->MaxLength(),&current_section);
    if (ret == 0)
    {
    iEof=ETrue;
    /* EOF */
    }
    else if (ret < 0)
    {
    iEof=EFalse;
    /* error in the stream. Not a problem, just reporting it in
    case we (the app) cares. In this case, we don't. */
    }
    else
    {
    /* we don't bother dealing with sample rate changes, etc, but
    you'll have to*/
    bufPtr->SetLength(ret);
    BufferFullL(*bufPtr);
    }
    return ret;
    }

    // Write the buffer to the audio stream
    void CVorbisEngine::BufferFullL(TDes8 &aBuffer)
    {

    iEngineState=EStreamPlaying;
    iOutputStream->WriteL( aBuffer );
    }

    The 2 buffers I'm using are declared as:
    TBuf8<4096> iPcmout1; //Buffers for holding decoded data
    TBuf8<4096> iPcmout2;

    Asheesh

  5. #5
    Regular Contributor
    Join Date
    May 2007
    Posts
    114

    Re: How to repeat audio playback in audio streaming?

    Quote Originally Posted by skumar_rao
    I Suggest using a Queue to store the Audio.

    like A1 -> A2 -> A3-> A4 .... -> An
    so you play A1 then A2 . after play you delete that node.

    and you fill like An ->A(n+1)->A(n+2) .... etc...

    So there will not be any delay ..

    Also make a check so that under Flow does not occur..
    Hi thanx for this reply..

    but sir i did such things but i did'nt get any changes.i mean i got same delay.may be i m doing somthing wrong in my code..
    i m showing my code where i m doing this..if there is somthing wrong plz tell me where it is..

    void xxxx::BufferToBeFilled(CMMFBuffer*aBuffer)
    {
    // reqSize - will be used

    int reqSize = aBuffer->RequestSize();

    TDes8& bufData = ((CMMFDataBuffer*)aBuffer)->Data();
    TBuf8<1024> bufData1;
    TBuf8<1024> bufData2;


    //here i m reading data(amr) from a file(size is 520 byte) and req size is 13 byte.
    iFile.Read(bufData1, reqSize);
    iFile.Read(bufData2, reqSize);

    TInt iCurrentBuffer ;

    if (iCurrentBuffer == 0)
    {
    bufData = bufData1 ;
    iCurrentBuffer = 1;
    }
    else
    {
    bufData = bufData2 ;
    iCurrentBuffer = 0;
    }


    if(bufData.Length()<reqSize)
    {
    iFile.Close();
    iFs.Delete(iOpenFileName);
    PlayNext();
    }
    else
    {
    iDevSound->PlayData();
    }}


    after did it i got same delay.i m not well known person and may be i did here some kiddish coding so plz help me.
    thanx in advance...
    Last edited by mobile_don; 2007-07-11 at 09:47.

  6. #6
    Regular Contributor
    Join Date
    May 2007
    Posts
    114

    Re: How to repeat audio playback in audio streaming?

    Quote Originally Posted by asheesh_v
    Hi Mobile_don..
    I've posted the sample code.
    Hope this helps..
    int CVorbisEngine::FillSampleBufferL()
    {
    // Fill one buffer of PCM samples
    int current_section;
    TDes8 * bufPtr ;

    // Swap buffers
    // So that there is always one buffer ready waiting to
    // be streamed.

    if (iCurrentBuffer == 0)
    {
    bufPtr = &iPcmout1;
    iCurrentBuffer = 1;
    }
    else
    {
    bufPtr = &iPcmout2;
    iCurrentBuffer = 0;
    }

    // Decode the ogg file
    long ret=ov_read(&iVf,(char *) bufPtr->Ptr(),bufPtr->MaxLength(),&current_section);
    if (ret == 0)
    {
    iEof=ETrue;
    /* EOF */
    }
    else if (ret < 0)
    {
    iEof=EFalse;
    /* error in the stream. Not a problem, just reporting it in
    case we (the app) cares. In this case, we don't. */
    }
    else
    {
    /* we don't bother dealing with sample rate changes, etc, but
    you'll have to*/
    bufPtr->SetLength(ret);
    BufferFullL(*bufPtr);
    }
    return ret;
    }

    // Write the buffer to the audio stream
    void CVorbisEngine::BufferFullL(TDes8 &aBuffer)
    {

    iEngineState=EStreamPlaying;
    iOutputStream->WriteL( aBuffer );
    }

    The 2 buffers I'm using are declared as:
    TBuf8<4096> iPcmout1; //Buffers for holding decoded data
    TBuf8<4096> iPcmout2;

    Asheesh

    Hi Asheesh
    thanx for reply.
    i m using ur code but may be i m doing somthing wrong so plz see my code and give me some advice.
    thanx in advance.

  7. #7
    Regular Contributor
    Join Date
    May 2007
    Posts
    114

    Re: How to repeat audio playback in audio streaming?

    Quote Originally Posted by asheesh_v
    Hi Mobile_don..
    I've posted the sample code.
    Hope this helps..
    int CVorbisEngine::FillSampleBufferL()
    {
    // Fill one buffer of PCM samples
    int current_section;
    TDes8 * bufPtr ;

    // Swap buffers
    // So that there is always one buffer ready waiting to
    // be streamed.

    if (iCurrentBuffer == 0)
    {
    bufPtr = &iPcmout1;
    iCurrentBuffer = 1;
    }
    else
    {
    bufPtr = &iPcmout2;
    iCurrentBuffer = 0;
    }

    // Decode the ogg file
    long ret=ov_read(&iVf,(char *) bufPtr->Ptr(),bufPtr->MaxLength(),&current_section);
    if (ret == 0)
    {
    iEof=ETrue;
    /* EOF */
    }
    else if (ret < 0)
    {
    iEof=EFalse;
    /* error in the stream. Not a problem, just reporting it in
    case we (the app) cares. In this case, we don't. */
    }
    else
    {
    /* we don't bother dealing with sample rate changes, etc, but
    you'll have to*/
    bufPtr->SetLength(ret);
    BufferFullL(*bufPtr);
    }
    return ret;
    }

    // Write the buffer to the audio stream
    void CVorbisEngine::BufferFullL(TDes8 &aBuffer)
    {

    iEngineState=EStreamPlaying;
    iOutputStream->WriteL( aBuffer );
    }

    The 2 buffers I'm using are declared as:
    TBuf8<4096> iPcmout1; //Buffers for holding decoded data
    TBuf8<4096> iPcmout2;

    Asheesh
    Hi Asheesh
    can u help me at this point where i m little bit confused.
    i m using file instead of buffers. and read the data from file and store it into a single buffer with 13 bytes at each pass.now i m trying to store that data into more than one buffer .here i m not getting any audio sound.plz help me.
    i m posting my code here where i m doing these things..

    void xxxx::BufferToBeFilled(CMMFBuffer*aBuffer)
    {
    // reqSize - will be used
    int reqSize = aBuffer->RequestSize();


    TDes8& bufData = ((CMMFDataBuffer*)aBuffer)->Data();

    TBuf8<520> iBufData1;
    TBuf8<520> iBufData2;

    static TInt iCuurentBuffer=0;


    iFile.Read(iBufData1,reqSize);
    iFile.Read(iBufData2,reqSize);
    if(iCuurentBuffer==0)
    {
    iBufData2.Delete(0,reqSize);

    bufData=iBufData1;
    iCuurentBuffer=1;
    }else
    {
    iBufData1.Delete(0,reqSize);

    bufData=iBufData2;
    iCuurentBuffer=0;
    }



    if(bufData.Length()<reqSize)
    {
    iFile.Close();
    iFs.Delete(iOpenFileName);

    PlayNext();

    }
    else
    {
    iDevSound->PlayData();

    }



    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
×