Best Tutorial for Android Jetpack Compose

Android App Development

Stay ahead with the latest tools, trends, and best practices in Android development

Android’s Inbuilt Equalizer in Jetpack Compose

Android’s Inbuilt Equalizer in Jetpack Compose - Coding Bihar
Android’s Inbuilt Equalizer in Jetpack Compose
A Beginner’s Guide to Using Android’s Inbuilt Equalizer + Sliders

Music sounds different to every ear. Some people love deep bass, others prefer clear vocals, while many enjoy a balanced mix of both. Android gives you a simple way to shape your sound through its inbuilt Equalizer. You don’t need to be an audio engineer to use it—just a little curiosity and a few taps.

What is an Equalizer?

An equalizer (often shortened to EQ) is a tool that lets you adjust different parts of the sound spectrum.
  • Bass → The low, rumbling sounds (like drums or deep beats).
  • Mid → The middle range, usually where voices and guitars sit.
  • Treble → The higher, sharper sounds (like cymbals, strings, or “s” sounds in vocals).
By raising or lowering these ranges, you can make music sound warmer, brighter, punchier, or smoother.

Android’s Inbuilt Equalizer

Most Android devices ship with a simple system equalizer built into the operating system. It usually comes with:
  • Five frequency bands (Bass → Low Mid → Mid → High Mid → Treble).
  • Presets like “Rock,” “Jazz,” “Pop,” or “Classical” for quick adjustments.
  • A Bass Boost slider for extra low-end power.
  • A Virtualizer to add a sense of 3D space.
The exact look and features can vary depending on your phone brand (Samsung, Xiaomi, OnePlus, etc.), but the core idea remains the same.

How to Access It

Here’s a common way to open the equalizer on Android:
  • Play a song in your favorite music player (Spotify, YouTube Music, local MP3 player).
  • Open the Now Playing screen and look for the sound settings icon or the equalizer option in the app’s settings.
  • If the app supports it, it will open Android’s default equalizer panel.
  • Adjust the sliders or choose a preset to hear instant changes.
👉 Note: Some apps (like Spotify) include their own equalizer, but they often link to the Android system EQ underneath.

Tips for Beginners

Start with Presets

If you’re new, try presets like “Rock” or “Pop.” They give a quick taste of how EQ changes the sound.

Small Moves, Big Difference

Move sliders gently. A little boost (+2 or +3 dB) can make music more enjoyable without distorting it.

Balance Over Boosting

Instead of pushing frequencies too high, try lowering others to create space. This avoids making the sound harsh.

Tailor to Your Environment

  • In a noisy bus? Boost mids and treble for clearer vocals.
  • At home with good speakers? Add bass for warmth.

Limitations of the Inbuilt Equalizer + Sliders

  • It usually works only with media playback, not phone calls.
  • Some apps may bypass the equalizer completely.
  • Sound quality improvements are noticeable, but not as detailed as professional EQ apps or hardware.
Still, for most users, the inbuilt EQ is more than enough to make music sound personalized and fun.

Why Use It?

Because music is personal. The same song can sound too heavy, too thin, or just right depending on your taste, headphones, or speakers. The equalizer gives you the freedom to sculpt the sound so it matches your style.

🎛️ Equalizer UI with Sliders + Presets

This example shows:

  • Five frequency sliders (Bass → Treble).
  • Preset buttons styled like modern chips.
  • A clean card layout with rounded corners, shadows, and spacing.
@Composable
fun EqualizerScreen() {
    val frequencyLabels = listOf("60Hz", "230Hz", "910Hz", "3.6kHz", "14kHz")
    val sliderValues = remember { mutableStateListOf(0f, 0f, 0f, 0f, 0f) }
    val presets = listOf("Normal", "Rock", "Pop", "Jazz", "Classical")
    var selectedPreset by remember { mutableStateOf("Normal") }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {

        // Preset Section
        Text("Presets", style = MaterialTheme.typography.titleMedium)
        Spacer(Modifier.height(8.dp))
        FlowRow(
            mainAxisSpacing = 8.dp,
            crossAxisSpacing = 8.dp
        ) {
            presets.forEach { preset ->
                FilterChip(
                    selected = preset == selectedPreset,
                    onClick = { selectedPreset = preset },
                    label = { Text(preset) }
                )
            }
        }

        Spacer(Modifier.height(24.dp))

        // Sliders Section
        Text("Custom Equalizer", style = MaterialTheme.typography.titleMedium)
        Spacer(Modifier.height(12.dp))
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceEvenly
        ) {
            frequencyLabels.forEachIndexed { index, label ->
                Column(
                    horizontalAlignment = Alignment.CenterHorizontally,
                    modifier = Modifier.weight(1f)
                ) {
                    // Vertical Slider
                    Slider(
                        value = sliderValues[index],
                        onValueChange = { sliderValues[index] = it },
                        valueRange = -10f..10f,
                        modifier = Modifier
                            .height(200.dp)
                            .rotate(-90f) // make vertical
                    )
                    Spacer(Modifier.height(8.dp))
                    Text(label, style = MaterialTheme.typography.labelSmall)
                }
            }
        }

        Spacer(Modifier.height(16.dp))

        // Reset Button
        Button(
            onClick = {
                sliderValues.indices.forEach { sliderValues[it] = 0f }
                selectedPreset = "Normal"
            },
            modifier = Modifier.align(Alignment.CenterHorizontally),
            shape = RoundedCornerShape(50)
        ) {
            Text("Reset")
        }
    }
}

✨ Features of this UI

  • Presets as Chips → quick, modern, and tappable.
  • Vertical Sliders → more natural for equalizer controls.
  • Rounded card style + shadows (can be wrapped in Card { . }).
  • Reset button to bring everything back to normal.

🔹 EqualizerPlayerScreen

@Composable
fun EqualizerPlayerScreen() {
👉 Declares a Composable function. This will show the entire Equalizer screen UI.
val context = LocalContext.current
👉 Gets the current Android Context (needed to access assets, MediaPlayer, etc.).
    var mediaPlayer by remember { mutableStateOf(null) }
    var equalizer by remember { mutableStateOf(null) }

👉 Two states: 

  •  mediaPlayer → Plays audio
  • equalizer → Controls sound bands/presets.
    var isPlaying by remember { mutableStateOf(false) }
    var presetNames by remember { mutableStateOf(listOf()) }
    var selectedPreset by remember { mutableIntStateOf(-1) }
    var bandStates by remember { mutableStateOf(listOf>()) }
👉 UI states:
  • isPlaying → true if music is playing.
  • presetNames → list of Equalizer preset names (e.g., "Rock", "Jazz"). 
  • selectedPreset → index of current preset.
  • bandStates → list of (centerFrequency, normalizedLevel) for each EQ band.

🎵 Setup when screen opens

LaunchedEffect(Unit) {
👉 Runs once when Composable first appears.
try {
            val mp = MediaPlayer()
👉 Create a new MediaPlayer.
            val afd = context.assets.openFd("sample.mp3")
            mp.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
            afd.close()
👉 Load sample.mp3 from assets and set it as the MediaPlayer source.
mp.isLooping = true
            mp.prepare()
            mediaPlayer = mp
👉 Loop song forever, prepare it, and store in mediaPlayer.
val eq = Equalizer(0, mp.audioSessionId)
            eq.enabled = true
            equalizer = eq
👉 Create Equalizer linked to MediaPlayer’s session. Enable it and save in state.
            val names = mutableListOf()
            for (i in 0 until eq.numberOfPresets) names += eq.getPresetName(i.toShort())
            presetNames = names
            if (names.isNotEmpty()) selectedPreset = 0
👉 Get all preset names from Equalizer (Rock, Pop, etc.) Store in presetNames. 
If list not empty → select first preset.
            val range = eq.bandLevelRange
            val min = range[0].toInt()
            val max = range[1].toInt()
👉 Each EQ band has min & max level in millibels (mB). 
We need them to normalize values to 0–1 for sliders.
            val bands = (0 until eq.numberOfBands).map { b ->
                val center = eq.getCenterFreq(b.toShort()).toInt()
                val level = eq.getBandLevel(b.toShort()).toInt()
                val normalized = (level - min).toFloat() / (max - min).toFloat()
                center to normalized
            }
            bandStates = bands
👉 For each EQ band: 
  • Get center frequency (e.g., 60 Hz, 1kHz). 
  • Get current level. 
  • Normalize between 0–1 for slider UI.
  • Store (frequency, normalizedLevel) in bandStates.
        } catch (e: Exception) {
            Log.e("EQ", "Init failed: ${e.message}")
        }
    }
👉 Catch any errors during setup (like file not found).

🔹 Release resources

    DisposableEffect(Unit) {
        onDispose {
            equalizer?.release()
            mediaPlayer?.release()
        }
    }
👉 When Composable leaves screen → release MediaPlayer & Equalizer to free memory.

UI Layout

    Column(
        Modifier
            .fillMaxSize()
            .padding(20.dp)
            .verticalScroll(rememberScrollState())
    ) {
👉 Whole screen is a scrollable Column with padding.

Header

        Text(
            "🎵 Equalizer Player",
            fontSize = 26.sp,
            fontWeight = FontWeight.Bold,
            color = MaterialTheme.colorScheme.primary
        )
👉 Title at top.

Player Controls

        Card(
            Modifier
                .fillMaxWidth()
                .padding(bottom = 16.dp),
            shape = RoundedCornerShape(24.dp),
            elevation = CardDefaults.cardElevation(8.dp),
            colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surfaceVariant)
        ) {
👉 Music control section inside a card with rounded corners.
                IconButton(
                    onClick = {
                        mediaPlayer?.let {
                            if (isPlaying) {
                                it.pause(); isPlaying = false
                            } else {
                                it.start(); isPlaying = true
                            }
                        }
                    },
👉 Play/Pause button toggles MediaPlayer. 
Updates isPlaying.
                    Icon(painter = painterResource(if (isPlaying)R.drawable.pause else R.drawable.play),
                        contentDescription = "Play/Pause", tint = MaterialTheme.colorScheme.onPrimaryContainer,
                        modifier = Modifier.size(50.dp))
👉 Shows Pause icon if playing, else Play icon.

Presets

        LazyColumn {
            items(presetNames.chunked(3)) { rowItems ->
👉 List of presets, shown in rows of 3 chips.
                        AssistChip(
                            onClick = {
                                equalizer?.usePreset(index.toShort())
                                selectedPreset = index
👉 When chip clicked → Apply preset to Equalizer. 
Update selectedPreset.
bandStates = (0 until eq.numberOfBands).map { b -> ... }
👉 Refresh band sliders to show preset’s band levels.

Bands

        Row(
            Modifier
                .fillMaxWidth()
                .height(220.dp),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.Bottom
        ) {
👉 Show bands in a Row (side-by-side vertical sliders).
                    EqualizerBar(
                        value = normalized,
                        onValueChange = { newValue ->
                            bandStates = bandStates.toMutableList().also { list ->
                                list[index] = center to newValue
                            }
                            equalizer?.let { eq ->
                                val range = eq.bandLevelRange
                                val min = range[0].toInt()
                                val max = range[1].toInt()
                                val level = (min + (max - min) * newValue).toInt().toShort()
                                eq.setBandLevel(index.toShort(), level)
                            }
                        },
👉 Custom slider EqualizerBar:
UI value updated in bandStates. 
Equalizer band updated with new level (converted back from 0–1 scale → real mB value).
                    Text(
                        "${center / 1000} Hz",
                        fontSize = 12.sp,
                        color = MaterialTheme.colorScheme.onSurfaceVariant
                    )
👉 Show frequency label (e.g., "60 Hz").

🔹 EqualizerBar (Custom Vertical Slider)

@Composable
fun EqualizerBar(
    value: Float,           // 0f → min, 1f → max
    onValueChange: (Float) -> Unit,
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colorScheme.primary
) {
👉 A custom bar representing one EQ band.
    var dragPosition by remember { mutableStateOf(value) }
👉 Current slider value, stored in state.
    Box(
        modifier
            .pointerInput(Unit) {
                detectVerticalDragGestures { change, dragAmount ->
                    change.consume()
                    dragPosition = (dragPosition - dragAmount / size.height)
                        .coerceIn(0f, 1f) // clamp 0–1
                    onValueChange(dragPosition)
                }
            }
    ) {
👉 Detect vertical drag. 
  • When dragged → adjust dragPosition. 
  • Clamp between 0f and 1f. 
  • Send updated value back to parent.
        Canvas(modifier = Modifier.fillMaxSize()) {
            // background track
            drawRoundRect(
                color = Color.Gray.copy(alpha = 0.3f),
                cornerRadius = CornerRadius(12f, 12f)
            )
👉 Draw background rectangle (slider track).
            val filledHeight = size.height * dragPosition
            drawRoundRect(
                color = color,
                topLeft = Offset(0f, size.height - filledHeight),
                size = Size(size.width, filledHeight),
                cornerRadius = CornerRadius(12f, 12f)
            )
        }
    }
}
👉 Draw filled rectangle (active level). It grows upwards as dragPosition increases.

@Composable
fun EqualizerPlayerScreen() {
    val context = LocalContext.current
    var mediaPlayer by remember { mutableStateOf(null) }
    var equalizer by remember { mutableStateOf(null) }

    var isPlaying by remember { mutableStateOf(false) }
    var presetNames by remember { mutableStateOf(listOf()) }
    var selectedPreset by remember { mutableIntStateOf(-1) }
    var bandStates by remember { mutableStateOf(listOf>()) } // (centerFreq, normalized)

    // setup
    LaunchedEffect(Unit) {
        try {
            val mp = MediaPlayer()
            val afd = context.assets.openFd("sample.mp3")
            mp.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
            afd.close()
            mp.isLooping = true
            mp.prepare()
            mediaPlayer = mp

            val eq = Equalizer(0, mp.audioSessionId)
            eq.enabled = true
            equalizer = eq

            // presets
            val names = mutableListOf()
            for (i in 0 until eq.numberOfPresets) names += eq.getPresetName(i.toShort())
            presetNames = names
            if (names.isNotEmpty()) selectedPreset = 0

            // bands
            val range = eq.bandLevelRange
            val min = range[0].toInt()
            val max = range[1].toInt()
            val bands = (0 until eq.numberOfBands).map { b ->
                val center = eq.getCenterFreq(b.toShort()).toInt()
                val level = eq.getBandLevel(b.toShort()).toInt()
                val normalized = (level - min).toFloat() / (max - min).toFloat()
                center to normalized
            }
            bandStates = bands
        } catch (e: Exception) {
            Log.e("EQ", "Init failed: ${e.message}")
        }
    }

    DisposableEffect(Unit) {
        onDispose {
            equalizer?.release()
            mediaPlayer?.release()
        }
    }

    Column(
        Modifier
            .fillMaxSize()
            .padding(20.dp)
            .systemBarsPadding()
            .verticalScroll(rememberScrollState())
    ) {
        // Header
        Text(
            "🎵 Equalizer Player",
            fontSize = 26.sp,
            fontWeight = FontWeight.Bold,
            color = MaterialTheme.colorScheme.primary
        )

        Spacer(Modifier.height(12.dp))

        // Player Controls
        Card(
            Modifier
                .fillMaxWidth()
                .padding(bottom = 16.dp),
            shape = RoundedCornerShape(24.dp),
            elevation = CardDefaults.cardElevation(8.dp),
            colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surfaceVariant)
        ) {
            Row(
                Modifier.fillMaxWidth().padding(16.dp),
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(32.dp)
            ) {
                IconButton(
                    onClick = {
                        mediaPlayer?.let {
                            if (isPlaying) {
                                it.pause(); isPlaying = false
                            } else {
                                it.start(); isPlaying = true
                            }
                        }
                    },
                    modifier = Modifier
                        .size(60.dp)
                        .background(
                            MaterialTheme.colorScheme.primaryContainer,
                            CircleShape
                        )
                ) {
                    Icon(
                        painter = painterResource(if (isPlaying) R.drawable.pause else R.drawable.play),
                        contentDescription = "Play/Pause",
                        tint = MaterialTheme.colorScheme.onPrimaryContainer,
                        modifier = Modifier.size(50.dp),
                    )
                }
                Text(
                    if (isPlaying) "Now Playing..." else "Paused",
                    fontSize = 16.sp,
                    color = MaterialTheme.colorScheme.onSurfaceVariant
                )
            }
        }

        // Presets
        Text("🎚 Presets", fontSize = 18.sp, fontWeight = FontWeight.Medium)
        LazyVerticalGrid(
            columns = GridCells.Adaptive(minSize = 90.dp),
            modifier = Modifier
                .fillMaxWidth()
                .height(280.dp)

        ) {
            items(presetNames.size) { i ->
                TextButton(
                    onClick = {
                        equalizer?.usePreset(i.toShort())
                        selectedPreset = i

                        equalizer?.let { eq ->
                            val range = eq.bandLevelRange
                            val min = range[0].toInt()
                            val max = range[1].toInt()
                            bandStates = (0 until eq.numberOfBands).map { b ->
                                val center = eq.getCenterFreq(b.toShort()).toInt()
                                val level = eq.getBandLevel(b.toShort()).toInt()
                                val normalized =
                                    (level - min).toFloat() / (max - min).toFloat()
                                center to normalized
                            }
                        }
                    },
                    modifier = Modifier
                        .padding(8.dp)
                        .clip(RoundedCornerShape(12.dp))
                        .background(
                            if (i == selectedPreset)
                                MaterialTheme.colorScheme.primaryContainer
                            else
                                MaterialTheme.colorScheme.surfaceVariant
                        )
                ) {
                    Text(
                        presetNames[i],
                        fontSize = 14.sp,
                        color = if (i == selectedPreset)
                            MaterialTheme.colorScheme.onPrimaryContainer
                        else
                            MaterialTheme.colorScheme.onSurface
                    )
                }
            }
        }

        // Bands
        Text("🎼 Bands", fontSize = 18.sp, fontWeight = FontWeight.Medium)
        Spacer(Modifier.height(8.dp))

        Row(
            Modifier
                .fillMaxWidth()
                .height(200.dp),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.Bottom
        ) {
            bandStates.forEachIndexed { index, (center, normalized) ->
                Column(
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.Bottom
                ) {
                    Text(
                        "${center / 1000} Hz",
                        fontSize = 12.sp,
                        color = MaterialTheme.colorScheme.onSurfaceVariant
                    )
                    EqualizerBar(
                        value = normalized,
                        onValueChange = { newValue ->
                            bandStates = bandStates.toMutableList().also { list ->
                                list[index] = center to newValue
                            }
                            equalizer?.let { eq ->
                                val range = eq.bandLevelRange
                                val min = range[0].toInt()
                                val max = range[1].toInt()
                                val level = (min + (max - min) * newValue).toInt().toShort()
                                eq.setBandLevel(index.toShort(), level)
                            }
                        },
                        modifier = Modifier
                            .width(20.dp)
                            .fillMaxHeight()
                    )
                }
            }
        }
    }
}

@Composable
fun EqualizerBar(
    value: Float,           // 0f → min, 1f → max
    onValueChange: (Float) -> Unit,
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colorScheme.primary
) {
    var targetValue by remember { mutableFloatStateOf(value) }

    // Whenever parent updates (preset change), update target
    LaunchedEffect(value) {
        targetValue = value
    }

    // Always animate towards targetValue (drag OR preset)
    val animatedValue by animateFloatAsState(
        targetValue = targetValue,
        animationSpec = tween(durationMillis = 200, easing = LinearEasing) // fast & smooth for drag
    )

    Box(
        modifier
            .pointerInput(Unit) {
                detectVerticalDragGestures { change, dragAmount ->
                    change.consume()
                    val newValue = (targetValue - dragAmount / size.height)
                        .coerceIn(0f, 1f)
                    targetValue = newValue
                    onValueChange(newValue)
                }
            }
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            // background track
            drawRoundRect(
                color = Color.Gray.copy(alpha = 0.3f),
                cornerRadius = CornerRadius(12f, 12f)
            )

            // active level
            val filledHeight = size.height * animatedValue
            drawRoundRect(
                color = color,
                topLeft = Offset(0f, size.height - filledHeight),
                size = Size(size.width, filledHeight),
                cornerRadius = CornerRadius(12f, 12f)
            )
        }
    }
}

OUTPUT:

Android’s Inbuilt Equalizer in Jetpack Compose Screenshot 1

Android’s Inbuilt Equalizer in Jetpack Compose Screenshot 2

Our Thoughts

Android’s inbuilt equalizer is like a secret sound control panel hiding in your phone. It’s simple, free, and surprisingly powerful once you know how to use it. Experiment with presets, play with sliders, and trust your ears. You might rediscover your favorite songs in a brand-new way.

Special Message

Welcome to Coding