mirror of
https://github.com/irmen/prog8.git
synced 2024-07-14 03:29:14 +00:00
cx16 adpcm example is now able to decode and play stereo music as well as mono.
This commit is contained in:
parent
68e62e4bd2
commit
a37769aafe
@ -1,7 +1,7 @@
|
|||||||
TODO
|
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: 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
|
- [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
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
adpcm {
|
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/IMA_ADPCM
|
||||||
; https://wiki.multimedia.cx/index.php/Microsoft_IMA_ADPCM
|
; https://wiki.multimedia.cx/index.php/Microsoft_IMA_ADPCM
|
||||||
|
|
||||||
@ -8,10 +8,21 @@ adpcm {
|
|||||||
; thus compressing the audio data by a factor of 4.
|
; 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.
|
; 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
|
; $ 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
|
; $ 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]
|
ubyte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
|
||||||
@ -29,17 +40,29 @@ adpcm {
|
|||||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
||||||
32767]
|
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
|
||||||
|
ubyte @requirezp index_2
|
||||||
uword @zp pstep
|
uword @zp pstep
|
||||||
|
uword @zp pstep_2
|
||||||
|
|
||||||
sub init(uword startPredict, ubyte startIndex) {
|
sub init(uword startPredict, ubyte startIndex) {
|
||||||
|
; initialize first decoding channel.
|
||||||
predict = startPredict
|
predict = startPredict
|
||||||
index = startIndex
|
index = startIndex
|
||||||
pstep = t_step[index]
|
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) {
|
sub decode_nibble(ubyte nibble) {
|
||||||
|
; decoder for nibbles for the first channel.
|
||||||
; this is the hotspot of the decoder algorithm!
|
; this is the hotspot of the decoder algorithm!
|
||||||
cx16.r0s = 0 ; difference
|
cx16.r0s = 0 ; difference
|
||||||
if nibble & %0100
|
if nibble & %0100
|
||||||
@ -62,4 +85,29 @@ adpcm {
|
|||||||
index = len(t_step)-1
|
index = len(t_step)-1
|
||||||
pstep = t_step[index]
|
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]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
;
|
;
|
||||||
; Simple IMA ADPCM playback example. (factor 4 lossy compressed pcm audio)
|
; Simple IMA ADPCM playback example. (factor 4 lossy compressed pcm audio)
|
||||||
;
|
;
|
||||||
; NOTE: this program requires 16 bits MONO audio, and 256 byte encoded block size!
|
; NOTE: this program requires 16 bits MONO or STEREO audio, and 256 byte encoded block size!
|
||||||
; HOW TO CREATE SUCH IMA-ADPCM ENCODED AUDIO? Use sox or ffmpeg:
|
; HOW TO CREATE SUCH IMA-ADPCM ENCODED AUDIO? Use sox or ffmpeg:
|
||||||
; $ sox --guard source.mp3 -r 8000 -c 1 -e ima-adpcm out.wav trim 01:27.50 00:09
|
; $ 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
|
; $ 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
|
||||||
@ -40,10 +40,15 @@ main {
|
|||||||
txt.print_uw(wavfile.sample_rate)
|
txt.print_uw(wavfile.sample_rate)
|
||||||
txt.print(" vera rate = ")
|
txt.print(" vera rate = ")
|
||||||
txt.print_uw(vera_rate_hz)
|
txt.print_uw(vera_rate_hz)
|
||||||
txt.print("\n(b)enchmark or (p)layback? ")
|
txt.print(" #channels = ")
|
||||||
|
txt.print_ub(wavfile.nchannels)
|
||||||
|
txt.print("\n\n(b)enchmark or (p)layback? ")
|
||||||
|
|
||||||
when cbm.CHRIN() {
|
when cbm.CHRIN() {
|
||||||
'b' -> benchmark()
|
'b' -> when wavfile.nchannels {
|
||||||
|
1-> benchmark_mono()
|
||||||
|
2-> benchmark_stereo()
|
||||||
|
}
|
||||||
'p' -> playback()
|
'p' -> playback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,58 +64,168 @@ main {
|
|||||||
num_adpcm_blocks = (adpcm_size / 256) as ubyte ; THE ADPCM DATA NEEDS TO BE ENCODED IN 256-byte BLOCKS !
|
num_adpcm_blocks = (adpcm_size / 256) as ubyte ; THE ADPCM DATA NEEDS TO BE ENCODED IN 256-byte BLOCKS !
|
||||||
}
|
}
|
||||||
|
|
||||||
sub benchmark() {
|
sub benchmark_mono() {
|
||||||
nibblesptr = &wavdata.wav_data + wavfile.data_offset
|
nibblesptr = &wavdata.wav_data + wavfile.data_offset
|
||||||
|
|
||||||
txt.print("\ndecoding all blocks...\n")
|
txt.print("\ndecoding all blocks...\n")
|
||||||
cbm.SETTIM(0,0,0)
|
cbm.SETTIM(0,0,0)
|
||||||
repeat num_adpcm_blocks {
|
repeat num_adpcm_blocks {
|
||||||
|
|
||||||
; 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.
|
|
||||||
|
|
||||||
; The code here assumes mono.
|
|
||||||
adpcm.init(peekw(nibblesptr), @(nibblesptr+2))
|
adpcm.init(peekw(nibblesptr), @(nibblesptr+2))
|
||||||
nibblesptr += 4
|
nibblesptr += 4
|
||||||
|
decode_mono_nibbles()
|
||||||
|
}
|
||||||
|
|
||||||
repeat 252 {
|
decoding_report(1 + 252*2)
|
||||||
ubyte @zp nibble = @(nibblesptr)
|
}
|
||||||
adpcm.decode_nibble(nibble & 15) ; first word
|
|
||||||
adpcm.decode_nibble(nibble>>4) ; second word
|
sub decode_mono_nibbles() {
|
||||||
nibblesptr++
|
; slightly unrolled
|
||||||
|
ubyte @zp nibble
|
||||||
|
repeat 252/2 {
|
||||||
|
unroll 2 {
|
||||||
|
nibble = @(nibblesptr)
|
||||||
|
adpcm.decode_nibble(nibble & 15) ; first word
|
||||||
|
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
||||||
|
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
||||||
|
adpcm.decode_nibble(nibble>>4) ; second word
|
||||||
|
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
||||||
|
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
||||||
|
nibblesptr++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uword[8] left
|
||||||
|
uword[8] right
|
||||||
|
|
||||||
|
sub benchmark_stereo() {
|
||||||
|
nibblesptr = &wavdata.wav_data + wavfile.data_offset
|
||||||
|
txt.print("\n\ndecoding all blocks...\n")
|
||||||
|
cbm.SETTIM(0,0,0)
|
||||||
|
|
||||||
|
repeat num_adpcm_blocks {
|
||||||
|
|
||||||
|
adpcm.init(peekw(nibblesptr), @(nibblesptr+2))
|
||||||
|
nibblesptr += 4
|
||||||
|
adpcm.init_second(peekw(nibblesptr), @(nibblesptr+2))
|
||||||
|
nibblesptr += 4
|
||||||
|
|
||||||
|
repeat 248/8 {
|
||||||
|
decode_stereo_nibbles()
|
||||||
|
nibblesptr += 8
|
||||||
|
copy_stereo_to_fifo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decoding_report(2 + 248*4)
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub copy_stereo_to_fifo() clobbers(A, Y) {
|
||||||
|
%asm {{
|
||||||
|
; copy to vera PSG fifo buffer
|
||||||
|
ldy #0
|
||||||
|
- lda p8_left,y
|
||||||
|
sta cx16.VERA_AUDIO_DATA
|
||||||
|
lda p8_left+1,y
|
||||||
|
sta cx16.VERA_AUDIO_DATA
|
||||||
|
lda p8_right,y
|
||||||
|
sta cx16.VERA_AUDIO_DATA
|
||||||
|
lda p8_right+1,y
|
||||||
|
sta cx16.VERA_AUDIO_DATA
|
||||||
|
iny
|
||||||
|
iny
|
||||||
|
cpy #16
|
||||||
|
bne -
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub decode_stereo_nibbles() {
|
||||||
|
; decode 4 left channel nibbles
|
||||||
|
ubyte @zp nibble = @(nibblesptr)
|
||||||
|
adpcm.decode_nibble(nibble & 15) ; first word
|
||||||
|
left[0] = adpcm.predict
|
||||||
|
adpcm.decode_nibble(nibble>>4) ; second word
|
||||||
|
left[1] = adpcm.predict
|
||||||
|
nibble = @(nibblesptr+1)
|
||||||
|
adpcm.decode_nibble(nibble & 15) ; first word
|
||||||
|
left[2] = adpcm.predict
|
||||||
|
adpcm.decode_nibble(nibble>>4) ; second word
|
||||||
|
left[3] = adpcm.predict
|
||||||
|
nibble = @(nibblesptr+2)
|
||||||
|
adpcm.decode_nibble(nibble & 15) ; first word
|
||||||
|
left[4] = adpcm.predict
|
||||||
|
adpcm.decode_nibble(nibble>>4) ; second word
|
||||||
|
left[5] = adpcm.predict
|
||||||
|
nibble = @(nibblesptr+3)
|
||||||
|
adpcm.decode_nibble(nibble & 15) ; first word
|
||||||
|
left[6] = adpcm.predict
|
||||||
|
adpcm.decode_nibble(nibble>>4) ; second word
|
||||||
|
left[7] = adpcm.predict
|
||||||
|
|
||||||
|
; decode 4 right channel nibbles
|
||||||
|
nibble = @(nibblesptr+4)
|
||||||
|
adpcm.decode_nibble_second(nibble & 15) ; first word
|
||||||
|
right[0] = adpcm.predict_2
|
||||||
|
adpcm.decode_nibble_second(nibble>>4) ; second word
|
||||||
|
right[1] = adpcm.predict_2
|
||||||
|
nibble = @(nibblesptr+5)
|
||||||
|
adpcm.decode_nibble_second(nibble & 15) ; first word
|
||||||
|
right[2] = adpcm.predict_2
|
||||||
|
adpcm.decode_nibble_second(nibble>>4) ; second word
|
||||||
|
right[3] = adpcm.predict_2
|
||||||
|
nibble = @(nibblesptr+6)
|
||||||
|
adpcm.decode_nibble_second(nibble & 15) ; first word
|
||||||
|
right[4] = adpcm.predict_2
|
||||||
|
adpcm.decode_nibble_second(nibble>>4) ; second word
|
||||||
|
right[5] = adpcm.predict_2
|
||||||
|
nibble = @(nibblesptr+7)
|
||||||
|
adpcm.decode_nibble_second(nibble & 15) ; first word
|
||||||
|
right[6] = adpcm.predict_2
|
||||||
|
adpcm.decode_nibble_second(nibble>>4) ; second word
|
||||||
|
right[7] = adpcm.predict_2
|
||||||
|
}
|
||||||
|
|
||||||
|
sub decoding_report(float pcm_words_per_block) {
|
||||||
const float REFRESH_RATE = 25.0e6/(525.0*800) ; Vera VGA refresh rate is not precisely 60 hz!
|
const float REFRESH_RATE = 25.0e6/(525.0*800) ; Vera VGA refresh rate is not precisely 60 hz!
|
||||||
float duration_secs = (cbm.RDTIM16() as float) / REFRESH_RATE
|
float duration_secs = (cbm.RDTIM16() as float) / REFRESH_RATE
|
||||||
floats.print_f(duration_secs)
|
floats.print_f(duration_secs)
|
||||||
txt.print(" seconds (approx)\n")
|
txt.print(" seconds (approx)\n")
|
||||||
const float PCM_WORDS_PER_BLOCK = 1 + 252*2
|
|
||||||
float words_per_second = PCM_WORDS_PER_BLOCK * (num_adpcm_blocks as float) / duration_secs
|
|
||||||
txt.print_uw(words_per_second as uword)
|
|
||||||
txt.print(" decoded pcm words/sec\n")
|
|
||||||
float src_per_second = adpcm_size as float / duration_secs
|
float src_per_second = adpcm_size as float / duration_secs
|
||||||
txt.print_uw(src_per_second as uword)
|
txt.print_uw(src_per_second as uword)
|
||||||
txt.print(" adpcm data bytes/sec\n")
|
txt.print(" adpcm data bytes/sec\n")
|
||||||
|
float words_per_second = pcm_words_per_block * (num_adpcm_blocks as float) / duration_secs
|
||||||
|
when wavfile.nchannels {
|
||||||
|
1 -> {
|
||||||
|
txt.print_uw(words_per_second as uword)
|
||||||
|
txt.print(" decoded mono pcm words/sec (max hz)\n")
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
txt.print_uw(words_per_second as uword)
|
||||||
|
txt.print(" decoded pcm words/sec\n")
|
||||||
|
txt.print_uw(words_per_second/2 as uword)
|
||||||
|
txt.print(" decoded stereo audio frames/sec (max hz)\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub playback() {
|
sub playback() {
|
||||||
nibblesptr = &wavdata.wav_data + wavfile.data_offset
|
nibblesptr = &wavdata.wav_data + wavfile.data_offset
|
||||||
adpcm_blocks_left = num_adpcm_blocks
|
adpcm_blocks_left = num_adpcm_blocks
|
||||||
|
|
||||||
cx16.VERA_AUDIO_CTRL = %10101111 ; mono 16 bit
|
when wavfile.nchannels {
|
||||||
|
1 -> cx16.VERA_AUDIO_CTRL = %10101111 ; mono 16 bit
|
||||||
|
2 -> cx16.VERA_AUDIO_CTRL = %10111111 ; stereo 16 bit
|
||||||
|
}
|
||||||
cx16.VERA_AUDIO_RATE = 0 ; halt playback
|
cx16.VERA_AUDIO_RATE = 0 ; halt playback
|
||||||
repeat 1024 {
|
repeat 1024 {
|
||||||
cx16.VERA_AUDIO_DATA = 0
|
cx16.VERA_AUDIO_DATA = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
sys.set_irqd()
|
sys.set_irqd()
|
||||||
cx16.CINV = &irq_handler
|
when wavfile.nchannels {
|
||||||
|
1 -> cx16.CINV = &irq_handler_mono
|
||||||
|
2 -> cx16.CINV = &irq_handler_stereo
|
||||||
|
}
|
||||||
cx16.VERA_IEN = %00001000 ; enable AFLOW
|
cx16.VERA_IEN = %00001000 ; enable AFLOW
|
||||||
sys.clear_irqd()
|
sys.clear_irqd()
|
||||||
|
|
||||||
@ -128,25 +243,59 @@ main {
|
|||||||
; txt.print("audio off.\n")
|
; txt.print("audio off.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irq_handler() {
|
sub irq_handler_mono() {
|
||||||
if cx16.VERA_ISR & %00001000 {
|
if cx16.VERA_ISR & %00001000 {
|
||||||
; AFLOW irq.
|
; AFLOW irq.
|
||||||
;; cx16.vpoke(1,$fa0c, $a0) ; paint a screen color
|
;; cx16.vpoke(1,$fa0c, $a0) ; paint a screen color
|
||||||
|
|
||||||
; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data)
|
; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data)
|
||||||
adpcm.init(peekw(nibblesptr), @(nibblesptr+2))
|
adpcm.init(peekw(nibblesptr), @(nibblesptr+2))
|
||||||
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
||||||
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
||||||
nibblesptr += 4
|
nibblesptr += 4
|
||||||
repeat 252 {
|
decode_mono_nibbles()
|
||||||
ubyte @zp nibble = @(nibblesptr)
|
adpcm_blocks_left--
|
||||||
adpcm.decode_nibble(nibble & 15) ; first word
|
if adpcm_blocks_left==0 {
|
||||||
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
; restart adpcm data from the beginning
|
||||||
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
nibblesptr = &wavdata.wav_data + wavfile.data_offset
|
||||||
adpcm.decode_nibble(nibble>>4) ; second word
|
adpcm_blocks_left = num_adpcm_blocks
|
||||||
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
txt.print("end of data, restarting.\n")
|
||||||
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
}
|
||||||
nibblesptr++
|
|
||||||
|
} else {
|
||||||
|
; it's not AFLOW, handle other IRQ here.
|
||||||
|
}
|
||||||
|
|
||||||
|
;; cx16.vpoke(1,$fa0c, 0) ; back to other screen color
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
ply
|
||||||
|
plx
|
||||||
|
pla
|
||||||
|
rti
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub irq_handler_stereo() {
|
||||||
|
if cx16.VERA_ISR & %00001000 {
|
||||||
|
; AFLOW irq.
|
||||||
|
;; cx16.vpoke(1,$fa0c, $a0) ; paint a screen color
|
||||||
|
|
||||||
|
; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data)
|
||||||
|
; left channel
|
||||||
|
adpcm.init(peekw(nibblesptr), @(nibblesptr+2))
|
||||||
|
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
||||||
|
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
||||||
|
nibblesptr += 4
|
||||||
|
; right channel
|
||||||
|
adpcm.init_second(peekw(nibblesptr), @(nibblesptr+2))
|
||||||
|
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict_2)
|
||||||
|
cx16.VERA_AUDIO_DATA = msb(adpcm.predict_2)
|
||||||
|
nibblesptr += 4
|
||||||
|
repeat 31 {
|
||||||
|
decode_stereo_nibbles()
|
||||||
|
nibblesptr += 8
|
||||||
|
copy_stereo_to_fifo()
|
||||||
}
|
}
|
||||||
|
|
||||||
adpcm_blocks_left--
|
adpcm_blocks_left--
|
||||||
@ -164,10 +313,10 @@ main {
|
|||||||
;; cx16.vpoke(1,$fa0c, 0) ; back to other screen color
|
;; cx16.vpoke(1,$fa0c, 0) ; back to other screen color
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
ply
|
ply
|
||||||
plx
|
plx
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
examples/cx16/pcmaudio/small-adpcm-stereo.wav
Normal file
BIN
examples/cx16/pcmaudio/small-adpcm-stereo.wav
Normal file
Binary file not shown.
@ -242,15 +242,18 @@ interrupt {
|
|||||||
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
||||||
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
||||||
nibblesptr += 4
|
nibblesptr += 4
|
||||||
repeat 252 {
|
ubyte @zp nibble
|
||||||
ubyte @zp nibble = @(nibblesptr)
|
repeat 252/2 {
|
||||||
adpcm.decode_nibble(nibble & 15) ; first word
|
unroll 2 {
|
||||||
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
nibble = @(nibblesptr)
|
||||||
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
adpcm.decode_nibble(nibble & 15) ; first word
|
||||||
adpcm.decode_nibble(nibble>>4) ; second word
|
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
||||||
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
||||||
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
adpcm.decode_nibble(nibble>>4) ; second word
|
||||||
nibblesptr++
|
cx16.VERA_AUDIO_DATA = lsb(adpcm.predict)
|
||||||
|
cx16.VERA_AUDIO_DATA = msb(adpcm.predict)
|
||||||
|
nibblesptr++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user