diff --git a/.gitignore b/.gitignore index ece0a1b..d1c7eb1 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,19 @@ hs_err_pid* .DS_Store ._* Thumbs.db + +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..823567a --- /dev/null +++ b/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'idea' + id 'java-library' +} + +/*properties { + zmVersion { + major = 0; + minor = 0; + rev = 21; + } +}*/ + +group 'schule.ngb' +version '0.0.21-SNAPSHOT' +//version '{$zmVersion.major}.{$zmVersion.minor}.{$zmVersion.rev}-SNAPSHOT' + +compileJava { + options.release = 11 +} + +repositories { + mavenCentral() +} + +dependencies { + runtimeOnly 'com.googlecode.soundlibs:jlayer:1.0.1.4' + runtimeOnly 'com.googlecode.soundlibs:tritonus-share:0.3.7.4' + runtimeOnly 'com.googlecode.soundlibs:mp3spi:1.9.5.4' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' +} + +test { + useJUnitPlatform() +} + +tasks.register('jarMP3SPI', Jar) { + archiveClassifier = 'all' + duplicatesStrategy = 'exclude' + archivesBaseName = 'zeichenmaschine-mp3spi' + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..41d9927 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..41dfb87 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..fb84f31 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'zeichenmaschine' + diff --git a/src/schule/ngb/zm/Color.java b/src/main/java/schule/ngb/zm/Color.java similarity index 91% rename from src/schule/ngb/zm/Color.java rename to src/main/java/schule/ngb/zm/Color.java index e858806..33c2033 100644 --- a/src/schule/ngb/zm/Color.java +++ b/src/main/java/schule/ngb/zm/Color.java @@ -3,12 +3,12 @@ package schule.ngb.zm; /** * Repräsentiert eine Farbe in der Zeichenmaschine. *
- * Farben bestehen entweder aus einem Grauwert (zwischen 0 und
- * 255) oder einem Rot-, Grün- und Blauanteil (jeweils zwischen
- * 0 und 255).
+ * Farben bestehen entweder aus einem Grauwert (zwischen 0 und
+ * 255) oder einem Rot-, Grün- und Blauanteil (jeweils zwischen
+ * 0 und 255).
*
- * Eine Farbe hat außerdem einen Transparenzwert zwischen
+ * Die Lautstärke wird auf einer linearen Skale angegeben, wobei 0 kein Ton
+ * und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des
+ * Mediums.
+ *
+ * @return Die Lautstärke als linear skalierter Wert.
+ */
+ double getVolume();
+
/**
* Startet die Wiedergabe des Mediums und beendet die Methode. Das
* Audio-Medium wird einmal abgespielt und stoppt dann.
*/
- void playOnce();
+ void play();
/**
* Startet die Wiedergabe des Mediums und blockiert das Programm, bis die
* Wiedergabe beendet ist.
*/
- void playOnceAndWait();
+ void playAndWait();
/**
* Spielt das Medium in einer kontinuierlichen Schleife ab. Die Methode
diff --git a/src/schule/ngb/zm/media/Mixer.java b/src/main/java/schule/ngb/zm/media/Mixer.java
similarity index 72%
rename from src/schule/ngb/zm/media/Mixer.java
rename to src/main/java/schule/ngb/zm/media/Mixer.java
index 91b2064..f94ec4f 100644
--- a/src/schule/ngb/zm/media/Mixer.java
+++ b/src/main/java/schule/ngb/zm/media/Mixer.java
@@ -50,30 +50,53 @@ public class Mixer implements Audio {
pAudio.setVolume(pVolumeFactor * volume);
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public boolean isPlaying() {
return audios.stream().anyMatch(aw -> aw.audio.isPlaying());
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public boolean isLooping() {
return audios.stream().anyMatch(aw -> aw.audio.isLooping());
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void setVolume( double pVolume ) {
volume = (float) pVolume;
audios.stream().forEach(aw -> aw.audio.setVolume(aw.volumeFactor * pVolume));
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public void playOnce() {
- audios.stream().forEach(aw -> aw.audio.playOnce());
+ public double getVolume() {
+ return volume;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public void playOnceAndWait() {
- audios.stream().forEach(aw -> aw.audio.playOnce());
+ public void play() {
+ audios.stream().forEach(aw -> aw.audio.play());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void playAndWait() {
+ audios.stream().forEach(aw -> aw.audio.play());
while( audios.stream().anyMatch(aw -> aw.audio.isPlaying()) ) {
try {
Thread.sleep(10);
@@ -83,16 +106,25 @@ public class Mixer implements Audio {
}
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void loop() {
audios.stream().forEach(aw -> aw.audio.loop());
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void stop() {
audios.stream().forEach(aw -> aw.audio.stop());
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void dispose() {
if( isPlaying() ) {
@@ -101,6 +133,18 @@ public class Mixer implements Audio {
audios.stream().forEach(aw -> aw.audio.dispose());
}
+ /**
+ * Ändert die Lautstärke aller hinzugefügten Audiomedien in der angegebenen
+ * Zeit schrittweise, bis die angegebene Lautstärke erreicht ist.
+ *
+ * Zu beachten ist, dass die Lautstärke des Mixers angepasst wird. Das
+ * bedeutet, dass die Lautstärke der hinzugefügten Medien mit ihrem
+ * Lautstärkefaktor multipliziert werden. Die Medien haben am Ende also
+ * nicht unbedingt die Lautstärke {@code to}.
+ *
+ * @param to Der Zielwert für die Lautstärke.
+ * @param time Die Zeit, nach der die Änderung abgeschlossen sein soll.
+ */
public void fade( final double to, final int time ) {
TaskRunner.run(new Runnable() {
@Override
@@ -108,9 +152,6 @@ public class Mixer implements Audio {
final long start = System.currentTimeMillis();
double t = 0.0;
double from = volume;
- if( !isPlaying() ) {
- playOnce();
- }
do {
setVolume(Constants.interpolate(from, to, t));
t = (double) (System.currentTimeMillis() - start) / (double) time;
diff --git a/src/schule/ngb/zm/media/Music.java b/src/main/java/schule/ngb/zm/media/Music.java
similarity index 56%
rename from src/schule/ngb/zm/media/Music.java
rename to src/main/java/schule/ngb/zm/media/Music.java
index 1c53ba7..72ad34c 100644
--- a/src/schule/ngb/zm/media/Music.java
+++ b/src/main/java/schule/ngb/zm/media/Music.java
@@ -1,41 +1,82 @@
package schule.ngb.zm.media;
import schule.ngb.zm.tasks.TaskRunner;
+import schule.ngb.zm.util.Log;
import schule.ngb.zm.util.ResourceStreamProvider;
+import schule.ngb.zm.util.Validator;
import javax.sound.sampled.*;
import java.io.IOException;
-import java.io.InputStream;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import java.net.URL;
+/**
+ * Ein Musikstück, dass im Projekt abgespielt werden soll.
+ *
+ * Im gegensatz zu einem {@link Sound} sind Musikstücke längere Audiodateien,
+ * die zum Beispiel als Hintergrundmusik ablaufen sollen. Die Musik wird daher
+ * nicht komplett in den Speicher geladen, sondern direkt aus der Audioquelle
+ * gestreamt und wiedergegeben.
+ */
public class Music implements Audio {
// size of the byte buffer used to read/write the audio stream
private static final int BUFFER_SIZE = 4096;
+ /**
+ * Ob der Sound gerade abgespielt wird.
+ */
private boolean playing = false;
+ /**
+ * Ob der Sound gerade in einer Schleife abgespielt wird.
+ */
private boolean looping = false;
+ /**
+ * Die Quelle des Musikstücks.
+ */
private String audioSource;
+ /**
+ * Der AudioStream, um die AUdiosdaten zulsen, falls dieser schon geöffnet
+ * wurde. Sonst {@code null}.
+ */
private AudioInputStream audioStream;
+ /**
+ * Die Line für die Ausgabe, falls diese schon geöffnet wurde. Sonst
+ * {@code null}.
+ */
private SourceDataLine audioLine;
+ /**
+ * Die Lautstärke der Musik.
+ */
private float volume = 0.8f;
+ /**
+ * Erstellt eine Musik aus der angegebenen Datei oder Webadresse.
+ *
+ * @param source Ein Dateipfad oder eine Webadresse.
+ * @throws NullPointerException Falls die Quelle {@code null} ist.
+ */
public Music( String source ) {
+ Validator.requireNotNull(source);
this.audioSource = source;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public boolean isPlaying() {
return playing;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public boolean isLooping() {
if( !playing ) {
@@ -44,6 +85,9 @@ public class Music implements Audio {
return looping;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void setVolume( double volume ) {
this.volume = (float) volume;
@@ -52,6 +96,18 @@ public class Music implements Audio {
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getVolume() {
+ return volume;
+ }
+
+ /**
+ * Interne Methode, um die gesetzte Lautstärke vor dem Abspielen
+ * anzuwenden.
+ */
private void applyVolume() {
FloatControl gainControl =
(FloatControl) audioLine.getControl(FloatControl.Type.MASTER_GAIN);
@@ -61,8 +117,11 @@ public class Music implements Audio {
gainControl.setValue(vol);
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public void playOnce() {
+ public void play() {
if( openLine() ) {
TaskRunner.run(new Runnable() {
@Override
@@ -73,19 +132,28 @@ public class Music implements Audio {
}
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public void playOnceAndWait() {
+ public void playAndWait() {
if( openLine() ) {
stream();
}
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void loop() {
looping = true;
- playOnce();
+ play();
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void stop() {
playing = false;
@@ -93,6 +161,9 @@ public class Music implements Audio {
dispose();
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public void dispose() {
if( audioLine != null ) {
@@ -110,7 +181,8 @@ public class Music implements Audio {
if( audioStream != null ) {
audioStream.close();
}
- } catch( IOException ex ) {}
+ } catch( IOException ex ) {
+ }
audioLine = null;
audioStream = null;
@@ -124,15 +196,14 @@ public class Music implements Audio {
int bytesRead = -1;
try {
- while (playing && (bytesRead = audioStream.read(bytesBuffer)) != -1) {
+ while( playing && (bytesRead = audioStream.read(bytesBuffer)) != -1 ) {
audioLine.write(bytesBuffer, 0, bytesRead);
}
audioLine.drain();
audioLine.stop();
} catch( IOException ex ) {
- LOGGER.warning("Error while playing Music source <" + audioSource + ">");
- LOGGER.throwing("Music", "stream", ex);
+ LOG.warn(ex, "Error while playing Music source <%s>", audioSource);
}
// Wait for the remaining audio to play
@@ -154,14 +225,14 @@ public class Music implements Audio {
}
try {
- InputStream in = ResourceStreamProvider.getResourceStream(audioSource);
- if( in != null ) {
- final AudioInputStream inStream = AudioSystem.getAudioInputStream(in);
+ URL url = ResourceStreamProvider.getResourceURL(audioSource);
+ if( url != null ) {
+ final AudioInputStream inStream = AudioSystem.getAudioInputStream(url);
AudioFormat format = inStream.getFormat();
final int ch = format.getChannels();
final float rate = format.getSampleRate();
- AudioFormat outFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch*2, rate, false);
+ AudioFormat outFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch * 2, rate, false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, outFormat);
@@ -172,28 +243,33 @@ public class Music implements Audio {
audioStream = AudioSystem.getAudioInputStream(outFormat, inStream);
return true;
} else {
- LOGGER.warning("Sound source <" + audioSource + "> could not be played: No audio source found.");
+ LOG.warn("Sound source <%s> could not be played: No audio source found.", audioSource);
}
} catch( UnsupportedAudioFileException ex ) {
- LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: The specified audio file is not supported.", ex);
+ LOG.warn(ex, "Sound source <%s> could not be played: The specified audio file is not supported.", audioSource);
} catch( LineUnavailableException ex ) {
- LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: Audio line for playing back is unavailable.", ex);
+ LOG.warn(ex, "Sound source <%s> could not be played: Audio line for playing back is unavailable.", audioSource);
} catch( IOException ex ) {
- LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: Error playing the audio file.", ex);
+ LOG.warn(ex, "Sound source <%s> could not be played: Error playing the audio file.", audioSource);
}
return false;
}
private void streamingStopped() {
- playing = false;
dispose();
if( looping ) {
- playOnce();
+ if( openLine() ) {
+ stream();
+ } else {
+ playing = false;
+ looping = false;
+ }
+ } else {
+ playing = false;
}
}
- //private static final Logger LOGGER = Logger.getLogger("schule.ngb.zm.media.Music");
- private static final Logger LOGGER = Logger.getLogger(Music.class.getName());
+ private static final Log LOG = Log.getLogger(Music.class);
}
diff --git a/src/main/java/schule/ngb/zm/media/Sound.java b/src/main/java/schule/ngb/zm/media/Sound.java
new file mode 100644
index 0000000..ece650d
--- /dev/null
+++ b/src/main/java/schule/ngb/zm/media/Sound.java
@@ -0,0 +1,309 @@
+package schule.ngb.zm.media;
+
+import schule.ngb.zm.util.Log;
+import schule.ngb.zm.util.ResourceStreamProvider;
+import schule.ngb.zm.util.Validator;
+
+import javax.sound.sampled.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Logger;
+
+/**
+ * Wiedergabe kurzer Soundclips, die mehrmals wiederverwendet werden.
+ *
+ * In Spielen und anderen Projekten gibt es oftmals eine Reihe kurzer Sounds,
+ * die zusammen mit bestimmten Aktionen wiedergegeben werden (zum Beispiel, wenn
+ * die Spielfigur springt, wenn zwei Objekte kollidieren, usw.). Sounds werden
+ * komplett in den Speicher geladen und können dadurch immer wieder, als
+ * Schleife oder auch nur Abschnittsweise abgespielt werden.
+ *
+ * Für längre Musikstücke (beispielsweise Hintergrundmusik) bietet sich eher die
+ * KLasse {@link Music} an.
+ */
+@SuppressWarnings( "unused" )
+public class Sound implements Audio {
+
+ /**
+ * Ob der Sound gerade abgespielt wird.
+ */
+ private boolean playing = false;
+
+ /**
+ * Ob der Sound gerade in einer Schleife abgespielt wird.
+ */
+ private boolean looping = false;
+
+ /**
+ * Die Quelle des Musikstücks.
+ */
+ private String audioSource;
+
+ /**
+ * Der Clip, falls er schon geladen wurde, sonst {@code null}.
+ */
+ private Clip audioClip;
+
+ /**
+ * Ob die Resourcen des Clips im Speicher nach dem nächsten Abspielen
+ * freigegeben werden sollen.
+ */
+ private boolean disposeAfterPlay = false;
+
+ /**
+ * Die Lautstärke des Clips.
+ */
+ private float volume = 0.8f;
+
+ /**
+ * Erstellt einen Sound aus der angegebene Quelle.
+ *
+ * @param source Ein Dateipfad oder eine Webadresse.
+ * @throws NullPointerException Falls die Quelle {@code null} ist.
+ */
+ public Sound( String source ) {
+ Validator.requireNotNull(source);
+ this.audioSource = source;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isPlaying() {
+ // return audioClip != null && audioClip.isRunning();
+ return playing;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isLooping() {
+ if( !playing ) {
+ looping = false;
+ }
+ return looping;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setVolume( double volume ) {
+ this.volume = (float) volume;
+ if( audioClip != null ) {
+ applyVolume();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getVolume() {
+ return volume;
+ }
+
+ /**
+ * Interne Methode, um die gesetzte Lautstärke vor dem Abspielen
+ * anzuwenden.
+ */
+ private void applyVolume() {
+ FloatControl gainControl =
+ (FloatControl) audioClip.getControl(FloatControl.Type.MASTER_GAIN);
+
+ float vol = 20f * (float) Math.log10(volume);
+ // vol = (float) Constants.limit(vol, gainControl.getMinimum(), gainControl.getMaximum());
+ gainControl.setValue(vol);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stop() {
+ looping = false;
+ if( audioClip.isRunning() ) {
+ audioClip.stop();
+ }
+ playing = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void play() {
+ if( this.openClip() ) {
+ audioClip.start();
+ playing = true;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void playAndWait() {
+ this.play();
+
+ long audioLen = audioClip.getMicrosecondLength();
+ while( playing ) {
+ try {
+ long ms = (audioLen - audioClip.getMicrosecondPosition()) / 1000L;
+ Thread.sleep(ms);
+ } catch( InterruptedException ex ) {
+ // Ignore
+ }
+ }
+
+ audioClip.close();
+ }
+
+ /**
+ * Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips
+ * frei.
+ *
+ * Der Aufruf ist effektiv gleich zu
+ *
+ * Der Aufruf entspricht
+ * 0
- * (unsichtbar) und 255 (deckend).
+ * Eine Farbe hat außerdem einen Transparenzwert zwischen 0
+ * (unsichtbar) und 255 (deckend).
*/
public class Color {
@@ -106,7 +106,7 @@ public class Color {
/**
* Erstellt eine graue Farbe entsprechend des Grauwertes gray.
*
- * @param gray Ein Grauwert zwischen 0 und 255.
+ * @param gray Ein Grauwert zwischen 0 und 255.
*/
public Color( int gray ) {
this(gray, gray, gray, 255);
@@ -116,7 +116,7 @@ public class Color {
* Erstellt eine graue Farbe entsprechend des Grauwertes gray und
* des Transparentwertes alpha.
*
- * @param gray Ein Grauwert zwischen 0 und 255.
+ * @param gray Ein Grauwert zwischen 0 und 255.
*/
public Color( int gray, int alpha ) {
this(gray, gray, gray, alpha);
@@ -125,12 +125,11 @@ public class Color {
/**
* Erstellt eine Farbe. Die Parameter red, green und
* blue geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
- * liegen zwischen 0 und 255.
+ * liegen zwischen 0 und 255.
*
- * @param red Rotwert zwischen 0 und 255.
- * @param green Grünwert zwischen 0 und 255.
- * @param blue Blauwert zwischen 0 und 255.
- * @return Ein passendes Farbobjekt.
+ * @param red Rotwert zwischen 0 und 255.
+ * @param green Grünwert zwischen 0 und 255.
+ * @param blue Blauwert zwischen 0 und 255.
*/
public Color( int red, int green, int blue ) {
this(red, green, blue, 255);
@@ -139,17 +138,16 @@ public class Color {
/**
* Erstellt eine Farbe. Die Parameter red, green und
* blue geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
- * liegen zwischen 0 und 255.
+ * liegen zwischen 0 und 255.
* alpha gibt den den Transparentwert an (auch zwischen
- * code>0 und 255), wobei
- * 0 komplett durchsichtig ist und 255 komplett
+ * 0 und 255), wobei
+ * 0 komplett durchsichtig ist und 255 komplett
* deckend.
*
- * @param red Rotwert zwischen 0 und 255.
- * @param green Grünwert zwischen 0 und 255.
- * @param blue Blauwert zwischen 0 und 255.
- * @param alpha Transparentwert zwischen 0 und 255.
- * @return Ein passendes Farbobjekt.
+ * @param red Rotwert zwischen 0 und 255.
+ * @param green Grünwert zwischen 0 und 255.
+ * @param blue Blauwert zwischen 0 und 255.
+ * @param alpha Transparentwert zwischen 0 und 255.
*/
public Color( int red, int green, int blue, int alpha ) {
rgba = (alpha << 24) | (red << 16) | (green << 8) | blue;
@@ -239,7 +237,7 @@ public class Color {
/**
* Erzeugt eine Farbe aus einem hexadezimalen Code. Der Hexcode kann
* sechs- oder achtstellig sein (wenn ein Transparentwert vorhanden ist).
- * Dem Code kann ein # Zeichen vorangestellt sein.
+ * Dem Code kann ein {@code #} Zeichen vorangestellt sein.
*
* @param hexcode
* @return
diff --git a/src/schule/ngb/zm/ColorLayer.java b/src/main/java/schule/ngb/zm/ColorLayer.java
similarity index 100%
rename from src/schule/ngb/zm/ColorLayer.java
rename to src/main/java/schule/ngb/zm/ColorLayer.java
diff --git a/src/schule/ngb/zm/Constants.java b/src/main/java/schule/ngb/zm/Constants.java
similarity index 93%
rename from src/schule/ngb/zm/Constants.java
rename to src/main/java/schule/ngb/zm/Constants.java
index bde0492..561b3aa 100644
--- a/src/schule/ngb/zm/Constants.java
+++ b/src/main/java/schule/ngb/zm/Constants.java
@@ -387,10 +387,10 @@ public class Constants {
/**
* Erstellt eine graue Farbe. Der Parameter {@code gray} gibt einen Grauwert
- * zwischen 0 und 255 an, wobei
- * 0 schwarz und 255 weiß ist.
+ * zwischen 0 und 255 an, wobei
+ * 0 schwarz und 255 weiß ist.
*
- * @param gray Grauwert zwischen 0 und 255.
+ * @param gray Grauwert zwischen 0 und 255.
* @return Ein passendes Farbobjekt.
*/
public static final Color color( int gray ) {
@@ -399,16 +399,16 @@ public class Constants {
/**
* Erstellt eine graue Farbe. Der Parameter {@code gray} gibt einen Grauwert
- * zwischen 0 und 255 an, wobei
- * 0 schwarz und 255 weiß ist.
+ * zwischen 0 und 255 an, wobei
+ * 0 schwarz und 255 weiß ist.
* {@code alpha} gibt den den Transparentwert an (auch zwischen
- * 0 und 255), wobei
- * 0 komplett durchsichtig ist und 255 komplett
+ * 0 und 255), wobei
+ * 0 komplett durchsichtig ist und 255 komplett
* deckend.
*
- * @param gray Grauwert zwischen 0 und 255.
- * @param alpha Transparentwert zwischen 0 und
- * 255.
+ * @param gray Grauwert zwischen 0 und 255.
+ * @param alpha Transparentwert zwischen 0 und
+ * 255.
* @return Ein passendes Farbobjekt.
*/
public static final Color color( int gray, int alpha ) {
@@ -418,11 +418,11 @@ public class Constants {
/**
* Erstellt eine Farbe. Die Parameter {@code red}, {@code green} und
* {@code blue} geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
- * liegen zwischen 0 und 255.
+ * liegen zwischen 0 und 255.
*
- * @param red Rotwert zwischen 0 und 255.
- * @param green Grünwert zwischen 0 und 255.
- * @param blue Blauwert zwischen 0 und 255.
+ * @param red Rotwert zwischen 0 und 255.
+ * @param green Grünwert zwischen 0 und 255.
+ * @param blue Blauwert zwischen 0 und 255.
* @return Ein passendes Farbobjekt.
*/
public static final Color color( int red, int green, int blue ) {
@@ -432,17 +432,17 @@ public class Constants {
/**
* Erstellt eine Farbe. Die Parameter {@code red}, {@code green} und
* {@code blue} geben die Rot-, Grün- und Blauanteile der Farbe. Die Werte
- * liegen zwischen 0 und 255. {@code alpha} gibt
- * den den Transparentwert an (auch zwischen code>0 und
- * 255), wobei
- * 0 komplett durchsichtig ist und 255 komplett
+ * liegen zwischen 0 und 255. {@code alpha} gibt
+ * den den Transparentwert an (auch zwischen 0 und
+ * 255), wobei
+ * 0 komplett durchsichtig ist und 255 komplett
* deckend.
*
- * @param red Rotwert zwischen 0 und 255.
- * @param green Grünwert zwischen 0 und 255.
- * @param blue Blauwert zwischen 0 und 255.
- * @param alpha Transparenzwert zwischen 0 und
- * 255.
+ * @param red Rotwert zwischen 0 und 255.
+ * @param green Grünwert zwischen 0 und 255.
+ * @param blue Blauwert zwischen 0 und 255.
+ * @param alpha Transparenzwert zwischen 0 und
+ * 255.
* @return Ein passendes Farbobjekt.
*/
public static final Color color( int red, int green, int blue, int alpha ) {
@@ -589,7 +589,7 @@ public class Constants {
* Ermittelt das Vorzeichen der Zahl {@code x}.
*
* @param x Eine Zahl.
- * @return -1, 1 oder 0.
+ * @return -1, 1 oder 0.
*/
public static final double sign( double x ) {
return Math.signum(x);
@@ -670,7 +670,7 @@ public class Constants {
* Ermittelt den Sinus der Zahl {@code x}.
*
* @param x Eine Zahl.
- * @return sin(x).
+ * @return {@code sin(x)}.
*/
public static final double sin( double x ) {
return Math.sin(x);
@@ -680,7 +680,7 @@ public class Constants {
* Ermittelt den Kosinus der Zahl {@code x}.
*
* @param x Eine Zahl.
- * @return cos(x).
+ * @return {@code cos(x)}.
*/
public static final double cos( double x ) {
return Math.cos(x);
@@ -690,7 +690,7 @@ public class Constants {
* Ermittelt den Tangens der Zahl {@code x}.
*
* @param x Eine Zahl.
- * @return tan(x).
+ * @return {@code tan(x)}.
*/
public static final double tan( double x ) {
return Math.tan(x);
@@ -700,7 +700,7 @@ public class Constants {
* Ermittelt den Arkussinus der Zahl {@code x}.
*
* @param x Eine Zahl.
- * @return asin(x).
+ * @return {@code asin(x)}.
*/
public static final double arcsin( double x ) {
return Math.asin(x);
@@ -710,7 +710,7 @@ public class Constants {
* Ermittelt den Arkuskosinus der Zahl {@code x}.
*
* @param x Eine Zahl.
- * @return acos(x).
+ * @return {@code acos(x)}.
*/
public static final double arccos( double x ) {
return Math.acos(x);
@@ -720,20 +720,20 @@ public class Constants {
* Ermittelt den Arkusktangens der Zahl {@code x}.
*
* @param x Eine Zahl.
- * @return atan(x).
+ * @return {@code atan(x)}.
*/
public static final double arctan( double x ) {
return Math.atan(x);
}
/**
- * Beschränkt die Zahl {@code x} auf das Intervall [min, max].
+ * Beschränkt die Zahl {@code x} auf das Intervall {@code [min, max]}.
* Liegt {@code x} außerhalb des Intervalls, wird eine der Grenzen
* zurückgegeben.
*
* @param x Eine Zahl.
* @param max Das Maximum des Intervalls.
- * @return Eine Zahl im Intervall [min, max].
+ * @return Eine Zahl im Intervall {@code [min, max]}.
*/
public static final double limit( double x, double min, double max ) {
if( x > max ) {
@@ -746,13 +746,13 @@ public class Constants {
}
/**
- * Beschränkt die Zahl {@code x} auf das Intervall [min, max].
+ * Beschränkt die Zahl {@code x} auf das Intervall {@code [min, max]}.
* Liegt {@code x} außerhalb des Intervalls, wird eine der Grenzen
* zurückgegeben.
*
* @param x Eine Zahl.
* @param max Das Maximum des Intervalls.
- * @return Eine Zahl im Intervall [min, max].
+ * @return Eine Zahl im Intervall {@code [min, max]}.
*/
public static final int limit( int x, int min, int max ) {
if( x > max ) {
@@ -774,7 +774,7 @@ public class Constants {
*
* @param from Startwert
* @param to Zielwert
- * @param t Wert zwischen 0 und 1.
+ * @param t Wert zwischen 0 und 1.
* @return Das Ergebnis der linearen Interpolation.
*/
public static final double interpolate( double from, double to, double t ) {
@@ -885,8 +885,8 @@ public class Constants {
* Erzeugt einen zufälligen Wahrheitswert. {@code true} wird mit der
* Wahrscheinlichkeit {@code percent} Prozent erzeugt.
*
- * @param percent Eine Prozentzahl zwischen 0 und
- * 100.
+ * @param percent Eine Prozentzahl zwischen 0 und
+ * 100.
* @return Ein Wahrheitswert.
*/
public static final boolean randomBool( int percent ) {
diff --git a/src/schule/ngb/zm/Drawable.java b/src/main/java/schule/ngb/zm/Drawable.java
similarity index 92%
rename from src/schule/ngb/zm/Drawable.java
rename to src/main/java/schule/ngb/zm/Drawable.java
index 3511f73..0b93f88 100644
--- a/src/schule/ngb/zm/Drawable.java
+++ b/src/main/java/schule/ngb/zm/Drawable.java
@@ -12,7 +12,7 @@ public interface Drawable {
* Gibt an, ob das Objekt derzeit sichtbar ist (also gezeichnet werden
* muss).
*
- * @return true, wenn das Objekt sichtbar ist.
+ * @return {@code true}, wenn das Objekt sichtbar ist.
*/
boolean isVisible();
diff --git a/src/schule/ngb/zm/DrawableLayer.java b/src/main/java/schule/ngb/zm/DrawableLayer.java
similarity index 100%
rename from src/schule/ngb/zm/DrawableLayer.java
rename to src/main/java/schule/ngb/zm/DrawableLayer.java
diff --git a/src/schule/ngb/zm/DrawingLayer.java b/src/main/java/schule/ngb/zm/DrawingLayer.java
similarity index 100%
rename from src/schule/ngb/zm/DrawingLayer.java
rename to src/main/java/schule/ngb/zm/DrawingLayer.java
diff --git a/src/schule/ngb/zm/GraphicsLayer.java b/src/main/java/schule/ngb/zm/GraphicsLayer.java
similarity index 100%
rename from src/schule/ngb/zm/GraphicsLayer.java
rename to src/main/java/schule/ngb/zm/GraphicsLayer.java
diff --git a/src/schule/ngb/zm/ImageLayer.java b/src/main/java/schule/ngb/zm/ImageLayer.java
similarity index 100%
rename from src/schule/ngb/zm/ImageLayer.java
rename to src/main/java/schule/ngb/zm/ImageLayer.java
diff --git a/src/schule/ngb/zm/Layer.java b/src/main/java/schule/ngb/zm/Layer.java
similarity index 100%
rename from src/schule/ngb/zm/Layer.java
rename to src/main/java/schule/ngb/zm/Layer.java
diff --git a/src/schule/ngb/zm/Options.java b/src/main/java/schule/ngb/zm/Options.java
similarity index 100%
rename from src/schule/ngb/zm/Options.java
rename to src/main/java/schule/ngb/zm/Options.java
diff --git a/src/schule/ngb/zm/Shape2DLayer.java b/src/main/java/schule/ngb/zm/Shape2DLayer.java
similarity index 100%
rename from src/schule/ngb/zm/Shape2DLayer.java
rename to src/main/java/schule/ngb/zm/Shape2DLayer.java
diff --git a/src/schule/ngb/zm/Spielemaschine.java b/src/main/java/schule/ngb/zm/Spielemaschine.java
similarity index 100%
rename from src/schule/ngb/zm/Spielemaschine.java
rename to src/main/java/schule/ngb/zm/Spielemaschine.java
diff --git a/src/schule/ngb/zm/Updatable.java b/src/main/java/schule/ngb/zm/Updatable.java
similarity index 91%
rename from src/schule/ngb/zm/Updatable.java
rename to src/main/java/schule/ngb/zm/Updatable.java
index 0a60447..0998e1c 100644
--- a/src/schule/ngb/zm/Updatable.java
+++ b/src/main/java/schule/ngb/zm/Updatable.java
@@ -9,7 +9,7 @@ public interface Updatable {
/**
* Gibt an, ob das Objekt gerade auf Aktualisierungen reagiert.
- * @return true, wenn das Objekt aktiv ist.
+ * @return {@code true}, wenn das Objekt aktiv ist.
*/
public boolean isActive();
diff --git a/src/schule/ngb/zm/Vector.java b/src/main/java/schule/ngb/zm/Vector.java
similarity index 100%
rename from src/schule/ngb/zm/Vector.java
rename to src/main/java/schule/ngb/zm/Vector.java
diff --git a/src/schule/ngb/zm/Zeichenfenster.java b/src/main/java/schule/ngb/zm/Zeichenfenster.java
similarity index 100%
rename from src/schule/ngb/zm/Zeichenfenster.java
rename to src/main/java/schule/ngb/zm/Zeichenfenster.java
diff --git a/src/schule/ngb/zm/Zeichenleinwand.java b/src/main/java/schule/ngb/zm/Zeichenleinwand.java
similarity index 95%
rename from src/schule/ngb/zm/Zeichenleinwand.java
rename to src/main/java/schule/ngb/zm/Zeichenleinwand.java
index 73b9bae..232b688 100644
--- a/src/schule/ngb/zm/Zeichenleinwand.java
+++ b/src/main/java/schule/ngb/zm/Zeichenleinwand.java
@@ -90,7 +90,7 @@ public class Zeichenleinwand extends Canvas {
* als letzte eingefügt. Die aufrufende Methode kann also nicht sicher sein,
* dass die neue Ebene am Ende wirklich am Index {@code i} steht.
*
- * @param i Index der Ebene, beginnend mit 0.
+ * @param i Index der Ebene, beginnend mit 0.
* @param layer Die neue Ebene.
*/
public void addLayer( int i, Layer layer ) {
@@ -125,10 +125,10 @@ public class Zeichenleinwand extends Canvas {
}
/**
- * Holt die Ebene am Index i (beginnend bei 0).
+ * Holt die Ebene am Index i (beginnend bei 0).
*
- * @param i Index der Ebene (beginnend bei 0).
- * @return Die Ebene am Index i oder null.
+ * @param i Index der Ebene (beginnend bei 0).
+ * @return Die Ebene am Index i oder {@code null}.
* @throws IndexOutOfBoundsException Falls der Index nicht existiert.
*/
public Layer getLayer( int i ) {
@@ -141,7 +141,7 @@ public class Zeichenleinwand extends Canvas {
/**
* Sucht die erste Ebene des angegebenen Typs aus der Liste der Ebenen.
- * Existiert keine solche Ebene, wird null zurückgegeben.
+ * Existiert keine solche Ebene, wird {@code null} zurückgegeben.
*
* @param clazz Typ der Ebene.
* @param
+ * allerdings wird der aufrufende Thread nicht blockiert und
+ * {@link #dispose()} automatisch am Ende aufgerufen.
+ */
+ public void playOnce() {
+ disposeAfterPlay = true;
+ play();
+ }
+
+ /**
+ * Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips
+ * frei.
+ *
+ * clip.playAndWait();
+ * clip.dispose();
+ *
+ */
+ public void playOnceAndWait() {
+ disposeAfterPlay = true;
+ playAndWait();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void loop() {
+ loop(Clip.LOOP_CONTINUOUSLY);
+ }
+
+ /**
+ * Wiederholt den Sound die angegebene Anzahl an Wiederholungen ab und stoppt
+ * die Wiedergabe dann.
+ * @param count Anzahl der Wiederholungen.
+ */
+ public void loop( int count ) {
+ if( count > 0 ) {
+ int loopCount = count;
+ if( loopCount != Clip.LOOP_CONTINUOUSLY ) {
+ if( loopCount <= 0 ) {
+ return;
+ }
+ // Adjust Number of loops
+ loopCount -= 1;
+ }
+ if( openClip() ) {
+ looping = true;
+ audioClip.loop(loopCount);
+ playing = true;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dispose() {
+ if( audioClip != null ) {
+ if( audioClip.isRunning() ) {
+ audioClip.stop();
+ }
+ audioClip.close();
+ audioClip = null;
+ }
+ }
+
+ private boolean openClip() {
+ if( audioClip != null ) {
+ audioClip.setFramePosition(0);
+ return true;
+ }
+
+ try {
+ InputStream in = ResourceStreamProvider.getResourceStream(audioSource);
+ if( in != null ) {
+ AudioInputStream audioStream = AudioSystem.getAudioInputStream(in);
+ AudioFormat format = audioStream.getFormat();
+ DataLine.Info info = new DataLine.Info(Clip.class, format);
+
+ audioClip = (Clip) AudioSystem.getLine(info);
+ audioClip.addLineListener(new LineListener() {
+ @Override
+ public void update( LineEvent event ) {
+ if( event.getType() == LineEvent.Type.STOP ) {
+ playbackStopped();
+ }
+ }
+ });
+ audioClip.open(audioStream);
+ applyVolume();
+ return true;
+ } else {
+ LOG.warn("Sound source <%s> could not be played: No audio source found.", audioSource);
+ }
+ } catch( UnsupportedAudioFileException ex ) {
+ LOG.warn(ex, "Sound source <%s> could not be played: The specified audio file is not supported.", audioSource);
+ } catch( LineUnavailableException ex ) {
+ LOG.warn(ex, "Sound source <%s> could not be played: Audio line for playing back is unavailable.", audioSource);
+ } catch( IOException ex ) {
+ LOG.warn(ex, "Sound source <%s> could not be played: Error playing the audio file.", audioSource);
+ }
+ return false;
+ }
+
+ /*@Override
+ public void update( LineEvent event ) {
+ LineEvent.Type type = event.getType();
+
+ if( type == LineEvent.Type.START ) {
+ playing = true;
+ } else if( type == LineEvent.Type.STOP ) {
+ playing = false;
+ if( disposeAfterPlay ) {
+ this.dispose();
+ disposeAfterPlay = false;
+ }
+ }
+ }*/
+
+ /**
+ * Interne Methode, die aufgerufen wird, wenn die Wiedergabe gestoppt wird.
+ * Entweder durch einen Aufruf von {@link #stop()} oder, weil die Wiedergabe
+ * nach {@link #playOnce()} beendet wurde.
+ */
+ private void playbackStopped() {
+ playing = false;
+ if( disposeAfterPlay ) {
+ this.dispose();
+ disposeAfterPlay = false;
+ }
+ }
+
+ private static final Log LOG = Log.getLogger(Sound.class);
+
+}
diff --git a/src/schule/ngb/zm/shapes/Arc.java b/src/main/java/schule/ngb/zm/shapes/Arc.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Arc.java
rename to src/main/java/schule/ngb/zm/shapes/Arc.java
diff --git a/src/schule/ngb/zm/shapes/Arrow.java b/src/main/java/schule/ngb/zm/shapes/Arrow.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Arrow.java
rename to src/main/java/schule/ngb/zm/shapes/Arrow.java
diff --git a/src/schule/ngb/zm/shapes/Bounds.java b/src/main/java/schule/ngb/zm/shapes/Bounds.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Bounds.java
rename to src/main/java/schule/ngb/zm/shapes/Bounds.java
diff --git a/src/schule/ngb/zm/shapes/Circle.java b/src/main/java/schule/ngb/zm/shapes/Circle.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Circle.java
rename to src/main/java/schule/ngb/zm/shapes/Circle.java
diff --git a/src/schule/ngb/zm/shapes/Curve.java b/src/main/java/schule/ngb/zm/shapes/Curve.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Curve.java
rename to src/main/java/schule/ngb/zm/shapes/Curve.java
diff --git a/src/schule/ngb/zm/shapes/CustomShape.java b/src/main/java/schule/ngb/zm/shapes/CustomShape.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/CustomShape.java
rename to src/main/java/schule/ngb/zm/shapes/CustomShape.java
diff --git a/src/schule/ngb/zm/shapes/Ellipse.java b/src/main/java/schule/ngb/zm/shapes/Ellipse.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Ellipse.java
rename to src/main/java/schule/ngb/zm/shapes/Ellipse.java
diff --git a/src/schule/ngb/zm/shapes/FilledShape.java b/src/main/java/schule/ngb/zm/shapes/FilledShape.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/FilledShape.java
rename to src/main/java/schule/ngb/zm/shapes/FilledShape.java
diff --git a/src/schule/ngb/zm/shapes/Kite.java b/src/main/java/schule/ngb/zm/shapes/Kite.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Kite.java
rename to src/main/java/schule/ngb/zm/shapes/Kite.java
diff --git a/src/schule/ngb/zm/shapes/Line.java b/src/main/java/schule/ngb/zm/shapes/Line.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Line.java
rename to src/main/java/schule/ngb/zm/shapes/Line.java
diff --git a/src/schule/ngb/zm/shapes/Picture.java b/src/main/java/schule/ngb/zm/shapes/Picture.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Picture.java
rename to src/main/java/schule/ngb/zm/shapes/Picture.java
diff --git a/src/schule/ngb/zm/shapes/Point.java b/src/main/java/schule/ngb/zm/shapes/Point.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Point.java
rename to src/main/java/schule/ngb/zm/shapes/Point.java
diff --git a/src/schule/ngb/zm/shapes/Polygon.java b/src/main/java/schule/ngb/zm/shapes/Polygon.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Polygon.java
rename to src/main/java/schule/ngb/zm/shapes/Polygon.java
diff --git a/src/schule/ngb/zm/shapes/Quad.java b/src/main/java/schule/ngb/zm/shapes/Quad.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Quad.java
rename to src/main/java/schule/ngb/zm/shapes/Quad.java
diff --git a/src/schule/ngb/zm/shapes/Rectangle.java b/src/main/java/schule/ngb/zm/shapes/Rectangle.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Rectangle.java
rename to src/main/java/schule/ngb/zm/shapes/Rectangle.java
diff --git a/src/schule/ngb/zm/shapes/Rhombus.java b/src/main/java/schule/ngb/zm/shapes/Rhombus.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Rhombus.java
rename to src/main/java/schule/ngb/zm/shapes/Rhombus.java
diff --git a/src/schule/ngb/zm/shapes/RoundedRectangle.java b/src/main/java/schule/ngb/zm/shapes/RoundedRectangle.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/RoundedRectangle.java
rename to src/main/java/schule/ngb/zm/shapes/RoundedRectangle.java
diff --git a/src/schule/ngb/zm/shapes/Shape.java b/src/main/java/schule/ngb/zm/shapes/Shape.java
similarity index 99%
rename from src/schule/ngb/zm/shapes/Shape.java
rename to src/main/java/schule/ngb/zm/shapes/Shape.java
index 7d3d3cc..6e3866b 100644
--- a/src/schule/ngb/zm/shapes/Shape.java
+++ b/src/main/java/schule/ngb/zm/shapes/Shape.java
@@ -169,7 +169,7 @@ public abstract class Shape extends FilledShape {
* Unterklassen sollten diese Methode überschreiben, um weitere
* Eigenschaften zu kopieren (zum Beispiel den Radius eines Kreises). Mit
* dem Aufruf
- *
+ * clip.playAndWait();
+ * clip.dispose();
+ * super.copyFrom(shape) sollten die Basiseigenschaften
+ * {@code super.copyFrom(shape)} sollten die Basiseigenschaften
* kopiert werden.
*
* @param shape
@@ -222,7 +222,7 @@ public abstract class Shape extends FilledShape {
* {@code buff} nach oben verschoben.
*
* @param shape
- * @param anchor
+ * @param dir
* @param buff
*/
public void moveTo( Shape shape, Options.Direction dir, double buff ) {
diff --git a/src/schule/ngb/zm/shapes/ShapeGroup.java b/src/main/java/schule/ngb/zm/shapes/ShapeGroup.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/ShapeGroup.java
rename to src/main/java/schule/ngb/zm/shapes/ShapeGroup.java
diff --git a/src/schule/ngb/zm/shapes/ShapesLayer.java b/src/main/java/schule/ngb/zm/shapes/ShapesLayer.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/ShapesLayer.java
rename to src/main/java/schule/ngb/zm/shapes/ShapesLayer.java
diff --git a/src/schule/ngb/zm/shapes/StrokedShape.java b/src/main/java/schule/ngb/zm/shapes/StrokedShape.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/StrokedShape.java
rename to src/main/java/schule/ngb/zm/shapes/StrokedShape.java
diff --git a/src/schule/ngb/zm/shapes/Text.java b/src/main/java/schule/ngb/zm/shapes/Text.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Text.java
rename to src/main/java/schule/ngb/zm/shapes/Text.java
diff --git a/src/schule/ngb/zm/shapes/Triangle.java b/src/main/java/schule/ngb/zm/shapes/Triangle.java
similarity index 100%
rename from src/schule/ngb/zm/shapes/Triangle.java
rename to src/main/java/schule/ngb/zm/shapes/Triangle.java
diff --git a/src/schule/ngb/zm/tasks/DelayedTask.java b/src/main/java/schule/ngb/zm/tasks/DelayedTask.java
similarity index 100%
rename from src/schule/ngb/zm/tasks/DelayedTask.java
rename to src/main/java/schule/ngb/zm/tasks/DelayedTask.java
diff --git a/src/schule/ngb/zm/tasks/FrameSynchronizedTask.java b/src/main/java/schule/ngb/zm/tasks/FrameSynchronizedTask.java
similarity index 100%
rename from src/schule/ngb/zm/tasks/FrameSynchronizedTask.java
rename to src/main/java/schule/ngb/zm/tasks/FrameSynchronizedTask.java
diff --git a/src/schule/ngb/zm/tasks/FramerateLimitedTask.java b/src/main/java/schule/ngb/zm/tasks/FramerateLimitedTask.java
similarity index 100%
rename from src/schule/ngb/zm/tasks/FramerateLimitedTask.java
rename to src/main/java/schule/ngb/zm/tasks/FramerateLimitedTask.java
diff --git a/src/schule/ngb/zm/tasks/RateLimitedTask.java b/src/main/java/schule/ngb/zm/tasks/RateLimitedTask.java
similarity index 100%
rename from src/schule/ngb/zm/tasks/RateLimitedTask.java
rename to src/main/java/schule/ngb/zm/tasks/RateLimitedTask.java
diff --git a/src/schule/ngb/zm/tasks/Task.java b/src/main/java/schule/ngb/zm/tasks/Task.java
similarity index 100%
rename from src/schule/ngb/zm/tasks/Task.java
rename to src/main/java/schule/ngb/zm/tasks/Task.java
diff --git a/src/schule/ngb/zm/tasks/TaskRunner.java b/src/main/java/schule/ngb/zm/tasks/TaskRunner.java
similarity index 100%
rename from src/schule/ngb/zm/tasks/TaskRunner.java
rename to src/main/java/schule/ngb/zm/tasks/TaskRunner.java
diff --git a/src/schule/ngb/zm/turtle/TurtleLayer.java b/src/main/java/schule/ngb/zm/turtle/TurtleLayer.java
similarity index 100%
rename from src/schule/ngb/zm/turtle/TurtleLayer.java
rename to src/main/java/schule/ngb/zm/turtle/TurtleLayer.java
diff --git a/src/schule/ngb/zm/util/FontLoader.java b/src/main/java/schule/ngb/zm/util/FontLoader.java
similarity index 100%
rename from src/schule/ngb/zm/util/FontLoader.java
rename to src/main/java/schule/ngb/zm/util/FontLoader.java
diff --git a/src/schule/ngb/zm/util/ImageLoader.java b/src/main/java/schule/ngb/zm/util/ImageLoader.java
similarity index 99%
rename from src/schule/ngb/zm/util/ImageLoader.java
rename to src/main/java/schule/ngb/zm/util/ImageLoader.java
index 3bddbfb..4a7c5c9 100644
--- a/src/schule/ngb/zm/util/ImageLoader.java
+++ b/src/main/java/schule/ngb/zm/util/ImageLoader.java
@@ -47,7 +47,7 @@ public class ImageLoader {
/**
* Lädt ein Bild von der angegebenen Quelle source und gibt das
- * Bild zurück oder null, wenn das Bild nicht geladen werden
+ * Bild zurück oder {@code null}, wenn das Bild nicht geladen werden
* konnte. Ist ein Bild mit der angegebenen Quelle im Cache, wird das
* gespeicherte Bild zurückgegeben. Dies kann mit {@code caching = false}
* verhindert werden.
diff --git a/src/schule/ngb/zm/util/Log.java b/src/main/java/schule/ngb/zm/util/Log.java
similarity index 79%
rename from src/schule/ngb/zm/util/Log.java
rename to src/main/java/schule/ngb/zm/util/Log.java
index dd22f72..e7cac7f 100644
--- a/src/schule/ngb/zm/util/Log.java
+++ b/src/main/java/schule/ngb/zm/util/Log.java
@@ -1,5 +1,11 @@
package schule.ngb.zm.util;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.function.Supplier;
@@ -30,6 +36,10 @@ public final class Log {
private static final String ROOT_LOGGER = "schule.ngb.zm";
+ private static final String DEFAULT_LOG_FORMAT = "[%1$tT] [%4$11s] %5$s %6$s%n";
+
+ private static final String DEFAULT_DEBUG_FORMAT = "[%1$tT] [%4$11s] (%2$s) %5$s %6$s%n";
+
private static boolean LOGGING_INIT = false;
/**
@@ -43,7 +53,7 @@ public final class Log {
*
* @see #enableGlobalLevel(Level)
*/
- public static final void enableGlobalDebugging() {
+ public static void enableGlobalDebugging() {
enableGlobalLevel(ALL);
}
@@ -68,32 +78,18 @@ public final class Log {
* @param level Das Level, auf das alle {@code Logger} und {@code Handler}
* mindestens herabgesenkt werden sollen.
*/
- public static final void xenableGlobalLevel( Level level ) {
+ public static void enableGlobalLevel( Level level ) {
int lvl = Validator.requireNotNull(level).intValue();
+ ensureRootLoggerIntialized();
- Logger rootLogger = Logger.getLogger(ROOT_LOGGER);
- rootLogger.setLevel(level);
-
- for( Handler handler : rootLogger.getHandlers() ) {
- if( handler instanceof ConsoleHandler ) {
- Level handlerLevel = handler.getLevel();
- if( handlerLevel == null || handler.getLevel().intValue() > lvl ) {
- handler.setLevel(level);
- }
- }
- }
- }
-
- public static final void enableGlobalLevel( Level level ) {
- int lvl = Validator.requireNotNull(level).intValue();
-
- // Decrease level of root level ConsoleHandlers for outpu
+ // Decrease level of root level ConsoleHandlers for output
Logger rootLogger = Logger.getLogger("");
for( Handler handler : rootLogger.getHandlers() ) {
if( handler instanceof ConsoleHandler ) {
Level handlerLevel = handler.getLevel();
if( handlerLevel == null || handler.getLevel().intValue() > lvl ) {
handler.setLevel(level);
+ handler.setFormatter(new LogFormatter());
}
}
}
@@ -117,16 +113,42 @@ public final class Log {
}
}
}
+
+ Logger LOG = Logger.getLogger(ROOT_LOGGER);
+ LOG.fine("Debug logging enabled.");
}
- public static final Log getLogger( Class> clazz ) {
- if( !LOGGING_INIT ) {
- Logger.getLogger(ROOT_LOGGER);
- LOGGING_INIT = true;
- }
+ public static Log getLogger( Class> clazz ) {
+ ensureRootLoggerIntialized();
return new Log(clazz);
}
+ private static void ensureRootLoggerIntialized() {
+ if( LOGGING_INIT ) {
+ return;
+ }
+
+ Logger rootLogger = Logger.getLogger(ROOT_LOGGER);
+ rootLogger.setLevel(Level.INFO);
+
+ if( System.getProperty("java.util.logging.SimpleFormatter.format") == null
+ && LogManager.getLogManager().getProperty("java.util.logging.SimpleFormatter.format") == null ) {
+ System.setProperty("java.util.logging.SimpleFormatter.format", DEFAULT_LOG_FORMAT);
+ rootLogger.addHandler(new StreamHandler(System.err, new LogFormatter()));
+ rootLogger.setUseParentHandlers(false);
+ }
+ if( rootLogger.getUseParentHandlers() ) {
+ // This logger was not configured somewhere else
+ // Add a Handler and Formatter
+
+ //StreamHandler rootHandler = new StreamHandler(System.out, new SimpleFormatter());
+ //rootLogger.addHandler(rootHandler);
+ //rootLogger.setUseParentHandlers(false);
+ }
+
+ LOGGING_INIT = true;
+ }
+
private final Logger LOGGER;
private final Class> sourceClass;
@@ -315,4 +337,40 @@ public final class Log {
this.log(FINER, throwable, msgSupplier);
}
+ static final class LogFormatter extends Formatter {
+
+ @Override
+ public String format( LogRecord record ) {
+ ZonedDateTime zdt = ZonedDateTime.ofInstant(
+ record.getInstant(), ZoneId.systemDefault());
+ String source;
+ if (record.getSourceClassName() != null) {
+ source = record.getSourceClassName();
+ if (record.getSourceMethodName() != null) {
+ source += " " + record.getSourceMethodName();
+ }
+ } else {
+ source = record.getLoggerName();
+ }
+ String message = formatMessage(record);
+ String throwable = "";
+ if (record.getThrown() != null) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ pw.println();
+ record.getThrown().printStackTrace(pw);
+ pw.close();
+ throwable = sw.toString();
+ }
+ return String.format(DEFAULT_LOG_FORMAT,
+ zdt,
+ source,
+ record.getLoggerName(),
+ record.getLevel().getLocalizedName(),
+ message,
+ throwable);
+ }
+
+ }
+
}
diff --git a/src/schule/ngb/zm/util/ResourceStreamProvider.java b/src/main/java/schule/ngb/zm/util/ResourceStreamProvider.java
similarity index 100%
rename from src/schule/ngb/zm/util/ResourceStreamProvider.java
rename to src/main/java/schule/ngb/zm/util/ResourceStreamProvider.java
diff --git a/src/schule/ngb/zm/util/Validator.java b/src/main/java/schule/ngb/zm/util/Validator.java
similarity index 100%
rename from src/schule/ngb/zm/util/Validator.java
rename to src/main/java/schule/ngb/zm/util/Validator.java
diff --git a/res/Icon.afdesign b/src/main/resources/Icon.afdesign
similarity index 100%
rename from res/Icon.afdesign
rename to src/main/resources/Icon.afdesign
diff --git a/res/icon_128.png b/src/main/resources/icon_128.png
similarity index 100%
rename from res/icon_128.png
rename to src/main/resources/icon_128.png
diff --git a/res/icon_32.png b/src/main/resources/icon_32.png
similarity index 100%
rename from res/icon_32.png
rename to src/main/resources/icon_32.png
diff --git a/res/icon_512.png b/src/main/resources/icon_512.png
similarity index 100%
rename from res/icon_512.png
rename to src/main/resources/icon_512.png
diff --git a/res/icon_64.png b/src/main/resources/icon_64.png
similarity index 100%
rename from res/icon_64.png
rename to src/main/resources/icon_64.png
diff --git a/src/schule/ngb/zm/media/Sound.java b/src/schule/ngb/zm/media/Sound.java
deleted file mode 100644
index ceda917..0000000
--- a/src/schule/ngb/zm/media/Sound.java
+++ /dev/null
@@ -1,204 +0,0 @@
-package schule.ngb.zm.media;
-
-import schule.ngb.zm.util.ResourceStreamProvider;
-
-import javax.sound.sampled.*;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.logging.Logger;
-
-public class Sound implements Audio {
-
- private boolean playing = false;
-
- private boolean looping = false;
-
- private String audioSource;
-
- private Clip audioClip;
-
- private boolean disposeAfterPlay = false;
-
- private float volume = 0.8f;
-
- public Sound( String source ) {
- this.audioSource = source;
- }
-
- public boolean isPlaying() {
- // return audioClip != null && audioClip.isRunning();
- return playing;
- }
-
- public boolean isLooping() {
- if( !playing ) {
- looping = false;
- }
- return looping;
- }
-
- public void setVolume( double volume ) {
- this.volume = (float) volume;
- if( audioClip != null ) {
- applyVolume();
- }
- }
-
- private void applyVolume() {
- FloatControl gainControl =
- (FloatControl) audioClip.getControl(FloatControl.Type.MASTER_GAIN);
-
- float vol = 20f * (float) Math.log10(volume);
- // vol = (float) Constants.limit(vol, gainControl.getMinimum(), gainControl.getMaximum());
- gainControl.setValue(vol);
- }
-
- public void stop() {
- looping = false;
- if( audioClip.isRunning() ) {
- audioClip.stop();
- }
- playing = false;
- }
-
- public void play() {
- if( this.openClip() ) {
- audioClip.start();
- playing = true;
- }
- }
-
- public void playOnce() {
- disposeAfterPlay = true;
- play();
- }
-
- public void playOnceAndWait() {
- disposeAfterPlay = true;
- playAndWait();
- }
-
- public void playAndWait() {
- this.play();
-
- long audioLen = audioClip.getMicrosecondLength();
- while( playing ) {
- try {
- long ms = (audioLen - audioClip.getMicrosecondPosition()) / 1000L;
- Thread.sleep(ms);
- } catch( InterruptedException ex ) {
- // Ignore
- }
- }
-
- audioClip.close();
- }
-
- public void loop() {
- loop(Clip.LOOP_CONTINUOUSLY);
- }
-
- public void loop( int count ) {
- int loopCount = count;
- if( loopCount != Clip.LOOP_CONTINUOUSLY ) {
- if( loopCount <= 0 ) {
- return;
- }
- // Adjust Number of loops
- loopCount -= 1;
- }
- if( openClip() ) {
- looping = true;
- audioClip.loop(loopCount);
- playing = true;
- }
- }
-
- public void dispose() {
- if( audioClip != null ) {
- if( audioClip.isRunning() ) {
- audioClip.stop();
- }
- audioClip.close();
- audioClip = null;
- }
- }
-
- private boolean openClip() {
- if( audioClip != null ) {
- audioClip.setFramePosition(0);
- return true;
- }
-
- try {
- InputStream in = ResourceStreamProvider.getResourceStream(audioSource);
- if( in != null ) {
- AudioInputStream audioStream = AudioSystem.getAudioInputStream(in);
- AudioFormat format = audioStream.getFormat();
- DataLine.Info info = new DataLine.Info(Clip.class, format);
-
- audioClip = (Clip) AudioSystem.getLine(info);
- audioClip.addLineListener(new LineListener() {
- @Override
- public void update( LineEvent event ) {
- if( event.getType() == LineEvent.Type.STOP ) {
- playbackStopped();
- }
- }
- });
- audioClip.open(audioStream);
- applyVolume();
- return true;
- } else {
- LOGGER.warning("Sound source " + audioSource + " could not be played: No audio source found.");
- }
- } catch( UnsupportedAudioFileException ex ) {
- LOGGER.warning("Sound source " + audioSource + " could not be played: The specified audio file is not supported.");
- LOGGER.throwing("Sound", "openClip", ex);
- } catch( LineUnavailableException ex ) {
- LOGGER.warning("Sound source " + audioSource + " could not be played: Audio line for playing back is unavailable.");
- LOGGER.throwing("Sound", "openClip", ex);
- } catch( IOException ex ) {
- LOGGER.warning("Sound source " + audioSource + " could not be played: Error playing the audio file.");
- LOGGER.throwing("Sound", "openClip", ex);
- }
- return false;
- }
-
- /*@Override
- public void update( LineEvent event ) {
- LineEvent.Type type = event.getType();
-
- if( type == LineEvent.Type.START ) {
- playing = true;
- } else if( type == LineEvent.Type.STOP ) {
- playing = false;
- if( disposeAfterPlay ) {
- this.dispose();
- disposeAfterPlay = false;
- }
- }
- }*/
-
- private void playbackStopped() {
- playing = false;
- if( disposeAfterPlay ) {
- this.dispose();
- disposeAfterPlay = false;
- }
- }
-
- /*
- public void addLineListener( LineListener listener ) {
- if( audioClip == null ) {
- openClip();
- }
- if( audioClip != null ) {
- audioClip.addLineListener(listener);
- }
- }
- */
-
- private static final Logger LOGGER = Logger.getLogger(Sound.class.getName());
-
-}
diff --git a/test/src/schule/ngb/zm/TestColor.java b/src/test/java/schule/ngb/zm/ColorTest.java
similarity index 51%
rename from test/src/schule/ngb/zm/TestColor.java
rename to src/test/java/schule/ngb/zm/ColorTest.java
index a27ca89..58f2ec3 100644
--- a/test/src/schule/ngb/zm/TestColor.java
+++ b/src/test/java/schule/ngb/zm/ColorTest.java
@@ -1,13 +1,13 @@
package schule.ngb.zm;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
-public class TestColor {
+class ColorTest {
@Test
- public void colors() {
+ public void init() {
Color c;
c = new Color();
@@ -39,7 +39,7 @@ public class TestColor {
}
@Test
- public void parseColors() {
+ public void parse() {
Color c;
c = Color.getRGBColor(0x00FF00FF);
@@ -86,7 +86,74 @@ public class TestColor {
}
@Test
- public void hsl() {
+ void testEquals() {
+ Color c1 = new Color(254, 98, 12);
+ Color c2 = new Color(254, 98, 12);
+ assertNotSame(c1, c2);
+ assertEquals(c1, c2);
+ assertEquals(c1, c1);
+ c2 = new Color(c1);
+ assertNotSame(c1, c2);
+ assertEquals(c1, c2);
+
+ Color yellow = new Color(255, 255, 0);
+ assertEquals(java.awt.Color.YELLOW, yellow);
+ assertNotEquals(java.awt.Color.YELLOW, Color.YELLOW);
+ }
+
+ @Test
+ void construct() {
+ Color c;
+ // Empty color is white
+ c = new Color();
+ assertEquals(Color.BLACK, c);
+ // One arg is shade of gray
+ c = new Color(255);
+ assertEquals(Color.WHITE, c);
+ c = new Color(0);
+ assertEquals(Color.BLACK, c);
+ c = new Color(64);
+ assertEquals(Color.DARKGRAY, c);
+ c = new Color(192);
+ assertEquals(Color.LIGHTGRAY, c);
+ // RGB colors
+ c = new Color(0,0,0);
+ assertEquals(Color.BLACK, c);
+ c = new Color(255,0,0);
+ assertEquals(java.awt.Color.RED, c.getJavaColor());
+ c = new Color(0,255,0);
+ assertEquals(java.awt.Color.GREEN, c.getJavaColor());
+ c = new Color(0,0,255);
+ assertEquals(java.awt.Color.BLUE, c.getJavaColor());
+ // From java.awt.Color
+ c = new Color(java.awt.Color.YELLOW);
+ assertEquals(java.awt.Color.YELLOW, c.getJavaColor());
+ }
+
+ @Test
+ void getRGBColor() {
+ Color c1 = Color.getRGBColor(0xFFFF0000);
+ assertEquals(Color.RED, c1);
+ }
+
+ @Test
+ void getHSBColor() {
+ }
+
+ @Test
+ void getHSLColor() {
+ }
+
+ @Test
+ void parseHexcode() {
+ }
+
+ @Test
+ void morph() {
+ }
+
+ @Test
+ void RGBtoHSL() {
Color c;
float[] hsl;
@@ -101,45 +168,60 @@ public class TestColor {
assertEquals(.5647f, hsl[2], .0001f);
}
- public static void main( String[] args ) {
- new ColorPalette();
+ @Test
+ void HSLtoRGB() {
}
- static class ColorPalette extends Zeichenmaschine {
- public static final int SIZE = 10, COLORS = 16;
+ @Test
+ void testHSLtoRGB() {
+ }
- public void setup() {
- setSize(SIZE*COLORS, SIZE*COLORS);
- setTitle("Colors");
+ @Test
+ void copy() {
+ }
- drawing.noStroke();
- drawing.setAnchor(NORTHWEST);
+ @Test
+ void getRGBA() {
+ Color yellow = new Color(255, 255, 0);
+ assertEquals(java.awt.Color.YELLOW.getRGB(), Color.YELLOW.getRGBA());
+ }
- int steps = (int) (255.0/COLORS);
- Color c;
- for( int i = 0; i < COLORS; i++ ) {
- for( int j = 0; j < COLORS; j++ ) {
- c = new Color(i * steps, j * steps, (i+j)/2 * steps);
- drawing.setFillColor(c);
- drawing.rect(i*SIZE, j*SIZE, SIZE, SIZE);
- }
- }
- }
+ @Test
+ void getRed() {
+ }
- public void draw() {
- Color c = Color.HGGREEN;
- drawing.setFillColor(c);
- drawing.rect(0, 0, width/2, height);
+ @Test
+ void getGreen() {
+ }
- for( int p = 10; p < 100; p += 10 ) {
- drawing.setFillColor(c.brighter(p));
- drawing.rect(width / 2, 0, width / 2, height / 2);
- drawing.setFillColor(c.darker(p));
- drawing.rect(width / 2, height / 2, width / 2, height / 2);
+ @Test
+ void getBlue() {
+ }
- delay(1000);
- }
- }
+ @Test
+ void getAlpha() {
+ }
+
+ @Test
+ void getJavaColor() {
+ assertEquals(java.awt.Color.YELLOW, Color.YELLOW.getJavaColor());
+ assertEquals(new java.awt.Color(255, 31, 124), new Color(255, 31, 124).getJavaColor());
+ }
+
+ @Test
+ void brighter() {
+ }
+
+ @Test
+ void testBrighter() {
+ }
+
+ @Test
+ void darker() {
+ }
+
+ @Test
+ void testDarker() {
}
}
diff --git a/test/src/schule/ngb/zm/ConstantsTest.java b/src/test/java/schule/ngb/zm/ConstantsTest.java
similarity index 100%
rename from test/src/schule/ngb/zm/ConstantsTest.java
rename to src/test/java/schule/ngb/zm/ConstantsTest.java
diff --git a/test/src/schule/ngb/zm/OptionsTest.java b/src/test/java/schule/ngb/zm/OptionsTest.java
similarity index 100%
rename from test/src/schule/ngb/zm/OptionsTest.java
rename to src/test/java/schule/ngb/zm/OptionsTest.java
diff --git a/test/src/schule/ngb/zm/TestAttraction.java b/src/test/java/schule/ngb/zm/TestAttraction.java
similarity index 100%
rename from test/src/schule/ngb/zm/TestAttraction.java
rename to src/test/java/schule/ngb/zm/TestAttraction.java
diff --git a/test/src/schule/ngb/zm/TestDrawing.java b/src/test/java/schule/ngb/zm/TestDrawing.java
similarity index 100%
rename from test/src/schule/ngb/zm/TestDrawing.java
rename to src/test/java/schule/ngb/zm/TestDrawing.java
diff --git a/test/src/schule/ngb/zm/TestInput.java b/src/test/java/schule/ngb/zm/TestInput.java
similarity index 100%
rename from test/src/schule/ngb/zm/TestInput.java
rename to src/test/java/schule/ngb/zm/TestInput.java
diff --git a/test/src/schule/ngb/zm/TestShapes.java b/src/test/java/schule/ngb/zm/TestShapes.java
similarity index 100%
rename from test/src/schule/ngb/zm/TestShapes.java
rename to src/test/java/schule/ngb/zm/TestShapes.java
diff --git a/test/src/schule/ngb/zm/TestTurtle.java b/src/test/java/schule/ngb/zm/TestTurtle.java
similarity index 100%
rename from test/src/schule/ngb/zm/TestTurtle.java
rename to src/test/java/schule/ngb/zm/TestTurtle.java
diff --git a/test/src/schule/ngb/zm/VectorTest.java b/src/test/java/schule/ngb/zm/VectorTest.java
similarity index 58%
rename from test/src/schule/ngb/zm/VectorTest.java
rename to src/test/java/schule/ngb/zm/VectorTest.java
index d8ec662..d8608db 100644
--- a/test/src/schule/ngb/zm/VectorTest.java
+++ b/src/test/java/schule/ngb/zm/VectorTest.java
@@ -2,10 +2,79 @@ package schule.ngb.zm;
import org.junit.jupiter.api.Test;
+import java.awt.geom.Point2D;
+
import static org.junit.jupiter.api.Assertions.*;
class VectorTest {
+ @Test
+ public void init() {
+ Vector vec;
+
+ vec = new Vector();
+ assertEquals(0.0, vec.x, 0.0);
+ assertEquals(0.0, vec.y, 0.0);
+
+ vec = new Vector(4.5, 5.1);
+ assertEquals(4.5, vec.x, 0.0);
+ assertEquals(5.1, vec.y, 0.0);
+
+ vec = new Vector(vec);
+ assertEquals(4.5, vec.x, 0.0);
+ assertEquals(5.1, vec.y, 0.0);
+
+ vec = new Vector(Double.MAX_VALUE, Double.MIN_VALUE);
+ assertEquals(Double.MAX_VALUE, vec.x, 0.0);
+ assertEquals(Double.MIN_VALUE, vec.y, 0.0);
+
+ vec = new Vector(new Point2D.Double(7.2, 3.5677));
+ assertEquals(7.2, vec.x, 0.0);
+ assertEquals(3.5677, vec.y, 0.0);
+
+ vec = new Vector(new Point2D.Float(7.2f, 3.5677f));
+ assertEquals(7.2, vec.x, 0.0001);
+ assertEquals(3.5677, vec.y, 0.0001);
+
+ vec.set(new Vector(5.1, 8.9));
+ assertEquals(5.1, vec.x, 0.0001);
+ assertEquals(8.9, vec.y, 0.0001);
+
+ Vector vec2 = vec.copy();
+ assertNotSame(vec, vec2);
+ assertEquals(vec, vec2);
+ assertEquals(5.1, vec2.x, 0.0001);
+ assertEquals(8.9, vec2.y, 0.0001);
+
+ assertEquals(vec, vec.set(100, 100));
+ assertEquals(200, vec.set(200, 200).x, 0.0001);
+ assertEquals(8.9, vec.set(vec2).y, 0.0001);
+
+ for( int i = 0; i < 50; i++ ) {
+ vec = Vector.random();
+ assertTrue(vec.x >= 0);
+ assertTrue(vec.y >= 0);
+ assertTrue(vec.x < 100);
+ assertTrue(vec.y < 100);
+ }
+
+ for( int i = 1; i < 50; i++ ) {
+ vec = Vector.random(0, i*10);
+ assertTrue(vec.x >= 0);
+ assertTrue(vec.y >= 0);
+ assertTrue(vec.x < i*10);
+ assertTrue(vec.y < i*10);
+ }
+
+ for( int i = 1; i < 50; i++ ) {
+ vec = Vector.random(0, i*10, (i+1)*10, (i+2)*10);
+ assertTrue(vec.x >= 0);
+ assertTrue(vec.y >= (i+1)*10);
+ assertTrue(vec.x < i*10);
+ assertTrue(vec.y < (i+2)*10);
+ }
+ }
+
@Test
void setLength() {
Vector vec = new Vector(2.0, 5.4);
diff --git a/test/src/schule/ngb/zm/shapes/ShapeTest.java b/src/test/java/schule/ngb/zm/shapes/ShapeTest.java
similarity index 100%
rename from test/src/schule/ngb/zm/shapes/ShapeTest.java
rename to src/test/java/schule/ngb/zm/shapes/ShapeTest.java
diff --git a/test/src/schule/ngb/zm/util/ValidatorTest.java b/src/test/java/schule/ngb/zm/util/ValidatorTest.java
similarity index 100%
rename from test/src/schule/ngb/zm/util/ValidatorTest.java
rename to src/test/java/schule/ngb/zm/util/ValidatorTest.java
diff --git a/test/res/WitchCraftIcons_122_t.PNG b/src/test/resources/WitchCraftIcons_122_t.PNG
similarity index 100%
rename from test/res/WitchCraftIcons_122_t.PNG
rename to src/test/resources/WitchCraftIcons_122_t.PNG
diff --git a/test/res/bluej-project/BluejTest.java b/test/res/bluej-project/BluejTest.java
deleted file mode 100644
index 24948c6..0000000
--- a/test/res/bluej-project/BluejTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-
-import schule.ngb.zm.Zeichenmaschine;
-
-import java.util.List;
-
-/**
- * Beschreiben Sie hier die Klasse BluejTest.
- *
- * @author (Ihr Name)
- * @version (eine Versionsnummer oder ein Datum)
- */
-public class BluejTest extends Zeichenmaschine
-{
- public void setup() {
- if( !IN_BLUEJ ) {
- drawing.clear(schule.ngb.zm.Color.HGRED);
- } else {
- drawing.clear(schule.ngb.zm.Color.HGGREEN);
-
- }
- }
-
- public void listClasses() {
- // find all classes in classpath
- List