filoe / cscore

An advanced audio library, written in C#. Provides tons of features. From playing/recording audio to decoding/encoding audio streams/files to processing audio data in realtime (e.g. applying custom effects during playback, create visualizations,...). The possibilities are nearly unlimited.
Other
2.17k stars 450 forks source link

WriteWriter: writing 16-bit samples from float values #74

Closed ziriax closed 8 years ago

ziriax commented 8 years ago

Why is WriteSample not multiplying the sample when BitsPerSample is 16? Is this by design?

    /// <summary>Encodes a single sample.</summary>
    /// <param name="sample">The sample to encode.</param>
    public void WriteSample(float sample)
    {
      if (this._waveFormat.IsPCM())
      {
        switch (this._waveFormat.BitsPerSample)
        {
          case 8:
            this.Write((byte) ((double) byte.MaxValue * (double) sample));
            break;
          case 16:
            this.Write((short) sample);
            break;
          case 24:
            byte[] bytes = BitConverter.GetBytes((int) (2147483648.0 * (double) sample));
            this.Write(new byte[3]
            {
              bytes[0],
              bytes[1],
              bytes[2]
            }, 0, 3);
            break;
          default:
            throw new InvalidOperationException("Invalid Waveformat", (Exception) new InvalidOperationException("Invalid BitsPerSample while using PCM encoding."));
        }
      }
filoe commented 8 years ago

float referes to IeeeFloat 32bit. In CSCore, PCM samples aren't expressed as floating point values.

ziriax commented 8 years ago

Sorry, let me refine my question.

Why is the sample argument scaled when BitsPerSample is 8 or 24, but not when it is 16?

For 8-bit, sample is mapped from [0..1] to [0..0xFF]

For 24-bit, sample is mapped from [0..1] to [0..2147483648.0] (also strange, I would expect a mapping to [0..0xFFFFFF]

But for 16-bit, sample is not mapped at all

ziriax commented 8 years ago

Furthermore, it seems that Pcm16BitToSample does scale when reading back to float:

        buffer[index] = (float) BitConverter.ToInt16(this._buffer, startIndex) / 32768f;
filoe commented 8 years ago

Ahh I see. You were right. That was a mistake.

ziriax commented 8 years ago

Thanks for fixing this! This might break existing code of course :)

One small thing, with your fix, the scaling is not symmetric, since now you multiply by short.MaxValue = 32767 when writing, but divide by 32768f when reading. Shouldn't that also be 32767f?