001package jmri.jmrit.audio; 002 003import java.io.InputStream; 004import java.nio.ByteBuffer; 005import jmri.implementation.AbstractAudio; 006import jmri.util.FileUtil; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Base implementation of the AudioBuffer class. 012 * <p> 013 * Specific implementations will extend this base class. 014 * <br> 015 * <hr> 016 * This file is part of JMRI. 017 * <p> 018 * JMRI is free software; you can redistribute it and/or modify it under the 019 * terms of version 2 of the GNU General Public License as published by the Free 020 * Software Foundation. See the "COPYING" file for a copy of this license. 021 * <p> 022 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 024 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 025 * 026 * @author Matthew Harris copyright (c) 2009, 2011 027 */ 028public abstract class AbstractAudioBuffer extends AbstractAudio implements AudioBuffer { 029 030 /** 031 * Holds the location of the sound sample used in this buffer 032 */ 033 private String url = ""; 034 035 /** 036 * Start loop point for this buffer represented as a number of samples 037 */ 038 private long startLoopPoint; 039 040 /** 041 * End loop point for this buffer represented as a number of samples 042 */ 043 private long endLoopPoint; 044 045 /** 046 * Flag to determine if this buffer is to be streamed from file 047 */ 048 private boolean streamed = false; 049 050 /** 051 * Flag to determine if streaming has been forced 052 */ 053 private boolean streamedForced = false; 054 055// /** 056// * 057// */ 058// private WaveFileReader waveFile; 059 /** 060 * Identifier of start loop point 061 */ 062 protected static final int LOOP_POINT_START = 0x01; 063 064 /** 065 * Identifier of end loop point 066 */ 067 protected static final int LOOP_POINT_END = 0x02; 068 069 /** 070 * Identifier of both loop points 071 */ 072 protected static final int LOOP_POINT_BOTH = 0x03; 073 074 /** 075 * Abstract constructor for new AudioBuffer with system name 076 * 077 * @param systemName AudioBuffer object system name (e.g. IAB4) 078 */ 079 public AbstractAudioBuffer(String systemName) { 080 super(systemName); 081 this.setState(STATE_EMPTY); 082 } 083 084 /** 085 * Abstract constructor for new AudioBuffer with system name and user name 086 * 087 * @param systemName AudioBuffer object system name (e.g. IAB4) 088 * @param userName AudioBuffer object user name 089 */ 090 public AbstractAudioBuffer(String systemName, String userName) { 091 super(systemName, userName); 092 this.setState(STATE_EMPTY); 093 } 094 095 @Override 096 public char getSubType() { 097 return BUFFER; 098 } 099 100 @Override 101 public String getURL() { 102 return this.url; 103 } 104 105 @Override 106 public void setURL(String url) { 107 this.url = FileUtil.getPortableFilename(url); 108 109 // Run the loadBuffer method on the main AWT thread to avoid any 110 // potential issues with interrupted exceptions if run on the audio 111 // command thread 112 loadBuffer(); 113 if (log.isDebugEnabled()) { 114 log.debug("Set url of Buffer {} to {}", this.getSystemName(), url); 115 } 116 } 117 118 @Override 119 public void setInputStream(InputStream stream) { 120 this.url = "stream"; 121 122 loadBuffer(stream); 123 if (log.isDebugEnabled()) { 124 log.debug("Set inputstream of Buffer {} to stream", this.getSystemName()); 125 } 126 } 127 128 @Override 129 public int getFrameSize() { 130 switch (this.getFormat()) { 131 case AudioBuffer.FORMAT_8BIT_MONO: 132 return 1; 133 case AudioBuffer.FORMAT_8BIT_STEREO: 134 return 2; 135 case AudioBuffer.FORMAT_8BIT_QUAD: 136 return 4; 137 case AudioBuffer.FORMAT_8BIT_5DOT1: 138 return 6; 139 case AudioBuffer.FORMAT_8BIT_6DOT1: 140 return 7; 141 case AudioBuffer.FORMAT_8BIT_7DOT1: 142 return 8; 143 case AudioBuffer.FORMAT_16BIT_MONO: 144 return 2; 145 case AudioBuffer.FORMAT_16BIT_STEREO: 146 return 4; 147 case AudioBuffer.FORMAT_16BIT_QUAD: 148 return 8; 149 case AudioBuffer.FORMAT_16BIT_5DOT1: 150 return 12; 151 case AudioBuffer.FORMAT_16BIT_6DOT1: 152 return 14; 153 case AudioBuffer.FORMAT_16BIT_7DOT1: 154 return 16; 155 default: //AudioBuffer.FORMAT_UNKNOWN: 156 return 0; 157 } 158 } 159 160 /** 161 * Method used to load the actual sound data into the buffer 162 * 163 * @return True if successful; False if not 164 */ 165 abstract protected boolean loadBuffer(); 166 167 /** 168 * Method used to load the actual sound data from an InputStream into the 169 * buffer 170 * 171 * @param s InputStream containing sound data 172 * @return True if successful; False if not 173 */ 174 abstract protected boolean loadBuffer(InputStream s); 175 176 @Override 177 public void setStartLoopPoint(long startLoopPoint) { 178 this.setStartLoopPoint(startLoopPoint, true); 179 } 180 181 // Can be made abstract later. 182 @Override 183 public boolean loadBuffer(ByteBuffer b, int format, int frequency) { 184 return false; 185 } 186 187 /** 188 * Internal method used to set the start loop point of this buffer with 189 * optional generation of loop buffers 190 * 191 * @param startLoopPoint position of start loop point in samples 192 * @param generateLoopBuffers True if loop buffers to be generated 193 */ 194 protected void setStartLoopPoint(long startLoopPoint, boolean generateLoopBuffers) { 195 this.startLoopPoint = startLoopPoint; 196 if (generateLoopBuffers) { 197 generateLoopBuffers(LOOP_POINT_START); 198 } 199 if (log.isDebugEnabled()) { 200 log.debug("Set start loop point of Buffer {} to {}", this.getSystemName(), startLoopPoint); 201 } 202 } 203 204 @Override 205 public long getStartLoopPoint() { 206 return this.startLoopPoint; 207 } 208 209 @Override 210 public void setEndLoopPoint(long endLoopPoint) { 211 this.setEndLoopPoint(endLoopPoint, true); 212 } 213 214 /** 215 * Internal method used to set the end loop point of this buffer with 216 * optional generation of loop buffers 217 * 218 * @param endLoopPoint position of end loop point in samples 219 * @param generateLoopBuffers True if loop buffers to be generated 220 */ 221 protected void setEndLoopPoint(long endLoopPoint, boolean generateLoopBuffers) { 222 this.endLoopPoint = endLoopPoint; 223 if (generateLoopBuffers) { 224 generateLoopBuffers(LOOP_POINT_END); 225 } 226 if (log.isDebugEnabled()) { 227 log.debug("Set end loop point of Buffer {} to {}", this.getSystemName(), endLoopPoint); 228 } 229 } 230 231 @Override 232 public long getEndLoopPoint() { 233 return this.endLoopPoint; 234 } 235 236 @Override 237 public void setStreamed(boolean streamed) { 238 if (streamed) { 239 log.warn("Streaming not yet supported!!"); 240 streamed = !streamed; 241 } 242 boolean changed = this.streamed != streamed; 243 this.streamed = this.streamedForced == true ? true : streamed; 244 if (log.isDebugEnabled()) { 245 log.debug("Set streamed property of Buffer {} to {}; changed = {}", this.getSystemName(), streamed, changed); 246 } 247 if (streamed && changed) { 248 generateStreamingBuffers(); 249 } else if (!streamed && changed) { 250 removeStreamingBuffers(); 251 } 252 } 253 254 @Override 255 public boolean isStreamed() { 256 return this.streamed; 257 } 258 259 /** 260 * Protected method used internally to modify the forced streaming flag 261 * 262 * @param streamedForced True if required; False if not 263 */ 264 protected void setStreamedForced(boolean streamedForced) { 265 boolean changed = this.streamedForced == false && streamedForced == true; 266 this.streamedForced = streamedForced; 267 if (log.isDebugEnabled()) { 268 log.debug("Set streamedForced property of Buffer {} to {}; changed = {}", this.getSystemName(), streamedForced, changed); 269 } 270 this.setStreamed(streamedForced == true ? true : this.streamed); 271 if (changed) { 272 this.generateLoopBuffers(LOOP_POINT_BOTH); 273 } 274 } 275 276 @Override 277 public boolean isStreamedForced() { 278 return this.streamedForced; 279 } 280 281 /** 282 * Method used to generate any necessary loop buffers. 283 * 284 * @param which the loop buffer to generate: 285 * <br>{@link #LOOP_POINT_START} for the start loop buffer 286 * <br>{@link #LOOP_POINT_END} for the end loop buffer 287 * <br>{@link #LOOP_POINT_BOTH} for both loop buffers 288 */ 289 abstract protected void generateLoopBuffers(int which); 290 291 /** 292 * Internal method used to generate buffers for streaming 293 * 294 * @return True if successful; False if not 295 */ 296 abstract protected boolean generateStreamingBuffers(); 297 298 /** 299 * Internal method used to remove streaming buffers 300 */ 301 abstract protected void removeStreamingBuffers(); 302 303 @Override 304 public void stateChanged(int oldState) { 305 // Move along... nothing to see here... 306 } 307 308 private static final Logger log = LoggerFactory.getLogger(AbstractAudioBuffer.class); 309}