United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4673682 DOC: Mixer.getMaxLines() returns incorrect number
JDK-4673682 : DOC: Mixer.getMaxLines() returns incorrect number

Details
Type:
Bug
Submit Date:
2002-04-24
Status:
Resolved
Updated Date:
2003-10-24
Project Name:
JDK
Resolved Date:
2003-10-24
Component:
client-libs
OS:
generic
Sub-Component:
javax.sound
CPU:
generic
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.4.1
Fixed Versions:
5.0 (b26)

Related Reports

Sub Tasks

Description
       if ( (lineToOpen instanceof Port) ) {
                lineToOpen.open();
                openResult = true;
            }
            // other lines can not be as source lines
            // so they are not opeened
        } catch (SecurityException securityException) {
            // line is not available due to security restrictions
        } catch (Throwable thrown) {
            //  Other Exception was thrown: pass it to caller
            throw thrown;
        }
        return openResult;
    }
    
}    // end of test class 
-------------------------------------------------------------------------
 
======================================================================
###@###.### 2003-10-23
fixed


Name: abR10010			Date: 04/24/2002



The specification for the getMaxLines(Line.Info) method of the javax.sound.sampled.Mixer
class says:

"
Obtains the maximum number of lines of the requested type that can be open simultaneously
on the mixer. ...
"

However the test (see below) shows that the Mixer.getMaxLines(Line.Info) method for
the one of installed on the system mixers returns int value that is less than 
actual number of lines which can be open simultaneously.

Such behavior is shown by the test running with JDK 1.4.1-beta-b10.


Please, see test log:

% java -version
java version "1.4.1-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-beta-b10)
Java HotSpot(TM) Client VM (build 1.4.1-beta-b10, mixed mode)

% java test

==> Test for Mixer.getMaxLines(Line.Info) method:

>>>  testedMixer[0] = com.sun.media.sound.HeadspaceMixer@c2a132

>>  supportedSourceLineInfo.length = 2

>  testSourceLineInfo[0] = interface SourceDataLine supporting 8 audio formats

>  getMaxLines(testSourceLineInfo) = 32
## Mixer.getMaxLines(Line.Info) FAILED:
#  Mixer = com.sun.media.sound.HeadspaceMixer@c2a132
#  Line.Info = interface SourceDataLine supporting 8 audio formats
#  Mixer.getMaxLines(Line.Info) = 32
#  Number of open Lines         = 33
#  openLines[0] = com.sun.media.sound.MixerSourceLine@1e4457d
#  openLines[1] = com.sun.media.sound.MixerSourceLine@18e2b22
#  openLines[2] = com.sun.media.sound.MixerSourceLine@b1c5fa
#  openLines[3] = com.sun.media.sound.MixerSourceLine@13caecd
#  openLines[4] = com.sun.media.sound.MixerSourceLine@f84386
#  openLines[5] = com.sun.media.sound.MixerSourceLine@1194a4e
#  openLines[6] = com.sun.media.sound.MixerSourceLine@15d56d5
#  openLines[7] = com.sun.media.sound.MixerSourceLine@efd552
#  openLines[8] = com.sun.media.sound.MixerSourceLine@19dfbff
#  openLines[9] = com.sun.media.sound.MixerSourceLine@10b4b2f
#  openLines[10] = com.sun.media.sound.MixerSourceLine@750159
#  openLines[11] = com.sun.media.sound.MixerSourceLine@1abab88
#  openLines[12] = com.sun.media.sound.MixerSourceLine@18a7efd
#  openLines[13] = com.sun.media.sound.MixerSourceLine@1971afc
#  openLines[14] = com.sun.media.sound.MixerSourceLine@16cd7d5
#  openLines[15] = com.sun.media.sound.MixerSourceLine@cdedfd
#  openLines[16] = com.sun.media.sound.MixerSourceLine@1c39a2d
#  openLines[17] = com.sun.media.sound.MixerSourceLine@bf2d5e
#  openLines[18] = com.sun.media.sound.MixerSourceLine@13bad12
#  openLines[19] = com.sun.media.sound.MixerSourceLine@df8ff1
#  openLines[20] = com.sun.media.sound.MixerSourceLine@1632c2d
#  openLines[21] = com.sun.media.sound.MixerSourceLine@1e97676
#  openLines[22] = com.sun.media.sound.MixerSourceLine@60420f
#  openLines[23] = com.sun.media.sound.MixerSourceLine@19106c7
#  openLines[24] = com.sun.media.sound.MixerSourceLine@540408
#  openLines[25] = com.sun.media.sound.MixerSourceLine@1d4c61c
#  openLines[26] = com.sun.media.sound.MixerSourceLine@1a626f
#  openLines[27] = com.sun.media.sound.MixerSourceLine@34a1fc
#  openLines[28] = com.sun.media.sound.MixerSourceLine@176c74b
#  openLines[29] = com.sun.media.sound.MixerSourceLine@116471f
#  openLines[30] = com.sun.media.sound.MixerSourceLine@1975b59
#  openLines[31] = com.sun.media.sound.MixerSourceLine@1ee3914
#  openLines[32] = com.sun.media.sound.MixerSourceLine@e5855a

>  testSourceLineInfo[1] = interface Clip supporting 8 audio formats, and buffers of 0 to 4194304 bytes

>  getMaxLines(testSourceLineInfo) = 32
>  OK - expected LineUnavailableException was thrown while line openning
>  because number of open lines has come up maxLinesNumberToOpen
>  Number of open Lines = 32

>>>  testedMixer[1] = com.sun.media.sound.SimpleInputDevice@7bd9f2

>>  supportedSourceLineInfo.length = 0

>>>  testedMixer[2] = com.sun.media.sound.SimpleOutputDevice@121cc40

>>  supportedSourceLineInfo.length = 1

>  testSourceLineInfo[0] = interface SourceDataLine supporting 24 audio formats

>  getMaxLines(testSourceLineInfo) = 1
>  Line of this type was not opened due to security restrictions

==> test FAILED!
 
 
The test source:
------------------------------- test.java --------------------------------
// File: %Z%%M% %I% %E% 
// Copyright %G% Sun Microsystems, Inc. All Rights Reserved

import javax.sound.sampled.*;

public class test {

    static final int STATUS_PASSED = 0;
    static final int STATUS_FAILED = 2;
    static final int STATUS_TEMP = 95;
    
    public static void main(String argv[]) {
        int testExitStatus = run(argv, System.out) + STATUS_TEMP;
        System.exit(testExitStatus);
    }

    public static int run(String argv[], java.io.PrintStream out) {
        int testResult = STATUS_PASSED;
    
        out.println
            ("==> Test for Mixer.getMaxLines(Line.Info) method:");

        Mixer.Info[] installedMixersInfo = AudioSystem.getMixerInfo();
        
        if ( installedMixersInfo == null ) {
            out.println("## AudioSystem.getMixerInfo() returned unexpected result:");
            out.println("#  expected: an array of Mixer.Info objects (may be array of length 0);");
            out.println("#  produced: null;");
            return STATUS_FAILED;
        }

        if ( installedMixersInfo.length == 0 ) {
            // there are no mixers installed on the system - so this testcase can not be tested
            return STATUS_PASSED;
        }

        Mixer testedMixer = null;
        for (int i=0; i < installedMixersInfo.length; i++) {
            try {
                testedMixer = AudioSystem.getMixer(installedMixersInfo[i]);
            } catch (SecurityException securityException) {
                // installed Mixer is unavailable because of security restrictions
                continue;
            } catch (Throwable thrown) {
                out.println("## AudioSystem.getMixer() threw unexpected exception:");
                thrown.printStackTrace(out);
                testResult = STATUS_FAILED;
                continue;
            }

            try {
                testedMixer.open();
            } catch (LineUnavailableException lineUnavailableException) {
                // testedMixer is not available due to resource restrictions
                continue;
            } catch (SecurityException securityException) {
                // testedMixer is not available due to security restrictions
                continue;
            } catch (Throwable thrown) {
                out.println("## Mixer.open() threw unexpected exception:");
                out.println("#  Mixer = " + testedMixer);
                thrown.printStackTrace(out);
                testResult = STATUS_FAILED;
                continue;
            }
            Line.Info supportedSourceLineInfo[] = null;
            try {
                supportedSourceLineInfo = testedMixer.getSourceLineInfo();
            } catch (Throwable thrown) {
                out.println("## Mixer.getSourceLineInfo() threw unexpected exception:");
                out.println("#  Mixer = " + testedMixer);
                thrown.printStackTrace(out);
                testResult = STATUS_FAILED;
                testedMixer.close();
                continue;
            }
            if ( supportedSourceLineInfo == null ) {
                out.println("## Mixer.getSourceLineInfo() returned null array");
                out.println("#  Mixer = " + testedMixer);
                testResult = STATUS_FAILED;
                testedMixer.close();
                continue;
            }
            out.println("\n>>>  testedMixer["+i+"] = " + testedMixer);
            out.println("\n>>  supportedSourceLineInfo.length = " + supportedSourceLineInfo.length);

            for (int j=0; j < supportedSourceLineInfo.length; j++) {
                Line.Info testSourceLineInfo = supportedSourceLineInfo[j];
                out.println("\n>  testSourceLineInfo["+j+"] = " + testSourceLineInfo);

                int maxLinesNumberToOpen = testedMixer.getMaxLines(testSourceLineInfo);
                out.println("\n>  getMaxLines(testSourceLineInfo) = " + maxLinesNumberToOpen);
                if ( maxLinesNumberToOpen == AudioSystem.NOT_SPECIFIED ) {
                    // there is no limit for open lines number of this Line.Info type
                    // so this test case for this Line.Info type can not be tested
                    break;
                }
                if ( maxLinesNumberToOpen < 0 ) {
                    out.println("## Mixer.getMaxLines(Line.Info) FAILED:");
                    out.println("#  Mixer = " + testedMixer);
                    out.println("#  Line.Info = " + testSourceLineInfo);
                    out.println("#  unexpected negative int value is returned = " + maxLinesNumberToOpen);
                    testResult = STATUS_FAILED;
                    break;
                }

                int linesNumberToOpen = maxLinesNumberToOpen + 1;
                Line testSourceLine = null;
                Line openLines[] = new Line[linesNumberToOpen];
                int openLinesNumber = 0;
                for (int k=0; k < linesNumberToOpen; k++) {
                    try {
                        testSourceLine = testedMixer.getLine(testSourceLineInfo);
                    } catch (LineUnavailableException lineUnavailableException) {
                        // line is not available due to resource restrictions
                        break;
                    } catch (SecurityException securityException) {
                        // line is not available due to security restrictions
                        break;
                    } catch (Throwable thrown) {
                        out.println("## Mixer.getLine(Line.Info) threw unexpected Exception:");
                        out.println("#  Mixer = " + testedMixer);
                        out.println("#  Line.Info = " + testSourceLineInfo);
                        thrown.printStackTrace(out);
                        testResult = STATUS_FAILED;
                        break;
                    }

                    boolean openLineResult = false;
                    boolean isExpectedLineUnavailableException = false;
                    try {
                        openLineResult = openLine(testSourceLine);
                    } catch (LineUnavailableException lineUnavailableException) {
                        if ( k == maxLinesNumberToOpen ) {
                            isExpectedLineUnavailableException = true;
                        } else {
                            // line is not available due to resource restrictions
                            // so this test case for this line can not be tested
                            break;
                        }
                    } catch (Throwable thrown) {
                        out.println("## Unexpected Exception was thrown by Line.open():");
                        out.println("#  Line = " + testSourceLine);
                        thrown.printStackTrace(out);
                        testResult = STATUS_FAILED;
                        break;
                    }
                    if ( ! openLineResult ) {
                        if ( isExpectedLineUnavailableException ) {
                            // OK - expected LineUnavailableException was thrown while line openning
                            // because number of open lines has come up maxLinesNumberToOpen
                            out.println(">  OK - expected LineUnavailableException was thrown while line openning");
                            out.println(">  because number of open lines has come up maxLinesNumberToOpen");
                            out.println(">  Number of open Lines = " + openLinesNumber);
                            break;
                        }
                        // else
                        // Line was not opened due to security restrictions
                        out.println(">  Line of this type was not opened due to security restrictions");
                        break;
                    }
                    openLines[openLinesNumber] = testSourceLine;
                    openLinesNumber++;
                    if ( openLinesNumber > maxLinesNumberToOpen ) {
                        out.println("## Mixer.getMaxLines(Line.Info) FAILED:");
                        out.println("#  Mixer = " + testedMixer);
                        out.println("#  Line.Info = " + testSourceLineInfo);
                        out.println("#  Mixer.getMaxLines(Line.Info) = " + maxLinesNumberToOpen);
                        out.println("#  Number of open Lines         = " + openLinesNumber);
                        for (int m=0; m < openLinesNumber; m++) {
                            out.println("#  openLines[" + m + "] = " + openLines[m]);
                        }
                        testResult = STATUS_FAILED;
                    }

                }  // for (int k=0; k < linesNumberToOpen; k++)

                for (int k=0; k < openLinesNumber; k++) {
                    openLines[k].close();
                }
                
            }  // for (int j=0; j < supportedSourceLineInfo.length; j++)
            testedMixer.close();

        }  // for (int i=0; i < installedMixersInfo.length; i++)

        if ( testResult == STATUS_FAILED ) {
            out.println("\n==> test FAILED!");
        } else {
            out.println("\n==> test PASSED!");
        }
        return testResult;
    }
    
    static boolean openLine(Line lineToOpen) throws Throwable {
        boolean openResult = false;

        try {
            if ( (lineToOpen instanceof Clip) ) {
                int bufferSizeForOpen = 1;
                byte[] dataForOpen = new byte[bufferSizeForOpen];
                for (int i=0; i < bufferSizeForOpen; i++) {
                    dataForOpen[i] = (byte)1;
                }
                int offsetForOpen = 0;
                AudioFormat audioFormatForOpen =
                    new AudioFormat(((Clip)lineToOpen).getFormat().getEncoding(),  // AudioFormat.Encoding
                                    (float) 44100.0, // float SampleRate: the number of samples per second
                                    (int) 8, // int sampleSizeInBits
                                    (int) 1, // int channels
                                    (int) 1,    // int frameSize in bytes
                                    (float) 44100.0,    // float frameRate: the number of frames per second
                                    true    // boolean bigEndian
                                    );
                ((Clip)lineToOpen).open(audioFormatForOpen, dataForOpen, offsetForOpen, bufferSizeForOpen);
                openResult = true;
            }
            if ( (lineToOpen instanceof SourceDataLine) ) {
                AudioFormat audioFormatForOpen = ((SourceDataLine)lineToOpen).getFormat();
                ((SourceDataLine)lineToOpen).open(audioFormatForOpen);
                openResult = true;
            }
     

                                    

Comments
EVALUATION


###@###.### 2002-04-24
	I think providing more lines than reported by getMaxLines() is something good - it leaves more headroom for applications. If it was the opposite (i.e. not being able to provide the number of lines returned by getMaxLines(), that would be a serious bug, because apps might count on it.).

The fix would mean to change the implementation and refuse to open a new Line when getMaxLines() is reached, even when there are still enough resources. This is obviously not nice. Also I can't see the use case where this behavior could be needed.

Another way to fix this bug is to change the documentation so that it states that the return value of getMaxLines() is the minimum guaranteed number of lines. But this is difficult to enforce. Sound resources are subject to external limitations (i.e. another native process outside the VM is exclusively owning the audio device), so I prefer to leave spec and implementation as they are.

###@###.### 2002-04-29
	A small modification in the wording of this function may be appropriate. E.g. "Programs should not rely on being able of having more than max lines open at the same time."


###@###.### 2002-11-01
	Changed synopsis to denote a DOC bug.

###@###.### 2003-01-09
	Should be changed for tiger to:
Obtains the maximum number of lines of the requested type that can be open simultaneously on the mixer. Certain types of mixers do not have a hard bound and may allow  opening more lines. Since certain lines are a shared resource,
a mixer may not be able to open the maximum number of lines if another process has opened lines of this mixer.
The requested type is any line that matches the description in...
                                     
2004-06-11
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
tiger
tiger-beta

FIXED IN:
tiger-beta

INTEGRATED IN:
tiger-b26
tiger-beta


                                     
2004-06-14
PUBLIC COMMENTS

DOC: Mixer.getMaxLines() returns incorrect number
                                     
2004-06-10



Hardware and Software, Engineered to Work Together