001/* Copyright (C) Cross The Road Electronics 2024 */
002/*
003 *  Software License Agreement
004 *
005 * Copyright (C) Cross The Road Electronics.  All rights
006 * reserved.
007 * 
008 * Cross The Road Electronics (CTRE) licenses to you the right to 
009 * use, publish, and distribute copies of CRF (Cross The Road) firmware files (*.crf) and Software
010 * API Libraries ONLY when in use with Cross The Road Electronics hardware products.
011 * 
012 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
013 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
014 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
015 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
016 * CROSS THE ROAD ELECTRONICS BE LIABLE FOR ANY INCIDENTAL, SPECIAL, 
017 * INDIRECT OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
018 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
019 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
020 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
021 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
022 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE
023 */
024package com.ctre.phoenix.music;
025
026import com.ctre.phoenix.ErrorCode;
027import com.ctre.phoenix.motorcontrol.can.TalonFX;
028import java.util.Collection;
029
030/**
031 * An Orchestra is used to play music through Talon FX motor controllers.
032 * It uses a "Chirp" (.chrp) music file that can be generated using Phoenix Tuner.
033 * 
034 * Chirp files are generated from standard MIDI files.
035 * Each Talon FX can only play a single track within the music file.
036 * For multi-track files, multiple Talon FXs are needed.
037 *  ie, The first track will be played through the first Talon FX added,
038 *  the second track will be played through the second Talon FX added, etc.
039 * 
040 * Any Chirp file located in the src/main/deploy directory of your FRC project 
041 *  will automatically be copied to the roboRIO on code deploy.
042 * 
043 * To use the Orchestra:
044 *  - Add the Talon FXs to be used as instruments
045 *  - Load the Chirp file to be played using the loadMusic routine.
046 * Both of these can also be done in the Orchestra constructor.
047 * 
048 * Once ready, the Orchestra can be controlled using standard
049 * play/pause/stop routines.
050 * 
051 * New music files can be loaded at any time.
052 * 
053 * The robot must be enabled to play music.
054 * 
055 * Calling set on any of the TalonFX instruments while the orchestra is
056 * playing will pause the orchestra.
057 */
058public class Orchestra {
059    private long m_handle;
060
061    /**
062     * Constructor for an Orchestra Object.
063     * Call AddInstrument after this to add the instruments.
064     */
065    public Orchestra() {
066        m_handle = OrchestraJNI.JNI_new_Orchestra();
067    }
068    /**
069     * Constructor for an Orchestra Object.
070     * @param instruments
071     *          A collection of TalonFX's that will be used as instruments
072     *          inside the orchestra.
073     */
074    public Orchestra(Collection<TalonFX> instruments) {
075        this();
076
077        for(TalonFX instrument : instruments) {
078            addInstrument(instrument);
079        }
080    }
081    /**
082     * Constructor for an Orchestra Object
083     * @param instruments
084     *          A collection of TalonFX's that will be used as instruments
085     *          inside the orchestra.
086     * @param filePath
087     *          The path to the music file to immediately load into
088     *          the orchestra.
089     */
090    public Orchestra(Collection<TalonFX> instruments, String filePath) {
091        this(instruments);
092
093        loadMusic(filePath);
094    }
095
096    /**
097     * Loads a Chirp file at the specified file path.
098     * 
099     * If the Chirp file is inside your "src/main/deploy" directory
100     * this file will be automatically deployed to a default directory in
101     * the RoboRIO when you deploy code. For these files, the name and file 
102     * extension is sufficient.
103     * 
104     * Use Tuner to create a Chirp file.
105     * @param filePath
106     *              The path to the Chirp File.
107     * @return Error Code generated by function. 0 indicates no error. 
108     */
109    public ErrorCode loadMusic(String filePath) {
110        int retval = OrchestraJNI.JNI_LoadMusic(m_handle, filePath);
111        return ErrorCode.valueOf(retval);
112    }
113
114    /**
115     * Plays the music file that's loaded. 
116     * If the player is paused, this will resume.
117     * This will also resume a song if the orchestra was interrupted.
118     * @return Error Code generated by function. 0 indicates no error. 
119     */
120    public ErrorCode play() {
121        int retval = OrchestraJNI.JNI_Play(m_handle);
122        return ErrorCode.valueOf(retval);
123    }
124
125    /**
126     * Stops the music file that's loaded. 
127     * This resets the current position in the track to the start.
128     * @return Error Code generated by function. 0 indicates no error. 
129     */
130    public ErrorCode stop() {
131        int retval = OrchestraJNI.JNI_Stop(m_handle);
132        return ErrorCode.valueOf(retval);
133    }
134
135    /**
136     * Pauses the music file that's loaded. 
137     * This saves the current position in the track, so it can be resumed later.
138     * Pausing while stopped is an invalid request.
139     * @return Error Code generated by function. 0 indicates no error. 
140     */
141    public ErrorCode pause() {
142        int retval = OrchestraJNI.JNI_Pause(m_handle);
143        return ErrorCode.valueOf(retval);
144    }
145
146    /**
147     * Returns whether the current track is actively playing or not
148     * @return True if playing, false otherwise 
149     */
150    public boolean isPlaying() {
151        return OrchestraJNI.JNI_IsPlaying(m_handle);
152    }
153    
154    /**
155     * @return The current timestamp of the music file (playing or paused) in milliseconds.
156     * The timestamp will reset to zero whenever loadMusic() or stop() is called.
157     * If isPlaying() returns false, this routine can be used to determine if music is stopped or paused.
158     */
159    public int getCurrentTime() {
160        return OrchestraJNI.JNI_GetCurrentTime(m_handle);
161    }
162
163    /**
164     * Clears all instruments in the orchestra.
165     * @return Error Code generated by function. 0 indicates no error. 
166     */
167    public ErrorCode clearInstruments() {
168        int retval = OrchestraJNI.JNI_ClearInstruments(m_handle);
169        return ErrorCode.valueOf(retval);
170    }
171
172    /**
173     * Adds another instrument to the orchestra.
174     * @param instrument
175     *              TalonFX to add to orchestra
176     * @return Error Code generated by function. 0 indicates no error. 
177     */
178    public ErrorCode addInstrument(TalonFX instrument) {
179        int retval = OrchestraJNI.JNI_AddInstrument(m_handle, instrument.getHandle());
180        return ErrorCode.valueOf(retval);
181    }
182}