Skip to content

Commit

Permalink
cx16 adpcm example is now able to decode and play stereo music as wel…
Browse files Browse the repository at this point in the history
…l as mono.
  • Loading branch information
irmen committed Oct 10, 2023
1 parent 68e62e4 commit a37769a
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 52 deletions.
2 changes: 1 addition & 1 deletion docs/source/todo.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
TODO
====

- make the adpcm example able to decode and play stereo music.
- stream-wav example: add stereo adpcm support

- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
- [on branch: ir-less-branch-opcodes] IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction
Expand Down
56 changes: 52 additions & 4 deletions examples/cx16/pcmaudio/adpcm.p8
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
adpcm {

; IMA ADPCM decoder.
; IMA ADPCM decoder. Supports mono and stereo streams.
; https://wiki.multimedia.cx/index.php/IMA_ADPCM
; https://wiki.multimedia.cx/index.php/Microsoft_IMA_ADPCM

; IMA ADPCM encodes two 16-bit PCM audio samples in 1 byte (1 word per nibble)
; thus compressing the audio data by a factor of 4.
; The encoding precision is about 13 bits per sample so it's a lossy compression scheme.
;
; HOW TO CREATE IMA-ADPCM ENCODED AUDIO? Use sox or ffmpeg:
; HOW TO CREATE IMA-ADPCM ENCODED AUDIO? Use sox or ffmpeg like so (example):
; $ sox --guard source.mp3 -r 8000 -c 1 -e ima-adpcm out.wav trim 01:27.50 00:09
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
; Or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set the correct block size)
; And/or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set the correct block size, -b8)


; IMA-ADPCM file data stream format:
; If the IMA data is mono, an individual chunk of data begins with the following preamble:
; bytes 0-1: initial predictor (in little-endian format)
; byte 2: initial index
; byte 3: unknown, usually 0 and is probably reserved
; If the IMA data is stereo, a chunk begins with two preambles, one for the left audio channel and one for the right channel.
; (so we have 8 bytes of preamble).
; The remaining bytes in the chunk are the IMA nibbles. The first 4 bytes, or 8 nibbles,
; belong to the left channel and -if it's stereo- the next 4 bytes belong to the right channel.


ubyte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
Expand All @@ -29,17 +40,29 @@ adpcm {
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767]

uword @zp predict
uword @zp predict ; decoded 16 bit pcm sample for first channel.
uword @zp predict_2 ; decoded 16 bit pcm sample for second channel.
ubyte @requirezp index
ubyte @requirezp index_2
uword @zp pstep
uword @zp pstep_2

sub init(uword startPredict, ubyte startIndex) {
; initialize first decoding channel.
predict = startPredict
index = startIndex
pstep = t_step[index]
}

sub init_second(uword startPredict_2, ubyte startIndex_2) {
; initialize second decoding channel.
predict_2 = startPredict_2
index_2 = startIndex_2
pstep_2 = t_step[index_2]
}

sub decode_nibble(ubyte nibble) {
; decoder for nibbles for the first channel.
; this is the hotspot of the decoder algorithm!
cx16.r0s = 0 ; difference
if nibble & %0100
Expand All @@ -62,4 +85,29 @@ adpcm {
index = len(t_step)-1
pstep = t_step[index]
}

sub decode_nibble_second(ubyte nibble_2) {
; decoder for nibbles for the second channel.
; this is the hotspot of the decoder algorithm!
cx16.r0s = 0 ; difference
if nibble_2 & %0100
cx16.r0s += pstep_2
pstep_2 >>= 1
if nibble_2 & %0010
cx16.r0s += pstep_2
pstep_2 >>= 1
if nibble_2 & %0001
cx16.r0s += pstep_2
pstep_2 >>= 1
cx16.r0s += pstep_2
if nibble_2 & %1000
cx16.r0s = -cx16.r0s
predict_2 += cx16.r0s as uword
index_2 += t_index[nibble_2]
if_neg ; was: if index & 128
index_2 = 0
else if index_2 > len(t_step)-1
index_2 = len(t_step)-1
pstep_2 = t_step[index_2]
}
}
Loading

0 comments on commit a37769a

Please sign in to comment.