[PYTHON] SmartVolumer, eine Android-App, die die Lautstärke der Musikwiedergabe an die umgebende Lautstärke anpasst

Überblick

Dies ist eine Abschrift der App, die ich in meinem dritten Jahr der Grundschule erstellt habe.

Ich denke, dass man auf Reisen mit dem Zug oft Musik mit Kopfhörern hört. Wie laut hörst du zu? Einige Leute möchten vielleicht mit lauter Lautstärke hören. Ich mag die Lautstärke, damit ich ein wenig von den Umgebungsgeräuschen hören kann. Es ist traurig, dass ich die Musik nicht hören kann, und im Gegenteil, es ist ärgerlich, dass sie immer laut ist. Als ich es bemerkte, klickte ich immer wieder auf die Lautstärketaste im Auto. Da dies problematisch ist, habe ich beschlossen, eine Anwendung zu erstellen, die die Wiedergabelautstärke von Musik usw. automatisch so einstellt, dass sie etwas lauter als das Umgebungsgeräusch ist.

Realisierungsmethode

Grundprinzip

Es wurde in eine Smartphone-Anwendung (Android) umgewandelt. Die Umgebungslautstärke wird mit dem Mikrofon des Smartphones gemessen, die Wiedergabelautstärke wird durch das Modell zur Bestimmung der Wiedergabelautstärke (später beschrieben) bestimmt und die Lautstärke wird geändert. Ich benutze AudioManager oder AudioRecord.

Erstens gibt es das Problem, wie viele Millisekunden das Umgebungsvolumen gemessen werden soll. Wenn es zu kurz ist, können Sie die Umgebungslautstärke nicht richtig einstellen. Als nächstes stellt sich die Frage, wie oft der Zyklus (Umgebungslautstärke erfassen-> Wiedergabelautstärke ändern) ausgeführt wird. Wenn die Frequenz zu hoch ist, ändert sich die Lautstärke zu schnell, was ärgerlich ist.

Ich habe verschiedene Dinge ausprobiert, aber abschließend habe ich beschlossen, das Peripherievolumen alle 100 Millisekunden als Maximalwert unter den mehrmals gemessenen Werten zu ermitteln. Dies wurde 10 Mal wiederholt und der Durchschnittswert dieser gemessenen Werte wurde als endgültiger Umgebungsvolumenwert behandelt. Der Einfachheit halber wird der Zyklus (Umgebungslautstärke abrufen-> Wiedergabelautstärke ändern) alle 1000 Millisekunden ausgeführt.

Dieser Vorgang ist möglicherweise leichter zu verstehen, wenn Sie den Quellcode lesen. Der Quellcode, der diesen Zyklus ausführt, lautet wie folgt.

package com.gmail.axis38akasira.autovolumer;

import android.os.Handler;
import android.support.annotation.NonNull;
import android.widget.TextView;

import com.gmail.axis38akasira.autovolumer.notifications.NotificationWrapper;

class VolumeManager implements Runnable {

    private AudioResources aRes;
    private Handler handler;
    private TextView textView_envVol, textView_playingVol;
    private NotificationWrapper notificationWrapper;
    private int micSenseCnt = 0, micSenseSum = 0;

    VolumeManager(@NonNull AudioResources aRes, @NonNull Handler handler,
                  @NonNull TextView textView_envVol, @NonNull TextView textView_playingVol,
                  @NonNull NotificationWrapper notificationWrapper) {
        this.aRes = aRes;
        this.handler = handler;
        this.textView_envVol = textView_envVol;
        this.textView_playingVol = textView_playingVol;
        this.notificationWrapper = notificationWrapper;
    }

    @Override
    public void run() {
        if (aRes.getMicAccessAllowed()) {
            final short[] buffer = aRes.readByAudioRecorder();

            //maximal
            int max_val = Integer.MIN_VALUE;
            for (short x : buffer) {
                max_val = Math.max(max_val, x);
            }

            //Messen Sie viele Male und verwenden Sie den Durchschnittswert als Messergebnis während dieses Zeitintervalls.
            micSenseSum += max_val;
            if (micSenseCnt != 9) micSenseCnt++;
            else {
                final double inputLevel = micSenseSum / 10.0;
                micSenseSum = 0;
                micSenseCnt = 0;

                textView_envVol.setText(String.valueOf(inputLevel));
                final int outLevel = aRes.applyPlayingVolume(inputLevel, textView_playingVol);
                if (outLevel != -1) {
                    notificationWrapper.post(
                            MainActivity.class, "Die automatische Lautstärkeregelung ist aktiviert",
                            notificationWrapper.getActivity().getString(R.string.vol_playing)
                                    + String.valueOf(outLevel)
                    );
                }
            }
        }
        handler.postDelayed(this, 100);
    }
}

Modell zur Bestimmung der Wiedergabelautstärke

Zuerst habe ich darüber nachgedacht, so etwas wie eine Funktion zu erstellen, die die Wiedergabelautstärke anhand des umgebenden Lautstärkepegels bestimmt.

Wenn zu diesem Zeitpunkt die Anzahl der Schritte der Wiedergabelautstärke für alle Geräte gleich ist (und garantiert ist, dass sie sich auch bei Aktualisierung des Betriebssystems nicht ändert), scheint dies durch Schreiben einiger Wenns leicht zu realisieren zu sein.

Input:Peripherievolumen
return:Wiedergabelautstärke

Funktion zur Bestimmung der Lautstärke der Funkwiedergabe:
wenn periphere Lautstärke< 750:
        return 0
wenn periphere Lautstärke< 3750:
        return 1
wenn periphere Lautstärke< 9750:
        return 2
(Fortsetzung für die Anzahl der Schritte der Wiedergabelautstärke)

Ich fand es jedoch schwierig anzunehmen (und es war zu einfach und nicht interessant). Aus diesem Grund habe ich beschlossen, ein mathematisches Modell zu erstellen, das die Wiedergabelautstärke aus dem umgebenden Lautstärkepegel in Form einer kontinuierlichen Funktion ableitet, die die Ausgabe auf reale Werte erweitert. Insbesondere habe ich versucht, ein Modell zu erstellen, das das Verhältnis des peripheren Lautstärkepegels zum maximalen Volumen als realen Wert zurückgibt.

Zunächst wurde unter Verwendung des tatsächlichen Geräts (Samsung Galaxy Feel SC-04J) die ideale Wiedergabelautstärke (Verhältnis zur maximalen Lautstärke) in Bezug auf die Umgebungslautstärke gemessen. (Es ist jedoch nur erforderlich, den Grenzteil zu untersuchen, da intuitiv klar ist, dass die zu erzeugende Funktion im weitesten Sinne monoton zunimmt.)

Basierend auf den Messergebnissen werden die entsprechenden Daten der Peripherie-Lautstärke und der Wiedergabelautstärke erstellt. Hier ist ein Streudiagramm, das dies veranschaulicht.

image.png

Da der Eingangslautstärkepegel vom Mikrofon als vorzeichenbehafteter 16-Bit-Integer-Typ angegeben wird, beträgt der Bereich [0, 32767]. Die horizontale Achse ist auf den Rohvolumenpegel * 10 ^ 5 eingestellt, damit sie später problemlos verarbeitet werden kann.

Basierend auf diesen Daten möchte ich das Modell anpassen und die folgende nicht glatte Funktion erhalten (?).

image.png

Ich möchte nicht, dass es eine allmähliche Form annimmt. Vermeiden Sie daher sehr ausdrucksstarke Modelle wie NN und verwenden Sie ein möglichst einfaches Modell. Die folgenden Funktionen werden als Kandidaten vorbereitet.

a+bx \\
a+bx+cx^2 \\
a+b \sqrt{x} \\
a+b \log{x}

Lassen Sie uns auf Jupyter Notebook trainieren und die Genauigkeit und die Parameter nach dem Training sehen.

# a +Passend für bx
from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(np.array(x).reshape(-1, 1), y)
print(lr.score(np.array(x).reshape(-1, 1),y), lr.intercept_, lr.coef_)

# a + bx + cx^Passend für 2
lr2 = LinearRegression().fit(np.dstack((np.power(np.array(x),2),np.array(x)))[0], y)
print(lr2.score(np.dstack((np.power(np.array(x),2),np.array(x)))[0],y), lr2.intercept_, lr2.coef_)

# a + b sqrt(x)Passend zu
lr3 = LinearRegression().fit(np.array(np.sqrt(x)).reshape(-1, 1), y)
print(lr3.score(np.array(np.sqrt(x)).reshape(-1, 1),y), lr3.intercept_, lr3.coef_)

# a + b log(x)Passend zu
log_r = LinearRegression().fit(np.array(np.log(x[1:])).reshape(-1, 1), y[1:])
print(log_r.score(np.array(np.log(x[1:])).reshape(-1, 1),y[1:]), log_r.intercept_, log_r.coef_)

Genauigkeit, konstanter Term, Koeffizient

0.9566515430381373 0.05703034713007743 [0.85320093]
0.9858850873387448 0.035720497728703726 [-1.91782117  1.43981898]
0.9981469854250034 -0.013011305980174026 [0.56593706]
0.9695780780725732 0.39569447022473625 [0.09291432]

Stellen wir es uns als Grafik vor.

#Diagrammdarstellung
RNG = np.linspace(0, 0.32, 100)
DIV_NUM = 15  #Maximale Wiedergabelautstärke, die von Gerät zu Gerät variiert
plt.figure(figsize=(18,9))
plt.xlabel("env_noise")
plt.ylabel("play_level")
plt.scatter(df["Lärm"]/100000, df["Wiedergabelautstärke"]/DIV_NUM, label="data")
plt.plot(RNG, lr.intercept_ + lr.coef_ * RNG, label="a+bx", color="green")
plt.plot(RNG, lr2.intercept_ + lr2.coef_[1] * RNG + lr2.coef_[0] * RNG * RNG, label="a+bx+cx^2", color="red")
plt.plot(RNG, lr3.intercept_ + lr3.coef_ * np.sqrt(RNG), label="a+ b sqrt(x)", color="purple")
plt.plot(RNG, log_r.intercept_ + log_r.coef_ * np.log(RNG), label="a+ b log(x)", color="cyan")
plt.legend(loc='upper left', prop={'size':20})

Unknown.png

** Ich weiß nicht welche! ** ** **

Bei der tatsächlichen Bestimmung der Wiedergabelautstärke muss diese berechnet werden (maximales Lautstärke * Lautstärkeverhältnis) und anschließend in eine Ganzzahl konvertiert werden. Daher wurde das Wiedergabevolumen (ganzzahliger Wert) nach der Berechnung im Streudiagramm angezeigt und die Differenz zu den gemessenen Daten bestätigt.

#Versuchen Sie, die Wiedergabelautstärke des Geräts mithilfe der Funktion zu bestimmen, um die Wiedergabelautstärke aus dem Rauschen zu ermitteln
#Runden Sie den Wert der Funktion ab, um sie zu einer Ganzzahl zu machen

RNG = np.linspace(0.001, 0.32, 500)
DIV_NUM = 15  #Maximale Wiedergabelautstärke, die von Gerät zu Gerät variiert

plt.figure(figsize=(18,12))
plt.scatter(df["Lärm"]/100000, df["Wiedergabelautstärke"], label="data")
plt.plot(RNG, np.round(DIV_NUM * (lr.intercept_ + lr.coef_[0] * RNG)), label="a+bx round", color="green")
plt.plot(RNG, np.round(DIV_NUM * (lr2.intercept_ + lr2.coef_[1] * RNG + lr2.coef_[0] * RNG * RNG)), label="a+bx+cx^2 round", color="red")
plt.plot(RNG, np.round(DIV_NUM * (lr3.intercept_ + lr3.coef_ * np.sqrt(RNG))), label="a+ b sqrt(x)", color="purple")
plt.plot(RNG, np.round(DIV_NUM * (log_r.intercept_ + log_r.coef_ * np.log(RNG))), label="a+ b log(x)", color="cyan")
plt.legend(loc='upper left', prop={'size':20})

Ich habe dies gewählt, weil es den Anschein hat, dass die richtige Wiedergabelautstärke mit $ a + b \ sqrt {x} $ bestimmt werden kann (ich möchte wirklich eine quantitativere Beurteilung vornehmen). Aus dem Ergebnis, wenn es passt, zu diesem Zeitpunkt

\begin{align}
a &= 0.56593706 \\
b &= -0.013011305980174026
\end{align}

Sie können sehen, dass. Fügen Sie abschließend der App eine Funktion hinzu, um diese zu verwenden. (Variablennamen unterscheiden sich geringfügig, aber bitte raten Sie)

package com.gmail.axis38akasira.autovolumer;

class RegressionModel {
    private final static double[] beta = {-0.012529002932674477, 0.56377634};

    static Double infer(final double x) {
        return beta[0] + beta[1] * Math.sqrt(x);
    }
}

UI

Ich habe eine angemessene Aktivität durchgeführt.

Ich dachte, es wäre besser, wenn es leicht zu verstehen wäre, ob es gültig ist oder nicht, also habe ich es möglich gemacht, den Status in der Benachrichtigung anzuzeigen.

Quellcode

Hier https://github.com/ryhoh/SmartVolumer

Zusammenfassung

Persönlich ist es eine bequeme App. Ich bedauere, dass dies ein guter Weg war, um das Modell zu bestimmen. Dies wurde durch die Verwendung eines Smartphone-Mikrofons erreicht, aber der Effekt wird natürlich schlimmer, wenn das Smartphone in eine Tasche gesteckt wird. In der Realität ist es ideal, wenn sich das Mikrofon in der Nähe der Kopfhörer befindet. In dieser Hinsicht verwende ich es mit drahtlosen Kopfhörern, aber ich frage mich, was passiert, wenn ich einen kabelgebundenen Kopfhörer mit eingebautem Mikrofon verwende.

Recommended Posts

SmartVolumer, eine Android-App, die die Lautstärke der Musikwiedergabe an die umgebende Lautstärke anpasst
Ändern Sie die Lautstärke von Pepper entsprechend der Umgebung (Ton).