This is a cheat sheet and my personal best practice collection aimed at ongoing developers, DevOps engineers, sysadmins anyone else trying to get started with Git. After roughly 10 years of using it myself and having successfully introduced quite a few fellow sysadmins and developers to it, I thought it’s time for a write-up.
Basically this document lists Git commands including --options
I personally find most helpful and wish I’d known when I first got in to contact with Git.
Close to the end of the document there is an examples section that runs through possible real-life scenarios, putting some of the contents together into common workflows.
If you have a rough idea of how Git works already, I recommend reading these chapters to get you going quickly:
Use the rest of the chapters as a reference and cheat sheet only!
Following that, I recommend proceeding to the Best practice and theory section.
Don’t feel overwhelmed by the amount of commands in this document, as often I’m presenting several possible ways of achieving the same. Pick and remember those you feel the most comfortable with.
Use these listings as a cheat sheet only. Type the commands into your terminal each time, rather than copying/pasting, and you will soon have learned them by heart.
There is dozen’s of Git how-tos out there, Git is super-powerful, nobody knows everything about it. This document tries to be practical and give some workflow ideas already without getting into too much detail on all the possibilities.
Make using Git a habit! Creating habits for most humans at first feels like a burden but once it is established each execution feels like an achievement.
I promise Git will be a life-safer, your second brain and a tremendous help even for the tiniest of projects.
Git is offline first! Despite of common misbelieve, Git does not require any server part!
Git is not simply an upload tool for GitHub, Gitlab, Gitea or any other public or private Git backend system. It can certainly be used for for that but it’s not its main purpose.
Git is a “versioning” or “version control” tool. That does not literally mean it’s ment to create “release versions” of a program. It can certainly help with that but it’s not its sole purpose. A more descriptive name for tools like Git is “revision control”. If desired read the wikipedia entry about version control.
Git not only helps to save your code, it is a documentation tool as well. Whenever you go back to a previous project to look up something “you know you must have done” but don’t recall the details, you’ll be happy to find a description in your own words as well as an isolated version of “the changes required” to achieve it. Looking up things in horribly huge programs will be a thing of the past!
No coding project is too small to be put into a Git repo!
The main goal of this document is to give a starting point for personal coding projects.
When working in teams though, it’s all about common team rules. Some of the things in this document might be opinionated but I strongly believe that every developer/team member should at least know these basics, to successfully collaborate.
Check out the appendix chapters if you’re not savvy with any of the above.
or The “staying informed” commands or simply The “looking around” commands
You use them all the time: Before committing, after commiting, whenever you get back to work, when you are not sure about the state of your repo, …
As a rule of thumb, before you do anything with Git, use them!
They all are read-only and can be issued safely any time.
git status |
Displays files with uncommited changes, untracked files and gives hints in what state Git is in general. Your most important git command! |
git log |
The history. Displays the list of commits including all the details. |
git log --oneline |
A brief variant of the history. |
git show <hash> |
Displays the diff(erence) of one commit to its previous one. In short, it displays a commit referenced by its commit-hash. Omit <hash> to see the last commit (the top-most in the history) |
git diff |
Show the diff between uncommited changes and the last commit (the top-most in the history). |
git diff <hash1> <hash2> |
Show the diff between the two commits |
Hint: The Commit-hashes you see in git log
are pretty longish, but they can be abbreviated to a certain amount. If you look at git log --oneline
you see about the maximum a hash can be abbreviated. Just try it out by looking at git log
and then marking and copying only a part of the hash you see, starting from the left. Use that hash in another command, for example git show abcdef123456
. It should work!
Coming to Git’s main purpose: Tracking changes in files. The “chunks” we save those changes in, Git calls commits.
My personal rule of thumb and number 1 recommendation to any Git-non-experts: Never commit without double checking what will be committed with git diff
and git status
. Make it a habit!
git add <filename> |
Hand over a file into Git’s control. It will be added with the next commit. We call this “staging for a commit” |
git add . |
Hand over all files in the directory into Git’s control. It will be added with the next commit. |
git commit |
Finally record all staged changes into a new commit. You’ll be prompted to write a description - The Commit Message. |
git commit -m "Sentence" |
Finally record all staged changes into a new commit. You pass the description on the command line already. |
git commit -a |
A combination of “staging” and “commiting”: All changes in files known to Git will be staged and commited. You’ll be asked for a description. |
git commit -a -m "Sentence" |
The same as above, providing the commit message on the command line already. |
git commit -v |
My personal favorite: Comitting all staged changes. The editor prompting to input the description will additionally show the diff of your change. This helps to describe your changes right within the editor! |
git commit -v -a |
The same as above but do it for all changes in all files known to Git! |
Hint: After staging (git add
ing) something for a commit, git status
tells you that it’s staged (green). If you change a file (again) _after that, you have to stage that file again (use git add
again)! git status
will tell you that, so always use it before finally committing!_
Hint: Commit often! Don’t make the beginners mistake of finishing the program, then doing one commit, just so you can upload it to GitHub!
If a commit is done it can still be changed, you can “amend” to it. The order of commits can also be changed (but that’s not demonstrated here).
Note (generalizing): Changing history is perfectly fine in personal projects but needs to be thought through carefully when working in teams. Don’t worry about it for now and use below commands often to fix/improve your existing commits
git commit --amend |
Add all staged changes to the last commit (and be prompted for commit message changes). If no staged changes are present, this simply is the command to correct your commit message! |
git commit -a --amend |
Add all changes in all files to last commit |
git commit -a -v --amend |
My personal favorite: Add all changes in all files to last commit, adapt commit message, while additionally showing the diff of all those changes within the commit message editor. This is an immensive help when writing commit messages! |
To change the current state of a repo you can jump to any commit referencing it by its commit-hash. A commit can also have a “human readable” name, that is called a branch. The default branch Git creates for you is called main
(or master
in older Git versions).
git checkout <hash> |
Set the repo to the state of that commit |
git checkout main |
Set the repo back to the state of the branch named main (essentially this usually is the command to return to the most recent commit.) |
Any directory, no matter if entirely empty or populated with files already can be translated into a Git repo.
git init |
Initialize the current directory as a Git repo (essentially creating a hidden subdirectory called .git ) |
Hint: Typical steps after git init
are creating and committing a .gitignore
file, and adding and committing all files you might already have (git add .; git commit
)
Sometimes you’d want to “move aside” your current, uncommited changes (without commiting them) to do something else with git that requires it.
git stash |
Move aside all unstaged changes onto the stash stack |
git stash pop |
Get back the top-most entry from the stash stack |
Hint: One use-case of stash
is whenever you forgot something in your last commit and had started to work *on the next thing* already (i.e. changed code already): git stash
all those changes. Fix what you forgot and git commit -a --amend
to your previous commit. Get back your changes with git stash pop
and move on.
git rm <filename> |
Delete a file and additionally stage that change for the next commit |
Hint: Deleting with regular operating system methods (File browser, rm
command) is certainly also possible. You’d have to stage that change with git add <filename>
in that case. git rm
. spares you that step
Hint: Keeping deletions in separate commits, makes it easy to restore those files later on.
Sometimes you experimented with something which doesn’t work anyway, want to get rid of it and start from a “known good” state.
git reset --hard HEAD |
Delete all uncommited changes and reset the repo the state of the previous/the top-most commit. HEAD references the top-most commit! |
Hint: Instead of HEAD
any commit-hash or branch-name can be used to set back the repo to exactly that state!
Any change you ever recorded with Git can be restored, even entire file deletions!
Lookup the change you want to retrieve with git log
(read your commit messages) and display the details (the actual changes) of a commit with git show <commit-hash>
. If you found what you were looking for, use below’s checkout command to get that file’s change back.
git checkout <commit-hash> <filename> |
Get back the state of the file as it was in the specified commit. You will get it as staged changes already. Use git status and git diff --staged to see them. |
Hint: Usually if you find the commit that _removed something (a line or even an entire file), you would want to checkout
from the commit before that, because it still has that required data present (Your recorded the deletion back then, thus want to restore from the state right before that operation!)_
A local Git repo can be connected to another Git repo hosted on a server. Git calls those adresses remotes. A Git repo can be connected to one or multiple remotes.
git remote -v |
Display all remotes |
git remote add myrepo https://github.com/username/reponame.git |
Connects the local Git repo to the remote repo hosted on GitHub and naming the remote myrepo |
When a repo is existing on a server already, it can be cloned, which creates a local copy of that repo into a subdirectory including a remote entry already.
git clone https://github.com/username/reponame.git myrepo |
Clone the repo into the local directory myrepo |
git clone https://github.com/username/reponame.git |
Clone the repo into the local directory reponame (automatically picks the remote repo’s name for the foldername) |
As long as proper remote entries are available, data can be transferred to servers, as well as retrieved from them. Assuming there is only one remote configured, use:
git push |
Send to the remote |
git pull |
Retrieve from remote |
When collaborating on the same repository, Git needs to handle scenarios where others may have pushed changes to the server while you worked on your offline copy. If you attempt to push your latest commits, Git will prevent it. Instead, you must first pull down those changes, integrate them locally, and then proceed with pushing your changes. The command best suited for this task is:
git pull --rebase |
Move local changes aside, retrieve new commits from remote, integrate them into local copy, and put your previous local changes on top of it again. Now Git will allow sending your changes with git push . |
Since this document’s main focus is not about Git as a collaboration tool, you shouldn’t worry too much about this last concept for now. You’ll get guidance from your team colleauges in the future.
Hint: There is one reason though this concept might come in handy for you: If you happen to work on the same Git repo yourself from different computers!
One of the things people struggle most with when starting out with Git is deciding what and when to commit…
Writing good commit messages is on one hand an art form itself and on the other: Something a developing team needs to work out and agree on and if working alone, totally up to you!
I’ve read dozens of articles about writing commit messages but this one rule is sticking with me ever since and I found it most useful:
So for example, your commit message could be: Sort entries by date on user accounts page
, thus that sentence wouldn’t be perfect english but it would make sense: If this commit is applied, it will sort entries by ID on user accounts page.
git log --oneline
)A proper commit message could look like this:
Sort entries by ID on user accounts page
- By using the built-in sort method for arrays before passing it
to the Table component.
- FIXME remember to fix the leading-zero problem later on!
I recommend this article on freecodecamp if you want to further improve your commit message skills.
.gitignore
Seldomly you’d want each and every file in your project repo to be tracked by Git. It makes sense to generally hide some files from it.
A .gitignore
file resides in the root of the repo and has a very simple syntax. Each line represents a filename or part of a filename. Attaching /
ensures matching a directory.
Keeping .gitignore
changes in their own commits and not mixing with actual code commits is a good practice.
Examples for files you usually do not want to be commited ever are:
.vscode/
)node_modules/
).DS_Store
, Thumbs.db
).dist/
, .build/
).cache
, .tmp
)A .gitignore
file for a JavaScript project could look like this:
.DS_Store
.vscode/
.cache
node_modules/
build/
dist/
requirements_checklists_for_this_project/
Hint: Some tools/toolchains bring their own .gitignore
template already, for example projects created with vite.js
, or npx create-react-app
. Use those provided .gitignore
files, commit them separately and add to them if required during the course of your project.
Hint: GitHub lets you select from a list of .gitignore
templates when creating a fresh repo.
Hint: GitHub saves their collection of templates in a public repo. For example this is their JavaScript/Node template, you can copy/paste to your existing project!
Beginning developers tend to comment out a lot of stuff they are not sure about, want to keep it for reference and the likes. That basically is a good thing but there is a better way: Learn to read diffs and how to get those changes back!
So instead of putting a comment mark in front of a line (#
, //
, …), simply delete it. Keeping such a change (the deletion) in a separate commit with a descriptive commit message, allows for easy retrieval later on. Often the purpose of “commenting out” is because of “keeping it for reference”.
So for example we deleted a line from a CSS file and recorded that change in a separate commit, we can easily look it up later on with git show <commit-hash>
:
commit 27399aedf2c6f4295b80e42ad2970006f9317bde (HEAD -> main)
Author: J0J0 Todos <jojo@peek-a-boo.at>
Date: Thu Jan 18 11:28:38 2024 +0100
Delete border-bottom style of .nav-link.active CSS class
diff --git a/styles.css b/styles.css
index 023332d..b8f2c91 100644
--- a/styles.css
+++ b/styles.css
@@ -52,7 +52,6 @@ hr {
.nav-link.active {
color: rgba(var(--dark_800), 1);
- border-bottom: 1px solid;
}
Hint: Git remembers everything for you! Use this valuable feature!
Hint: Looking at diffs might be confusing at first. Get accustomed to it, it is an essential skill for any striving developer.
Hint: In most default terminal configurations diffs are presented in colors already. A tremendeous help for reading them. If that’s not the case, research how to fix your shell’s configuration!
Branches mostly come in when working in teams and thus are not a focus in this write-up. A very basic understanding for them would be if you think of a branch as a “human readable name” for a specific commit which additionally allows to stack further commits on top of it.
For now it is sufficient if you stick with the idea that a branch simply is a “human readable name” for a commit hash.
In this chapter you’ll find some recipies for real-life scenarios putting to use some of the above commands and concepts.
You have a directory containing a static website project already. You have files named index.html
, styles.css
, scripts.js
. You plan to use some more JavaScript in the project and might install some packages from npm
soon.
You cd
into the directory and run git init
.
To make sure Git never commits some things, you create a file .gitignore
in the directory containing the following:
.vscode
node_modules
You add and commit:
git add .gitignore
git commit -m "Initial commit, adding .gitignore"
You realize that you want to elaborate on your commit message, so you call:
git commit --amend
You extend your commit message to be:
Initial commit, adding .gitignore
to make sure the vscode config dir and any installed node modules won't ever
be committed to Git.
In a second commit you want to add what you already had coded in your html, css and js files. You first inform yourself which files git sees as “untracked files” by issuing git status
. You see your three files as “untracked” and decide you want to add them all together in one commit:
git add .
git commit -v
You get presented an editor that prompts you for the commit message and additionally displays the contents of your files (-v option), which helps you to find the right words for your message. You describe what you roughly had coded so far:
Basic structure of the project
The following features are working so far:
- ...
- ...
- ...
You save and quit the editor and have a look at your history with git log
. You see your two commits and think it’s enough for the day.
The next day you come back to your project, you decide you want to use the axios
JavaScript package. You install it with npm install axios
and then check with git status
which new files/dirs Git now sees as “untracked”. It shows two files named package.json
and package-lock.json
. The node_modules
directory (that npm
just created) is not being shown since yesterday you made sure it will be ignored already. You commit those two files:
git add package.json package-lock.json
git commit -m "Add npm package files after installing axios"
You make sure with git status
that there are no “untracked” files and open changes.
You start working on your project’s html, js and css files and after some time have a navigation bar working that required changes in all three files. You display the changes with git diff
, skim quickly through the diff to remind yourself what roughly you had coded and then commit them with:
git commit -a -v
You elaborate in the editor being presented what new features are working.
You create a new React app using npx create-react-app myapp
.
You cd
into the myapp
directory and run git status
. You realize that create-react-app
already initialized the directory as a git repo. You don’t have to issu git init
.
git status
also tells you nothing to commit, working tree clean
, which seems odd but after issuing git log
you understand what’s going on: create-react-app
already did an initial commit for you. It looks like this:
commit ff28dc2bb2b0f40d625e790fbdaf7895041232ec (HEAD -> main)
Author: J0J0 Todos <jojo@peek-a-boo.at>
Date: Thu Jan 18 14:06:47 2024 +0100
Initialize project using Create React App
You type git show
to view that commit. You skim through the changes quickly, see that .gitignore
already lists a lot of stuff, so it seems you don’t need have to bother about that anymore. You find a default README.md
provided you would probably want to adapt later on. All the source code of the example app seems to reside below a src/
directory.
You start by deleting stuff you most probably won’t use right now. You’d like to record these deletions in an orderly fashion, to have a way to look up how it was done later on.
You stage the deletion of the logo with git rm src/logo.svc
as a first step. You check with git status
.
You remove all code that relates to that log file in the index.js
git diff
now tells you what exactly you had removed and you check visually if the change looks alright:
diff --git a/src/App.js b/src/App.js
index 3784575..e914079 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,11 +1,9 @@
-import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
- <img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
@@ -22,4 +20,4 @@ function App() {
);
}
You see that the import logo...
line will be removed and so will the line with the <img>
tag. You have another look on the real App in the webbrowser and decide that it’s fine.
You stage all those changes and commit with:
git commit -a -m "Remove default logo and all code referencing it"
You convince yourself again that your deletions are recorded nicely, so you can look it up if you ever need it later. You issue git show
and see that now you have everyhing together in one commit: The svg-logo-file deletion as well as the deletions of the relevant code lines (you saw a moment ago when checking with git diff
prior to committing!)
As a final step you check git log
and it looks like this:
commit 61eacfab5213daa3685329976ed5a259a0fc15f4 (HEAD -> main)
Author: J0J0 Todos <jojo@peek-a-boo.at>
Date: Thu Jan 18 14:45:08 2024 +0100
Remove default logo and all code referencing it
commit ff28dc2bb2b0f40d625e790fbdaf7895041232ec
Author: J0J0 Todos <jojo@peek-a-boo.at>
Date: Thu Jan 18 14:06:47 2024 +0100
Initialize project using Create React App
Whenever you would want to look up how that logo of the default App was implemented, you simply would issue git show 61eacfab52
.
You do some more cleanup commits but decide that cleaning makes tired. It was an important first step but you need a break. You look forward to starting fresh with an already tidied up project repo after your break :-)
Often beginning developers come from a mostly GUI oriented world of computers. These chapters should give the crashiest of crash courses to command line interfaces.
UNIX-like commands typically follow a pattern with a command
accepting options, often with long (--something
) and abbreviated (-s
) forms. Due to the limited alphabet, shortforms may not be intuitive, but you can find them using --help
or -h
.
A shell, very simply put, is a program that accepts interactive command inputs from a user. Two common ones are named bash
and zsh
.
A shell on a graphical operating system requires a Terminal Emulator Program to be run!
So to be exact, a shell is not a Terminal. A Terminal just runs a shell as the very first thing when launched.
git
is a UNIX-like command. I suppose because of its UNIX-like nature, things like git-bash
were invented to adapt it for Windows (where command syntax actually follows a different pattern (eg. /s
for an option instead of -s
).
git-bash
is a Windows version of the widely-used bash
shell known from Linux/BSD/macOS and other *NIX systems. It’s not directly related to the functionality of the git
command itself.
Nowadays macOS and Linux often use zsh
as the default shell, which in fact is a bash
-compatible shell.
To find out which shell you are using, do
echo $SHELL
If that points to something like this: /bin/sh
, and you still are not sure, do
sh --version
To configure your shell, find and edit the .zshrc
or .bashrc
file in your home directory. If not existing, simply create it!
git-bash
also uses .bashrc
.
The cd
command without any arguments always sends you “back home”. To find out where your homedirectory is located exactly, use echo $HOME
)
Append a line to the config file of your shell, to modify the EDITOR
environment variable, which sets the default editor for CLI tools like git
. For example on Windows to use notepad:
export EDITOR="notepad"
Restart the shell for changes to take effect (use the exit
command). Verify the new setting with echo $EDITOR
.
It helps to choose an editor that supports syntax highlighting, since Git can display source code within the editor while writing commit-messages. So on Windows you could use notepad++
if you have it installed, or nano
, which ships with git-bash, on macOS also try nano
.
If for some reason setting EDITOR
does not work, it could be that Git’s own config overrides it.
git config --global --get core.editor
git config --global core.editor notepad
The git-bash installer asks a ton of questions, those are worth considering:
EDITOR
shell environment variable. If you’ve had changed it.Hint: To change settings after you’ve completed setup already, re-run the git-bash installation fiile.
The git-bash installation, comes with it’s own terminal emulater named minTTY.
Microsofts “own” (but developed open-source) Windows Terminal is a good alternative to minTTY.
Hint: To use git-bash
as the default shell when opening a new Windows Terminal window, go to its Settings dialog -> Startup -> Default Profile, and select “Git Bash”
Hint: Copy marked text in Windows Terminal to the clipboard by using the Return key. Paste text into it with a “Right Click”.
On macOS I find the default Terminal app lacking. My recommendation is the Open Source alternative iTerm. It remembers the state of open terminals when your computer reboots (or even crashes), it supports “split panes” and offers loads of other handy features!
For Linux, I personally was using Terminator
for ages, but nowadays like Konsole
. Both supporting “split panes”. Numerous excellent alternatives exist on any Linux distro.
vim
stands out as a super-powerful text editors that became famous with UNIX admins and programmers because of it’s wide spread availability on commercial *NIX systems as well as Linux and macOS. For those new to vim
, its unconventional interface can be intimidating.
Here are some life saving concepts you need to know to at least write commit messages with it. If you don’t want to go down that rabbit hole, just reconfigure your shell to use your favorite editor as shown in the previous chapter.
vim
initiates in the normal mode, requiring commands for actions. Copy lines by moving over them and typing yy
. Paste elsewhere with p
. (just an example, so you get the idea!)i
command.Esc
(back to normal mode). There’s also an ex mode, accessed with :
.:w
.:q
.:wq
.Many might exclaim, “That is crazy!” Indeed, it might be, but the strength of vim
lies in its design for complete mouse-less control. Mastering numerous shortcuts enables swift and efficient editing.
Anyway, choose your poison and HTH!
A slightly off-topic chapter around the “dependency hell” new developers will face often. JavaScript programmers want to use NVM, Python programmers have several options, I recommend pyenv.
Allows changing the Node version during a shell session with simple commands. Read the installation chapter in the official GitHub repo.
nvm
usually is installed directly into the the user’s home directory into a hidden directory named .nvm/
. A main concept of how it works is it’s simply writing a few lines to the user’s shell configuration (.bashrc
, .zshrc
). Details if interested in this section of its docs.
For developing on Windows there’s another project existing. It’s unrelated to the original nvm
project and follows an entirely different approach. Get nvm-windows here
Hint: There is a caveat with nvm-windows when being used with git-bash: Due to its differnt nature (not configured via shell configuration files) it does not work when git-bash is run via minTTY (The original “Git Bash” entry in Start Menu!). You have to run git-bash via a Windows Terminal profile as described previously.
pyenv
is my recommendation for creating self-contained virtual Python development environments without touching the “built-in” version of the OS. Learn what it does and follow its excellent installation documentation.
But wait, why not leave a little big history here?
Back in autumn 2018 I decided that it’s about time for my DJ set notes to move from my Moleskin notebook to a proper digital version. Before that, I went for some clumsy excourses using spreadsheet software. I wrote down my set lists and also tried to put in my pool of records but as always spreadsheets are not a database, data is doubled, constantly has to be copied around, all is basically just errorprone and a nightmare to keep up-to-date. Hence Moleskin and my favorite pencil stayed my best option for a very long time.
The idea of using what is already there - my personal record collection on discogs.com - and not reinventing the wheel too much probably was in my head a long time ago but since my programming skills only became decent enough roughly during the last 5 years, probably it took that long until I decided that I’m good enough to do it.
First hackish approaches were finished in a couple of weeks - I could import my records from Discogs into a local SQLite database, search my collection, create mixes and it was usable good enough for me personally.
By the start of 2019 I called it a version 0.0.1 but at the same time decided that my code was crap and since I was just about to understand a lot more programming concepts I started refactoring a lot of the original code, rewrote things entirely and threw away old code.
Somewhere in the middle of 2019 my friend F. joined the project. He was eager to learn GUI programming in Python and started to build a Tkinter version of DiscoDOS. Also this gave me a lot of thought and motivation on improving the backend code, so again a lot of stuff was refactored, improved and thrown away. Also around this time I decided that since Python is cross platform but not every DJ knows how to install Python software out of a github repo, there should be proper support for the major Desktop platforms. My journey on learning on how to package for Windows and macOS began.
By the start of 2020 I actually thought “We are almost 1.0 now, just a couple of things”, but things turned out differently and a couple of months passed until the first release candidate came out by the end of April. I think it was exactely the 1st of May actually, which usually is a great day and kind of starts summer for me ;-)
Since I am a fan of Open Source software, especially Linux, since the late 90’s, my next decision for gathering even more work was obvious: “There needs to be a software package for Debian GNU/Linux”.
Again this was new territory for me and it took another 1-2 months to get it straight. Since I learned a lot more about proper Python packaging and Debian being pretty strict on how a package has to look like (for understandable reasons), major things had to be changed and also a lot of work went into macOS and Windows packaging again.
After the next release candidates around June 2020 I took a break from the project since there where other things to do (too many projects as usual) and actually I was happy that DiscoDOS made it that far and was pretty darn stable already.
And now we are here: Between January 2021 and last week I fixed and refactored a couple of things I was aware of since a long time already but never found the time to do. I teached myself on how to use the Sphinx framework to build beautiful documentation, built a website, packaging for macOS was improved again and most importantly: Both Windows and macOS packaging was automated pretty good using oldschool bash magic but also using the wonderful CI/CD pipeline possibilites of github actions, which will keep me from fiddling around with boring, errorprone packaging stuff ever again… famous last words… ;-)
]]>Probably the biggest news is that you now don’t need any form of Python installed on your computer, because I provide executable files for MacOS and Windows that contain all the Python stuff the tool needs. Full release notes and download here: mpc1k-seq version 1.2
Read what this tool is actually about in the updated original blog post.
And as always: your valuable feedback is appreciated!
]]>| Value (dec) | Value (Hex) | Command | Data bytes |
| ------------| ----------- | --------------- | ------------------------- |
| 128-143 | 80-8F | Note off | 2 (note, velocity) |
| 144-159 | 90-9F | Note on | 2 (note, velocity) |
| 160-175 | A0-AF | Key Pressure | 2 (note, key pressure) |
| 176-191 | B0-BF | Control Change | 2 (controller no., value) |
| 192-207 | C0-CF | Program Change | 1 (program no.) |
| 208-223 | D0-DF | Channel Pressure | 1 (pressure) |
| 224-239 | E0-EF | Pitch Bend | |
| ------------| ----------- | ---------------- | --------------------------|
16 MIDI channels
Note byte 1 (channel):
Note byte 2 (note number):
C0 = 0
C1 = 24
C#1 = 25
C4 = 60
A4 = 60
C5 = 72
another MIDI note numbers list
CC message:
Send a NoteOn message for the note of D#3 on channel 1 with a velocity of 90:
144 | 36 | 90
Send a Control Change message for Controller number 81 on channel 2 with a value of 65
177 | 81 | 65
Can we choose any CC number for our controller?
official Arduino specs comparision table
The Nano is very similar to the well-known Arduino Uno, it only is smaller and has more connection possibilities.
The Arduino Nano clones we use, have a CH340 chip that does the USB to serial conversion. To program it we need a USB-driver specific to our computers operating system.
For Mac OS X High Sierra and above we need this driver
For Windows 10 we need this one
Golden USB socket version:
Silver USB socket version:
Both types:
If our programming settings or the CH340 driver are wrong this is one of the possible errors:
avrdude: stk500_getsync(): not in sync: resp=0x00
Sparkfun Pro Micro hookup guide
nice Pro Micro connections video-tutorial, also about multiplexing input
Specifically this chip: ATmega32U4 5V 16MHz
To power the plus- and minus-rails of our breadboard we connect the 5V and GND pins to it. On the Pro Micro the 5V pin is called VCC.
The short answer is: Because we assumed that these switches work similar to a lot of other switch types.
The square-button-switches we use are so called DPDT switches (Dual Pole Double Throw). This basically means that we can use them to switch on/off or toggle two things with one buttonpress. Or put differently: Two switches in one.
Let's assume the same pin numbering as on the regular DPDT diagram on the left, for our oddly wired switches on the right: In off-state 1+5 and 2+6 connect. In on-state it's 1+3 and 4+6.
So what actually happens when we plug in the power to the breadboard (connect the Arduino to a USB port) and keep the button unpressed, is: We connect + and - poles together (via pins 1 and 5) and thus short-circuit the Arduino which it can withstand for a short time only.
To correctly wire these types of switches we need to:
For more detailed schematics and information on MIDI Output and MIDI input wiring refer to the official MIDI specification of the MIDI manufacturers association
When we twist the knob to the left we decrease the resistance between ground (purple wire) and the pots output (yellow wire) until it is 0 Ohms. When twisted to the right the resistance between the two wires increases until it reaches almost the nominal value of the pot (10kOhms). The actual Ohms are not of interest when using pots with Arduinos. When we read out the current value of an analog input we always receive a number between 0 and 1023
There is a second yellow wire that is supposed to be connected to a second pot but for now we put it on ground so the Arduinos input A1 does not deliver measuring values and disturb us while coding and/or debugging.
The code we produce is available on this github repo
Direct links to “git commits”:
final code day 1 - reading in a button’s state
final code day 2 - sending out midi CC on and off
day 3 - four switches for-loop example
day 3 - two pots for-loop example
“No P., we don’t do premium content on this blog! It’s para todos!”
Alright, so this is replacing the Machinedrum’s phones socket explained in pictures, not words!
If you have your MIDI basics covered and have a little experience with Arduino-type microcontrollers it’s just a matter of capturing an inputdevice (button, knob, …) into your microcontroller and sending some bytes out via a serial port, which MIDI essentially is.
There is a lot about building basic MIDI controllers on the net already so I won’t cover that, but give an overview of what helped me to get going:
Speaking of: just to get one slightly dissapointing thing straight from the beginning: Most of the common Arduino types are not capable of natively spitting out MIDI on their USB ports, but I’m going to show a hardware-hackish approach to still make it work. A few Arduinos have native USB capabilities and can make use of the MIDIUSB library , but that’s usually the type of Arduino you don’t have at hand at the right moment. The only one’s that have USB-MIDI out of the box are the Teensy’s but they cost a little more, if you don’t want any hassle go get one! This is the website of the founder, you’ll find excellent documentation there.
This is going to be my chosen input device, a button matrix out of a 1995 landline phone, built by the company Kapsch, model TP80.
So wtf is a key matrix? This guy explains it quite well, just ignore the voltage ladder for now. The german speaking guy over here, altough a bit more advanced, is explaining useful things too. He uses a button matrix out of an old chipcard terminal.
Personally my first experience with a button/keyboard matrix was the tiny 80s keyboard featured in one of my blog posts. I found a lot of information about a similar keyboard on this website. The site owner even has put together a whole FAQ about circuit bending and especially hacking keyboards. Scroll down a little (well a lot, it’s long) and look for headlines containing keyboard matrix. Deadly useful information there!!!
If it still all doesn’t add up, check this and certainly wikipedia
I guess you see where this is going by now. If not, go back and rewatch the youtube videos an links posted above. Ok, so we can think of our matrix that it has 3 column lines, the X-wires, and 4 row lines, the Y-wires.
Before I started with writing code, I did some fitting to the case I was planning to put it in. I found this in my parent’s basement. Eventually I built it myself during my apprenticeship back in the 90ies. As far as I remember it should have become a battery powered radio and due to time reasons we never even started to build the electronics.
I won’t copy tons of code to here but leave it on github and post links to commits/diffs and whole code checkouts for copying and pasting.
I use some libraries because they make sense. They can be installed from the Arduino IDE “Library Manager”.
Let’s check if the matrix and our breadboarding setup works and we actually understand what we are doing here with this test sketch. Manually set one of the output pins to HIGH level in lines 36-38 of the program and see what happens on the serial monitor.
With this next commit I loop through the 3 outputs, set them HIGH, and see which of the input lines have received the HIGH (because a button was pressed). This is the whole file to copy and paste. Actually this is the whole magic about reading out the state of any matrix keypad.
You should see something like that on the serial monitor when pressing buttons. It tells you wich row and col IDs are HIGH at the moment:
yes it's high: 0 0
...
...
yes it's high: 2 3
Read through the commit messages and the comments in the code to see what I was thinking
As mentioned at the very top of this post, most Arduinos are not capable of showing up as proper MIDI device in your computer’s operating system. If you are fine with using a software MIDI bridge like the hairless MIDI bridge, just look at the first picture in this chapter and you are done.
If it is not, one of the cheapest solutions around is to steal the electronics out of a USB to MIDI Adapter cable. You can get them in Europe for about 7-10 €. Certainly if you order them in China, a few pieces of them will cost you almost nothing. And btw: These cheap adapter cables are pretty useless for serious MIDI applications like controlling a synth from your your DAW. They are built so cheap, they literally loose data! Believe me, I measured it. But they are definitely good enough to be used for sending some knob or button movement aka some MIDI CC bytes. That’s only a tiny amount of data and my experience is that they handle that savely.
Now that we don’t have to use the hairless MIDI bridge anymore, we change the mode of the program to send native MIDI speed (31250 bps) out of the Arduinos TX1 pin:
The Nano’s onboard LEDs and the Adapter-PCB’s MIDI-IN LED should blink shortly when MIDI data is transmitted from the controller.
Just connect GND, +5V and TX1 pins of the Arduino to a 5-pin DIN-socket like described in this sparkfun tutorial.
The drawing on the bottom of this pic shows where the pins of your DIN jack should go:
As you’d expect, just use an ordinary MIDI cable (jack-jack) to connect from your controller’s socket to the device you want to control.
I had 5 digital pins left on the Arduino Nano, let’s use them!
Just a little addition to the code: blink 5 LEDs when sending CC
Maybe using red LEDs would bring a more dramatic effect.
MIDI mapping in Ableton Live - The momentary-mode buttons behave differently depending on which controls they are mapped to:
As usual I recommend supporting and buying from musikding.de, because it’s just well sorted and has good prices (first two links). For the MIDI adapter cables and Arduinos I have put together some links to cheap buys on amazon.de. If you like what you see on this blog please support me by using these links. I get a little nothing from amazon then ;-)
Some time ago I built a couple of rechargeable button-cell-battery-packs which are featured in this post
After trying to recharge one of them with an old NiCd fast-charger I learned the hard way how my batteries actually look from the inside:
Don’t try this at home kids!
So far so good, I didn’t burn my flat yet, so I decided to either buy or build a charger that is designed to charge Nickel-Metal Hybride (NiMH) batteries with very little capacity.
First off, my tiny button cell accumulators have the following specs:
In short: They are ordinary rechargeable batteries and it actually should be save to load them with an ordinary charger.
So why did my test-candidate explode then?
It was simply overcharged. NiMH cells are designed to be only fast-charged until they reach their full capacity. After that, keeping them connected to high current results in a lot of heat and…you saw the pics.
So either the charger has thermal sensors to shut off before overheating, or it just uses a low current the cell can withstand for a longer (infinite) time duration.
Sensoring seemed tedious so I decided to build a low current charger.
A safe current for NiMH cells is 1/10 of their capacity (1/10 C) or even less. In my batteries case this would be 1/10 * 80 = 8mA. There you have it: No affordable charger on the market seems to handle such a low current. Typically chargers go as low as 150 mA, which is fine for typical batteries which have a capacity of 1500 mAh to 2500 mAh (or even more), but not for my tiny 80 mAh cells.
Some googling and studying charger designs brought up this forum thread. The schematic suggested by member SgtWookie seemed to be a perfect fit:
It features two regulator ICs (TLV1117C): One limiting the current (U1) and the other one the voltage (U2). The battery(pack) is represented by capacitor C2 (he uses the similar-to-battery-behaviour of the capacitor for the software simulation). I substituted the two regulators with the more common and easy to get LM317 type.
If you have a look on pages 12 and 13 in the LM317 datasheet you will find examples for current and voltage limiting circuits that look almost identical to SgtWookies design. His circuit is basically a combination of two examples. Well done!
This is my final customized version. The 120k resistor R1 configures LM317_1 to limit charging current to 6mA, being even more healthy for my batteries than the discussed 8mA:
Skip this if it’s boring ;-) I’ll try to explain why in my case R2 surely is not necessary.
Assuming R2 is missing:
The formula for calculating the power consumption of a device (eg. a resistor) is
P = V * I
The V on R1 is unknown so just calculate it using Ohm’s law:
V = R * I = 10 Ohm * 260 mA = 10 * 0,26 = 2,6 V
Now find out how much power R1 has to withstand:
P = V * I = 2,6 * 0,26 = 0,676 W (Watts)
The most common resistors support max power levels of 0,3 to 0,5 Watts. With splitting the current in half by using a second resistor R2 in parallel with R1, also the power consumption is halfed to 0,338 W for each R.
V = R * I = 120 Ohm * 6 mA = 120 * 0,006 = 0,72 V
P = V * I = 0,72 * 0,006 = 0,00432 W = 4,3 mW
4,3 milliwatts is almost nothing and every resistor form factor I know of would withstand it. No need for splitting the current in half with a second R.
theoretically:
t = C / I = 80mAh / 6mA = 0,080 / 0,006 = 13,33 h
I -> current in A
C -> capacity in Ah
t -> time in h
typically a loss of 30-40% has to be counted in, lets calc with 40%:
C = 0,080 + 40% = 0,080 * 1,4 = 0,112 t = C / A = 0,112 / 0,006 = 18,66 h
So let’s say a night and something. It should be safe to leave the charger connected for a couple of days.
…and why it’s got its name. Click the thumbnails to zoom in and read short description.
Those are for building power and battery connector plugs:
10 pin strip (for 4-pin battery connector and 5-pin power supply connector
Unfortunately musikding.de doesn’t have the female ones in stock.
Take these 20 pin inline sockets for the resistor connector and the additional regular-battery-holder connector. They are also called transistor-sockets. Keep in mind that they have small holes. Above pin strips won’t fit together with them!
These are some combination packs with headers and sockets that fit together. It’s Amazon affiliate links. Click them if you like what you read, I get a tiny percentage of what you bought then. Thanks!
30 pieces pin headers and sockets pack
60 pieces pin headers and sockets pack
40 pieces pin headers and sockets pack
For resistors buy anything, anywhere. I like Metal Film types, they are quite cheap but can stand about 0,6 Watts, which is a little more than the most common ones with 0,25 Watts (I think they are called carbon-layer). You won’t need higher power resistors for this project but it’s good to have some in the stash.
The power supply can be almost anything you have lying around. It should deliver a minimum of 6V, I think the LM317 needs this, maybe it works with a 5V one. The absolute maximum input voltage for this IC is up to 30V, so you have a wide range of choices between these values.
]]>Squeezing in a project with a more or less predictable end might help gain back some motivation…and these days…could help making another lockdown bearable.
This is the first in a series of posts aimed at beginners and people experiencing aforementioned syndrome trying to finally finish something ;-). Building “guitar FX stomp boxes” from made-up kits - We start with The Phazer. My favorite components shop musikding.de kindly sent me this kit for free a long time ago already. I had asked them for some promo material and promised I’ll do a post about building it. Years later…
Funnily enough I didn’t take any picture of soldering the actual circuit board but let me tell you that getting done the PCB is not the tricky part. If you’ve never soldered before, watch some youtube. Future posts in this series might contain some PCB soldering pics but here’s some basics I usually try to follow: Start with small components, then advance to bigger ones. Don’t put too much heat on semiconductor-based components but don’t make the typical beginners-mistake of putting too less heat on your soldering iron: It would only force you to touch the components long enough to harm them because that damn solder is not melting!
Finally, let’s get to what this post is about: Fitting everything into a tiny case…
Once you’ve drilled holes in your case its too late to reconsider! For this project I used an aluminium case from Hammond Manufacturing. These cases seem to be kind of a standard for DIY guitar stomp boxes. You might want to get a pre-colored case along with your order or choose the cheaper option and color it yourself. For a lot of kits in the shop, the “Box Type B” is the right choice. The manufacturer’s partnumber is “1590B”. Make up your mind where you want the device’s in/out jacks, knobs and switches, the LED and the power connector.
To really make sure all your components fit into the case, consider building a cardboard prototype. At this point it’s likely you’ll reconsider the placing of certain components. You’ll find exact measurements on the case manufacturers website. Draw the template yourself or just google for “Hammond 1590B drilling template”.
You’ve planned thoroughly - drill the holes! In my case I decided last minute to use a regular toggle switch instead of the foot switch that came with the kit. It needs to be a 3PTD switch, e.g this one.
Download the wiring schematics from the “Build Documents” tab on the kit’s product page. I didn’t use the little extra PCB that came with the kit to ease soldering the foot switch but soldered the wires to my 3PTD switch directly. In case you want to use the original foot switch, using that thing makes soldering a lot easier.
Which LED color works best with the color of case and knobs? Also to me personally it’s important to properly adjust the brightness of the LED. I find devices with super-bright LED’s also super-annoying ;-). This can be adjusted by playing around with different limiting resistors. Fortunately this resistor is not on the PCB and ment to be soldered right onto either of the LED’s connectors using some extra pieces of wire. I used two pairs of clamps to try out different resistors to adjust the brightness and decide on the color.
Next to its slightly smaller brother - The 3-Verb - which hopefully will be featured in a future post:
]]>Everything that’s MIDI-CC-controllable on the Beats can be controlled via the dial controls.
The “4x4 MODE” switch moves Agogo and Claves to MIDI notes inide the 4x4 grid of the “first bank” of your MPC-style controller. No more switching to the next 16 pads!
The “SEND STATE” button sends, as you probably have already guessed, the current state (duh!), of all dials to your Beats - Thus you can save the settings of your machine together with your Ableton Live project!
The device was developed around Mar to May 2018, since then I’ve used it on each and every gig with my band ADHS - It’s tried and tested well, consider it stable! :-)
I am up for feature requests and suggestions!
]]>I often use my MPC 1000 as a player for backing tracks or drum loops when practicing music or just quickly want something to jam along. I usually create a drum loop in my DAW of choice and then export several wav files in different speeds. I would then copy those files on to my MPC and save one sequence for each wav file. The next time I want to practice or jam I just have to quickly load a folder of sequence and wav files and can easily switch between several speeds.
Creating all the sequences on the MPC itself is a very tedious task, that’s why I wrote this tool. I now just have to create one “template sequence” on the MPC, copy it over to the computer, and create several (renamed) files from it. I then let the tool help me show meta information of the sequence files and do repititive tasks like replacing the filename in the AUDIO tracks or replacing the sequences BPM.
Though I wrote it for sequence files created from the MPC 1000 running JJOS, I assume the tool would work with the MPC 2500’s files as well, I think they share the same format. Maybe also the one’s from the MPC 500 would work, not sure about that. I’d certainly appreciate any testing and feedback about usage with those MPC models files.
If you don’t want to bother with installing Python on your OS, and are not interested in running the latest development version, just use the self-contained executables available since release v1.2: https://github.com/JOJ0/mpc1k-seq/releases/tag/v1.2
seq.exe
seq
(not sure if newer MacOS versions will work, please report back any problems)To be able to execute seq
from anywhere on your system, copy it to a place that is searched for:
Use Windows Explorer to copy the seq.exe file to c:\windows\system32\
Note that this is a dirty hack. If you don’t want to do this or don’t have the privileges to do it, on your command prompt you can always just “cd” to the place where seq.exe is saved and execute it from there :-)
Make sure you are residing inside the directory where seq is saved. Your user needs to have admin privileges. You will be asked for your password when executing the following command:
sudo cp seq /usr/local/bin/
Please just follow the steps in the following chapter!
skip this chapter if you are using the executables as describe above
First of all, check if you already have a Python version on your system with python --version
You need Python 2.7.x
Clone the github repo and jump into the directory.
git clone https://github.com/JOJ0/mpc1k-seq.git
cd mpc1k-seq
Download an msi installer here
install the tool by adding the cloned repo directory to the system %path% variable,
or just quick and dirty copy it to a path that already is in the systems search path
copy seq.py c:\windows\system32\
Mac OS X 10.11 “El Capitan” ships with Python 2.7.6 pre-installed, which is the version the utility was developed on and is tested with. OX X 10.8 had Python 2.6, which probably also would work. 10.9 and newer all have 2.7.x, which should be fine.
If you don’t have above, install the latest 2.7 package from here or use homebrew to get it.
install the tool
cp seq.py /usr/local/bin/
You most probably have a running Python version already! Check as described above!
Some modern Linux Distributions already use Python 3.x by default, you would have to install a python2 package
Debian based systems
apt install python2.7
Redhat based
yum install python27
and set the first line of the script to use this python version (eg #!/usr/bin/python2.7
)
finally, install the tool
cp seq.py /usr/local/bin/
In case you are using the developement version, you would have to execute seq.py instead of seq. Further note that the Windows seq.exe can be executed without the .exe ending. The MacOS executable is called just seq and doesn’t have a file ending
The utility comes as a UNIX-style command line utility and as such shows all it’s capabilities when being run with the typical –help or -h options:
seq -h
usage: seq [-h] [--search SEARCHTERM] [--replace REPLACETERM]
[--correct-wav] [--correct-wav-bpm] [--filter BPM_LIST]
[--correct-bpm] [--hex] [--verbose]
path
positional arguments:
path path of *.SEQ files to be processed
optional arguments:
-h, --help show this help message and exit
--search SEARCHTERM, -s SEARCHTERM
search for given string in file contents
--replace REPLACETERM, -r REPLACETERM
replace SEARCHTERM with REPLACETERM
--correct-wav, -w sets basename of .SEQ file to the place where
SEARCHTERM is found. Use this if your seq and wav
files are named identically
--correct-wav-bpm, -p
replace BPM in found SEARCHTERM with BPM found in
filename
--filter BPM_LIST, --bpm BPM_LIST, -b BPM_LIST
historically was used as a space seperated BPM list
but actually it is a simple filter: only filenames
containing one of the strings in the list, will be
processed
--correct-bpm, -c set BPM to the same as in filename
--correct-length, -l set the sequences looplength (bars) to the same as in
filename. Assumes value in filename is marked with
trailing "b" (eg 8b)
--hex, -x show hex values next to decimal and strings
--verbose, -v also show border markers and not yet studied header
information
just show meta information of all seq files in current directory
seq .
show info of all seq files that have 64 or 512 in the filename (usually BPM values)
seq -b "64 512" .
also display values in hex
seq -b "64 512" -x .
search for a string
seq -b "64 512" -x -s "FunkBG" .
replace first occurence of SEARCHTERM with REPLACETERM (run script again to replace next instance of SEARCHTERM)
FIXME - “replacecount” may be configurable in future releases
seq -b "64 512" -x -s "FunkBG" -r "Blues01" .
Show all .SEQ files in the current directory (.
) that have 80 in the filename (-b "80"
or --filter "80"
and search for the term "FunkBG"
in the file
Usually this is useful if we would like to search and replace a wav files name in an audio track, but we probably also could use it to replace the name of an MPC “program file” (.PGM) somewhere in the (binary) seq file.
Let’s have a look at the command line and it’s output:
seq -b "80" -s FunkBG .
* PATH used: .
* searching for "FunkBG" (after End of header)
* bpm_list (filter_list): ['80']
############### FunkBG_080_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "FunkBG_0"
and this would be the second half: "80_8bar"
** REPLACE OPTIONS: ********************************
** --replace simply replaces FunkBG with REPLACETERM.
** --correct-wav (-w) puts this files basename at found terms position,
** it would replace "FunkBG_0" with "FunkBG_0",
** and "80_8bar" with "80_8bar".
** --correct-wav-bpm (-p) just replaces the bpm part in the found term,
?? didn't find a possible bpm value in given term (FunkBG),
?? use underscores or dashes as seperating characters!
** it would replace "FunkBG" with "FunkBG".
** If this all looks like crap, don't do it! Existing files will be OVERWRITTEN!
The first section of the output is showing us meta information saved in the files header like version, number of bars and the BPM of the sequence.
After the “End of header” marker we see that our searchterm “FunkBG” was found and it most likely is the start of the name of a wav file in an AUDIO track.
Let’s assume we would like to replace part of the wav files name configured into the seq file. The name of a wav file oddly is saved in two 8 Byte chunks in different places. The script is trying to help us with finding out if it just found part of a wav file name or something else (like a pgm file name or some other string).
Next are our possibilities to replace that string:
--replace (-r)
is the simplest form of replacement, it just puts the REPLACETERM at the position where it found SEARCHTERM. If REPLACETERM is longer than SEARCHTERM it will overwrite the remaining part.
--correct-wav (-w)
is the option to use when our wav files are exactely identically named to our wav files (except the file ending of course). This is the option I use most. In case of the example seq file from the github repo, the wav and seq file names where identically already, so this option currently is not very useful.
--correct-wav-bpm (-p)
only makes sense when SEARCHTERM contains numbers that represent BPM values. I’ll show it in another example.
Each of the options exactely state what they would replace, so if we are happy with one of them we just rerun the script and additionally add the replace option to the command line.
For example if we chose -r
to be the option to use, because we want to simply replace “FunkBG” with “PunkBG”, this would be the command and its resulting output:
seq -b "80" -s FunkBG -r "PunkBG" .
* PATH used: .
* searching for "FunkBG" (after End of header)
* replace is enabled! REPLACETERM is "PunkBG"
* bpm_list (filter_list): ['80']
############### FunkBG_080_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "FunkBG_0"
and this would be the second half: "80_8bar"
!!! replacing FIRST occurence of "FunkBG" with "PunkBG",
!!! and overwriting ./FunkBG_080_8bar.SEQ ...
If we now search for FunkBG again, we certainly won’t find it anymore:
seq -b "80" -s "FunkBG" .
* PATH used: .
* searching for "FunkBG" (after End of header)
* bpm_list (filter_list): ['80']
############### FunkBG_080_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
################## End of header ###################
your SEARCHTERM "FunkBG" was not found!
Punk would instead be found and we would have similar options as with our first search above:
seq -b "80" -s "Punk" .
* PATH used: .
* searching for "Punk" (after End of header)
* bpm_list (filter_list): ['80']
############### FunkBG_080_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 4 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "80_8bar"
** REPLACE OPTIONS: ********************************
** --replace simply replaces Punk with REPLACETERM.
** --correct-wav (-w) puts this files basename at found terms position,
** it would replace "PunkBG_0" with "FunkBG_0",
** and "80_8bar" with "80_8bar".
** --correct-wav-bpm (-p) just replaces the bpm part in the found term,
?? didn't find a possible bpm value in given term (Punk),
?? use underscores or dashes as seperating characters!
** it would replace "Punk" with "Punk".
** If this all looks like crap, don't do it! Existing files will be OVERWRITTEN!
This is the use case I actually wrote this script for. Let’s take the file from above example where we had replaced Funk with Punk, but let’s copy and rename them. You can do the copy/renaming however you like, eg iOS X Finder has a nice mass renaming tool built-in. I do it directly on the commandline now, while we are at it:
cp FunkBG_080_8bar.SEQ PunkBG_080_8bar.SEQ
cp FunkBG_080_8bar.SEQ PunkBG_090_8bar.SEQ
cp FunkBG_080_8bar.SEQ PunkBG_100_8bar.SEQ
Ok now we’d like to set the wav files name in all of the 3 “Punk sequence files” to the same as the filename. We first search for Punk and see what we have. Probably there are other seq files in this folder so we particularily select our 3 files with the --filter (-b)
option:
seq --filter Punk -s "PunkBG" .
* PATH used: .
* searching for "PunkBG" (after End of header)
* bpm_list (filter_list): ['Punk']
############### PunkBG_080_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "80_8bar"
** REPLACE OPTIONS: ********************************
** --replace simply replaces PunkBG with REPLACETERM.
** --correct-wav (-w) puts this files basename at found terms position,
** it would replace "PunkBG_0" with "PunkBG_0",
** and "80_8bar" with "80_8bar".
** --correct-wav-bpm (-p) just replaces the bpm part in the found term,
?? didn't find a possible bpm value in given term (PunkBG),
?? use underscores or dashes as seperating characters!
** it would replace "PunkBG" with "PunkBG".
** If this all looks like crap, don't do it! Existing files will be OVERWRITTEN!
############### PunkBG_090_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
bpm in filename is different! correct with -c
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "80_8bar"
** REPLACE OPTIONS: ********************************
** --replace simply replaces PunkBG with REPLACETERM.
** --correct-wav (-w) puts this files basename at found terms position,
** it would replace "PunkBG_0" with "PunkBG_0",
** and "80_8bar" with "90_8bar".
** --correct-wav-bpm (-p) just replaces the bpm part in the found term,
?? didn't find a possible bpm value in given term (PunkBG),
?? use underscores or dashes as seperating characters!
** it would replace "PunkBG" with "PunkBG".
** If this all looks like crap, don't do it! Existing files will be OVERWRITTEN!
############### PunkBG_100_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
bpm in filename is different! correct with -c
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "80_8bar"
** REPLACE OPTIONS: ********************************
** --replace simply replaces PunkBG with REPLACETERM.
** --correct-wav (-w) puts this files basename at found terms position,
** it would replace "PunkBG_0" with "PunkBG_1",
** and "80_8bar" with "00_8bar".
** --correct-wav-bpm (-p) just replaces the bpm part in the found term,
?? didn't find a possible bpm value in given term (PunkBG),
?? use underscores or dashes as seperating characters!
** it would replace "PunkBG" with "PunkBG".
** If this all looks like crap, don't do it! Existing files will be OVERWRITTEN!
If we closely examine the output for the 3 files we’d find these useful possibilities
--correct-bpm (-c)
could correct the BPM of the sequence in files 2 and 3 (the copies)--correct-wav (-w)
could replace the name of the AUDIO tracks wav file so it’s equal to the seq files name. Also in files 2 and 3 (the copies)If we would now use options -w
and -c
option we are getting the following output:
seq --filter Punk -s "PunkBG" -w -c
* PATH used: .
* searching for "PunkBG" (after End of header)
* bpm_list (filter_list): ['Punk']
* correct-bpm is enabled!
* correct-wav is enabled!
############### PunkBG_080_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "80_8bar"
-> found underscore seperated bpm value in given term: 80
!!! putting "PunkBG_0" where "PunkBG_0",
!!! putting "80_8bar" where "80_8bar",
!!! replacing bpm value,
!!! and overwriting ./PunkBG_080_8bar.SEQ ...
############### PunkBG_090_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
bpm in filename is different! This will be fixed now!
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "80_8bar"
-> found underscore seperated bpm value in given term: 90
!!! putting "PunkBG_0" where "PunkBG_0",
!!! putting "90_8bar" where "80_8bar",
!!! replacing bpm value,
!!! and overwriting ./PunkBG_090_8bar.SEQ ...
############### PunkBG_100_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
bpm in filename is different! This will be fixed now!
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "80_8bar"
-> found underscore seperated bpm value in given term: 100
!!! putting "PunkBG_1" where "PunkBG_0",
!!! putting "00_8bar" where "80_8bar",
!!! replacing bpm value,
!!! and overwriting ./PunkBG_100_8bar.SEQ ...
A last check is showing us that the wav file name and also the BPM have been corrected:
seq --filter Punk -s "PunkBG" .
* PATH used: .
* searching for "PunkBG" (after End of header)
* bpm_list (filter_list): ['Punk']
############### PunkBG_080_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 80
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "80_8bar"
** REPLACE OPTIONS: ********************************
** --replace simply replaces PunkBG with REPLACETERM.
** --correct-wav (-w) puts this files basename at found terms position,
** it would replace "PunkBG_0" with "PunkBG_0",
** and "80_8bar" with "80_8bar".
** --correct-wav-bpm (-p) just replaces the bpm part in the found term,
?? didn't find a possible bpm value in given term (PunkBG),
?? use underscores or dashes as seperating characters!
** it would replace "PunkBG" with "PunkBG".
** If this all looks like crap, don't do it! Existing files will be OVERWRITTEN!
############### PunkBG_090_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 90
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_0"
and this would be the second half: "90_8bar"
** REPLACE OPTIONS: ********************************
** --replace simply replaces PunkBG with REPLACETERM.
** --correct-wav (-w) puts this files basename at found terms position,
** it would replace "PunkBG_0" with "PunkBG_0",
** and "90_8bar" with "90_8bar".
** --correct-wav-bpm (-p) just replaces the bpm part in the found term,
?? didn't find a possible bpm value in given term (PunkBG),
?? use underscores or dashes as seperating characters!
** it would replace "PunkBG" with "PunkBG".
** If this all looks like crap, don't do it! Existing files will be OVERWRITTEN!
############### PunkBG_100_8bar.SEQ ################
4:20 version: MPC1000 SEQ 4.40
28:30 bars: 8
32:34 bpm: 100
################## End of header ###################
Found first occurence of SEARCHTERM at index 7168, it's 6 chars long
If SEARCHTERM is the START of a wav filename in an AUDIO track,
this would be the first half: "PunkBG_1"
and this would be the second half: "00_8bar"
** REPLACE OPTIONS: ********************************
** --replace simply replaces PunkBG with REPLACETERM.
** --correct-wav (-w) puts this files basename at found terms position,
** it would replace "PunkBG_1" with "PunkBG_1",
** and "00_8bar" with "00_8bar".
** --correct-wav-bpm (-p) just replaces the bpm part in the found term,
?? didn't find a possible bpm value in given term (PunkBG),
?? use underscores or dashes as seperating characters!
** it would replace "PunkBG" with "PunkBG".
** If this all looks like crap, don't do it! Existing files will be OVERWRITTEN!
FIXME… example how to use –correct-wav-bpm
I’d appreciate any form of feedback regarding on what use cases you could think of for mass manipulating .SEQ files (eg with .PGM filenames, track names or anything else that is saved in a sequence). Also usability improvement suggestions are very welcome. I created this tool as a helper for my very own special use case, I am sure there would be more possibilities. Now that I own the basics about what’s in a .SEQ file, it would be rather easy to expand it. Tell me what you think!
]]>