Skip to content

Instantly share code, notes, and snippets.

@LucasAlfare
Last active April 18, 2025 14:41
Show Gist options
  • Save LucasAlfare/5540075fdea7f4e94c4d4ba0bb59c253 to your computer and use it in GitHub Desktop.
Save LucasAlfare/5540075fdea7f4e94c4d4ba0bb59c253 to your computer and use it in GitHub Desktop.
Custom MP3 decoder from Scratch in Kotlin // Rascunho de decodificador de MP3 PRÓPRIO do ZERO
@file:OptIn(ExperimentalUnsignedTypes::class)
import com.lucasalfare.flbinary.Reader
import com.lucasalfare.flbinary.readBits
import java.io.File
val bitrateTable = IntArray(0b1111) { -1 }
val sampleRateTable = IntArray(0b11) { -1 }
fun main() {
bitrateTable[0b0001] = 32000
bitrateTable[0b0010] = 40000
bitrateTable[0b0011] = 48000
bitrateTable[0b0100] = 56000
bitrateTable[0b0101] = 64000
bitrateTable[0b0110] = 80000
bitrateTable[0b0111] = 96000
bitrateTable[0b1000] = 112000
bitrateTable[0b1001] = 128000
bitrateTable[0b1010] = 160000
bitrateTable[0b1011] = 192000
bitrateTable[0b1100] = 224000
bitrateTable[0b1101] = 256000
bitrateTable[0b1110] = 320000
sampleRateTable[0b00] = 44100
sampleRateTable[0b01] = 48000
sampleRateTable[0b10] = 32000
val bytes = File("bip.mp3").readBytes().toUByteArray()
val reader = Reader(bytes)
skipTags(reader)
println("Reading audio data...")
var n = 0
while (reader.position < bytes.size - 1) {
val frameSynchronizer = reader.readBits(11)
if (frameSynchronizer == 0b11111111_111L) {
println("Found a frame header at position ${reader.position - 1}!")
readAudioFrame(reader)
println()
n++
if (n == 10) break
} else {
reader.position--
}
}
}
fun skipTags(reader: Reader) {
val id3Signature = reader.readString(3)
if (id3Signature == "ID3") {
val versionMajor = reader.read1Byte()
val versionMinor = reader.read1Byte()
val flags = reader.read1Byte()
val b3 = reader.read1Byte()
val b2 = reader.read1Byte()
val b1 = reader.read1Byte()
val b0 = reader.read1Byte()
val tagSize = ((b3 and 0x7F) shl 21) or
((b2 and 0x7F) shl 14) or
((b1 and 0x7F) shl 7) or
(b0 and 0x7F)
val hasExtendedHeader = (flags and 0x40) != 0
var totalSize = tagSize + 10
if (hasExtendedHeader) {
val extHeaderSize = (reader.read1Byte() shl 24) or
(reader.read1Byte() shl 16) or
(reader.read1Byte() shl 8) or
reader.read1Byte()
val skipSize = if (versionMajor == 3) extHeaderSize - 4 else extHeaderSize
reader.advancePosition(skipSize)
totalSize += extHeaderSize
}
reader.advancePosition(totalSize - 10)
println("Tag skipped ($totalSize bytes).")
} else {
reader.position -= 3
println("No tags found.")
}
}
fun readAudioFrame(reader: Reader) {
reader.position -= 2
// next bytes after frame synchronizer
// we care only about the bytes 1/2/3 because byte 0 is filled with bits '1'.
val headerBytes: UByteArray = reader.readBytes(4)
val b2 = headerBytes[1].toInt()
val b3 = headerBytes[2].toInt()
val b4 = headerBytes[3].toInt()
val mpegVersionID = (b2 shr 5) and 0b11
val layer = (b2 shr 3) and 0b11
val hasCrcProtection = (b2 shr 2) and 0b1
val bitrateIndex = (b3 shr 4) and 0b1111
val samplingRateFrequencyIndex = (b3 shr 2) and 0b11
val hasPadding = (b3 shr 1) and 0b1
val privateBit = b3 and 0b1
val channelMode = (b4 shr 6) and 0b11
val modeExtension = (b4 shr 4) and 0b11
val isCopyrighted = (b4 shr 3) and 0b1
val isOriginal = (b4 shr 2) and 0b1
val emphasis = b4 and 0b11
val bitrate = bitrateTable[bitrateIndex]
val sampleRate = sampleRateTable[samplingRateFrequencyIndex]
val currentFrameLength = (144 * bitrate / sampleRate) + hasPadding
// println("[BB] modeExtension: ${mpegVersionID.toString(2).padStart(2, '0')}")
// println("[CC] layer: ${layer.toString(2).padStart(2, '0')}")
// println("[D] hasCrcProtection: ${hasCrcProtection.toString(2).padStart(1, '0')}")
// println("[EEEE] bitrateIndex:${bitrateIndex.toString(2).padStart(4, '0')}")
// println("[FF] samplingRateFrequencyIndex: ${samplingRateFrequencyIndex.toString(2).padStart(2, '0')}")
// println("[G] hasPadding: ${hasPadding.toString(2).padStart(1, '0')}")
// println("[H] privateBit: ${privateBit.toString(2).padStart(1, '0')}")
// println("[II] channelMode: ${channelMode.toString(2).padStart(2, '0')}")
// println("[JJ] modeExtension: ${modeExtension.toString(2).padStart(2, '0')}")
// println("[K] isCopyrighted: ${isCopyrighted.toString(2).padStart(1, '0')}")
// println("[L] isOriginal: ${isOriginal.toString(2).padStart(1, '0')}")
// println("[MM] emphasis: ${emphasis.toString(2).padStart(2, '0')}")
println("Decoded bitrate: $bitrate")
println("Decoded sampleRate: $sampleRate")
println("Total frame length (in bytes): $currentFrameLength")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment