/*
* *****************************************************************************
* Copyright (C) 2014-2026 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* ****************************************************************************
*/
package io.github.dsheirer.audio.playback;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.Mixer;
/**
* Audio playback device manager.
*/
public class AudioPlaybackDeviceManager
{
/**
* Always returns the first available device in the system.
*/
public static AudioPlaybackDeviceDescriptor getDefaultAudioPLaybackDevice()
{
List devices = getAudioPlaybackDevices();
if(!devices.isEmpty())
{
return devices.getFirst();
}
return null;
}
/**
* Finds the matching audio playback device
* @param name of the device
* @param channels count
* @return matching entry or the default playback device.
*/
public static AudioPlaybackDeviceDescriptor getAudioPlaybackDevice(String name, int channels)
{
for(AudioPlaybackDeviceDescriptor device: getAudioPlaybackDevices())
{
if(device.getMixerInfo().getName().equals(name) && device.getAudioFormat().getChannels() == channels)
{
return device;
}
}
return getDefaultAudioPLaybackDevice();
}
public static List getAudioPlaybackDevices()
{
List devices = new ArrayList<>();
Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
for(Mixer.Info mixerInfo : mixerInfos)
{
Mixer mixer = AudioSystem.getMixer(mixerInfo);
if(mixer != null)
{
List discoveredFormats = new ArrayList<>();
for(Line.Info sourceLineInfo : mixer.getSourceLineInfo())
{
if(sourceLineInfo instanceof DataLine.Info dataLineInfo)
{
for(AudioFormat audioFormat : dataLineInfo.getFormats())
{
if(!audioFormat.isBigEndian() && audioFormat.getSampleSizeInBits() == 16 &&
!discoveredFormats.contains(audioFormat))
{
if(audioFormat.getChannels() == 1)
{
if(audioFormat.getSampleRate() == AudioSystem.NOT_SPECIFIED)
{
discoveredFormats.add(audioFormat);
//Modify the format for 8 kHz sample rate
AudioFormat updatedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
8000, 16, audioFormat.getChannels(), audioFormat.getFrameSize(),
8000, false);
devices.add(new AudioPlaybackDeviceDescriptor(mixerInfo, updatedFormat));
}
else if(audioFormat.getSampleRate() == 8000)
{
discoveredFormats.add(audioFormat);
devices.add(new AudioPlaybackDeviceDescriptor(mixerInfo, audioFormat));
}
}
//Stereo and 64-channel formats
else if(audioFormat.getChannels() == 2 || audioFormat.getChannels() > 8)
{
if(audioFormat.getSampleRate() == AudioSystem.NOT_SPECIFIED)
{
discoveredFormats.add(audioFormat);
//Modify the format for 8 kHz sample rate, 2 channels, 4 bytes per frame
AudioFormat updatedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
8000, 16, 2, 4,
8000, false);
devices.add(new AudioPlaybackDeviceDescriptor(mixerInfo, updatedFormat));
}
else if(audioFormat.getSampleRate() == 8000)
{
discoveredFormats.add(audioFormat);
//Modify the format for 8 kHz sample rate, 2 channels, 4 bytes per frame
AudioFormat updatedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
audioFormat.getSampleRate(), 16, 2, 4,
audioFormat.getFrameRate(), false);
devices.add(new AudioPlaybackDeviceDescriptor(mixerInfo, updatedFormat));
}
}
//TODO: remove the 2 channels max restriction once supported
// else if(audioFormat.getChannels() > 2 && audioFormat.getChannels() <= 8)
// {
// if(audioFormat.getSampleRate() == AudioSystem.NOT_SPECIFIED)
// {
// discoveredFormats.add(audioFormat);
// //Modify the format for 8 kHz sample rate
// AudioFormat updatedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
// 8000, 16, audioFormat.getChannels(), audioFormat.getFrameSize(),
// 8000, false);
// devices.add(new AudioPlaybackDeviceDescriptor(mixerInfo, updatedFormat));
// }
// else if(audioFormat.getSampleRate() == 8000)
// {
// discoveredFormats.add(audioFormat);
// devices.add(new AudioPlaybackDeviceDescriptor(mixerInfo, audioFormat));
// }
// }
}
}
}
}
}
}
Collections.sort(devices);
return devices;
}
static void main()
{
List devices = getAudioPlaybackDevices();
for(AudioPlaybackDeviceDescriptor device : devices)
{
System.out.println(device);
}
}
}