Newbie question-mp3/wma streaming issue

Started by cab0San,

cab0San

I'm working on streaming wm over the network using nBass+VB.net. I'm loading an mp3 into the nBASS.Stream object, and writing it to nBASS.WMAEncoder. Everything is fabulous until a certain few songs play, and they sound like they are playing at twice normal speed. Some are VBR some are not. Most songs play ok.

When I play the same problem songs using the .play method of the stream, they sound fine. They also play normally in other players,  (winamp, WM).

Can someone point me in the right direction?

Trekkan

I don't know if this will help any, but I've usually seen this as a problem(not with BASS, but in general) when a stream changes the bitrate it is encoded at.  Most players don't pick up this change in bitrate so the songs sound either too fast or too slow.  If the bitrate stays constant on the stream, then the songs will sound fine.

Like I said, I don't know if that'll help, but I know that at least is a problem in general.

Ian @ un4seen

QuoteI'm working on streaming wm over the network using nBass+VB.net. I'm loading an mp3 into the nBASS.Stream object, and writing it to nBASS.WMAEncoder. Everything is fabulous until a certain few songs play, and they sound like they are playing at twice normal speed. Some are VBR some are not. Most songs play ok.
It's probably that "the certain few songs" are not the same format (eg. 44100hz stereo) as the rest of them and the encoder.

If they sound twice as fast, then they are likely to be either 22050hz (instead of 44100hz) or mono (instead of stereo). The solution is to convert the sample data to the same format as the encoder before feeding it to the encoder. If the "certain few songs" are indeed 22050hz or mono, and the encoder is 44100hz stereo, then you could simply duplicate each sample (doubling the length, halving the speed). In C, you could do something like this...
// encode 1 second of data
void Encode1Sec(HWMENCODE encoder, DWORD chan)
{
      int freq,len=BASS_ChannelSeconds2Bytes(chan,1); // get bytes per sec
      short *buf=(short*)malloc(len); // allocate buffer
      len=BASS_ChannelGetData(chan,buf,len); // get data
      if (len>0) {
            BASS_ChannelGetAttributes(chan,&freq,NULL,NULL); // get sample rate
            if (freq==22050) { // it's 22050hz
                  buf=DoubleData(buf,len);
                  len*=2;
            }
            if (BASS_ChannelGetFlags(chan)&BASS_SAMPLE_MONO) { // it's mono
                  buf=DoubleData(buf,len);
                  len*=2;
            }
            BASS_WMA_EncodeWrite(encoder,buf,len); // encode it!
      }
      free(buf); // free buffer
}

// double the length of the data
short *DoubleData(short *buf, int len)
{
      int c;
      buf=(short*)realloc((void*)buf,len*2); // double the buffer length
      for (c=len/2-1;c>=0;c--) { // copy the data (last sample 1st)
            buf[c*2+1]=buf[c*2]=buf[c];
      }
      return buf; // return new buffer pointer
}

cab0San

Ian, thanks for your quick response!

I checked the mp3's, and you were right- I doubled the buffer and the song plays at the correct rate. The only problem is, the right channel sounds good, but the left channel is just hiss. How can I output the stream to both channels?

Ian @ un4seen

It sounds like there's probably a problem in the way you're doubling the data length... post the code you're using to do that :)

cab0San

Ian, here is the code. I checked some of the mp3's, and they (the problem ones)  are VBR 44100hz mono, or 22050 joint stereo.

 Dim buffer() As Byte
        ReDim buffer(4410)
      Dim bass As New nBASS.BASS(-1, 44100, DeviceSetupFlags.Latency Or _
      DeviceSetupFlags.LeaveVolume Me.Handle)

        Dim nwE As nBASS.WMAEncoder = nBASS.WMAEncoder.OpenEncoderNetwork(44100, _
        EncoderFlags.Default, 96000, 42042, 3)
        bass.Start()

        Dim m As nBASS.Stream = bass.LoadStream(False, "C:\somefolder\somemp3.mp3", _
        0, 0, StreamFlags.DecodeOnly)
       
            Do

                If (InStr(m.Flags, "Mono", CompareMethod.Text) > 0) Or m.Attributes.Frequency = 22050 Then
                    Dim i As Integer = 0
                    Dim buf2() As Byte
                    length = m.GetData(buffer, buffer.Length)
                    ReDim buf2((2 * buffer.Length) - 1)
                    For i = 0 To buffer.Length - 1
                        buf2(2 * i) = buffer(i)
                        buf2(2 * i + 1) = buffer(i)
                    Next

                    nwE.Write(buf2, buf2.Length)
                Else
                    length = m.GetData(buffer, buffer.Length)
                    nwE.Write(buffer, length)
                End If

            Loop While length > 0 And m.ActivityState = State.Playing

Ian @ un4seen

The sample data is 16-bit, so that's the unit you need to copy the data in. The problem is your "buffer" and "buf2" are 8-bit "byte" arrays, rather than 16-bit "short" arrays. I don't know what the VB.Net equivalent of the "short" type is, but that's what the buffers need to be :)

You may need to fiddle with the way you're using the "length" attribute too, if that's the byte length of the array (rather than the element count).

I'm not sure that the "-1" should be in the following line either?
                  ReDim buf2((2 * buffer.Length) - 1)

cab0San

Ian, changing the buffer type to short did the trick!

Thank you for your help!

As far as the redim statement goes, its correct, because it is not specifying the length, but rather the upper bound. Since the array is zero-based, it needs the -1.

QuoteReDim buf2((2 * buffer.Length) - 1)

Here's the modified code.

Dim buffer() As Short
       ReDim buffer(4410)
     Dim bass As New nBASS.BASS(-1, 44100, DeviceSetupFlags.Latency Or _
     DeviceSetupFlags.LeaveVolume Me.Handle)

       Dim nwE As nBASS.WMAEncoder = nBASS.WMAEncoder.OpenEncoderNetwork(44100, _
       EncoderFlags.Default, 96000, 42042, 3)
       bass.Start()

       Dim m As nBASS.Stream = bass.LoadStream(False, "C:\somefolder\somemp3.mp3", _
       0, 0, StreamFlags.DecodeOnly)
      
           Do

                If (InStr(m.Flags, "Mono", CompareMethod.Text) > 0) Or m.Attributes.Frequency = 22050 Then
                    Dim i As Integer = 0
                    Dim buf1() As Short
                    ReDim buf1(44100)
                    Dim buf2() As Short

                    length = m.GetData(buf1, buf1.Length)
                    ReDim buf2((2 * buf1.Length) - 1)
                    For i = 0 To buf1.Length - 1
                        buf2(2 * i) = buf1(i)
                        buf2(2 * i + 1) = buf1(i)
                    Next

                    nwE.Write(buf2, buf2.Length)
                Else
                    length = m.GetData(buffer, buffer.Length)
                    nwE.Write(buffer, length)
                End If

           Loop While length > 0 And m.ActivityState = State.Playing