我不能保證它可以在Mac上工作。在Mac上聽過Java聲音。是完全搞砸了。話雖這麼說..
/*
<applet
code='org.pscode.beeper.Beeper'
width='300'
height='300'>
<param name='samplerate' value='11025'>
<param name='fpw' value='40'>
<param name='addharmonic' value='true'>
<param name='autoloop' value='true'>
<param name='loopcount' value='10'>
<param name='volume' value='45'>
</applet>
*/
package org.pscode.beeper;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.text.DecimalFormat;
import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
/** Beeper presents a small, loopable tone that can be heard
by clicking on the Code Key. It uses a Clip to loop the sound,
as well as for access to the Clip's gain control.
@author Andrew Thompson
@version 2009-12-19
@license LGPL */
public class Beeper extends JApplet {
BeeperPanel bp;
public void init() {
bp = new BeeperPanel();
getContentPane().add(bp);
validate();
String sampleRate = getParameter("samplerate");
if (sampleRate!=null) {
try {
int sR = Integer.parseInt(sampleRate);
bp.setSampleRate(sR);
} catch(NumberFormatException useDefault) {
}
}
String fpw = getParameter("fpw");
if (fpw!=null) {
try {
int fPW = Integer.parseInt(fpw);
JSlider slider = bp.getFramesPerWavelengthSlider();
slider.setValue(fPW);
} catch(NumberFormatException useDefault) {
}
}
boolean harmonic = (getParameter("addharmonic")!=null);
bp.setAddHarmonic(harmonic);
bp.setUpSound();
if (getParameter("autoloop")!=null) {
String loopcount = getParameter("loopcount");
if (loopcount!=null) {
try {
Integer lC = Integer.parseInt(loopcount);
bp.loop(lC.intValue());
} catch(NumberFormatException doNotLoop) {
}
}
}
}
public void stop() {
bp.loopSound(false);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Beeper");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BeeperPanel BeeperPanel = new BeeperPanel();
f.setContentPane(BeeperPanel);
f.pack();
try {
f.setMinimumSize(f.getSize());
} catch(Exception ignore) {
}
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
/** The main UI of Beeper. */
class BeeperPanel extends JPanel {
JComboBox sampleRate;
JSlider framesPerWavelength;
JLabel frequency;
JCheckBox harmonic;
Clip clip;
DecimalFormat decimalFormat = new DecimalFormat("###00.00");
BeeperPanel() {
super(new BorderLayout());
// Use current OS look and feel.
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
// "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
// "com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {
e.printStackTrace();
}
setPreferredSize(new Dimension(300,300));
JPanel options = new JPanel();
BoxLayout bl = new BoxLayout(options,BoxLayout.Y_AXIS);
options.setLayout(bl);
Integer[] rates = {
new Integer(8000),
new Integer(11025),
new Integer(16000),
new Integer(22050)
};
sampleRate = new JComboBox(rates);
sampleRate.setToolTipText("Samples per second");
sampleRate.setSelectedIndex(1);
JPanel pSampleRate = new JPanel(new BorderLayout());
pSampleRate.setBorder(new TitledBorder("Sample Rate"));
pSampleRate.add(sampleRate);
sampleRate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
setUpSound();
}
});
options.add(pSampleRate);
framesPerWavelength = new JSlider(JSlider.HORIZONTAL,10,200,25);
framesPerWavelength.setPaintTicks(true);
framesPerWavelength.setMajorTickSpacing(10);
framesPerWavelength.setMinorTickSpacing(5);
framesPerWavelength.setToolTipText("Frames per Wavelength");
framesPerWavelength.addChangeListener(new ChangeListener(){
public void stateChanged(ChangeEvent ce) {
setUpSound();
}
});
JPanel pFPW = new JPanel(new BorderLayout());
pFPW.setBorder(new TitledBorder("Frames per Wavelength"));
pFPW.add(framesPerWavelength);
options.add(pFPW);
JPanel bottomOption = new JPanel(new BorderLayout(4,4));
harmonic = new JCheckBox("Add Harmonic", false);
harmonic.setToolTipText(
"Add harmonic to second channel, one octave up");
harmonic.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
setUpSound();
}
});
bottomOption.add(harmonic, BorderLayout.WEST);
frequency = new JLabel();
bottomOption.add(frequency, BorderLayout.CENTER);
options.add(bottomOption);
add(options, BorderLayout.NORTH);
JPanel play = new JPanel(new BorderLayout(3,3));
play.setBorder(new EmptyBorder(4,4,4,4));
JButton bPlay = new JButton("Code Key");
bPlay.setToolTipText("Click to make tone!");
Dimension preferredSize = bPlay.getPreferredSize();
bPlay.setPreferredSize(new Dimension(
(int)preferredSize.getWidth(),
(int)preferredSize.getHeight()*3));
// TODO comment out to try KeyListener!
//bPlay.setFocusable(false);
bPlay.addKeyListener(new KeyAdapter(){
@Override
public void keyPressed(KeyEvent ke) {
loopSound(true);
}
});
bPlay.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent me) {
loopSound(true);
}
@Override
public void mouseReleased(MouseEvent me) {
loopSound(false);
}
});
play.add(bPlay);
try {
clip = AudioSystem.getClip();
final FloatControl control = (FloatControl)
clip.getControl(FloatControl.Type.MASTER_GAIN);
final JSlider volume = new JSlider(
JSlider.VERTICAL,
(int)control.getMinimum(),
(int)control.getMaximum(),
(int)control.getValue()
);
volume.setToolTipText("Volume of beep");
volume.addChangeListener(new ChangeListener(){
public void stateChanged(ChangeEvent ce) {
control.setValue(volume.getValue());
}
});
play.add(volume, BorderLayout.EAST);
} catch(Exception e) {
e.printStackTrace();
}
add(play, BorderLayout.CENTER);
setUpSound();
}
public void loop(int loopcount) {
if (clip!=null) {
clip.loop(loopcount);
}
}
public void setAddHarmonic(boolean addHarmonic) {
harmonic.setSelected(addHarmonic);
}
/** Provides the slider for determining the # of frames per wavelength,
primarily to allow easy adjustment by host classes. */
public JSlider getFramesPerWavelengthSlider() {
return framesPerWavelength;
}
/** Sets the sample rate to one of the four
allowable rates. Is ignored otherwise. */
public void setSampleRate(int sR) {
switch (sR) {
case 8000:
sampleRate.setSelectedIndex(0);
break;
case 11025:
sampleRate.setSelectedIndex(1);
break;
case 16000:
sampleRate.setSelectedIndex(2);
break;
case 22050:
sampleRate.setSelectedIndex(3);
break;
default:
}
}
/** Sets label to current frequency settings. */
public void setFrequencyLabel() {
float freq = getFrequency();
if (harmonic.isSelected()) {
frequency.setText(
decimalFormat.format(freq) +
"(/" +
decimalFormat.format(freq*2f) +
") Hz");
} else {
frequency.setText(decimalFormat.format(freq) + " Hz");
}
}
/** Generate the tone and inform the user of settings. */
public void setUpSound() {
try {
generateTone();
setFrequencyLabel();
} catch(Exception e) {
e.printStackTrace();
}
}
/** Provides the frequency at current settings for
sample rate & frames per wavelength. */
public float getFrequency() {
Integer sR = (Integer)sampleRate.getSelectedItem();
int intST = sR.intValue();
int intFPW = framesPerWavelength.getValue();
return (float)intST/(float)intFPW;
}
/** Loops the current Clip until a commence false is passed. */
public void loopSound(boolean commence) {
if (commence) {
clip.setFramePosition(0);
clip.loop(Clip.LOOP_CONTINUOUSLY);
} else {
clip.stop();
}
}
/** Generates a tone, and assigns it to the Clip. */
public void generateTone()
throws LineUnavailableException {
if (clip!=null) {
clip.stop();
clip.close();
} else {
clip = AudioSystem.getClip();
}
boolean addHarmonic = harmonic.isSelected();
int intSR = ((Integer)sampleRate.getSelectedItem()).intValue();
int intFPW = framesPerWavelength.getValue();
float sampleRate = (float)intSR;
// oddly, the sound does not loop well for less than
// around 5 or so, wavelengths
int wavelengths = 20;
byte[] buf = new byte[2*intFPW*wavelengths];
AudioFormat af = new AudioFormat(
sampleRate,
8, // sample size in bits
2, // channels
true, // signed
false // bigendian
);
int maxVol = 127;
for(int i=0; i<intFPW*wavelengths; i++){
double angle = ((float)(i*2)/((float)intFPW))*(Math.PI);
buf[i*2]=getByteValue(angle);
if(addHarmonic) {
buf[(i*2)+1]=getByteValue(2*angle);
} else {
buf[(i*2)+1] = buf[i*2];
}
}
try {
byte[] b = buf;
AudioInputStream ais = new AudioInputStream(
new ByteArrayInputStream(b),
af,
buf.length/2);
clip.open(ais);
} catch(Exception e) {
e.printStackTrace();
}
}
/** Provides the byte value for this point in the sinusoidal wave. */
private static byte getByteValue(double angle) {
int maxVol = 127;
return (new Integer(
(int)Math.round(
Math.sin(angle)*maxVol))).
byteValue();
}
}
這工作在Mac上,只要它產生的聲音從揚聲器。我很欣賞答案。 – FishBasketGordo
感謝您的確認。也許這只是「角落案例」,不起作用 - 或者蘋果近期可以改善聲音支持。無論哪種方式,我有一個小應用程序。其中我很自豪(一款MP3公爵盒),現在看起來它可能會在Mac上使用。鑑於這個例子的工作。 :-) –