Recording multiple MIDI tracks - java

I am writing an application that records MIDI data from a keyboard.
It should be able to record multiple tracks and store them in one MIDI file.
It is controlled with three buttons - "record", "stop recording" and "write to a MIDI file".
I followed this tutorial on StackOverflow to write the recording method.
Everything was going well until I tried to record a second track. track#2 is recorded, but it is placed in the sequence behind the track#1 i.e. they donĀ“t play simultaneously in the MIDI file and if I try to play only track#2, there is a lot of blank space in the beginning of it.
I read that I have to use
Sequencer.setTickPosition(0);
which I did, but without any effect.
Here are parts of the code:
The user presses "record" button:
sequencer.open();
sequencer.setSequence(mySequence);
newTrack = mySequence.createTrack();
sequencer.setTickPosition(0);
sequencer.recordEnable(newTrack, -1);
sequencer.startRecording();
When "stop recording" button is pressed:
sequencer.stopRecording();
sequencer.recordDisable(newTrack);
And finally when "write to MIDI file" button is pressed:
Sequence tmp = sequencer.getSequence();
MidiSystem.write(tmp, 1, new File("Sequence.MID"));
//*******************************************************************
Here is my full code:
private MidiDevice inputDevice // Selected from a combo box
private Sequencer sequencer = MidiSystem.getSequencer();
private Transmitter transmitter = inputDevice.getTransmitter();
private Receiver receiver = sequencer.getReceiver();
transmitter.setReceiver(receiver);
private Sequence seq = new Sequence(Sequence.PPQ,24);
private Track newTrack;
/*
.
Buttons added
.
*/
public void actionPerformed( ActionEvent e ){
Object source = e.getSource();
if (source == record){
sequencer.open();
sequencer.setSequence(seq);
newTrack = seq.createTrack();
sequencer.setTickPosition(0);
sequencer.recordEnable(newTrack, -1);
sequencer.startRecording();
}
//*********************************************************************
if (source == stop){
sequencer.stopRecording();
sequencer.recordDisable(newTrack);
}
//******************************************************************
if(source == write){
Sequence tmp = sequencer.getSequence();
MidiSystem.write(tmp, 1, new File("MyMidiFile1.mid"));
}
}
Thanks in advance for any advice!

Related

Looping audio and inputdialog box cancel

Im trying to get a backtrack on my JOptionPane game working. I have the code for the audio to loop. But when a user clicks cancel on an inputdialog box it exits the game but does not close the audio thread.
I need something to kill the audio thread when this happens.
I have tried other methods of playing the audio .wav file. But when user hit cancel it still plays. Have tried using JFrames and disposing of the frame, but this doesn't dispose the audio thread too.
URL urlfog = theFog.class.getClassLoader().getResource("thefog.wav");
AudioInputStream audioStreamfog = AudioSystem.getAudioInputStream(urlfog);
AudioFormat formatfog = audioStreamfog.getFormat();
DataLine.Info infofog = new DataLine.Info(Clip.class, formatfog);
Clip audioClipfog = (Clip) AudioSystem.getLine(infofog);
audioClipfog.open(audioStreamfog);
audioClipfog.loop(audioClipfog.LOOP_CONTINUOUSLY);
String name;
do {
name = (String) JOptionPane.showInputDialog(null,"Hello what is your name?","The Fog",JOptionPane.INFORMATION_MESSAGE,icon1,null,"");
}
while (name.equalsIgnoreCase(""));
No error messages just the audio is still playing after a cancel of the program.
String name;
do {
name = (String) JOptionPane.showInputDialog(null, "Hello what is your name?", "The Fog", JOptionPane.INFORMATION_MESSAGE, icon1, null, "");
if ((name == null)) {
audioClipfog.stop();
System.exit(0);
}
}
while (name.equalsIgnoreCase(""));

How to get button press event from camera

I've got a dental camera and iam try to get windows to press space when the camera button is pressed
I have the OEM software and driver installed, it works perfect, gets the feed and makes a snapshot when camera button is pressed. I need to use another software for the feed and the snapshot, the software gets the feed but doesn't react to camera button, it only reacts to space key press(part of the oem driver), so my way of solving this was getting the device by product id and listening the button press event and remapping it space press.
I am pretty much stuck at this point.
How can I listen on events coming from the device I've got?
public static Device findDCam(){
// Create the libusb context
Context context = new Context();
// Initialize the libusb context
int result = LibUsb.init(context);
if (result < 0)
{
throw new LibUsbException("Unable to initialize libusb", result);
}
// Read the USB device list
DeviceList list = new DeviceList();
result = LibUsb.getDeviceList(context, list);
if (result < 0)
{
throw new LibUsbException("Unable to get device list", result);
}
try
{
// Iterate over all devices and list them
for (Device device: list)
{
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
if (result < 0)
{
throw new LibUsbException(
"Unable to read device descriptor", result);
}
if(descriptor.idProduct()== -3810){
System.out.println("D cam found");
return device;
}
}
}
finally
{
// Ensure the allocated device list is freed
LibUsb.freeDeviceList(list, true);
}
// Deinitialize the libusb context
LibUsb.exit(context);
return null;
}
I've also thought that maybe it's impossible using usb4java since as far as i understood, if i want to listen on the usb port i need to take control from the driver and then its pointless.
Maybe iam going all wrong and i should use the driver instead?
Or maybe there is an app that can read button presses from a specific device and remap it?
If the camera has a standard driver, this should work through this video capture SDK. To quick test it, run the demo executable included in the package, select the camera in the list, check the "webcam snapshot button" checkbox and start the camera. Then press the camera button to test the snapshot.

Javax Sound Sampled: Unsupported Control Type issues with FloatType.Control

I am having trouble added control types to a clip in the java sampled package. Please see the below code as a reference.
I have made sure to call clip.open() before adding a control to the clip and I still have the same issue. I have tried to print out all available controls for a clip and I find that I have no controls available to me.
Strangely, this works on other peoples machine but I am having trouble on mine. It is not recognising any of the FloatControl.Type's that are available such as MasterGain, Volume etc.
I have tried to downgrade from JDK 9 to 8 as my friend has 8. Java 7 is that last JDK to have no issue with JavaSound however I am baffled to why is works on other peoples machines.
I know that I am not running things concurrently with Threads at the moment and the code needs refactoring. Any advice SPECIFIC to my problem is appreciated.
The gain/volume is being controlled by a JSlider in another class and this works fine when printing out the values using a changelistener.
(CODE IN QUESTION IS AT THE VERY BOTTOM OF MY CODE SNIPPET)
public class AudioEngine {
private FileManager filemanager;
private File sound;
private Clip clip;
private ArrayList<File> files;
private ArrayList<Clip> clips;
private DataLine.Info[] lines;
private ArrayList<AudioInputStream> streams;
private long trackposition; // positioning of current clip to determine where in a track our play back starts from.
private Mixer mixer; // main mixer
private boolean playtrigger;
public AudioEngine() {
filemanager = new FileManager();
trackposition = 0; // set initial playback to beginning of track unless it is paused....
playtrigger = true;
Mixer.Info[] mixInfos = AudioSystem.getMixerInfo(); // get I/O devices for set up
for (Mixer.Info info : mixInfos) {
System.out.println(info.getName() + " --------- " + info.getDescription());
}
mixer = AudioSystem.getMixer(mixInfos[0]);
Line[] lines = mixer.getSourceLines();
}
/**
* Set up the Mixer with multiple data lines, input streams, files and clips.
*/
public void mixerSetUp(JComboBox<String> list, ArrayList<String> tracklist) throws Exception {
files = new ArrayList<>();
streams = new ArrayList<>();
clips = new ArrayList<>();
lines = new DataLine.Info[tracklist.size()];
for (int i = 0; i < tracklist.size(); i++) {
files.add(new File(tracklist.get(i)));
streams.add(AudioSystem.getAudioInputStream(files.get(i)));
lines[i] = new DataLine.Info(Clip.class, streams.get(i).getFormat());
clips.add((Clip) AudioSystem.getLine(lines[i]));
clips.get(i).open(streams.get(i));
}
System.out.println("mixer lines: " + lines.length);
System.out.println(files.size());
System.out.println(streams.size());
System.out.println(clips.size());
System.out.println(lines.length);
Line line = mixer.getLine(lines[0]);
Control [] controls = line.getControls();
System.out.println(controls.length);
for(Control control: controls) {
System.out.println(control);
}
}
/**
* Converts our .WAV file in to an audio stream. Then we convert stream into a clip for playback.
*
* #param list Our track list displayed in JComboBox (shortened version of full file path).
* #param tracklist Our list of tracks with their full path. Plays as a Clip if selected.
* #throws IOException
* #throws UnsupportedAudioFileException
* #throws LineUnavailableException
* #throws Exception - More generic as all 3 of the above exceptions would have need to be thrown.
*/
public void play(JComboBox<String> list, ArrayList<String> tracklist) throws LineUnavailableException {
// PRESS LOAD TRACKS EVERY TIME A NEW TRACK IS ADDED
for (Clip clip : clips) {
if (clip != null) {
System.out.println("Start Running");
clip.setMicrosecondPosition(trackposition);
clip.start();
}
}
}
/**
* If track is running, stop the clip and set track positioning back to 0.
*/
public void stop() {
for (Clip clip : clips) {
if (clip.isRunning()) {
clip.stop();
trackposition = 0;
}
}
}
/**
* Set the track position when the pause button is pressed. Play back will continue from this set position once
* user presses Play button. Track position will be set to 0 once user stops the track.
*/
public void pause() {
for (Clip clip : clips) {
trackposition = clip.getMicrosecondPosition();
clip.stop();
}
}
/**
* Iterates through all of the tracks and sets volume to value specified in parameter
* #param value The volume for the tracks to be set to.
*/
public void adjustVolume(int value) throws LineUnavailableException {
if (clips != null) {
for (Clip clip : clips) {
FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
gainControl.setValue((float)value);
}
}
}
}
The presence or lack of a Control varies with computer and OS. You can test whether a control is supported or not with isControlSupported
Even when controls are supported (hit or miss), they are often inadequately implemented for higher-end real time purposes. For example, they may only pass on changes at buffer boundaries, which can lead to zippering effects or other discontinuities if you are trying to do real-time mixing.
You may need to either code your own volume changes (using access into sound data provided by SourceDataLine) or make use of a public library that does this. For example, AudioCue was written to behave like a Clip, but implements real-time volume, panning and frequency changes. The code is on github (opensource) and will also let you inspect how to implement this if you decide to roll your own.

Java play AAC encoded audio ( JAAD decoder )

I have struggled with playing aac encoded audio files with Java a while now.
We had a group project at the end of our first semester and wanted to have a background music and few soundeffects in there.
At the end we used WAV files, as we couldn't get the AAC's to play.
This weekend I gave it another try and searched along again, and have got a working code searched together from different sites, but nowhere was a complete working solution.
For a more comfortable usage in future projects I made me a small library for aac playback.
As it was hard to find the working solution, I wanted to share it with you, in the hope some one having the same problem in the future will have it a bit easier.
The following code is a snippet from the lib I wrote. You can have a look onto the whole lib on GitLab at
Java AAC-Player.
This lib/solution uses the aac decoder JAAD.
The player I wrote around the JAAD library is available as Maven artifact:
<dependency>
<groupId>com.gitlab.9lukas5</groupId>
<artifactId>jaad</artifactId>
<!--
check latest version on https://search.maven.org
or try the latest tag you find in git
-->
</dependency>
public static void play(File[] files)
{
// local vars
byte[] b; // array for the actual audio Data during the playback
AudioTrack track; // track we are playing atm
AudioFormat af; // the track's format
SourceDataLine line; // the line we'll use the get our audio to the speaker's
Decoder dec; // decoder to get the audio bytes
Frame frame; //
SampleBuffer buf; //
int currentTrack; // index of current track from playlist
MP4Container cont; // container to open the current track with
Movie movie; // and get the content from the container
try
{
// for-next loop to play each titel from the playlist once
for (currentTrack = 0; currentTrack < files.length; currentTrack++)
{
cont = new MP4Container(new RandomAccessFile(files[currentTrack], "r")); // open titel with random access
movie = cont.getMovie(); // get content from container,
List<Track> content = movie.getTracks();
if (content.isEmpty()) // check if container HAS content
throw new Exception ("insert error message here"); // if so,
track = (AudioTrack) movie.getTracks().get(0); // grab first track and set the audioformat
af = new AudioFormat(track.getSampleRate(), track.getSampleSize(), track.getChannelCount(), true, true);
line = AudioSystem.getSourceDataLine(af); // get a DataLine from the AudioSystem
line.open(); // open and
line.start(); // start it
dec = new Decoder(track.getDecoderSpecificInfo());
buf = new SampleBuffer();
while(track.hasMoreFrames()) // while we have frames left
{
frame = track.readNextFrame(); // read next frame,
dec.decodeFrame(frame.getData(), buf); // decode it and put into the buffer
b = buf.getData(); // write the frame data from the buffer to our byte-array
line.write(b, 0, b.length); // and from there write the byte array into our open AudioSystem DataLine
while (paused) // check if we should pause
{
Thread.sleep(500); // if yes, stay half a second
if (Thread.interrupted()) // check if we should stop possibly
{
line.close(); // if yes, close line and
return; // exit thread
}
}
if (Thread.interrupted()) // if not in pause, still check on each frame if we should
{ // stop. If so
line.close(); // close line and
return; // exit thread
}
}
line.close(); // after titel is over, close line
if (loop) // if we should loop current titel, set currentTrack -1,
currentTrack--; // as on bottom of for-next it get's +1 and so the same titel get's played again
else if (repeat && (currentTrack == files.length -1)) // else check if we are at the end of the playlist
currentTrack = -1; // and should repeat the whole list. If so, set currentTrack -1, so it get's 0 on for-next bottom
}
}
catch (LineUnavailableException | IOException | InterruptedException e)
{
e.printStackTrace();
}
}

How to have lyrics highlighted as mp3 file plays (like karaoke) using JLayer

I have created a media player in Java using JLayer, and it accepts mp3 files. I also have the lyrics to a specific song appear once the user plays that song, but now I want to somehow highlight or change the text color of the lyrics as they are heard in the song (like karaoke). I only need to do this for one song - and I'll have the lyrics already implemented in my program. I have already searched for how to do this but can't seem to find exactly what I'm looking for. Below I added the code to my class that plays the music file.
public class PlayMusic {
/**
* Global variables. FileInputStream obtains input bytes from a file system
* and reads streas of raw bytes. BufferedInputStream adds functionality
* to the fis, and creates an internal buffer array.
*/
private String filename;
private Player player;
private boolean canResume;
private boolean valid;
private int total;
private int stopped;
FileInputStream fis;
BufferedInputStream bis;
/**
* Constructor the takes in the path of the mp3 file to be played.
* #param filename - path of the mp3 file
*/
public PlayMusic(String filename) {
this.filename = filename;
this.canResume = false;
this.valid = false;
this.total = 0;
this.stopped = 0;
this.fis = null;
this.bis = null;
}
/**
* Function called to stop a song altogether as opposed to pausing.
*/
public void close() {
if (player != null)
player.close();
stopped = 0;
fis = null;
bis = null;
player = null;
canResume = false;
}
/**
* Function called to pause a song. Fis.available() is a method that returns
* the number of remaining bytes that can be read from the input stream.
*/
public void pause(){
try {
if (fis!=null)
stopped = fis.available();
if (player!= null)
player.close();
fis = null;
bis = null;
player = null;
if(valid)
canResume = true;
} catch (IOException e) {
}
}
/**
* Function called when we want to resume a song from where it left off
* after being paused.
*/
public void resume()
{
if(!canResume)
return;
if(play(total-stopped))
canResume = false;
}
/**
* Function called to play the song and keep track of where in the song the
* user presses stop in order for the resume button to work properly. Fis.skip
* skips over and discards pos bytes of data from fis.
* #param pos - The position of the song in which we want to resume play
* #return
*/
public boolean play(int pos) {
valid = true;
canResume = false;
try {
fis = new FileInputStream(filename);
total = fis.available();
if(pos> -1)
fis.skip(pos);
bis = new BufferedInputStream(fis);
player = new Player(bis);
}
catch (Exception e) {
System.out.println("Problem playing file " + filename);
System.out.println(e);
}
/**
* Run the play button in a new thread so the music plays in the background.
*/
new Thread() {
public void run() {
try { player.play(); }
catch (Exception e) { System.out.println(e); valid = false; }
}
}.start();
return valid;
}
}
You won't be able to detect what words are being sung in the song (with ease at least), but if you have the lyrics of the song in a file, and you have the sound file of the song, then to me it sounds like you could just add more info to that lyrics file to create a map of when the words of the lyrics are being sung in the song.
For example, if I was to do this with the song Jingle Bells, I may have a tab separated file that contains the lyrics, where one line is one word, with a begin and end time relative to the start of the song in milliseconds.
jingle 0 1000
bells 1001 1500
jingle 1501 2500
bells 2501 3000
... and so on
Edit for explaining how to code up keeping track of how long a song has been playing.
Two methods to create an instance variable called say, totalTimeSongHasBeenPlaying
I'm not sure how you have abstracted out playing your sound files, but say that you abstracted that out to a Sound class, then you could have three methods, sound.soundStarted, sound.soundStopped, sound.soundRestarted, then at the start of playing the sound you can call soundStarted which could grab a System.nanoTime or a System.currentTimeMillis and on soundStopped, you could grab it again and take the difference and add it to totalTimeSongHasBeenPlaying, and on soundRestart you could set totalTimeSongHasBeenPlaying to zero.
Do some math against frame position the sound you are currently playing is versus how many frames is in a second for that file. I don't know the exact libraries for JLayer, it's been a while since I used it, but that method should also tell you how far along in the file you are.
After that, the Sound class could also then have a method such as currentWordBeingSung(), which looks at totalTimeSongHasBeenPlaying, and uses the lookup table you created off the lyrics file during construction and returns the specific word uniquely (may be duplicates). Your gui when you create it, say your JLyricsViewer, can hold an instance to your Sound object and you can use a SwingTimer to repaint it every 50 ms or so, where in your paintComponent method it looks at currentWordBeingSung().

Categories

Resources