Initial commit: Android App für Music Separation

This commit is contained in:
Lukas Ringeisen 2025-09-05 14:42:59 +02:00
commit ed32891202
73 changed files with 3331 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
app/src/main/assets/*.tflite filter=lfs diff=lfs merge=lfs -text

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

6
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

18
.idea/deploymentTargetSelector.xml generated Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-05-22T09:38:46.863315900Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Lukas\.android\avd\More_Ram_API_35.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

20
.idea/gradle.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

10
.idea/migrations.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

10
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

17
.idea/runConfigurations.xml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

55
app/build.gradle.kts Normal file
View File

@ -0,0 +1,55 @@
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "com.example.musicseparation"
compileSdk = 34
defaultConfig {
applicationId = "com.example.musicseparation"
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)
implementation(libs.navigation.fragment)
implementation(libs.navigation.ui)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
implementation("org.tensorflow:tensorflow-lite:2.15.0")
implementation("org.tensorflow:tensorflow-lite-select-tf-ops:2.15.0")
implementation("org.tensorflow:tensorflow-lite-support:0.4.0")
implementation("org.tensorflow:tensorflow-lite-metadata:0.4.0")
implementation("org.tensorflow:tensorflow-lite-gpu:2.9.0")
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,26 @@
package com.example.musicseparation;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.example.musicseparation", appContext.getPackageName());
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MusicSeparation"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:configChanges="orientation|screenSize"
android:screenOrientation="portrait"
android:theme="@style/Theme.MusicSeparation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SeparationActivity"
android:configChanges="orientation|screenSize"
android:screenOrientation="portrait"/>
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
</manifest>

BIN
app/src/main/assets/spleeter_2stems_optimized.tflite (Stored with Git LFS) Normal file

Binary file not shown.

BIN
app/src/main/assets/spleeter_4stems_optimized.tflite (Stored with Git LFS) Normal file

Binary file not shown.

BIN
app/src/main/assets/spleeter_5stems_optimized.tflite (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,199 @@
package com.example.musicseparation;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
private Button buttonLoadAudio;
private Button buttonStartSeparation;
//private EditText editTextAudioInfo;
private RadioGroup radioGroupStems;
private RadioGroup radioGroupChunks;
private WavFileLoader wavFileLoader;
private TextView textViewInfo;
private WaveformView waveformView;
private Uri selectedAudioUri;
private SeekBar seekBar;
private Handler handler = new Handler();
private WavFileLoader.WavData wavData;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("MainActivity", "onCreate called");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialisierung der UI-Elemente
buttonLoadAudio = findViewById(R.id.buttonLoadAudio);
buttonStartSeparation = findViewById(R.id.buttonStartSeparation);
//editTextAudioInfo = findViewById(R.id.editTextAudioInfo);
radioGroupStems = findViewById(R.id.radioGroupStems);
radioGroupChunks = findViewById(R.id.radioGroupChunks);
// Initialisierung des WavFileLoaders
wavFileLoader = new WavFileLoader();
// Initialisierung der WaveformView-Elemente
Button buttonPlay1 = findViewById(R.id.buttonPlay);
Button buttonStop1 = findViewById(R.id.buttonStop);
textViewInfo = findViewById(R.id.textViewInfo);
waveformView = findViewById(R.id.waveformOriginal);
seekBar = findViewById(R.id.seekBar);
setupSeekBar(seekBar, waveformView);
// Registrierung des ActivityResultLauncher
ActivityResultLauncher<Intent> filePickerLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
Log.d("MainActivity", "ActivityResultLauncher called");
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
Uri uri = result.getData().getData();
try {
getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.d("MainActivity", "Uri: " + uri);
wavData = wavFileLoader.loadWavFile(this, uri);
//float[] normalizedSamples = normalizeSamples(wavData.audioData);
float[] normalizedSamples = wavData.audioData;
selectedAudioUri = uri;
waveformView.setAudioUri(uri);
waveformView.setSamples(normalizedSamples);
textViewInfo.setText("Länge: " + wavData.audioData.length + " Samples\nSamplerate: " + wavData.sampleRate + " Hz");
} catch (IOException e) {
textViewInfo.setText("Fehler beim Laden: " + e.getMessage());
}
}
});
buttonLoadAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("MainActivity", "buttonLoad1 clicked");
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("audio/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
filePickerLauncher.launch(intent);
}
});
buttonPlay1.setOnClickListener(v -> {
Log.d("MainActivity", "buttonPlay1 clicked");
if (selectedAudioUri != null) {
waveformView.playAudio(this);
}
});
buttonStop1.setOnClickListener(v -> waveformView.stopAudio());
buttonStartSeparation.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("MainActivity", "Separation Button clicked");
Intent intent = new Intent(MainActivity.this, SeparationActivity.class);
// WavData und Stem-Option übergeben
//intent.putExtra("audioData", wavData.audioData);
//intent.putExtra("sampleRate", wavData.sampleRate);
//intent.putExtra("audioUri", selectedAudioUri);
WavDataHolder.setWavData(wavData);
WavDataHolder.setAudioUri(selectedAudioUri);
if (radioGroupStems.getCheckedRadioButtonId() == R.id.radioButton2Stems) {
intent.putExtra("stemOption", 2);
} else if (radioGroupStems.getCheckedRadioButtonId() == R.id.radioButton4Stems) {
intent.putExtra("stemOption", 4);
} else if (radioGroupStems.getCheckedRadioButtonId() == R.id.radioButton5Stems) {
intent.putExtra("stemOption", 5);
}
if(radioGroupChunks.getCheckedRadioButtonId() == R.id.radioButton1){
intent.putExtra("chunkSize", 220500);
}else if(radioGroupChunks.getCheckedRadioButtonId() == R.id.radioButton2){
intent.putExtra("chunkSize", 441000);
}else if(radioGroupChunks.getCheckedRadioButtonId() == R.id.radioButton3){
intent.putExtra("chunkSize", 882000);
}else if(radioGroupChunks.getCheckedRadioButtonId() == R.id.radioButton4){
intent.putExtra("chunkSize", 1764000);
}else if(radioGroupChunks.getCheckedRadioButtonId() == R.id.radioButton5){
intent.putExtra("chunkSize", 3528000);
}
// selectedStemOption = (radioGroupStems.getCheckedRadioButtonId() == R.id.radioButton2Stems) ? 2 : 4;
//intent.putExtra("stemOption", selectedStemOption);
startActivity(intent);
}
});
}
@Override
protected void onPause() {
super.onPause();
waveformView.stopAudio();
}
private void setupSeekBar(SeekBar seekBar, WaveformView waveformView) {
seekBar.setMax(1000);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
boolean touching = false;
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser && waveformView != null && waveformView.isPlaying()) {
int duration = waveformView.getDuration();
int position = (int) (progress / 1000f * duration);
waveformView.seekTo(position);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
touching = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
touching = false;
}
});
Runnable updater = new Runnable() {
@Override
public void run() {
if (waveformView != null && waveformView.isPlaying()) {
int duration = waveformView.getDuration();
int current = waveformView.getCurrentPosition();
int progress = (int) (current / (float) duration * 1000);
seekBar.setProgress(progress);
waveformView.updatePlaybackPosition(current);
}
handler.postDelayed(this, 200);
}
};
handler.post(updater);
}
}

View File

@ -0,0 +1,805 @@
package com.example.musicseparation;
import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.example.musicseparation.WaveformView;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SeparationActivity extends AppCompatActivity {
private float[] audioData;
private int sampleRate;
private int stemOption;
private int chunkoption;
private Uri audioUri;
private TextView textViewProgress;
private List<WaveformView> waveformViews = new ArrayList<>();
private List<Button> playButtons = new ArrayList<>();
private List<Button> stopButtons = new ArrayList<>();
private List<TextView> infoTexts = new ArrayList<>();
private List<SeekBar> seekBars = new ArrayList<>();
private Handler handler = new Handler();
private String timestamp;
WavFileLoader wavFileLoader;
WavFileLoader.WavData wavData;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("SeparationActivity", "onCreate called");
super.onCreate(savedInstanceState);
//System.out.println("SeparationActivity onCreate() called");
stemOption = getIntent().getIntExtra("stemOption", 2);
if (stemOption == 2) {
Log.d("SeparationActivity", "onCreate called with stemOption 2");
setContentView(R.layout.activity_separation_2stems);
} else if (stemOption == 4) {
Log.d("SeparationActivity", "onCreate called with stemOption 4");
setContentView(R.layout.activity_separation_4stems);
} else if (stemOption == 5) {
Log.d("SeparationActivity", "onCreate called with stemOption 5");
setContentView(R.layout.activity_separation_5stems);
}
// Get Data from MainActivity
chunkoption = getIntent().getIntExtra("chunkSize", 882000);
WavFileLoader.WavData wavData = WavDataHolder.getWavData();
if (wavData != null) {
audioData = wavData.audioData;
sampleRate = wavData.sampleRate;
audioUri = WavDataHolder.getAudioUri();
// Weiterarbeiten...
} else {
// Fehlerbehandlung (z.B. Fehlermeldung oder Activity schließen)
}
//audioData = getIntent().getFloatArrayExtra("audioData");
//sampleRate = getIntent().getIntExtra("sampleRate", 44100);
// Initialisierung der UI-Elemente
textViewProgress = findViewById(R.id.textViewProgress);
// Jetzt dynamisch alle Sets holen
setupWaveformElements();
setupButtonListeners();
//TextView infoText = findViewById(R.id.textViewInfo0);
//infoTexts.get(0).setText();
//WaveformView waveformView = findViewById(R.id.waveformView0);
//waveformView.setAudioUri(audioUri);
//waveformView.setSamples(audioData);
waveformViews.get(0).setSamples(audioData);
waveformViews.get(0).setAudioUri(audioUri);
infoTexts.get(0).setText("Pfad: " + audioUri.toString() + "\nLänge: " + wavData.audioData.length + " Samples\nSamplerate: " + wavData.sampleRate + " Hz");
//infoText.setText("Pfad: " + audioUri.toString() + "\nLänge: " + wavData.audioData.length + " Samples\nSamplerate: " + wavData.sampleRate + " Hz");
setupSeekBar(seekBars.get(0), waveformViews.get(0));
// Aufrufen der AsyncTask
if (stemOption == 2) {
new SeparationTask2Stems().execute();
} else if (stemOption == 4) {
new SeparationTask4Stems().execute();
} else if (stemOption == 5) {
new SeparationTask5Stems().execute();
}
}
@Override
protected void onPause() {
Log.d("SeparationActivity", "onPause called");
super.onPause();
stopAllAudioPlayback();
}
private void stopAllAudioPlayback() {
for (WaveformView view : waveformViews) {
if (view != null) {
view.stopAudio();
}
}
}
private void setupWaveformElements() {
Log.d("SeparationActivity", "setupWaveformElements() called");
//System.out.println("SeparationActivity.setupWaveformElements() called");
int numberOfElements = 1 + stemOption; // 1 Original + X Stems
for (int i = 0; i < numberOfElements; i++) {
// Waveform-Elemente suchen
int playButtonId = getResources().getIdentifier("buttonPlay" + i, "id", getPackageName());
int stopButtonId = getResources().getIdentifier("buttonStop" + i, "id", getPackageName());
int textViewInfoId = getResources().getIdentifier("textViewInfo" + i, "id", getPackageName());
int waveformViewId = getResources().getIdentifier("waveformView" + i, "id", getPackageName());
int seekBarId = getResources().getIdentifier("seekBar" + i, "id", getPackageName());
// Falls existieren
Button playButton = findViewById(playButtonId);
Button stopButton = findViewById(stopButtonId);
TextView infoText = findViewById(textViewInfoId);
WaveformView waveformView = findViewById(waveformViewId);
SeekBar seekBar = findViewById(seekBarId);
if (playButton != null && stopButton != null && infoText != null && waveformView != null && seekBar != null) {
playButtons.add(playButton);
stopButtons.add(stopButton);
infoTexts.add(infoText);
waveformViews.add(waveformView);
seekBars.add(seekBar);
}
}
}
private void setupButtonListeners() {
Log.d("SeparationActivity", "setupButtonListeners() called");
//System.out.println("SeparationActivity.setupButtonListeners() called");
for (int i = 0; i < playButtons.size(); i++) {
final int index = i;
playButtons.get(i).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Beispiel-Logik
System.out.println("Play Button " + index + " gedrückt");
waveformViews.get(index).playAudio(SeparationActivity.this);
}
});
stopButtons.get(i).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Beispiel-Logik
System.out.println("Stop Button " + index + " gedrückt");
waveformViews.get(index).stopAudio();
}
});
}
}
private void setupSeekBar(SeekBar seekBar, WaveformView waveformView) {
Log.d("SeparationActivity", "setupSeekBar() called");
seekBar.setMax(1000);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
boolean touching = false;
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser && waveformView != null && waveformView.isPlaying()) {
int duration = waveformView.getDuration();
int position = (int) (progress / 1000f * duration);
waveformView.seekTo(position);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
touching = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
touching = false;
}
});
Runnable updater = new Runnable() {
@Override
public void run() {
if (waveformView != null && waveformView.isPlaying()) {
int duration = waveformView.getDuration();
int current = waveformView.getCurrentPosition();
int progress = (int) (current / (float) duration * 1000);
seekBar.setProgress(progress);
waveformView.updatePlaybackPosition(current);
}
handler.postDelayed(this, 200);
}
};
handler.post(updater);
}
private class SeparationTask extends AsyncTask<Void, Integer, Void> {
@Override
protected void onPreExecute() {
//progressBar.setVisibility(View.VISIBLE);
textViewProgress.setVisibility(View.VISIBLE);
//progressBar.setProgress(0);
textViewProgress.setText("Separation startet...");
}
@Override
protected Void doInBackground(Void... voids) {
try {
Log.d("Debug", "doInBackground called!");
TFLiteModelProcessor modelProcessor = new TFLiteModelProcessor(SeparationActivity.this, "spleeter_2stems_optimized.tflite");
int chunkSize = 882000; // Falls nötig anpassen
int overlapSize = chunkSize / 2; // 50% Overlap
int stepSize = chunkSize - overlapSize; // Schrittweite für Overlap-Add
int numChunks = (int) Math.ceil((double) audioData.length / stepSize);
Log.d("Debug", "numChunks: " + numChunks + " von " + (audioData.length / stepSize) + ", Audiolänge: " + audioData.length);
File appDownloadDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "SeparatedAudio");
if (!appDownloadDir.exists()) {
appDownloadDir.mkdirs();
}
//File vocalsFile = new File(appDownloadDir, "vocals.wav");
File vocalsFile = new File(appDownloadDir, "vocals.wav");
File instrumentalsFile = new File(appDownloadDir, "instrumentals.wav");
FileOutputStream vocalsStream = new FileOutputStream(vocalsFile);
FileOutputStream instrumentalsStream = new FileOutputStream(instrumentalsFile);
//File vocalsFile = new File(getExternalFilesDir(null), "vocals.wav");
//File instrumentalsFile = new File(getExternalFilesDir(null), "instrumentals.wav");
//FileOutputStream vocalsStream = new FileOutputStream(vocalsFile);
//FileOutputStream instrumentalsStream = new FileOutputStream(instrumentalsFile);
writeEmptyWavHeader(vocalsStream);
writeEmptyWavHeader(instrumentalsStream);
Log.d("Debug", "WAV-Dateien geöffnet");
// **Letzte gespeicherte Overlap-Daten für die Glättung**
float[] prevVocalsOverlap = new float[overlapSize];
float[] prevInstrumentalsOverlap = new float[overlapSize];
for (int chunkIndex = 0; chunkIndex < numChunks; chunkIndex++) {
int start = chunkIndex * stepSize;
int end = Math.min(start + chunkSize, audioData.length);
Log.d("Debug", "chunkIndex: " + chunkIndex + " von " + numChunks);
Log.d("Debug", "start: " + start + ", end: " + end + ", audiolänge: " + audioData.length);
// **Chunk mit Overlap vorbereiten**
float[][] inputAudio = new float[2][chunkSize];
for (int i = start; i < end; i++) {
inputAudio[0][i - start] = audioData[i]; // Linker Kanal
inputAudio[1][i - start] = audioData[i]; // Rechter Kanal
}
for (int i = end - start; i < chunkSize; i++) {
inputAudio[0][i] = 0f;
inputAudio[1][i] = 0f;
}
Log.d("AudioSeparation", "inputAudio Length: " + inputAudio[0].length);
// **Model-Prozessierung**
float[][] separatedAudio = modelProcessor.processAudio(inputAudio);
float[] chunkVocals = separatedAudio[0];
float[] chunkInstrumentals = separatedAudio[1];
Log.d("AudioSeparation", "ChunkVocals Length: " + chunkVocals.length);
Log.d("AudioSeparation", "ChunkInstrumentals Length: " + chunkInstrumentals.length);
// **Overlap-Add für Übergänge**
applyOverlapAdd(prevVocalsOverlap, chunkVocals, overlapSize);
applyOverlapAdd(prevInstrumentalsOverlap, chunkInstrumentals, overlapSize);
// **Direktes Schreiben in WAV**
// Statt: writeAudioToStream(vocalsStream, chunkVocals, overlapSize);
int writeFrom = (chunkIndex == 0) ? 0 : overlapSize;
int validLength = (chunkIndex == numChunks - 1) ? (end - start) : chunkVocals.length;
writeAudioToStream(vocalsStream, chunkVocals, writeFrom, validLength);
writeAudioToStream(instrumentalsStream, chunkInstrumentals, writeFrom, validLength);
//writeAudioToStream(vocalsStream, chunkVocals, writeFrom);
//writeAudioToStream(instrumentalsStream, chunkInstrumentals, writeFrom);
//writeAudioToStream(vocalsStream, chunkVocals, overlapSize);
//writeAudioToStream(instrumentalsStream, chunkInstrumentals, overlapSize);
publishProgress((int) ((chunkIndex / (float) numChunks) * 100), chunkIndex, numChunks);
}
updateWavHeader(vocalsFile);
updateWavHeader(instrumentalsFile);
vocalsStream.close();
instrumentalsStream.close();
modelProcessor.close();
loadAndDisplayStems(new String[]{"vocals", "instrumentals"});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
//progressBar.setProgress(values[0]);
textViewProgress.setText("Verarbeite Chunk " + values[1] + " von " + values[2]);
}
@Override
protected void onPostExecute(Void aVoid) {
//progressBar.setVisibility(View.GONE);
textViewProgress.setText("Separation abgeschlossen!");
}
}
private class SeparationTask2Stems extends AsyncTask<Void, Integer, Void> {
@Override
protected void onPreExecute() {
textViewProgress.setVisibility(View.VISIBLE);
textViewProgress.setText("2-Stem Separation startet...");
}
@Override
protected Void doInBackground(Void... voids) {
try {
Log.d("SeparationActivity", "SeparationTask2Stems doInBackground called!");
TFLiteModelProcessor2Stems modelProcessor = new TFLiteModelProcessor2Stems(SeparationActivity.this, "spleeter_2stems_optimized.tflite");
int chunkSize = chunkoption;
int overlapSize = chunkSize / 2;
int stepSize = chunkSize - overlapSize;
int numChunks = (int) Math.ceil((double) audioData.length / stepSize);
timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File dir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "2stems_" + timestamp);
dir.mkdirs();
//File dir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "SeparatedAudio_2stems");
//dir.mkdirs();
File[] stemFiles = new File[] {
new File(dir, "vocals.wav"),
new File(dir, "instrumentals.wav")
};
FileOutputStream[] outputStreams = new FileOutputStream[2];
for (int i = 0; i < 2; i++) {
outputStreams[i] = new FileOutputStream(stemFiles[i]);
writeEmptyWavHeader(outputStreams[i]);
}
float[][] prevOverlap = new float[2][overlapSize];
for (int chunkIndex = 0; chunkIndex < numChunks; chunkIndex++) {
int start = chunkIndex * stepSize;
int end = Math.min(start + chunkSize, audioData.length);
float[][] input = new float[2][chunkSize];
for (int i = start; i < end; i++) {
input[0][i - start] = audioData[i];
input[1][i - start] = audioData[i];
}
Log.d("SeparationActivity", "modelProcessor about to start");
float[][] separated = modelProcessor.processAudio(input);
/*
int writeFrom = (chunkIndex == 0) ? 0 : overlapSize;
int validLength = (chunkIndex == numChunks - 1) ? (end - start) : separated[0].length;
for (int i = 0; i < 2; i++) {
applyOverlapAdd(prevOverlap[i], separated[i], overlapSize);
writeAudioToStream(outputStreams[i], separated[i], writeFrom, validLength);
}
*/
for (int i = 0; i < 2; i++) {
applyOverlapAdd(prevOverlap[i], separated[i], overlapSize);
int writeFrom = (chunkIndex == 0) ? 0 : overlapSize;
int validLength = (chunkIndex == numChunks - 1) ? (end - start) : separated[i].length;
writeAudioToStream(outputStreams[i], separated[i], writeFrom, validLength);
}
publishProgress((int) ((chunkIndex / (float) numChunks) * 100), chunkIndex, numChunks);
}
for (int i = 0; i < 2; i++) {
outputStreams[i].close();
updateWavHeader(stemFiles[i]);
}
modelProcessor.close();
//loadAndDisplayStems(new String[]{"vocals", "instrumentals"});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
textViewProgress.setText("Chunk " + values[1] + " von " + values[2]);
}
@Override
protected void onPostExecute(Void aVoid) {
textViewProgress.setText("2-Stem Separation abgeschlossen!");
loadAndDisplayStems(new String[]{"vocals", "instrumentals"});
}
}
private class SeparationTask4Stems extends AsyncTask<Void, Integer, Void> {
@Override
protected void onPreExecute() {
textViewProgress.setVisibility(View.VISIBLE);
textViewProgress.setText("4-Stem Separation startet...");
}
@Override
protected Void doInBackground(Void... voids) {
try {
Log.d("SeparationActivity", "SeparationTask4Stems doInBackground called!");
TFLiteModelProcessor4Stems modelProcessor = new TFLiteModelProcessor4Stems(SeparationActivity.this, "spleeter_4stems_optimized.tflite");
int chunkSize = chunkoption;
int overlapSize = chunkSize / 2;
int stepSize = chunkSize - overlapSize;
int numChunks = (int) Math.ceil((double) audioData.length / stepSize);
timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File dir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "4stems_" + timestamp);
dir.mkdirs();
//File dir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "SeparatedAudio_4stems");
//dir.mkdirs();
File[] stemFiles = new File[] {
new File(dir, "vocals.wav"),
new File(dir, "drums.wav"),
new File(dir, "bass.wav"),
new File(dir, "other.wav")
};
FileOutputStream[] outputStreams = new FileOutputStream[4];
for (int i = 0; i < 4; i++) {
outputStreams[i] = new FileOutputStream(stemFiles[i]);
writeEmptyWavHeader(outputStreams[i]);
}
float[][] prevOverlap = new float[4][overlapSize];
for (int chunkIndex = 0; chunkIndex < numChunks; chunkIndex++) {
int start = chunkIndex * stepSize;
int end = Math.min(start + chunkSize, audioData.length);
float[][] input = new float[2][chunkSize];
for (int i = start; i < end; i++) {
input[0][i - start] = audioData[i];
input[1][i - start] = audioData[i];
}
Log.d("SeparationActivity", "modelProcessor about to start");
float[][] separated = modelProcessor.processAudio(input);
for (int i = 0; i < 4; i++) {
applyOverlapAdd(prevOverlap[i], separated[i], overlapSize);
int writeFrom = (chunkIndex == 0) ? 0 : overlapSize;
int validLength = (chunkIndex == numChunks - 1) ? (end - start) : separated[i].length;
writeAudioToStream(outputStreams[i], separated[i], writeFrom, validLength);
//writeAudioToStream(outputStreams[i], separated[i], writeFrom);
//writeAudioToStream(outputStreams[i], separated[i], overlapSize);
}
publishProgress((int) ((chunkIndex / (float) numChunks) * 100), chunkIndex, numChunks);
}
for (int i = 0; i < 4; i++) {
outputStreams[i].close();
updateWavHeader(stemFiles[i]);
}
modelProcessor.close();
//loadAndDisplayStems(new String[]{"vocals", "drums", "bass", "other"});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
textViewProgress.setText("Chunk " + values[1] + " von " + values[2]);
}
@Override
protected void onPostExecute(Void aVoid) {
textViewProgress.setText("4-Stem Separation abgeschlossen!");
loadAndDisplayStems(new String[]{"vocals", "drums", "bass", "other"});
}
}
private class SeparationTask5Stems extends AsyncTask<Void, Integer, Void> {
@Override
protected void onPreExecute() {
textViewProgress.setVisibility(View.VISIBLE);
textViewProgress.setText("5-Stem Separation startet...");
}
@Override
protected Void doInBackground(Void... voids) {
try {
Log.d("SeparationActivity", "SeparationTask5Stems doInBackground called!");
TFLiteModelProcessor5Stems modelProcessor = new TFLiteModelProcessor5Stems(SeparationActivity.this, "spleeter_5stems_optimized.tflite");
int chunkSize = chunkoption; //882000
int overlapSize = chunkSize / 2;
int stepSize = chunkSize - overlapSize;
int numChunks = (int) Math.ceil((double) audioData.length / stepSize);
timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File dir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "5stems_" + timestamp);
dir.mkdirs();
//File dir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "SeparatedAudio_5stems");
//dir.mkdirs();
File[] stemFiles = new File[] {
new File(dir, "vocals.wav"),
new File(dir, "piano.wav"),
new File(dir, "drums.wav"),
new File(dir, "bass.wav"),
new File(dir, "other.wav")
};
FileOutputStream[] outputStreams = new FileOutputStream[5];
for (int i = 0; i < 5; i++) {
outputStreams[i] = new FileOutputStream(stemFiles[i]);
writeEmptyWavHeader(outputStreams[i]);
}
float[][] prevOverlap = new float[5][overlapSize];
for (int chunkIndex = 0; chunkIndex < numChunks; chunkIndex++) {
int start = chunkIndex * stepSize;
int end = Math.min(start + chunkSize, audioData.length);
float[][] input = new float[2][chunkSize];
for (int i = start; i < end; i++) {
input[0][i - start] = audioData[i];
input[1][i - start] = audioData[i];
}
Log.d("SeparationActivity", "modelProcessor about to start");
float[][] separated = modelProcessor.processAudio(input);
for (int i = 0; i < 5; i++) {
applyOverlapAdd(prevOverlap[i], separated[i], overlapSize);
int writeFrom = (chunkIndex == 0) ? 0 : overlapSize;
int validLength = (chunkIndex == numChunks - 1) ? (end - start) : separated[i].length;
writeAudioToStream(outputStreams[i], separated[i], writeFrom, validLength);
//writeAudioToStream(outputStreams[i], separated[i], writeFrom);
//writeAudioToStream(outputStreams[i], separated[i], overlapSize);
}
publishProgress((int) ((chunkIndex / (float) numChunks) * 100), chunkIndex, numChunks);
}
for (int i = 0; i < 5; i++) {
outputStreams[i].close();
updateWavHeader(stemFiles[i]);
}
modelProcessor.close();
//loadAndDisplayStems(new String[]{"vocals", "piano", "drums", "bass", "other"});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
textViewProgress.setText("Chunk " + values[1] + " von " + values[2]);
}
@Override
protected void onPostExecute(Void aVoid) {
textViewProgress.setText("5-Stem Separation abgeschlossen!");
loadAndDisplayStems(new String[]{"vocals", "piano", "drums", "bass", "other"});
}
}
private void loadAndDisplayStems(String[] stemNames) {
Log.d("SeparationActivity", "loadAndDisplayStems called!");
File baseDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "SeparatedAudio");
if (stemOption == 2) {
baseDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "2stems_" + timestamp);
} else if (stemOption == 4) {
baseDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "4stems_" + timestamp);
} else if (stemOption == 5) {
baseDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "5stems_" + timestamp);
}
//File baseDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "SeparatedAudio");
WavFileLoader loader = new WavFileLoader();
for (int i = 0; i < stemNames.length; i++) {
try {
File file = new File(baseDir, stemNames[i] + ".wav");
Uri uri = Uri.fromFile(file);
WavFileLoader.WavData stemData = loader.loadWavFile(this, uri);
waveformViews.get(i + 1).setSamples(stemData.audioData);
waveformViews.get(i + 1).setAudioUri(uri);
setupSeekBar(seekBars.get(i + 1), waveformViews.get(i + 1));
Log.d("SeparationActivity", "loadAndDisplayStems: stemName: " + stemNames[i] + ", uri: " + uri);
infoTexts.get(i + 1).setText(
stemNames[i]
);
} catch (IOException e) {
e.printStackTrace();
infoTexts.get(i + 1).setText("Fehler beim Laden von: " + stemNames[i]);
}
}
}
// **Overlap-Add direkt auf den nächsten Chunk anwenden**
private void applyOverlapAdd(float[] prevOverlap, float[] currentChunk, int overlapSize) {
Log.d("AudioSeparation", "applyOverlapAdd called!");
for (int i = 0; i < overlapSize; i++) {
float hannWeight = getHannWindowValue(i, overlapSize);
float linearWeight = (float) i / overlapSize;
// **Berechnung der Gesamtgewichtung**
float combinedWeight = 0.5f * (hannWeight + linearWeight);
// **Overlap korrekt verrechnen**
currentChunk[i] = (prevOverlap[i] * (1 - combinedWeight) + currentChunk[i] * combinedWeight);
}
// **Speichere den neuen Overlap für den nächsten Chunk**
System.arraycopy(currentChunk, currentChunk.length - overlapSize, prevOverlap, 0, overlapSize);
}
// **Hanning-Fenster für sanfte Übergänge**
private float getHannWindowValue(int index, int totalSize) {
return 0.5f * (1 - (float) Math.cos(2 * Math.PI * (index + 0.5) / (totalSize - 1)));
}
// **Audio mit Overlap direkt in WAV-Datei schreiben**
/*
private void writeAudioToStream(FileOutputStream stream, float[] audioData, int overlapSize) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate((audioData.length - overlapSize) * 2).order(ByteOrder.LITTLE_ENDIAN);
for (int i = overlapSize; i < audioData.length; i++) { // Jetzt ohne doppelten Overlap!
short pcmSample = (short) Math.max(Math.min(audioData[i] * 32767, Short.MAX_VALUE), Short.MIN_VALUE);
buffer.putShort(pcmSample);
}
stream.write(buffer.array());
}
*/
/*
private void writeAudioToStream(FileOutputStream stream, float[] audioData, int startIndex) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate((audioData.length - startIndex) * 2).order(ByteOrder.LITTLE_ENDIAN);
for (int i = startIndex; i < audioData.length; i++) {
short pcmSample = (short) Math.max(Math.min(audioData[i] * 32767, Short.MAX_VALUE), Short.MIN_VALUE);
buffer.putShort(pcmSample);
}
stream.write(buffer.array());
}
*/
// Create WaveFiles
private void writeAudioToStream(FileOutputStream stream, float[] audioData, int startIndex, int validLength) throws IOException {
Log.d("AudioSeparation", "writeAudioToStream called!");
if (validLength <= startIndex) {
return; // Sicherheits-Exit: nichts zu schreiben
}
ByteBuffer buffer = ByteBuffer.allocate((validLength - startIndex) * 2).order(ByteOrder.LITTLE_ENDIAN);
for (int i = startIndex; i < validLength; i++) {
short pcmSample = (short) Math.max(Math.min(audioData[i] * 32767, Short.MAX_VALUE), Short.MIN_VALUE);
buffer.putShort(pcmSample);
}
stream.write(buffer.array());
}
private void writeEmptyWavHeader(FileOutputStream stream) throws IOException {
Log.d("AudioSeparation", "writeEmptyWavHeader called!");
byte[] header = new byte[44];
stream.write(header);
}
private void updateWavHeader(File file) throws IOException {
Log.d("AudioSeparation", "updateWavHeader called!");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
int totalAudioLen = (int) (raf.length() - 44);
int totalDataLen = totalAudioLen + 36;
//int sampleRate = sampleRate; // 44100 48000
int byteRate = sampleRate * 2;
raf.seek(0);
raf.writeBytes("RIFF");
raf.writeInt(Integer.reverseBytes(totalDataLen));
raf.writeBytes("WAVE");
raf.writeBytes("fmt ");
raf.writeInt(Integer.reverseBytes(16));
raf.writeShort(Short.reverseBytes((short) 1));
raf.writeShort(Short.reverseBytes((short) 1));
raf.writeInt(Integer.reverseBytes(sampleRate));
raf.writeInt(Integer.reverseBytes(byteRate));
raf.writeShort(Short.reverseBytes((short) 2));
raf.writeShort(Short.reverseBytes((short) 16));
raf.writeBytes("data");
raf.writeInt(Integer.reverseBytes(totalAudioLen));
raf.close();
}
}

View File

@ -0,0 +1,177 @@
package com.example.musicseparation;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.util.Log;
import org.tensorflow.lite.DataType;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
public class TFLiteModelProcessor {
private Interpreter interpreter;
public TFLiteModelProcessor(Context context, String modelPath) throws IOException {
this.interpreter = new Interpreter(loadModelFile(context, modelPath));
// Hole die Eingabeform des Modells
int[] inputShape = interpreter.getInputTensor(0).shape();
Log.d("ModelProcessor", "Initial Input Shape: [" + inputShape[0] + "][" + inputShape[1] + "]");
// Setze dynamische Eingabegröße
interpreter.resizeInput(0, new int[]{882000, 2}); // Falls nötig anpassen!
interpreter.allocateTensors();
int[] newInputShape = interpreter.getInputTensor(0).shape();
Log.d("ModelProcessor", "New Input Shape: [" + newInputShape[0] + "][" + newInputShape[1] + "]");
}
private MappedByteBuffer loadModelFile(Context context, String modelPath) throws IOException {
AssetFileDescriptor fileDescriptor = context.getAssets().openFd(modelPath);
FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
FileChannel fileChannel = inputStream.getChannel();
return fileChannel.map(FileChannel.MapMode.READ_ONLY, fileDescriptor.getStartOffset(), fileDescriptor.getDeclaredLength());
}
public float[][] processAudio(float[][] inputAudio) {
Log.d("ModelProcessor", "processAudio called");
int numSamples = inputAudio[0].length; // Anzahl der Samples
// Transponiere inputAudio von [2][1024] zu [1024][2]
float[][] transposedInput = new float[inputAudio[0].length][inputAudio.length];
for (int i = 0; i < inputAudio.length; i++) {
for (int j = 0; j < inputAudio[0].length; j++) {
transposedInput[j][i] = inputAudio[i][j];
}
}
float[][] vocalsOutput = new float[2][1]; // Ändere die Größe entsprechend
float[][] instrumentsOutput = new float[2][1];
int[] Outputtensor1 = interpreter.getOutputTensor(0).shapeSignature();
Log.d("ModelProcessor", "Output 1 Shape Signature: [" + Outputtensor1[0] + "][" + Outputtensor1[1] + "]");
int[] Outputtensor2 = interpreter.getOutputTensor(1).shapeSignature();
Log.d("ModelProcessor", "Output 2 Shape Signature: [" + Outputtensor2[0] + "][" + Outputtensor2[1] + "]");
int add = 4028;
int[] outputShape = interpreter.getOutputTensor(0).shapeSignature();
if (outputShape[1] == -1) {
outputShape[1] = numSamples + add; // Falls dynamisch, setze erwartete Länge
}
Log.d("ModelProcessor", "Resized Output Shape: [" + outputShape[0] + "][" + outputShape[1] + "]");
int[] outputShape2 = interpreter.getOutputTensor(1).shapeSignature();
if (outputShape2[1] == -1) {
outputShape2[1] = numSamples + add; // Falls dynamisch, setze erwartete Länge
}
Log.d("ModelProcessor", "Resized Output 2 Shape: [" + outputShape2[0] + "][" + outputShape2[1] + "]");
TensorBuffer vocalsBuffer = TensorBuffer.createFixedSize(outputShape, DataType.FLOAT32);
TensorBuffer instrumentalsBuffer = TensorBuffer.createFixedSize(outputShape2, DataType.FLOAT32);
Object[] inputArray = {transposedInput};
Map<Integer, Object> outputMap = new HashMap<>();
outputMap.put(0, vocalsBuffer.getBuffer());
outputMap.put(1, instrumentalsBuffer.getBuffer());
//Log.d("ModelProcessor", "outputArrays created");
int[] newInputShape = interpreter.getInputTensor(0).shape();
Log.d("ModelProcessor", "Input Shape: [" + newInputShape[0] + "][" + newInputShape[1] + "]");
//Log.d("ModelProcessor", "Input Audio: [" + inputAudio.length + "][" + inputAudio[0].length + "]");
// Logge die neue Form nach der Transposition
Log.d("ModelProcessor", "Transposed Input Shape: [" + transposedInput.length + "][" + transposedInput[0].length + "]");
int count = interpreter.getOutputTensorCount();
Log.d("ModelProcessor", "Output Tensor Count: " + count);
int[] OutputShape = interpreter.getOutputTensor(0).shape();
Log.d("ModelProcessor", "Output 1 Shape: [" + OutputShape[0] + "][" + OutputShape[1] + "]");
int[] OutputShape2 = interpreter.getOutputTensor(1).shape();
Log.d("ModelProcessor", "Output 2 Shape: [" + OutputShape2[0] + "][" + OutputShape2[1] + "]");
Outputtensor1 = interpreter.getOutputTensor(0).shapeSignature();
Log.d("ModelProcessor", "Output 1 Shape Signature: [" + Outputtensor1[0] + "][" + Outputtensor1[1] + "]");
Outputtensor2 = interpreter.getOutputTensor(1).shapeSignature();
Log.d("ModelProcessor", "Output 2 Shape Signature: [" + Outputtensor2[0] + "][" + Outputtensor2[1] + "]");
Log.d("TFLite", "Vocals Output Shape: [" + vocalsOutput.length + "][" + vocalsOutput[0].length + "]");
Log.d("TFLite", "Instrumentals Output Shape: [" + instrumentsOutput.length + "][" + instrumentsOutput[0].length + "]");
// Modell ausführen
//interpreter.run(transposedInput, outputBuffer);
interpreter.runForMultipleInputsOutputs(inputArray, outputMap);
Log.d("ModelProcessor", "Interpreter run completed");
Log.d("ModelProcessor", "Vocals Output Shape: [" + vocalsOutput.length + "][" + vocalsOutput[0].length + "]");
Log.d("ModelProcessor", "Instrumentals Output Shape: [" + instrumentsOutput.length + "][" + instrumentsOutput[0].length + "]");
Log.d("ModelProcessor", "Actual Output Shape: [" + vocalsBuffer.getShape()[0] + "][" + vocalsBuffer.getShape()[1] + "]");
// Konvertiere TensorBuffer zurück zu float[]
float[] chunkVocals = vocalsBuffer.getFloatArray();
float[] chunkInstrumentals = instrumentalsBuffer.getFloatArray();
Log.d("ModelProcessor", "chunkVocals Shape: [" + chunkVocals.length + "]");
Log.d("ModelProcessor", "chunkInstrumentals Shape: [" + chunkInstrumentals.length + "]");
int numChannels = vocalsBuffer.getShape()[0]; // = 2
int numSamples_vocals = vocalsBuffer.getShape()[1]; // = 4096
float[][] separatedVocals = new float[numChannels][numSamples_vocals - add];
for (int i = 0; i < numSamples_vocals - add; i++) {
separatedVocals[0][i] = chunkVocals[i]; // Linker Kanal
separatedVocals[1][i] = chunkVocals[i + numSamples_vocals]; // Rechter Kanal
}
float[][] separatedInstrumentals = new float[numChannels][numSamples_vocals - add];
for (int i = 0; i < numSamples_vocals - add; i++) {
separatedInstrumentals[0][i] = chunkInstrumentals[i]; // Linker Kanal
separatedInstrumentals[1][i] = chunkInstrumentals[i + numSamples_vocals]; // Rechter Kanal
}
// Debugging
Log.d("ModelProcessor", "Separated Vocals Shape: [" + separatedVocals.length + "][" + separatedVocals[0].length + "]");
//interpreter.getOutputTensor(0).
//TensorBuffer outputBuffer = TensorBuffer.createFixedSize(interpreter.getOutputTensor(0).shapeSignature(), interpreter.getOutputTensor(0).dataType());
// Gib beide Tensoren als separate Arrays zurück
//return new float[][] { chunkVocals, chunkInstrumentals };
return new float[][] { flattenArray(separatedVocals), flattenArray(separatedInstrumentals) };
}
private float[] flattenArray(float[][] array) {
float[] flattened = new float[array[0].length];
for (int i = 0; i < array[0].length; i++) {
flattened[i] = array[0][i]; // Extrahiere das erste Element jeder Zeile
}
return flattened;
}
public void close() {
interpreter.close();
}
}

View File

@ -0,0 +1,73 @@
package com.example.musicseparation;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.util.Log;
import org.tensorflow.lite.Interpreter;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
public class TFLiteModelProcessor2Stems {
private final Interpreter interpreter;
public TFLiteModelProcessor2Stems(Context context, String modelPath) throws IOException {
this.interpreter = new Interpreter(loadModelFile(context, modelPath));
interpreter.allocateTensors();
}
private MappedByteBuffer loadModelFile(Context context, String modelPath) throws IOException {
AssetFileDescriptor fileDescriptor = context.getAssets().openFd(modelPath);
FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
FileChannel fileChannel = inputStream.getChannel();
return fileChannel.map(FileChannel.MapMode.READ_ONLY,
fileDescriptor.getStartOffset(),
fileDescriptor.getDeclaredLength());
}
public float[][] processAudio(float[][] stereoInput) {
Log.d("TFLiteModelProcessor2Stems", "processAudio called!");
int numSamples = stereoInput[0].length;
float[][] input = new float[numSamples][2];
for (int i = 0; i < numSamples; i++) {
input[i][0] = stereoInput[0][i];
input[i][1] = stereoInput[1][i];
}
int add = 0;
if (numSamples == 220500) add = 3756;
else if (numSamples == 441000) add = 3416;
else if (numSamples == 882000) add = 3760;
else if (numSamples == 1764000) add = 3424;
else if (numSamples == 3528000) add = 3776;
float[][][] outputs = new float[2][2][numSamples + add];
Map<Integer, Object> outputMap = new HashMap<>();
for (int i = 0; i < 2; i++) {
outputMap.put(i, outputs[i]);
}
Log.d("TFLiteModelProcessor2Stems", "Model about to run");
interpreter.runForMultipleInputsOutputs(new Object[]{input}, outputMap);
Log.d("TFLiteModelProcessor2Stems", "Model finished running");
float[][] separated = new float[2][numSamples];
for (int stem = 0; stem < 2; stem++) {
for (int i = 0; i < numSamples; i++) {
separated[stem][i] = (outputs[stem][0][i] + outputs[stem][1][i]) / 2f;
}
}
return separated;
}
public void close() {
interpreter.close();
}
}

View File

@ -0,0 +1,80 @@
package com.example.musicseparation;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.util.Log;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
public class TFLiteModelProcessor4Stems {
private final Interpreter interpreter;
public TFLiteModelProcessor4Stems(Context context, String modelPath) throws IOException {
this.interpreter = new Interpreter(loadModelFile(context, modelPath));
int[] inputShape = interpreter.getInputTensor(0).shape();
Log.d("ModelProcessor", "Initial Input Shape: [" + inputShape[0] + "][" + inputShape[1] + "]");
//interpreter.resizeInput(0, new int[]{882000, 2}); // Falls nötig anpassen!
interpreter.allocateTensors();
}
private MappedByteBuffer loadModelFile(Context context, String modelPath) throws IOException {
AssetFileDescriptor fileDescriptor = context.getAssets().openFd(modelPath);
FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
FileChannel fileChannel = inputStream.getChannel();
return fileChannel.map(FileChannel.MapMode.READ_ONLY,
fileDescriptor.getStartOffset(),
fileDescriptor.getDeclaredLength());
}
public float[][] processAudio(float[][] stereoInput) {
Log.d("TFLiteModelProcessor4Stems", "processAudio called!");
int numSamples = stereoInput[0].length;
float[][] input = new float[numSamples][2]; // [samples][channels]
for (int i = 0; i < numSamples; i++) {
input[i][0] = stereoInput[0][i];
input[i][1] = stereoInput[1][i];
}
int add = 0;
if (numSamples == 220500) add = 3756;
else if (numSamples == 441000) add = 3416;
else if (numSamples == 882000) add = 3760;
else if (numSamples == 1764000) add = 3424;
else if (numSamples == 3528000) add = 3776;
float[][][] outputs = new float[4][2][numSamples + add];
Map<Integer, Object> outputMap = new HashMap<>();
for (int i = 0; i < 4; i++) {
outputMap.put(i, outputs[i]);
}
Log.d("TFLiteModelProcessor4Stems", "Model about to run");
interpreter.runForMultipleInputsOutputs(new Object[]{input}, outputMap);
Log.d("TFLiteModelProcessor4Stems", "Model finished running");
float[][] separated = new float[4][numSamples];
for (int stem = 0; stem < 4; stem++) {
for (int i = 0; i < numSamples; i++) {
// Mono-Mix
separated[stem][i] = (outputs[stem][0][i] + outputs[stem][1][i]) / 2f;
}
}
return separated;
}
public void close() {
interpreter.close();
}
}

View File

@ -0,0 +1,74 @@
package com.example.musicseparation;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.util.Log;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
public class TFLiteModelProcessor5Stems {
private final Interpreter interpreter;
public TFLiteModelProcessor5Stems(Context context, String modelPath) throws IOException {
this.interpreter = new Interpreter(loadModelFile(context, modelPath));
interpreter.allocateTensors();
}
private MappedByteBuffer loadModelFile(Context context, String modelPath) throws IOException {
AssetFileDescriptor fileDescriptor = context.getAssets().openFd(modelPath);
FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
FileChannel fileChannel = inputStream.getChannel();
return fileChannel.map(FileChannel.MapMode.READ_ONLY,
fileDescriptor.getStartOffset(),
fileDescriptor.getDeclaredLength());
}
public float[][] processAudio(float[][] stereoInput) {
Log.d("TFLiteModelProcessor5Stems", "processAudio called!");
int numSamples = stereoInput[0].length;
float[][] input = new float[numSamples][2]; // [samples][channels]
for (int i = 0; i < numSamples; i++) {
input[i][0] = stereoInput[0][i];
input[i][1] = stereoInput[1][i];
}
int add = 0;
if (numSamples == 220500) add = 3756;
else if (numSamples == 441000) add = 3416;
else if (numSamples == 882000) add = 3760;
else if (numSamples == 1764000) add = 3424;
else if (numSamples == 3528000) add = 3776;
float[][][] outputs = new float[5][2][numSamples + add];
Map<Integer, Object> outputMap = new HashMap<>();
for (int i = 0; i < 5; i++) {
outputMap.put(i, outputs[i]);
}
Log.d("TFLiteModelProcessor5Stems", "Model about to run");
interpreter.runForMultipleInputsOutputs(new Object[]{input}, outputMap);
Log.d("TFLiteModelProcessor5Stems", "Model finished running");
float[][] separated = new float[5][numSamples];
for (int stem = 0; stem < 5; stem++) {
for (int i = 0; i < numSamples; i++) {
separated[stem][i] = (outputs[stem][0][i] + outputs[stem][1][i]) / 2f;
}
}
return separated;
}
public void close() {
interpreter.close();
}
}

View File

@ -0,0 +1,29 @@
package com.example.musicseparation;
import android.net.Uri;
public class WavDataHolder {
private static WavFileLoader.WavData wavData;
private static Uri audioUri;
public static void setWavData(WavFileLoader.WavData data) {
wavData = data;
}
public static WavFileLoader.WavData getWavData() {
return wavData;
}
public static void setAudioUri(Uri uri) {
audioUri = uri;
}
public static Uri getAudioUri() {
return audioUri;
}
public static void clear() {
wavData = null;
audioUri = null;
}
}

View File

@ -0,0 +1,125 @@
// WavFileLoader.java
package com.example.musicseparation;
import static java.nio.ByteOrder.BIG_ENDIAN;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
public class WavFileLoader {
public static class WavData {
//public short[] audioData;
public float[] audioData;
public int sampleRate;
}
public WavData loadWavFile(Context context, Uri uri) throws IOException {
Log.d("WavFileLoader", "loadWavFile called");
InputStream inputStream = context.getContentResolver().openInputStream(uri);
//AssetManager assetManager = getAssets();
//AssetManager assetManager = context.getAssets();
//InputStream inputStream = assetManager.open("uri");
DataInputStream dis = new DataInputStream(new BufferedInputStream(inputStream));
// **Lese die ersten 12 Bytes (RIFF-Header)**
byte[] riffHeader = new byte[12];
dis.readFully(riffHeader);
if (!new String(riffHeader, 0, 4).equals("RIFF") || !new String(riffHeader, 8, 4).equals("WAVE")) {
throw new IOException("Ungültiges WAV-Format (kein RIFF/WAVE Header gefunden)");
}
// **Suche den "data"-Chunk**
int sampleRate = 0, bitsPerSample = 0, numChannels = 0, dataSize = 0;
while (dis.available() > 0) {
byte[] chunkHeader = new byte[8];
dis.readFully(chunkHeader);
String chunkName = new String(chunkHeader, 0, 4);
int chunkSize = ByteBuffer.wrap(chunkHeader, 4, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
if (chunkName.equals("fmt ")) {
byte[] fmtChunk = new byte[chunkSize];
dis.readFully(fmtChunk);
ByteBuffer fmtBuffer = ByteBuffer.wrap(fmtChunk).order(ByteOrder.LITTLE_ENDIAN);
fmtBuffer.position(0);
int audioFormat = fmtBuffer.getShort(); // 1 = PCM
numChannels = fmtBuffer.getShort(); // **WICHTIG**
sampleRate = fmtBuffer.getInt();
fmtBuffer.getInt(); // ByteRate (ignorieren)
fmtBuffer.getShort(); // BlockAlign (ignorieren)
bitsPerSample = fmtBuffer.getShort();
} else if (chunkName.equals("data")) {
dataSize = chunkSize;
break; // Wir haben den Daten-Chunk gefunden!
} else {
dis.skipBytes(chunkSize);
}
}
Log.d("WavFileLoader", "number of channels: " + numChannels);
if (dataSize == 0) {
throw new IOException("Kein 'data' Chunk in WAV-Datei gefunden!");
}
// ** FALSCHE Sample-Berechnung**
// int numSamples = dataSize / (bitsPerSample / 8);
// ** KORREKTE Sample-Berechnung (Stereo wird berücksichtigt!)**
int numSamples = (dataSize / (bitsPerSample / 8)) / numChannels;
float[] samples = new float[numSamples];
// **Lese die PCM-Daten**
byte[] audioData = new byte[dataSize];
dis.readFully(audioData);
dis.close();
ByteBuffer byteBuffer = ByteBuffer.wrap(audioData).order(ByteOrder.LITTLE_ENDIAN);
// **Stereo auf Mono reduzieren**
if (numChannels > 1) {
for (int i = 0; i < numSamples; i++) {
float left = byteBuffer.getShort() / 32768.0f;
float right = byteBuffer.getShort() / 32768.0f;
samples[i] = (left + right) / 2; // Durchschnitt von L+R für Mono
}
} else {
for (int i = 0; i < numSamples; i++) {
samples[i] = byteBuffer.getShort() / 32768.0f;
}
}
// **Debug-Log zur Überprüfung**
System.out.println("Sample-Rate: " + sampleRate);
System.out.println("Kanäle: " + numChannels);
System.out.println("Bits per Sample: " + bitsPerSample);
System.out.println("Daten-Größe: " + dataSize);
System.out.println("Berechnete Samples: " + numSamples);
System.out.println("Berechnete Dauer (s): " + (numSamples / (float) sampleRate));
WavData wavData = new WavData();
//wavData.audioData = float32ToInt16(samples);
wavData.audioData = samples;;
//wavData.audioData = float32ToInt16(test);
wavData.sampleRate = sampleRate;
return wavData;
}
}

View File

@ -0,0 +1,179 @@
// WaveformView.java
package com.example.musicseparation;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.MediaPlayer;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.View;
import java.io.IOException;
public class WaveformView extends View {
private Paint paint = new Paint();
private float[] samples; // Downsampled and normalized samples
private MediaPlayer mediaPlayer;
private Uri audioUri;
private int playbackPositionMs = 0;
private final Paint markerPaint = new Paint();
public WaveformView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public WaveformView(Context context) {
super(context);
init();
}
private void init() {
paint.setColor(Color.BLUE);
paint.setStrokeWidth(2);
//paint.setAntiAlias(true); // Glattere Darstellung
markerPaint.setColor(Color.RED);
markerPaint.setStrokeWidth(3);
markerPaint.setAntiAlias(true);
mediaPlayer = new MediaPlayer();
}
public void updatePlaybackPosition(int positionMs) {
this.playbackPositionMs = positionMs;
invalidate(); // Neu zeichnen, um Marker zu zeigen
}
public void setAudioUri(Uri uri) {
this.audioUri = uri;
}
public void playAudio(Context context) {
if (audioUri == null) return;
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(context, audioUri);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void stopAudio() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
public boolean isPlaying() {
return mediaPlayer != null && mediaPlayer.isPlaying();
}
public int getCurrentPosition() {
return mediaPlayer != null ? mediaPlayer.getCurrentPosition() : 0;
}
public int getDuration() {
return mediaPlayer != null ? mediaPlayer.getDuration() : 1; // 1 zur Sicherheit gegen Division durch 0
}
public void seekTo(int positionMs) {
if (mediaPlayer != null) {
mediaPlayer.seekTo(positionMs);
}
}
/**
* Übergibt rohe Audiodaten (z.B. PCM float-Werte) und berechnet daraus
* eine vereinfachte Darstellung für die Breite des Views.
*/
public void setSamples(float[] rawSamples) {
int viewWidth = getWidth();
if (viewWidth == 0) viewWidth = 1000; // Fallback für nicht gemessenen View
float[] downsampled = new float[viewWidth];
int samplesPerPixel = rawSamples.length / viewWidth;
samplesPerPixel = Math.max(1, samplesPerPixel);
for (int i = 0; i < viewWidth; i++) {
float sum = 0f;
int count = 0;
for (int j = 0; j < samplesPerPixel; j++) {
int index = i * samplesPerPixel + j;
if (index < rawSamples.length) {
sum += Math.abs(rawSamples[index]);
count++;
}
}
downsampled[i] = (count > 0) ? (sum / count) : 0f; // Durchschnitt der Absolutwerte
}
this.samples = downsampled;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (samples == null || samples.length == 0) return;
int width = getWidth();
int height = getHeight();
int centerY = height / 2;
for (int i = 0; i < samples.length && i < width; i++) {
float sampleValue = samples[i] * centerY;
canvas.drawLine(i, centerY - sampleValue, i, centerY + sampleValue, paint);
}
if (mediaPlayer != null && mediaPlayer.getDuration() > 0) {
float progress = playbackPositionMs / (float) mediaPlayer.getDuration();
int markerX = (int) (progress * getWidth());
canvas.drawLine(markerX, 0, markerX, getHeight(), markerPaint);
}
}
/*
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (samples == null || samples.length == 0) return;
int width = getWidth();
int height = getHeight();
int centerY = height / 2;
Path path = new Path();
// Startpunkt der Linie
path.moveTo(0, centerY - samples[0] * centerY);
// Linie durch alle Samples
for (int i = 1; i < samples.length && i < width; i++) {
float x = i;
float y = centerY - samples[i] * centerY;
path.lineTo(x, y);
}
canvas.drawPath(path, paint);
}
*/
}

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonLoadAudio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Audio laden" />
</LinearLayout>
<TextView
android:id="@+id/textViewa"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2 Stems: Vocals and Instrumental \n4 Stems: Vocals, Bass, Drums, Other \n5 Stems: Vocals, Piano, Bass, Drums, Other"
android:paddingTop="4dp" />
<RadioGroup
android:id="@+id/radioGroupStems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_marginTop="4dp"
android:layout_marginBottom="0dp">
<RadioButton
android:id="@+id/radioButton2Stems"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="2 Stems" />
<RadioButton
android:id="@+id/radioButton4Stems"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="4 Stems"
android:layout_marginStart="16dp" />
<RadioButton
android:id="@+id/radioButton5Stems"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="5 Stems"
android:layout_marginStart="16dp" />
</RadioGroup>
<TextView
android:id="@+id/textViewInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Datei 1 Info erscheint hier..."
android:paddingTop="0dp" />
<com.example.musicseparation.WaveformView
android:id="@+id/waveformOriginal"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#DDDDDD"
android:layout_marginBottom="4dp" />
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonPlay"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Play 1" />
<Button
android:id="@+id/buttonStop"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Stop 1" />
</LinearLayout>
<TextView
android:id="@+id/textViewb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="How many Samples should be processed? \n 1: 220500 - 5: 3528000"
android:paddingTop="4dp" />
<RadioGroup
android:id="@+id/radioGroupChunks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_marginTop="4dp">
<RadioButton
android:id="@+id/radioButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="1" />
<RadioButton
android:id="@+id/radioButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2"
android:layout_marginStart="16dp" />
<RadioButton
android:id="@+id/radioButton3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3"
android:layout_marginStart="16dp" />
<RadioButton
android:id="@+id/radioButton4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="4"
android:layout_marginStart="16dp" />
<RadioButton
android:id="@+id/radioButton5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="5"
android:layout_marginStart="16dp" />
</RadioGroup>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonStartSeparation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Separation starten" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<!-- Hier später UI-Elemente hinzufügen -->
</LinearLayout>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/textViewProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Separation wird vorbereitet..."
android:textSize="18sp"
android:paddingBottom="16dp" />
<!-- Original Audio -->
<include layout="@layout/waveform_element_0" />
<!-- Stem 1 -->
<include layout="@layout/waveform_element_1" />
<!-- Stem 2 -->
<include layout="@layout/waveform_element_2" />
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/textViewProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Separation wird vorbereitet..."
android:textSize="18sp"
android:paddingBottom="16dp" />
<!-- Original Audio -->
<include layout="@layout/waveform_element_0" />
<!-- Stem 1 -->
<include layout="@layout/waveform_element_1" />
<!-- Stem 2 -->
<include layout="@layout/waveform_element_2" />
<!-- Stem 3 -->
<include layout="@layout/waveform_element_3" />
<!-- Stem 4 -->
<include layout="@layout/waveform_element_4" />
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/textViewProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Separation wird vorbereitet..."
android:textSize="18sp"
android:paddingBottom="16dp" />
<!-- Original Audio -->
<include layout="@layout/waveform_element_0" />
<!-- Stem 1 -->
<include layout="@layout/waveform_element_1" />
<!-- Stem 2 -->
<include layout="@layout/waveform_element_2" />
<!-- Stem 3 -->
<include layout="@layout/waveform_element_3" />
<!-- Stem 4 -->
<include layout="@layout/waveform_element_4" />
<!-- Stem 5 -->
<include layout="@layout/waveform_element_5" />
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp">
<TextView
android:id="@+id/textViewInfo0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Datei Info 0 erscheint hier..."
android:paddingTop="8dp" />
<com.example.musicseparation.WaveformView
android:id="@+id/waveformView0"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#DDDDDD"
android:layout_marginTop="8dp" />
<SeekBar
android:id="@+id/seekBar0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonPlay0"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Play" />
<Button
android:id="@+id/buttonStop0"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Stop" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp">
<TextView
android:id="@+id/textViewInfo1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Datei Info 1 erscheint hier..."
android:paddingTop="8dp" />
<com.example.musicseparation.WaveformView
android:id="@+id/waveformView1"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#DDDDDD"
android:layout_marginTop="8dp" />
<SeekBar
android:id="@+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonPlay1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Play" />
<Button
android:id="@+id/buttonStop1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Stop" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp">
<TextView
android:id="@+id/textViewInfo2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Datei Info 2 erscheint hier..."
android:paddingTop="8dp" />
<com.example.musicseparation.WaveformView
android:id="@+id/waveformView2"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#DDDDDD"
android:layout_marginTop="8dp" />
<SeekBar
android:id="@+id/seekBar2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonPlay2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Play" />
<Button
android:id="@+id/buttonStop2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Stop" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp">
<TextView
android:id="@+id/textViewInfo3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Datei Info 3 erscheint hier..."
android:paddingTop="8dp" />
<com.example.musicseparation.WaveformView
android:id="@+id/waveformView3"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#DDDDDD"
android:layout_marginTop="8dp" />
<SeekBar
android:id="@+id/seekBar3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonPlay3"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Play" />
<Button
android:id="@+id/buttonStop3"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Stop" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp">
<TextView
android:id="@+id/textViewInfo4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Datei Info 4 erscheint hier..."
android:paddingTop="8dp" />
<com.example.musicseparation.WaveformView
android:id="@+id/waveformView4"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#DDDDDD"
android:layout_marginTop="8dp" />
<SeekBar
android:id="@+id/seekBar4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonPlay4"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Play" />
<Button
android:id="@+id/buttonStop4"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Stop" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp">
<TextView
android:id="@+id/textViewInfo5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Datei Info 5 erscheint hier..."
android:paddingTop="8dp" />
<com.example.musicseparation.WaveformView
android:id="@+id/waveformView5"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#DDDDDD"
android:layout_marginTop="8dp" />
<SeekBar
android:id="@+id/seekBar5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<Button
android:id="@+id/buttonPlay5"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Play" />
<Button
android:id="@+id/buttonStop5"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Stop" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.musicseparation.MainActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/FirstFragment">
<fragment
android:id="@+id/FirstFragment"
android:name="com.example.musicseparation.FirstFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_first">
<action
android:id="@+id/action_FirstFragment_to_SecondFragment"
app:destination="@id/SecondFragment" />
</fragment>
<fragment
android:id="@+id/SecondFragment"
android:name="com.example.musicseparation.SecondFragment"
android:label="@string/second_fragment_label"
tools:layout="@layout/fragment_second">
<action
android:id="@+id/action_SecondFragment_to_FirstFragment"
app:destination="@id/FirstFragment" />
</fragment>
</navigation>

View File

@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">48dp</dimen>
</resources>

View File

@ -0,0 +1,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.MusicSeparation" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>

View File

@ -0,0 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.MusicSeparation" parent="Base.Theme.MusicSeparation">
<!-- Transparent system bars for edge-to-edge. -->
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
</style>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">200dp</dimen>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">48dp</dimen>
</resources>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">16dp</dimen>
</resources>

View File

@ -0,0 +1,46 @@
<resources>
<string name="app_name">MusicSeparation</string>
<string name="action_settings">Settings</string>
<!-- Strings used for fragments for navigation -->
<string name="first_fragment_label">First Fragment</string>
<string name="second_fragment_label">Second Fragment</string>
<string name="next">Next</string>
<string name="previous">Previous</string>
<string name="lorem_ipsum">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris
volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus
dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad
litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend
diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a,
ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n
Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus
egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed
neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada
fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae,
molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor
bibendum, vel congue leo egestas.\n\n
Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit
amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel,
molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer
interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at
lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula,
in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque
est.\n\n
Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh.
Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui
non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In
eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc,
quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra
ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a
placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus
convallis.\n\n
Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa
gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper,
libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper
sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus
libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus
vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim.
</string>
</resources>

View File

@ -0,0 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.MusicSeparation" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.MusicSeparation" parent="Base.Theme.MusicSeparation" />
</resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@ -0,0 +1,17 @@
package com.example.musicseparation;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

4
build.gradle.kts Normal file
View File

@ -0,0 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
}

21
gradle.properties Normal file
View File

@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

24
gradle/libs.versions.toml Normal file
View File

@ -0,0 +1,24 @@
[versions]
agp = "8.7.3"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
appcompat = "1.7.0"
material = "1.12.0"
constraintlayout = "2.2.1"
navigationFragment = "2.8.9"
navigationUi = "2.8.9"
[libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" }
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" }
navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Mon Apr 28 12:51:47 CEST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
gradlew vendored Normal file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or 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 UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$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 "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# 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
;;
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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View File

@ -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

24
settings.gradle.kts Normal file
View File

@ -0,0 +1,24 @@
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "MusicSeparation"
include(":app")