Opened 10 years ago

Last modified 5 months ago

#6091 open sponsoring request

support ds2 audio (dss pro audio) file format

Reported by: Vineet Goel Owned by:
Priority: wish Component: avcodec
Version: git-master Keywords: dss bounty ds2
Cc: Blocked By:
Blocking: Reproduced by developer: yes
Analyzed by developer: no

Description

Summary of the bug: ffmpeg already supports dss audio file decoding. Please add support for ds2 as well, which is an improved version of dss and called "dss pro"
How to reproduce:

ffmpeg started on 2017-01-19 at 04:20:46
Report written to "ffmpeg-20170119-042046.log"
Command line:
../bin/ffmpeg -report -i VoiceRec11.ds2 VoiceRec11.mp3
ffmpeg version N-83132-g9561de4 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.4) 20160609
  configuration: --prefix=/home/ubuntu/ffmpeg_build --pkg-config-flags=--static --extra-cflags='-I/home/ubuntu/ffmpeg_build/include -static' --extra-ldflags='-L/home/ubuntu/ffmpeg_build/lib -static' --bindir=/home/ubuntu/bin --enable-libmp3lame
  libavutil      55. 43.100 / 55. 43.100
  libavcodec     57. 73.100 / 57. 73.100
  libavformat    57. 62.100 / 57. 62.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 69.100 /  6. 69.100
  libswscale      4.  3.101 /  4.  3.101
  libswresample   2.  4.100 /  2.  4.100
Splitting the commandline.
Reading option '-report' ... matched as option 'report' (generate a report) with argument '1'.
Reading option '-i' ... matched as input url with argument 'VoiceRec11.ds2'.
Reading option 'VoiceRec11.mp3' ... matched as output url.
Finished splitting the commandline.
Parsing a group of options: global .
Applying option report (generate a report) with argument 1.
Successfully parsed a group of options.
Parsing a group of options: input url VoiceRec11.ds2.
Successfully parsed a group of options.
Opening an input file: VoiceRec11.ds2.
[file @ 0x3e5db40] Setting default whitelist 'file,crypto'
[AVIOContext @ 0x3e66f60] Statistics: 1048576 bytes read, 0 seeks
VoiceRec11.ds2: Invalid data found when processing input

A ds2 sample file is attached for testing.
Also making a small donation to the project

thank you,

Attachments (2)

VoiceRec11.ds2 (57.0 KB ) - added by Vineet Goel 10 years ago.
sample ds2 audio file for testing
VoiceRec11.aif (498.6 KB ) - added by Vineet Goel 10 years ago.
corresponding aiff file as exported from DSS player

Download all attachments as: .zip

Change History (23)

by Vineet Goel, 10 years ago

Attachment: VoiceRec11.ds2 added

sample ds2 audio file for testing

comment:1 by Vineet Goel, 10 years ago

I had to attach a different input ds2 file instead of the one used in the command line, due to file size upload limit of 2.5MB

comment:2 by Carl Eugen Hoyos, 10 years ago

Keywords: dss added
Priority: normalwish
Reproduced by developer: set
Status: newopen

Are you able to transcode the attached file to something audible?

comment:3 by Vineet Goel, 10 years ago

Not directly using earlier command. Since there is no output file generated.
But if I force the input file format to "dss", then it trancodes mp3 output successfully. However, the audio is all muzzled up, and the duration is double the input file's duration (32 seconds instead of 16 seconds)
fyi, input file's codec is dss_sp only as I checked in a dss player.

ffmpeg started on 2017-01-19 at 06:19:58
Report written to "ffmpeg-20170119-061958.log"
Command line:
ffmpeg -report -f dss -i VoiceRec11.ds2 VoiceRec11.mp3
ffmpeg version N-83132-g9561de4 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.4) 20160609
  configuration: --prefix=/home/ubuntu/ffmpeg_build --pkg-config-flags=--static --extra-cflags='-I/home/ubuntu/ffmpeg_build/include -static' --extra-ldflags='-L/home/ubuntu/ffmpeg_build/lib -static' --bindir=/home/ubuntu/bin --enable-libmp3lame
  libavutil      55. 43.100 / 55. 43.100
  libavcodec     57. 73.100 / 57. 73.100
  libavformat    57. 62.100 / 57. 62.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 69.100 /  6. 69.100
  libswscale      4.  3.101 /  4.  3.101
  libswresample   2.  4.100 /  2.  4.100
Splitting the commandline.
Reading option '-report' ... matched as option 'report' (generate a report) with argument '1'.
Reading option '-f' ... matched as option 'f' (force format) with argument 'dss'.
Reading option '-i' ... matched as input url with argument 'VoiceRec11.ds2'.
Reading option 'VoiceRec11.mp3' ... matched as output url.
Finished splitting the commandline.
Parsing a group of options: global .
Applying option report (generate a report) with argument 1.
Successfully parsed a group of options.
Parsing a group of options: input url VoiceRec11.ds2.
Applying option f (force format) with argument dss.
Successfully parsed a group of options.
Opening an input file: VoiceRec11.ds2.
[file @ 0x2798b80] Setting default whitelist 'file,crypto'
[dss @ 0x2798200] Before avformat_find_stream_info() pos: 1536 bytes read:32768 seeks:0 nb_streams:1
[dss @ 0x2798200] All info found
[dss @ 0x2798200] Estimating duration from bitrate, this may be inaccurate
[dss @ 0x2798200] After avformat_find_stream_info() pos: 3616 bytes read:32768 seeks:0 frames:50
Input #0, dss, from 'VoiceRec11.ds2':
  Metadata:
    author          : DPM 6000
    date            : 2017-01-12T10:44:19
    comment         : 
  Duration: 00:00:32.80, start: 0.000000, bitrate: 14 kb/s
    Stream #0:0, 50, 1/11025: Audio: dss_sp, 11025 Hz, mono, s16
Successfully opened the file.
Parsing a group of options: output url VoiceRec11.mp3.
Successfully parsed a group of options.
Opening an output file: VoiceRec11.mp3.
[file @ 0x27af4e0] Setting default whitelist 'file,crypto'
Successfully opened the file.
detected 1 logical cores
[graph_0_in_0_0 @ 0x27b7dc0] Setting 'time_base' to value '1/11025'
[graph_0_in_0_0 @ 0x27b7dc0] Setting 'sample_rate' to value '11025'
[graph_0_in_0_0 @ 0x27b7dc0] Setting 'sample_fmt' to value 's16'
[graph_0_in_0_0 @ 0x27b7dc0] Setting 'channel_layout' to value '0x4'
[graph_0_in_0_0 @ 0x27b7dc0] tb:1/11025 samplefmt:s16 samplerate:11025 chlayout:0x4
[format_out_0_0 @ 0x27b8880] Setting 'sample_fmts' to value 's32p|fltp|s16p'
[format_out_0_0 @ 0x27b8880] Setting 'sample_rates' to value '44100|48000|32000|22050|24000|16000|11025|12000|8000'
[format_out_0_0 @ 0x27b8880] Setting 'channel_layouts' to value '0x4|0x3'
[format_out_0_0 @ 0x27b8880] auto-inserting filter 'auto_resampler_0' between the filter 'Parsed_anull_0' and the filter 'format_out_0_0'
[AVFilterGraph @ 0x27b7c80] query_formats: 4 queried, 6 merged, 3 already done, 0 delayed
[auto_resampler_0 @ 0x27b9280] picking s16p out of 3 ref:s16
[auto_resampler_0 @ 0x27b9280] [SWR @ 0x27b9620] Using s16p internally between filters
[auto_resampler_0 @ 0x27b9280] ch:1 chl:mono fmt:s16 r:11025Hz -> ch:1 chl:mono fmt:s16p r:11025Hz
Output #0, mp3, to 'VoiceRec11.mp3':
  Metadata:
    author          : DPM 6000
    TDRC            : 2017-01-12T10:44:19
    comment         : 
    TSSE            : Lavf57.62.100
    Stream #0:0, 0, 1/11025: Audio: mp3 (libmp3lame), 11025 Hz, mono, s16p
    Metadata:
      encoder         : Lavc57.73.100 libmp3lame
Stream mapping:
  Stream #0:0 -> #0:0 (dss_sp (native) -> mp3 (libmp3lame))
Press [q] to stop, [?] for help
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
[dss_sp @ 0x27a3480] combined_pitch was too large
[dss_sp @ 0x27a3480] combined_pitch was too large
No more output streams to write to, finishing.
[libmp3lame @ 0x27ae880] Trying to remove 359 more samples than there are in the queue
size=      65kB time=00:00:32.81 bitrate=  16.1kbits/s speed= 214x    
video:0kB audio:64kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.502818%
Input file #0 (VoiceRec11.ds2):
  Input stream #0:0 (audio): 1369 packets read (57498 bytes); 1369 frames decoded (361416 samples); 
  Total: 1369 packets (57498 bytes) demuxed
Output file #0 (VoiceRec11.mp3):
  Output stream #0:0 (audio): 628 frames encoded (361416 samples); 630 packets muxed (65829 bytes); 
  Total: 630 packets (65829 bytes) muxed
1369 frames successfully decoded, 0 decoding errors
[AVIOContext @ 0x27af3a0] Statistics: 1 seeks, 632 writeouts
[AVIOContext @ 0x27a1f60] Statistics: 58368 bytes read, 0 seeks

comment:4 by Carl Eugen Hoyos, 10 years ago

What I meant was: Do you have other software that is able to provide reference output?

comment:5 by Vineet Goel, 10 years ago

Yes, the DSS player allows me to export the ds2 file to aiff format. attaching the exported aiff file.

by Vineet Goel, 10 years ago

Attachment: VoiceRec11.aif added

corresponding aiff file as exported from DSS player

comment:6 by Vineet Goel, 10 years ago

is the attached file VoiceRec11.aif useful or should I provide it in some other format?

thanks,

comment:7 by Carl Eugen Hoyos, 10 years ago

Whatever the dss player outputs directly is useful.

comment:8 by Carl Eugen Hoyos, 10 years ago

Component: avformatavcodec

comment:9 by Vineet Goel, 9 years ago

my humble request if the priority for this can be increased. Actually I am stuck in a difficult situation here and this seems to be only way out.
thank you

comment:10 by Elon Musk, 9 years ago

The only way to increase priority of bug is finding someone to do it.
Or giving bounty.

comment:11 by Vineet Goel, 9 years ago

how does bounty work in here? ready to pitch $250

in reply to:  11 comment:12 by llogan, 9 years ago

Keywords: bounty added

Replying to vineet156:

how does bounty work in here? ready to pitch $250

Someone adds the bounty keyword and that's pretty much it. Then you can communicate directly with any interested developers. Leaving your email address may be helpful for someone to contact you if you prefer. Or you can use Bountysource if you like that, but they do take a cut (make sure to leave a link if you want to use BS).

comment:13 by John Paul Hayes, 9 years ago

This is a feature that I require so I will gladly give a bounty of $500 for ds2 support.

comment:14 by John Paul Hayes, 9 years ago

Keywords: ds2 added
Type: enhancementsponsoring request

comment:15 by Elon Musk, 8 years ago

If you want to sponsor contact developers directly, as I'm developer I'm also interested in this.

comment:16 by traycerb, 3 years ago

Just chiming in about how useful this feature would still be.

There is still no open-source way to play or transcode these files. It looks like significant bounties were offered in the past, but no one was interested, so not sure if there will ever be any traction, but would be glad to contribute any way I can if someone was interested.

comment:17 by Mooga, 2 years ago

I also ask for FFmpeg to be able to process .ds2 files.

comment:18 by Micha, 16 months ago

I have the same problem. But maybe this helps us:

I found a software "ds2.converter.com". This software uses ffmpeg to convert the ds2-files. You can see it in the directory. The ffmpeg is just renamed as ds2converter.exe.

But you can't see the parameters wich are used. There is the main program called DS2.exe wich starts the ds2converter / ffmpeg.

Maybe also helpful is that the "olympusCodec" must be installed. (you find the download also at "ds2converter.com" in the help section). I don't know how I can use this codec with ffmpeg. Maybe this is the resolution.

I think this works:

  • someone should make a built of the ffmpeg wich makes a message box with the incoming command line for ffmpeg. ´
  • we put this built instead of the ds2converter.exe
  • then we find out wich command is used in ds2.exe for ffmpeg!!!


comment:19 by compn, 15 months ago

there is also a test ds2 file here at that ds2converter website: https://ds2converter.com/test.DS2

comment:20 by Regan, 6 months ago

I tested this out by replacing DS2Converter with a simple program that only saved the parameters to a file.

It is only called once and the parameters are: -ss 0 -t 30 -i <temporary file location and filename> <output file location and filename>

The DS2Converter.exe (ffmpeg) isn't actually being used for conversion. It is only being used to limit the conversion to 30 seconds in the demo mode.

Even with this dummy DS2Converter.exe it converted the full DS2 file to a wav file in the AppData/Local/Temp/ folder. It tries to delete it, but I was able to capture it by temporarily removing delete privileges on the Temp Folder.

Hope this helps someone.

comment:21 by Shabubu, 5 months ago

I've fully reverse-engineered the DS2 (DSS Pro) codec from Olympus's DssDecoder.dll / AudioSDK DLL using Ghidra, and built a working open-source decoder that produces output matching the proprietary DLLs (1.0000 correlation, bit-exact on all test files). Posting the complete findings here so this can finally get proper FFmpeg support.

Reference implementation: https://github.com/hirparak/dss-codec (Rust, MIT-licensed) — all algorithms, tables, and test vectors included.

Why FFmpeg's DSS decoder doesn't work for DS2

The current -f dss path fails for three independent reasons:

  1. Wrong sample rate: FFmpeg uses 11025 Hz. DS2 SP is 12000 Hz, DS2 QP is 16000 Hz.
  2. Different codec tables: None of FFmpeg's dss_sp tables (filter_cb, fixed_cb_gain, pulse_val, adaptive_gain, sinc) exist in the Olympus DLL. The codecs share a CELP architecture but every parameter differs.
  3. Different demuxing: DS2 SP uses a byte-swap packet scheme not present in FFmpeg's DSS demuxer. DS2 QP uses a continuous bitstream across blocks with no per-frame packet boundaries.

Format detection

DS2 files have magic bytes \x03ds2 (vs DSS: \x02dss or \x03dss).

The codec mode is determined by byte 4 of the first audio block (at file offset 0x604):

byte4 value Codec Sample rate Bitrate
0-1 DS2 SP 12000 Hz ~13.7 kbps
2-5 LP (unimplemented) 8000 Hz
6-7 DS2 QP 16000 Hz ~28 kbps

File structure

  • Header: 1536 bytes (0x600)
  • Audio data: sequence of 512-byte blocks starting at offset 0x600
  • Block header: 6 bytes per block (byte0 byte1 frame_count 0xFF format_type 0xFF)
    • byte0 bit 7: swap initialization flag (SP mode only)
    • byte1: continuation size parameter (DSS/SP only, for empty block handling)
    • byte2: number of frames starting in this block
    • byte4: format type (0=SP, 6=QP)
  • Block payload: 506 bytes of audio data per block

Total frame count = sum of byte2 across all blocks.

Bitstream reader

Both DS2 codecs use an MSB-first-within-16-bit-LE-words bitstream reader:

  • Read 16-bit words in little-endian byte order
  • Within each word, consume bits from MSB (bit 15) to LSB (bit 0)
  • Fields span word boundaries seamlessly

This is the same bit order as FFmpeg's existing DSS SP reader.

DS2 SP codec (mode 0-1, 12000 Hz)

Parameters

Parameter DS2 SP FFmpeg dss_sp
Sample rate 12000 Hz 11025 Hz
Subframe size 72 samples 66 samples
Subframes/frame 4 4
Samples/frame 288 264
Reflection coeffs 14 12
Pitch range 36–186 20–143
Excitation pulses 7 6
Codebook C(72,7) combinatorial different
Frame bits 328 264
Packet size 42 bytes 42 bytes
Synthesis filter Lattice (f64) Direct-form

Demuxing (SP mode)

SP mode uses a byte-swap alternation scheme similar to FFmpeg's existing DSS demuxer, but the swap logic differs:

  1. Strip 6-byte block headers, concatenate 506-byte payloads into a flat stream.
  2. Initialize swap state from bit 7 of the first block's byte0.
  3. Alternate between reading 42-byte (no-swap) and 40-byte (swap) chunks:
    • No-swap frame: Read 42 bytes directly as the packet. Save byte[40] as the swap byte.
    • Swap frame: Read 40 bytes into pkt[3..43], then shift even-indexed bytes: pkt[i] = pkt[i+4] for i in 0..40 step 2. Set pkt[1] = swap_byte.
  4. In both cases, zero out pkt[40] before decoding.
  5. Toggle swap state after each frame.

Frame bitfield layout (328 bits, MSB-first)

refl[0]:  5 bits    (codebook 0, 32 entries)
refl[1]:  5 bits    (codebook 1, 32 entries)
refl[2]:  4 bits    (codebook 2, 16 entries)
refl[3]:  4 bits    (codebook 3, 16 entries)
refl[4]:  4 bits    (codebook 4, 16 entries)
refl[5]:  4 bits    (codebook 5, 16 entries)
refl[6]:  4 bits    (codebook 6, 16 entries)
refl[7]:  4 bits    (codebook 7, 16 entries)
refl[8]:  3 bits    (codebook 8, 8 entries)
refl[9]:  3 bits    (codebook 9, 8 entries)
refl[10]: 3 bits    (codebook 10, 8 entries)
refl[11]: 3 bits    (codebook 11, 8 entries)
refl[12]: 3 bits    (codebook 12, 8 entries)
refl[13]: 3 bits    (codebook 13, 8 entries)
--- 52 bits total for reflection coefficients ---

Per subframe (x4):
  pitch_gain:  5 bits  (32 entries)
  cb_index:   31 bits  (combinatorial index into C(72,7))
  exc_gain:    6 bits  (64 entries)
  pulse[0..6]: 3 bits each = 21 bits  (7 pulses, 8 amplitudes each)
--- 63 bits per subframe, 252 total ---

combined_pitch: 24 bits  (encodes 4 pitch lags)
--- total: 52 + 252 + 24 = 328 bits ---

Combined pitch encoding (SP)

The 24-bit combined pitch value encodes 4 subframe pitch lags:

p0_idx = combined % 151          (pitch range = 186-36+1 = 151)
remainder = combined / 151
delta1 = remainder % 48          (delta range = 48)
remainder = remainder / 48
delta2 = remainder % 48
delta3 = remainder              (clamped to 0..47)

pitch[0] = p0_idx + 36
pitch[i] = base + delta[i-1]     where base = max(36, min(prev - 23, 163))

The base calculation for delta decoding: half_delta = 48/2 - 1 = 23. If prev > 163 (upper_limit = 186 - 23), base = 163 - 23 = 140. If prev >= 59 (min_pitch + half_delta), base = prev - 23. Otherwise base = 36.

DS2 QP codec (mode 6-7, 16000 Hz)

Parameters

Parameter DS2 QP
Sample rate 16000 Hz
Subframe size 64 samples
Subframes/frame 4
Samples/frame 256
Reflection coeffs 16
Pitch range 45–300
Excitation pulses 11
Codebook C(64,11) combinatorial
Frame bits 448
De-emphasis y[n] = x[n] + 0.1*y[n-1]

Demuxing (QP mode)

QP is simpler — no byte-swap. Strip 6-byte block headers, concatenate 506-byte payloads into a continuous bitstream. Frames are read sequentially from this stream (448 bits = 56 bytes each, but frames are not byte-aligned since the bitstream reader works at the bit level).

28 blocks = 14168 bytes of payload = 113344 bits. At 448 bits/frame, that's 253 frames per 28-block cycle.

Frame bitfield layout (448 bits, MSB-first)

refl[0]:   7 bits   (codebook 0, 128 entries)
refl[1]:   7 bits   (codebook 1, 128 entries)
refl[2]:   6 bits   (codebook 2, 64 entries)
refl[3]:   6 bits   (codebook 3, 64 entries)
refl[4]:   5 bits   (codebook 4, 32 entries)
refl[5]:   5 bits   (codebook 5, 32 entries)
refl[6]:   5 bits   (codebook 6, 32 entries)
refl[7]:   5 bits   (codebook 7, 32 entries)
refl[8]:   5 bits   (codebook 8, 32 entries)
refl[9]:   4 bits   (codebook 9, 16 entries)
refl[10]:  4 bits   (codebook 10, 16 entries)
refl[11]:  4 bits   (codebook 11, 16 entries)
refl[12]:  4 bits   (codebook 12, 16 entries)
refl[13]:  3 bits   (codebook 13, 8 entries)
refl[14]:  3 bits   (codebook 14, 8 entries)
refl[15]:  3 bits   (codebook 15, 8 entries)
--- 76 bits total ---

Per subframe (x4):
  pitch:       8 bits   (direct index, pitch = index + 45)
  pitch_gain:  6 bits   (64 entries)
  cb_index:   40 bits   (combinatorial index into C(64,11))
  exc_gain:    6 bits   (64 entries)
  pulse[0..10]: 3 bits each = 33 bits  (11 pulses)
--- 93 bits per subframe, 372 total ---

--- total: 76 + 372 = 448 bits ---

Note: QP encodes pitch directly per subframe (8 bits each), not as a combined value like SP.

Shared algorithms

Combinatorial codebook

Both codecs use the combinatorial number system to encode pulse positions. An index value selects k positions from {0..n-1}:

// Decode combinatorial index to k positions from {0..n-1}
// Positions are returned in DESCENDING order (do NOT sort!)
void decode_combinatorial(uint64_t index, int n, int k, int *positions) {
    uint64_t remaining = index;
    for (int i = k; i >= 1; i--) {
        int v = i - 1;
        while (v + 1 < n && comb(v + 1, i) <= remaining)
            v++;
        positions[k - i] = v;
        remaining -= comb(v, i);
    }
}
  • SP: C(72, 7), 31-bit index, max value 1,473,109,703
  • QP: C(64, 11), 40-bit index, max value 743,595,781,823

Excitation generation (per subframe)

excitation[i] = pitch_gain * adaptive_exc[i] + fixed_exc[i]

Where:

  • adaptive_exc = pitch memory repeated at the pitch period
  • fixed_exc = sparse pulse excitation: for each pulse position p[j], fixed_exc[p[j]] += pulse_amp[pulse_idx[j]] * exc_gain

When pitch < subframe_size, the adaptive excitation wraps: adaptive_exc[i] = pitch_memory[end - pitch + (i % pitch)].

Lattice synthesis filter

Both codecs use an identical normalized lattice filter with the reflection coefficients:

// p = number of reflection coefficients (14 for SP, 16 for QP)
// coeffs[] = dequantized reflection coefficients
// state[] = persistent filter state (p elements, init to 0)
for (int n = 0; n < subframe_size; n++) {
    double acc = excitation[n] - state[p-1] * coeffs[p-1];
    for (int k = p-2; k >= 0; k--) {
        acc -= state[k] * coeffs[k];
        state[k+1] = coeffs[k] * acc + state[k];
    }
    state[0] = acc;
    output[n] = acc;
}

De-emphasis (QP only)

QP applies a first-order de-emphasis filter after synthesis:

y[n] = x[n] + 0.1 * y[n-1]

SP does not use de-emphasis.

Quantization tables

All tables were extracted from the Olympus DssDecoder.dll / AudioSDK DLL via Ghidra. The complete tables (reflection coefficient codebooks + quantization tables) are available in the reference implementation. Summary of table sizes:

SP quantization tables

  • Pitch gain: 32 entries (5-bit), linear range 0.05–2.0
  • Excitation gain: 64 entries (6-bit), roughly exponential 0–5000
  • Pulse amplitude: 8 entries (3-bit), symmetric [-0.952, 0.952]
  • Reflection codebooks: 14 codebooks, sizes [32, 32, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8] entries (f64)

QP quantization tables

  • Pitch gain: 64 entries (6-bit), non-linear range 0.005–2.0
  • Excitation gain: 64 entries (6-bit), range 3.9–4970.3
  • Pulse amplitude: 8 entries (3-bit), asymmetric [-0.922, 0.931]
  • Reflection codebooks: 16 codebooks, sizes [128, 128, 64, 64, 32, 32, 32, 32, 32, 16, 16, 16, 16, 8, 8, 8] entries (f64)

The full table data is in the source repo. The SP and QP codecs use completely different quantization tables — they're at different DLL addresses and have different value ranges.

Implementation notes for FFmpeg

  1. Demuxer changes: The existing DSS demuxer (libavformat/dss.c) needs to detect \x03ds2 magic and handle the DS2 block structure. SP byte-swap logic is similar to DSS but not identical. QP needs a continuous-bitstream mode. The demuxer should set codec_id based on byte4 of the first block.
  2. New codec IDs: Need at least AV_CODEC_ID_DS2_SP and AV_CODEC_ID_DS2_QP (or a single AV_CODEC_ID_DS2 with mode detection in init).
  3. Arithmetic precision: The DLL uses f64 (double) throughout. An f32 implementation would likely introduce audible artifacts given the lattice filter's sensitivity to coefficient precision.
  4. State management: Each codec maintains persistent state across frames: lattice filter state (14 or 16 doubles), pitch memory buffer (up to 186+72 or 300+64 doubles), and de-emphasis state (QP only).
  5. The DSS SP codec in FFmpeg is unrelated: Despite the similar name, FFmpeg's existing dss_sp codec (11025 Hz, direct-form filter, different tables) cannot be adapted for DS2. The codec needs to be written from scratch.

Verification

The reference decoder has been verified against output from the proprietary Olympus DirectShow filters (via NCH Switch under Wine):

  • DS2 SP: 1.0000 correlation, 99% sample-exact (±1 from f64→i16 rounding)
  • DS2 QP: 1.0000 correlation, 100% sample-exact (bit-exact match)

Happy to provide test files and reference WAVs for anyone working on an FFmpeg implementation.

Note: See TracTickets for help on using tickets.