#### RemiAudio
#### Copyright (C) 2022-2024 Remilia Scarlet <remilia@posteo.jp>
####
#### This program is free software: you can redistribute it and/or modify it
#### under the terms of the GNU Affero General Public License as published by
#### the Free Software Foundation, either version 3 of the License, or (at your
#### option) any later version.
####
#### This program is distributed in the hope that it will be useful, but WITHOUT
#### ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#### FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
#### License for more details.
####
#### You should have received a copy of the GNU Affero General Public License
#### along with this program. If not, see <https://www.gnu.org/licenses/>.
module RemiAudio::Codecs
module G117
extend self
RemiAudio::Codecs::G117.initLawTables()
# :nodoc:
LINEAR_CODE_BIAS = 0x84
# :nodoc:
@@muLawTable : Array(UInt8) = [] of UInt8
@@aLawTable : Array(UInt8) = [] of UInt8
# Converts a mu-Law sample to a signed 16-bit integer sample.
def muLawToInt16(uval : UInt8) : Int16
# Taken from pcm_tablegen.h in ffmpeg
notUval : UInt8 = ~uval
foo : Int32 = ((uval & 0xf) << 3).to_i32! + LINEAR_CODE_BIAS
foo = foo << ((notUval & 0x70) >> 4)
if (notUval & 0x80) != 0
(LINEAR_CODE_BIAS - foo).to_i16!
else
(foo - LINEAR_CODE_BIAS).to_i16!
end
end
# Converts an a-Law sample to a signed 16-bit integer sample.
def aLawToInt16(aval : UInt8) : Int16
# Taken from pcm_tablegen.h in ffmpeg
seg : Int16 = 0
aval = aval ^ 0x55
foo : Int16 = (aval & 0xf).to_i16!
seg = ((aval & 0x70) >> 4).to_i16!
foo = if seg != 0
(foo + foo + 1 + 32) << (seg + 2)
else
(foo + foo + 1) << 3
end
if (aval & 0x80) != 0
foo
else
-foo
end
end
# :nodoc:
protected def makeLawTable(mask : UInt8, reverseFn : Proc(UInt8, Int16)) : Array(UInt8)
# Taken from pcm_tablegen.h in ffmpeg
j : Int32 = 1
ret : Array(UInt8) = Array.new(16384, 0u8)
ret[8192] = mask
127u8.times do |i|
v1 : Int32 = reverseFn.call((i ^ mask).to_u8!).to_i32!
v2 : Int32 = reverseFn.call(((i + 1) ^ mask).to_u8!).to_i32!
v : Int32 = (v1 + v2 + 4) >> 3
while j < v
ret[8192 - j] = i ^ (mask ^ 0x80)
ret[8192 + j] = i ^ mask
j += 1
end
end
while j < 8192
ret[8192 - j] = 127u8 ^ (mask ^ 0x80)
ret[8192 + j] = 127u8 ^ mask
j += 1
end
ret[0] = ret[1]
ret
end
# :nodoc:
def self.initLawTables
@@muLawTable = makeLawTable(0xff, ->muLawToInt16(UInt8))
@@aLawTable = makeLawTable(0xd5, ->aLawToInt16(UInt8))
end
# Converts a 16-bit signed integer sample into a mu-Law sample.
@[AlwaysInline]
def int16ToMuLaw(sample : Int16) : UInt8
@@muLawTable[(sample.to_i32! + 32768) >> 2]
end
# Converts an array of 16-bit signed integer samples into an array of mu-Law samples.
def int16ToMuLaw(samples : Array(Int16)) : Array(UInt8)
samples.map { |smp| int16ToMuLaw(smp) }
end
# Converts a 16-bit signed integer sample into a a-Law sample.
@[AlwaysInline]
def int16ToALaw(sample : Int16) : UInt8
@@aLawTable[(sample.to_i32! + 32768) >> 2]
end
# Converts an array of 16-bit signed integer samples into an array of a-Law samples.
def int16ToMuLaw(samples : Array(Int16)) : Array(UInt8)
samples.map { |smp| int16ToALaw(smp) }
end
# Converts an array of mu-Law samples into an array of 16-bit signed integer samples.
def muLawToInt16(samples : Array(Int16)) : Array(UInt8)
samples.map { |smp| muLawToInt16(smp) }
end
# Converts an array of ma-Law samples into an array of 16-bit signed integer samples.
def aLawToInt16(samples : Array(Int16)) : Array(UInt8)
samples.map { |smp| aLawToInt16(smp) }
end
end
end