If you feel you want to support my work consider to „buy me a coffee“ on https://buymeacoffee.com/mpfeifer!
Lange Zeit hat Google unbegrenzten Speicherplatz für alle Hobbyfotografen angeboten. Wenn man sich damit abfinden konnte die Fotos auf eine gewissen Auflösung herunter konvertieren zu lassen musste man sich über Speicher keine Sorgen machen (bei mir haben sich zum Beispiel in den letzten Jahren ~23 Gigabyte Fotos+Videos angesammelt). Damit soll leider ab dem 1. Juni Schluss sein 🙁 (artikel von techbook.de). Wenn man sich bei der Suche nach einem neuen Zuhause für seine Urlaubsfotos
- Die Auswahl aufheben
- Zu Google Fotos scrollen
- Häkchen setzen
- Weiter
- Bei größeren Datensätzen sollte es kein Problem sein, eine Dateigröße von bis zu 50 Gigabyte auszuwählen
- Kurz warten bis der Download verfügbar ist (dauert evtl 5 Minuten)
- Download starten
Als nächstes kann man seine Fotos in „Google Fotos“ guten Gewissens löschen (man hat sie ja mittlerweile auf dem lokalen Datenträger).
Geekbench 4 Pro is an android app that usually can be purchased in the google play store for 10,99 €. It is currently free for installation! The software evaluates the performance of the mobile device’s cpu. It is also multi-core aware. It is also able to test the mobile gpu performance. The test results into a programmer readable output in json format. My Huawei P8 phone came up with this. Once I had finished the test, the result was immediatly online (here)
The app comes really handy if you are currently in the situation to decide for a new smartphone. It also strengthens the impression that your phone is not up to date when you compare with other devices 🙂
Introduction
I am writing Java code with Emacs from time to time and normally miss a lot of the „sugar“ that „the other IDE I am using“ provides me with.
From time to time I also stretch my fingers and try to improve my Emacs experience with some tooling. This resulted recently in a short coding session the outcome of which was a Maven Plugin (For those used to maven language: It’s only a single mojo), that generated a .dirlocals.el file for emacs that contains mavens understanding of the compile time classpath. I am actually a bit suspicious about the result of this since it looks much too easy.
The emacs maven plugin
„The“ emacs maven plugin is hosted on github here It collects jar files from mavens compile time classpath and writes them into a .dir-locals.el file.
How to use it
What do do with this information is yet undetermined. Those who are used to Java programming know that the classpath is one of the more important parameters when running/compiling Java programs. It’s also an essential piece of information when writing Java code: What is not in the classpath should not be in the source code 🙂
I have taken the classpath and extracted the names of the classes contained within the jar files and then build a function that inserts for me the class name:
(defun java-read-classes-from-classpath ()
"Iterate over classpath and gather classes from jar files.
Evaluates into one large list containing all classes."
(let* ((jarfiles nil)
(jarfile nil)
(result '()))
(progn
(dolist (file (directory-files (concat jdk-location "jre/lib/") t "\.\*.jar\$"))
(setq jarfiles (cons file jarfiles)))
(dolist (file (reverse java-classpath))
(setq jarfiles (cons file jarfiles))))
(with-temp-buffer
(while jarfiles
(progn
(setq jarfile (car jarfiles)
jarfiles (cdr jarfiles))
(call-process "/usr/bin/unzip" nil t nil "-l" (expand-file-name jarfile))
(goto-char (point-min))
(let ((end 0)
(classname ""))
(while (search-forward ".class" nil t nil)
(end-of-line)
(setq end (point))
(beginning-of-line)
(goto-char (+ (point) 30))
(setq classname (substring
(replace-regexp-in-string "/" "."
(buffer-substring-no-properties (point) end))
0 -6))
(setq result (cons classname result))
(forward-line 1)
(beginning-of-line))
(erase-buffer)))))
result))
And the source for auto-complete:
(defun java-insert-classname-completing-read (prefix)
"Query the user for a class name.
With prefix argument insert classname with package name. Otherwise omit package name."
(interactive "P")
(let* ((default (thing-at-point 'symbol))
(classname (completing-read "Class: " java-classes-cache)))
(if prefix
(insert classname)
(insert (replace-regexp-in-string ".*\\." "" classname)))))
(defun java-mode-process-dir-locals ()
(when (derived-mode-p 'java-mode
(progn
(when (stringp java-project-root)
;; sell the stock from emacs-maven-plugin:
(progn
(setq-local java-classes-cache (java-read-classes-from-classpath)))
(local-set-key (kbd "C-x c") 'java-insert-classname-completing-read))))))
Now I can insert classnames (with or without package) enhanced with completing read (I do with ivy)).
The kill ring is a nice thing to have. Only the navigation is a bit to uncomfortable (I am refering to doing C-y again and again until the to desired element is found). So this is what I came up with to feel more ‚comfy‘:
(defun pre-process-kill-ring-element (element)
(replace-regexp-in-string "^[[:space:]]+" ""
(replace-regexp-in-string "[[:space:]]+$" "" (substring-no-properties element))))
(defun preprocess-kill-ring ()
(let ((result nil)
(element nil))
(dolist (element kill-ring)
(progn
(setq element (pre-process-kill-ring-element element))
(when (not (or
(eq 0 (length element))
(string-match-p "[\r\n]+" element)))
(setq result (cons element result)))))
(reverse result)))
(defun browse-kill-ring ()
(interactive)
(insert (completing-read "Pick an element: "
(preprocess-kill-ring))))
(global-set-key (kbd "C-M-y") 'browse-kill-ring)
Einleitung
Wenn man im Zusammenhang von Audio-CD’s von rippen spricht, meint man für gewöhnlich den Vorgang die Daten der CD auf einen Festplatte zu kopieren und sie aus dem wav-Format, in dem sie auf der CD vorliegen in ein komprimiertes, platzsparendes Format zu konvertieren.
Audio-WAS?
Da Audio CD’s schon seit längerem durch andere Medien überholt wurden und Musik zunehmend über andere Wege verteilt wird, läuft die Kunst zu rippen Gefahr in Vergessenheit zu geraten 🙂 Da es ferner ein Vorgang ist, den ich selbst so selten durchführe, dass ich jedes mal aufs neue nachsehen muss wie es genau geht, schreibe ich mir hier einfach mal auf wie ich es mache…
Zur Sache
Rippen ist eigentlich ein Vorgang in zwei Schritten:
- kopieren
- Konvertieren
Kopieren
Zum kopieren bietet sich cdparanoia an – der Beschreibung nach „an audio CD reading utility which includes extra data verification features„. cdparanoia kopiert also die Daten von der CD auf die Festplatte. Nicht mehr – aber auch nicht weniger 🙂 Folgendes Beispiel ist der manpage entnommen und kopiert die einzelnen Tracks einer CD in das aktuelle Arbeitsverzeichnis:
$ cdparanoia -B # that's all
Konvertieren
Beim konvertieren werden die Daten aus einem Format in ein anderes Format „übersetzt“. Dieser Aufgabe übernimmt üblicherweise ein Codec (COder/DECoder) – beim mp3 Format zum Beispiel lame. Natürlich gibt es auch Alternativen zu mp3 und zu jedem Codec gibt es Tools, die einem helfen die Daten aus dem jeweiligen Format zu lesen, bzw in dem Format zu schreiben.
Die Dateinamen anpassen
Mittlerweile wurden die Daten von der CD kopiert und konvertiert und liegen eigentlich fertig in… Ja
, adolescents might particularly be based if they do rapidly lead categories and right, they possess to plan Federation where they can not use it. By getting weak preparations, these low cases have attached factors of antibiotics especially at responsibility to a volume of extra antibiotics.
, wo eigentlich? In file_01.mp3?
Jetzt wäre es eigentlich an der Zeit einen vernünftigen Dateinamen zu vergeben 🙂 Das kann man natürlich mal eben händisch erledigen. Aber wenn man das jetzt für jeden Track erledigen soll, wird das doch irgendwann lästig. Zum Glück wurde das Problem schon erkannt und es gibt einen Weg herauszufinden welche CD da gerade gerippt wird: cddb. Mit Hilfe der CDDB lässt sich der Prozess der Namensvergabe automatisieren.
Die all-in-one Lösung
Rippen ist offensichtlich ein Vorgang der so oft genug durchgeführt, dass die Verwendung einer Software die diese Aufgaben automatisiert erledigt mehr Zeit einspart als die Entwicklung einer solchen in Anspruch nimmt. Wäre Software materiell gäbe es einen Berg von Programmen die kopieren, konvertieren und CDDB abfragen. Ich habe für mich RipIt aus diesem Berg ausgegraben und bin damit ganz zufrieden. Ein DEB-Paket wird mit der Linux Distribution meiner Wahl ausgeliefert – was mir die Installation enorm erleichterte. Es hilft außerdem in den zwei Anwendungsfällen die bei mir des öfteren Vorkommen:
Musik
Ich benötige ein Backup von einer verstaubten Musik-CD die ich auf dem Dachboden gefunden habe. Hier sollte möglichst jeder Track einzelnen gerippt werden.
ripit # los gehts...
Hörspiele
Ich möchte ein Backup von einer Hörspiel-CD erstellen (bevor sie im Kinderzimmer so arg verkratzt wird
, dass sie nicht mehr funktioniert). Hier soll bitte der ganze Inhalt der CD in einer Datei abgelegt werden.
ripit --merge 1- # und ab die Post...
I am using projectile since a couple of days and really love how I can change to another file in the current project (using C-c p f). However I am facing a little issue when I am generating a tags file for etags. I get
projectile-regenerate-tags: ctags: invalid option — ’e’
Try ’ctags –help’ for a complete list of options.
Whenever I „C-c p R“ even though I have picked etags as my tags program. However after reading this I have constructed a tags command for me like this:
(setq projectile-tags-command "etags -a TAGS \"%s\"")
Ans it looks like now it works nicely…
The Emacs built-in command (garbage-collect) gives detailed information about the data structures that currently consume memory. It is propably not the most usefull information but I wanted to collect the data and plot it. I started with writing functions to access the list returned from (garbage-collect):
(defsubst get-mem-conses (mi)
(let ((data (nth 0 mi)))
(/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
(defsubst get-mem-symbols (mi)
(let ((data (nth 1 mi)))
(/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
(defsubst get-mem-misc (mi)
(let ((data (nth 2 mi)))
(/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
(defsubst get-mem-string-header (mi)
(let ((data (nth 3 mi)))
(/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
(defsubst get-mem-string-bytes (mi)
(let ((data (nth 4 mi)))
(/ (* (nth 1 data) (nth 2 data)) (* 1024 1024.0))))
(defsubst get-mem-vector-header (mi)
(let ((data (nth 5 mi)))
(/ (* (nth 1 data) (nth 2 data)) (* 1024 1024.0))))
(defsubst get-mem-vector-slots (mi)
(let ((data (nth 6 mi)))
(/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
(defsubst get-mem-floats (mi)
(let ((data (nth 7 mi)))
(/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
(defsubst get-mem-intervals (mi)
(let ((data (nth 8 mi)))
(/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
(defsubst get-mem-buffers (mi)
(let ((data (nth 9 mi)))
(/ (* (nth 1 data) (nth 2 data)) (* 1024 1024.0))))
Then I had need for a function that will be called periodically. This function will call (garbage-collect) and store the data in the file-system:
(defun collector (filename)
"Write memory data into file with FILENAME."
(let ((mi (garbage-collect)))
(with-temp-buffer
(insert
(format "%f %f %f %f %f %f %f %f %f %f %f\r\n"
(float-time)
(get-mem-conses mi)
(get-mem-symbols mi)
(get-mem-misc mi)
(get-mem-string-header mi)
(get-mem-string-bytes mi)
(get-mem-vector-header mi)
(get-mem-vector-slots mi)
(get-mem-floats mi)
(get-mem-intervals mi)
(get-mem-buffers mi)))
(let ((message-log-max nil))
(append-to-file (point-min) (point-max) filename)))))
Next I have need for a function that starts the collection process and one that stops it again:
(defvar collector-timer nil)
(defun start-collection (filename interval)
(interactive "FEnter filename:\nMEnter interval: ")
(setq collector-filename filename
collector-timer (run-at-time
2
(string-to-number interval)
'collector filename)))
(defun stop-collection ()
(interactive)
(when (timerp collector-timer)
(cancel-timer collector-timer)))
Finally the collected data should be plotted into a nice graph:
(defun plot-data (datafile imagefile)
(interactive "FEnter data-filename: \nFEnter image-filename:")
(let ((gnuplot (start-process "gnuplot" "*gnuplot*" "gnuplot")))
(process-send-string gnuplot "set term png\n")
(process-send-string gnuplot (format "set output \"%s\"\n" imagefile))
(process-send-string gnuplot "set grid\n")
(process-send-string gnuplot "set title \"Emacs memory consumption by category\"\n")
(process-send-string gnuplot "set xlabel \"interval\"\n")
(process-send-string gnuplot "set autoscale\n")
(process-send-string gnuplot "set ylabel \"2^{20} bytes\"\n")
(process-send-string gnuplot (format "plot \"%s\" using 2 title \"cons cells\" with lines" datafile))
(process-send-string gnuplot (format "
, \"%s\" using 3 title \"symbols\" with lines" datafile))
(process-send-string gnuplot (format ", \"%s\" using 4 title \"\" with lines" datafile))
(process-send-string gnuplot (format ", \"%s\" using 5 title \"string header\" with lines" datafile))
(process-send-string gnuplot (format ", \"%s\" using 6 title \"string bytes\" with lines" datafile))
(process-send-string gnuplot (format ", \"%s\" using 7 title \"vector header\" with lines" datafile))
(process-send-string gnuplot (format ", \"%s\" using 8 title \"vector slots\" with lines" datafile))
(process-send-string gnuplot (format ", \"%s\" using 9 title \"floats\" with lines" datafile))
(process-send-string gnuplot (format ", \"%s\" using 10 title \"intervals\" with lines" datafile))
(process-send-string gnuplot (format ", \"%s\" using 11 title \"buffers\" with lines\n" datafile))))
Turns out that my emacs usage was really calm in the time when I sampled the data 🙂 In fact I have entered some kilobytes of test data into the scratch buffer with two seconds between two samples. (resulting chart lost, sorry…)
I recently wanted to have some more information about which of the packages I am using contributes more time to the total (emacs-init-time) I use to keep my emacs init code in a single file and I manually divide the file into sections of related code. A section is opened by entering a carefully prepared comment string and ended by another one so that it looks like this:
;; [ an example section
(some emacs lisp)
(and some more)
;; ]
Starting from here I could alter my init file to write down time values from the stop-clock (And since I also need to know to which section the time value belongs – the section names). Therefore I invoked query-replace-regexp and query-replace to surround my sections with emacs lisp to collect the necessary information:
query replace regexp: FROM: ;; []] \(.*\) TO: ;; [ \1[C-q C-j](add-to-list 'section-names "\1") query replace: FROM: ;; ] TO: (add-to-list 'init-runtime (get-internal-run-time))[C-q C-j];; ]
My example section would then look like this:
;; [ an example section
(add-to-list 'section-names "an example section")
(some emacs lisp)
(and some more)
(add-to-list 'init-runtime (get-internal-run-time))
;; ]
After the whole file is processed I end up with two lists (I called them section-names and init-runtime). These I then further-process. So I switched to an org-mode buffer and entered
#+NAME: perf_data_names #+BEGIN_SRC emacs-lisp (reverse section-names) #+END_SRC #+NAME: perf_data_times #+BEGIN_SRC emacs-lisp (progn (let ((bak-init-times init-runtime) (next nil) (result '())) (while bak-init-times (setq next (car bak-init-times)) (setq bak-init-times (cdr bak-init-times)) (add-to-list 'result (+ (nth 2 next) (* (nth 1 next) 1000000)))) result)) #+END_SRC #+BEGIN_SRC python :var sections=perf_data_names :var times=perf_data_times :results output xtics="set xtics (" numsections=len(sections) for i, section in enumerate(sections): xtics = xtics + "\"{0}\" {1}".format(section, i+1) if i<numsections-1: xtics = xtics + ", " xtics+=")" print("set grid") print(xtics) print("plot \"-\" using 1:2 title "Stopclock times") for i, time in enumerate(times): print("{0} {1}".format(i+1, time)) print("e") #+END_SRC
This results in a snippet of text that can be fed into gnuplot! Gnuplot kindly generates the following image:
(plot is lost, sorry…)
It turns out that no one can really be blamed for beeing responsible and it must be taken into consideration that some of the loading may be deferred and is not subject to the analysis (No, I did not look up how exactly use-package is deferring package loading). Some sections take more time then others:
- packages
- s
- global appearance
- yasnippet
- org mode
- java mode
- elpy python
- magit
There are a couple of symbols the emacs‘ lisp interpreter gives special meaning to. Since for some reason these never made it into my long-term memory I collect them here for later reference. Quotes are copied from all over the internet (mostly from GNU Emacs Lisp reference). A reference is given in the last column.
Symbol | Meaning | Reference |
---|---|---|
# | The sharp quote (or function quote puttygen download , or simply #‘) is an abbreviation for the function form. It is essentially a version of quote (or ‚) which enables byte-compilation, but its actual usefulness has changed throughout the years. | reference |
` | Backquote constructs allow you to quote a list, but selectively evaluate elements of that list. | reference |
: | A symbol whose name starts with a colon (‘:’) is called a keyword symbol. These symbols automatically act as constants, and are normally used only by comparing an unknown symbol with a few specific alternatives. | reference |
, | Unquotes expressions in backquote-constructs. | reference |
' | Prevents its single argument from beeing evaluated | reference |
? | Introduces a „character literal“ (eg ?a). Evaluates to the printed representation of the character. | reference |
,@ |
Splice an evaluated value into a list | reference |
; | Introduces a comment. | reference |
Neueste Kommentare