001/*
002 * Copyright (C) Cross The Road Electronics.  All rights reserved.
003 * License information can be found in CTRE_LICENSE.txt
004 * For support and suggestions contact support@ctr-electronics.com or file
005 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
006 */
007package com.ctre.phoenix6;
008
009import java.util.Collection;
010
011import com.ctre.phoenix6.hardware.ParentDevice;
012import com.ctre.phoenix6.jni.OrchestraJNI;
013
014/**
015 * Orchestra is used to play music through devices. It uses a "Chirp" (.chrp) music
016 * file that can be generated using Phoenix Tuner. Chirp files are generated from
017 * standard MIDI files.
018 * <p>
019 * Any Chirp file located in the src/main/deploy directory of your FRC project will
020 * automatically be copied to the roboRIO on code deploy.
021 * <p>
022 * The robot must be enabled to play music. Additionally, devices playing in Orchestra
023 * will not run any other control requests while Orchestra is running. Users can
024 * {@link #pause} or {@link #stop} the Orchestra to re-enable device control.
025 * <p>
026 * Each device can only play a single track within the music file. For multi-track
027 * files, multiple devices are needed. Devices can be added with an explicit track
028 * number. Otherwise, the first track will be played through the first Talon FX added,
029 * the second track will be played through the second Talon FX added, etc.
030 * <p>
031 * To use Orchestra:
032 * <ul>
033 *   <li> Add the Talon FXs to be used as instruments using {@link #addInstrument}.
034 *   <li> Load the Chirp file to be played using {@link #loadMusic}. This can also
035 *        be done in the Orchestra constructor.
036 * </ul>
037 * Both of these can also be done in the Orchestra constructor.
038 * <p>
039 * Once ready, the Orchestra can be controlled using {@link #play}/{@link #pause}/{@link #stop}.
040 * New music files can be loaded at any time.
041 */
042public class Orchestra implements AutoCloseable {
043    private OrchestraJNI jni = new OrchestraJNI();
044
045    /**
046     * Constructor for a new Orchestra.
047     */
048    public Orchestra() {
049        jni.JNI_Create();
050    }
051
052    /**
053     * Constructor for a new Orchestra using the given Chirp file.
054     *
055     * @param filepath The path to the music file to immediately load into the orchestra.
056     */
057    public Orchestra(String filepath) {
058        this();
059        loadMusic(filepath);
060    }
061
062    /**
063     * Constructor for a new Orchestra using the given instruments.
064     *
065     * @param instruments A collection of devices that will be used as instruments in the orchestra.
066     */
067    public Orchestra(Collection<ParentDevice> instruments) {
068        this();
069        for (var instrument : instruments) {
070            addInstrument(instrument);
071        }
072    }
073
074    /**
075     * Constructor for a new Orchestra using the given instruments and Chirp file.
076     *
077     * @param instruments A collection of devices that will be used as instruments in the orchestra.
078     * @param filepath The path to the music file to immediately load into the orchestra.
079     */
080    public Orchestra(Collection<ParentDevice> instruments, String filepath) {
081        this();
082        for (var instrument : instruments) {
083            addInstrument(instrument);
084        }
085        loadMusic(filepath);
086    }
087
088    /**
089     * Closes this Orchestra instance.
090     */
091    @Override
092    public void close() {
093        jni.JNI_Close();
094    }
095
096    /**
097     * Adds an instrument to the orchestra.
098     *
099     * @param instrument The device to add to the orchestra
100     * @return Status code of adding the device
101     */
102    public StatusCode addInstrument(ParentDevice instrument) {
103        return StatusCode.valueOf(jni.JNI_AddDevice(instrument.getNetwork(), instrument.getDeviceHash()));
104    }
105
106    /**
107     * Adds an instrument to the orchestra on the given track.
108     *
109     * @param instrument The device to add to the orchestra
110     * @param trackNumber The track number the device should play, starting at 0
111     * @return Status code of adding the device
112     */
113    public StatusCode addInstrument(ParentDevice instrument, int trackNumber) {
114        return StatusCode.valueOf(jni.JNI_AddDeviceWithTrack(instrument.getNetwork(), instrument.getDeviceHash(), trackNumber));
115    }
116
117    /**
118     * Clears all instruments in the orchestra.
119     *
120     * @return Status code of clearing all devices
121     */
122    public StatusCode clearInstruments() {
123        return StatusCode.valueOf(jni.JNI_ClearDevices());
124    }
125
126    /**
127     * Loads a Chirp file at the specified file path.
128     * <p>
129     * If the Chirp file is inside your "src/main/deploy" directory, it will be
130     * automatically deployed to a default directory on the roboRIO when you
131     * deploy code. For these files, the name and file extension is sufficient.
132     * <p>
133     * A Chirp file can be created from a MIDI file using Phoenix Tuner.
134     *
135     * @param filepath The path to the Chirp file
136     * @return Status code of loading the Chirp file
137     */
138    public StatusCode loadMusic(String filepath) {
139        return StatusCode.valueOf(jni.JNI_LoadMusic(filepath));
140    }
141
142    /**
143     * Plays the loaded music file. If the player is paused, this will resume
144     * the orchestra.
145     *
146     * @return Status code of playing the orchestra
147     */
148    public StatusCode play() {
149        return StatusCode.valueOf(jni.JNI_Play());
150    }
151
152    /**
153     * Pauses the loaded music file. This saves the current position in the
154     * track so it can be resumed later.
155     *
156     * @return Status code of pausing the orchestra
157     */
158    public StatusCode pause() {
159        return StatusCode.valueOf(jni.JNI_Pause());
160    }
161
162    /**
163     * Stops the loaded music file. This resets the current position in the
164     * track to the start.
165     *
166     * @return Status code of stopping the orchestra
167     */
168    public StatusCode stop() {
169        return StatusCode.valueOf(jni.JNI_Stop());
170    }
171
172    /**
173     * Gets whether the current track is actively playing.
174     *
175     * @return true if Orchestra is playing the music file
176     */
177    public boolean isPlaying() {
178        return jni.JNI_IsPlaying();
179    }
180
181    /**
182     * Gets the current timestamp of the music file. The timestamp will reset
183     * to zero whenever {@link #loadMusic} or {@link #stop} is called.
184     * <p>
185     * If {@link #isPlaying} returns false, this method can be used to determine
186     * if the music is stopped or paused.
187     *
188     * @return The current timestamp of the music file, in seconds
189     */
190    public double getCurrentTime() {
191        return jni.JNI_GetCurrentTime();
192    }
193}