split tutorials from main file, add scripts directory, move scripts from linuxaudio there

This commit is contained in:
nodiscc
2015-09-25 17:38:58 +02:00
parent bb22a690ae
commit fa0318dbcc
8 changed files with 1307 additions and 37 deletions

View File

@@ -6,6 +6,10 @@ A list of software and resources for professional audio/video/live events produc
This list is provided to help you build your own GNU/Linux based A/V production environment. Most of the listed software is packaged for [Debian](http://debian.org/), and should be directly installable using your package manager. Software that can be run on other GNU/Linux distributions may also be added to the list. This list focuses on sound, video, lighting and live applications.
Useful automation scripts may be found in the **[scripts](scripts/) directory**.
Tutorials and howto guides about Linux multimedia software can be found on the **[Tutorials](tutorials.md) page**.
 Unpackaged
▒ Only in KXStudio repositories
 Non-free/closed source
@@ -41,7 +45,6 @@ This list is provided to help you build your own GNU/Linux based A/V production
- [Unsorted](#unsorted)
- [DOCUMENTATION](#documentation)
- [General doc/software/forums](#general-docsoftwareforums)
- [Howtos](#howtos)
- [GLOSSARY](#glossary)
- [TODO](#todo)
- [LICENSE](#license)
@@ -255,7 +258,6 @@ two oscillator software synthesizer ([Homepage](http://code.google.com/p/amsynth
* [gwc](http://packages.debian.org/sid/gwc) - Audio file denoiser ([Homepage](http://gwc.sf.net))
http://panic.et.tudelft.nl/~costar/gramofile/ 404
* [declick](http://home.snafu.de/wahlm/dl8hbs/declick.html) - a dynamic digital declicker for audio sample files. ``
* [loopcrossfade](https://gist.github.com/nk23x/b14f9305b7e2a1bc0727) - makes an audiofile loop itself (seamless by using a crossfade trick)
### Meters & Analysis
@@ -297,6 +299,7 @@ http://panic.et.tudelft.nl/~costar/gramofile/ 404
* [qmidiarp](http://packages.debian.org/wheezy/qmidiarp) - arpégiateur MIDI pour ALSA ([Homepage](http://qmidiarp.sourceforge.net/))
* [qmidinet](http://packages.debian.org/wheezy/qmidinet) - MIDI Network Gateway via UDP/IP Multicast ([Homepage](http://qmidinet.sourceforge.net/))
* [vmpk](http://packages.debian.org/wheezy/vmpk) - Virtual MIDI Piano Keyboard ([Homepage](http://vmpk.sourceforge.net/))
* [m2hpc](http://dominodesigns.info/m2hpc/index.html) - MIDI to Hydrogen Pattern Converter ``
## System utilities
@@ -628,39 +631,7 @@ http://panic.et.tudelft.nl/~costar/gramofile/ 404
* [Linux Audio Announces](http://lists.linuxaudio.org/listinfo/linux-audio-announce/) - email list to publish announcements.
* [Gentoo Pro-Audio Overlay](http://proaudio.tuxfamily.org/wiki/index.php?title=Main_Page) - Pro-audio support for Gentoo users
### Howtos
Read [System Setup](system-setup.md) for system related topics.
* [LV2 plugins for mixing: My favorite basic plugins (by zthmusic) | Libre Music Production](http://libremusicproduction.com/articles/lv2-plugins-mixing-my-favorite-basic-plugins-zthmusic)
* [Loop-based Music Composition With Linux, Pt. 1](http://www.linuxjournal.com/node/1000304)
* [Dave Phillips' Articles and Tutorials - LinuxJournal](http://www.linuxjournal.com/users/dave-phillips)
* [▶ Rough Mix with Calf FX - YouTube](https://www.youtube.com/watch?v=JR6mRkFkoBQ)
* [▶ Hydrogen Drum Machine with CALF plugins - YouTube](https://www.youtube.com/watch?v=FJaSbPZgLnw)
* [Puredata - FLOSS Manuals](https://flossmanuals.net/PureData/)
* [Puredata tutorials](http://puredata.info/docs/tutorials)
* [new year with fluxus and mixxx](http://www.ponnuki.net/2011/01/year-event-fluxus-mixxx/)
* [Rosegarden - DebianEdu Tutorials](https://wiki.debian.org/DebianEdu/Documentation/Manuals/Rosegarden)
* [Making Music in the Rosegarden](http://www.penguinproducer.com/Blog/2011/11/making-music-in-the-rosegarden/)
* [abcmidi Tutorial](http://wiki.li(https://wiki.debian.org/DebianEdu/Documentation/Manuals/Rosegarden)nuxaudio.org/wiki/abcmiditutorial)
* [AlsaModularSynth - Making a vocoder](http://wiki.linuxaudio.org/wiki/amsvocodertutorial)
* [Musescore tutorials](https://musescore.org/en/tutorials)
* [Screencasting with FFmpeg, jack_capture and Xephyr [Linux-Sound]](http://wiki.linuxaudio.org/wiki/screencasttutorial)
* [seq24: toggle sequences with a MIDI controller [Linux-Sound]](http://wiki.linuxaudio.org/wiki/seq24togglemiditutorial)
* trackers: [Mod Tracking Tutorial -- Introduction](http://files.byondhome.com/Audiophiles/iainperegrine.modtracker_tutorial/modtracking_tutorial_intro.html)
* trackers: [Mod Tracking Tutorial -- Introduction](http://files.byondhome.com/Audiophiles/iainperegrine.modtracker_tutorial/modtracking_tutorial_part1.html)
* trackers: [Trackers and Linux. || kuro5hin.org](https://www.kuro5hin.org/story/2002/6/8/2524/90038)
* trackers: [A Tutorial on Cutting Up a Breakbeat Using a Tracker || kuro5hin.org](https://www.kuro5hin.org/story/2005/11/13/182235/45)
* trackers: [.:: Milkytracker Tutorial ::.](http://www.seele07.de/milkytutorial/data/start_here.html)
* [Cover - Prodigy - Breath - LMMS - ZynAddSubFX - Linux - YouTube](http://www.youtube.com/watch?v=gxTxhv8H9X0)
* [Getting Started With SooperLooper on Vimeo](http://vimeo.com/7315051)
* [Introduction_to_Ardour_3.0_MIDI : Dan MacDonald : Free Download & Streaming : Internet Archive](http://www.archive.org/details/Introduction_to_Ardour_3.0_MIDI) * [01_PADsynth_strings.ogv - YouTube](http://www.youtube.com/watch?v=IA-7tpTfE-E)
* [Linux music tutorial: seq24, part 1 - YouTube](http://www.youtube.com/watch?v=J2WDHS1wYeM)
* [Seq24 Tutorial, Part 1 - setting loops - YouTube](http://www.youtube.com/watch?v=51W9PQfyMsg)
* [Amsynth - Linux Synthesizer - YouTube](http://www.youtube.com/watch?v=YHR9hQVrRIQ)
* [Ardour - Music Editing in Linux - Part #1 - YouTube](http://www.youtube.com/watch?v=43ES7p4ejX0)
* [You, Too Can Learn Renoise: Video Tutorial](http://createdigitalmusic.com/2009/10/you-too-can-learn-renoise-video-tutorial-from-dac-makes-you-a-tracker/)
* [Linux soft synth tutorial: part 6.1 - YouTube](http://www.youtube.com/watch?v=p6SoNX4bA1Y)
## GLOSSARY
@@ -689,7 +660,7 @@ Read [System Setup](system-setup.md) for system related topics.
* http://www.kvraudio.com/news/discodsp-updates-vertigo-additive-synth-to-r3-5-including-linux-support-29997
* http://www.kvraudio.com/news/discodsp-updates-discovery-pro-va-and-wave-synth-to-6-4-5-29782
* Package scripts from http://www.pjb.com.au/midi/
* Group and package scripts from http://wiki.linuxaudio.org/wiki/script_midi2hydrogen, http://wiki.linuxaudio.org/wiki/script_lscp2rgd, http://wiki.linuxaudio.org/wiki/scripts_wav2specimen, http://wiki.linuxaudio.org/wiki/scripts_and_tools
* Group and package scripts from, http://wiki.linuxaudio.org/wiki/scripts_and_tools (partially done in scripts/)
* add software from http://bandshed.net/avlinux6-debs/
## LICENSE

View File

@@ -29,7 +29,6 @@ http://www.ibiblio.org/pub/Linux/apps/sound/editors/!INDEX.html
## Migrated
la-fait
http://www.apodio.org/
http://www.apo33.org/apodio-site/?p=48
http://artistx.org/blog/
@@ -44,7 +43,6 @@ http://bandshed.net/forum/index.php?topic=3502.0
http://wiki.linuxaudio.org/apps/categories/distributions
http://wiki.linuxaudio.org/apps/categories/linux_audio_bundles_distributions_and_music_collections
http://wiki.linuxaudio.org/wiki/script_midi2hydrogen
http://wiki.linuxaudio.org/wiki/open_discussion
http://easy.open.and.free.fr/didjix/features.html
http://wiki.linuxaudio.org/apps/screenshots
http://wiki.linuxaudio.org/apps/all/lat
@@ -55,6 +53,7 @@ http://wiki.linuxaudio.org/apps/all/lau
http://opensourcemusician.com/index.php/Main_Page
http://linuxaudio.org/resources
http://wiki.linuxaudio.org/site/about
http://wiki.linuxaudio.org/wiki/open_discussion
http://wiki.linuxaudio.org/wiki/request
http://wiki.linuxaudio.org/apps/logos
http://jackaudio.org/realtime_vs_realtime_kernel
@@ -164,6 +163,9 @@ http://jzu.free.fr/outils.html
http://gwc.sourceforge.net/
https://gist.github.com/coderofsalvation/7740333
http://wiki.linuxaudio.org/wiki/tutorials/start
http://wiki.linuxaudio.org/wiki/script_lscp2rgd
http://wiki.linuxaudio.org/wiki/scripts_wav2specimen
audiocutter: abandoned, sf 404
DAP Richard Kent's 404
glame abandoned 2007 http://sourceforge.net/projects/glame/files/

3
scripts/README.md Normal file
View File

@@ -0,0 +1,3 @@
### Scripts
* [midi-drums-to-h2song-hydrogen](http://italianmafia.altervista.org/blog/download.php?get=midi2hydrogen.tar.gz) - Usage: `python midi2hydrogen.py input.mid output.h2song`

View File

@@ -0,0 +1,53 @@
#!/usr/bin/python
# convert a lscp (linux sampler) instrument file to rgd (rosegarden sequencer) instrument file
# from http://wiki.linuxaudio.org/wiki/script_lscp2rgd
# from https://bb.linuxsampler.org/viewtopic.php?f=7&t=66
import sys, gzip, re
pattern = re.compile(r"MAP[\s]*MIDI_INSTRUMENT[\s]*([\d]+)[\s]*([\d]+)[\s]*([\d]+)[\s]*([\w]+)[\s]*'([\S]+)'[\s]*([\d]+)[\s]*([\d]+.[\d]+)[\s]*([\w]+)[\s]*'(.+)'[\s]*$")
preamble = '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rosegarden-data>
<rosegarden-data>
<studio thrufilter="0" recordfilter="0">
<device id="0" name="LinuxSampler" type="midi">
<librarian name="Anders Dahnielson" email="anders@dahnielson.com"/>
'''
postamble = '''
</device>
</studio>
</rosegarden-data>
'''
rgd = open('%s.rgd' % (sys.argv[1]), 'w')
out = gzip.GzipFile(mode='w', filename = "audio/x-rosegarden-device", fileobj=rgd)
out.filename = "audio/x-rosegarden-device"
out.write(preamble)
total_programs = 0
current_bank = -1
lscp = open(sys.argv[1], 'r')
for line in lscp:
line = line.strip()
tokens = line.split(' ')
if tokens[0] == '#' and tokens[1] and len(tokens) == 2:
bank_name = tokens[1]
elif tokens[0] == 'MAP' and tokens[1] == 'MIDI_INSTRUMENT':
total_programs += 1
m = pattern.match(line)
bank, program, name = int(m.group(2)), int(m.group(3)), m.group(9).replace(r"\'", "'")
if bank != current_bank:
if current_bank != -1:
out.write(' </bank>\n\n')
current_bank = int(bank)
out.write(' <bank name="%s" msb="%d" lsb="%d">\n' % (bank_name, 0, bank))
out.write(' <program id="%d" name="%s"/>\n' % (program, name))
out.write(' </bank>\n')
out.write(postamble)
lscp.close()
out.close()
rgd.close()
print "%d programs in %d banks" % (total_programs, current_bank+1)

33
scripts/loopcrossfade.sh Normal file
View File

@@ -0,0 +1,33 @@
# makes a audiofile to loop itself (seamless by using a crossfade trick) (needs sox audio utilities)
# @dependancy sox
# source https://gist.github.com/nk23x/b14f9305b7e2a1bc0727
# Usage: ./loopcrossfade <input.wav> <faderatio> [outputdir]
#
loopcrossfade(){
input="$1"; faderatio="$2"; outputdir="$3"; tmpinput="/tmp/$(basename "$input").loopcrossfade.wav"
[[ ! -f "$input" ]] && echo "cannot find $1" && exit 1
valid=$(echo "$faderatio > 1.99" | bc -l );
(( $valid == 0 )) && echo "faderatio should be 2.0 or bigger" && exit 1
[[ -d "$outputdir" ]] && outputfile="$outputdir/$(basename "$input")_loop.$faderatio.wav" \
|| outputfile="$input""_loop.$faderatio.wav"
# prepare input
format="-c 2 -e signed -b 16 -r 44100"
sox "$input" ${format} "$tmpinput" &&
samples="$(soxi "$tmpinput" | grep Duration | cut -d' ' -f11 )"
fadetime="$( echo "$samples/$faderatio" | bc )"
fadetimehalf="$( echo "$fadetime/2" | bc )"
middle="$( echo "$samples-$fadetime" | bc )"
# get middle part + add fadein
sox "$tmpinput" ${format} "$tmpinput.lmid.wav" fade t "$fadetimehalf"s trim 0 "$middle"s
# get end (+fadeout)
sox "$tmpinput" ${format} "$tmpinput.lend.wav" trim "$middle"s "$fadetime"s
sox "$tmpinput.lend.wav" "$tmpinput.lendfadeout.wav" fade t 0 0 "$fadetime"s
# combine together
sox -m "$tmpinput.lendfadeout.wav" "$tmpinput.lmid.wav" "$outputfile" norm
echo "written $outputfile"
rm /tmp/*.loopcrossfade.*
}
loopcrossfade "$1" "$2" "$3"

View File

@@ -0,0 +1,982 @@
#Convert midi drums to *.h2song hydrogen drum sequencer format
#########################################################################################
# ____________________________________________________________________
# / \
# | ____ __ ___ _____ / ___ ___ |
# | ____ / \/ \ ' / \ / / /__ / \ / \ |
# | / _ \ / / / / / / ___/ \__ / /____/ / / |
# | / |_ / / / / / / / / / \ / / /____/ |
# | \____/ / / \/_/ / \__/ _____/ \__/ \___/ / |
# | / |
# | |
# | Copyright (c) 2007 Herve Masson, MindStep SARL |
# | rvmindstep@users.sourceforge.net |
# \____________________________________________________________________/
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#########################################################################################
#
# midi2hydrogen.pl - MIDI file conversion script for hydrogen (hydrogen-music.org)
#
# ==> Read the usage message below for more info
#
# (Note: was created on ubuntu 7.x using perl 5.8, with the additional CPAN
# packages XML-Simple and MIDI-Perl)
#
# -=-
#
# Ideas/TODOs:
#
# - some quantization would probably be useful
# - detect identical patterns (that's tricky)
# - load instruments from multiple kits
# - ~home/.midi2hydrogen.cfg config file instead of options
# - find a way to implement some midi controls (volume, pan,...)
#
#########################################################################################
use strict;
use Data::Dumper;
use Getopt::Long;
use MIDI;
my($ME)="midi2hydrogen.pl";
my($USAGE)="
Usage: perl ${ME} [options] file.mid file.h2song
perl ${ME} [options] file.mid
This script extracts the drum track(s) from a midi file and generates an hydrogen
.h2song file. When the h2song file name is omitted, the program simply reports
information that tell how this file would be converted, without actually generating
anything. This information contains, amongst other things, the instrument
mapping between MIDI and H2, which can be altered using the following options:
-h, --help
Prints this message.
-v, --verbose
Increases verbosity.
";
#########################################################################################
#
# GLOBAL CONFIGURATION - following variables control important behaviors
#
#########################################################################################
package Cfg;
use vars qw($KITNAME $KITPATH %INSTRUMENTMAP %DFLSONG %DFLINSTRUMENT %DFLLAYER %DFLNOTE);
# This is the location where the h2 drumkits are
# ==============================================
$KITPATH="/usr/share/hydrogen/data/drumkits";
# Which H2 kit we are using
# =========================
$KITNAME="GMkit";
# This is the instrument mapping between MIDI and (GMkit)h2:
# ==========================================================
%INSTRUMENTMAP=
(
# MIDI => H2 # General midi name
#---------------------------------------
35 => 0, # Acoustic Bass Drum
36 => 0, # Bass Drum 1
37 => 1, # Side Stick
38 => 2, # Acoustic Snare
39 => 3, # Hand Clap
40 => 4, # Electric Snare
41 => 5, # Low Floor Tom
42 => 6, # Closed Hi-Hat
43 => 9, # High Floor Tom
44 => 8, # Pedal Hi-Hat
45 => 5, # Low Tom
46 => 10, # Open Hi-Hat
47 => 7, # Low-Mid Tom
48 => 7, # Hi-Mid Tom
49 => 13, # Crash Cymbal 1
50 => 9, # High Tom
51 => 12, # Ride Cymbal 1
52 => 15, # Chinese Cymbal
53 => 14, # Ride Bell
54 => undef, # Tambourin
55 => 15, # Splash Cymbal
56 => 11, # Cowbell
57 => 15, # Crash Cymbal 2
58 => undef, # Vibraslap
59 => 12, # Ride Cymbal 2
60 => undef, # Hi Bongo
61 => undef, # Low Bongo
62 => undef, # Mute Hi Conga
63 => undef, # Open Hi Conga
64 => undef, # Low Conga
65 => undef, # High Timbale
66 => undef, # Low Timbale
67 => undef, # High Agogo
68 => undef, # Low Agogo
69 => undef, # Cabasa
70 => undef, # Maracas
71 => undef, # Short Whistle
72 => undef, # Long Whistle
73 => undef, # Short Guiro
74 => undef, # Long Guiro
75 => undef, # Claves
76 => undef, # Hi Wood Block
77 => undef, # Low Wood Block
78 => undef, # Mute Cuica
79 => undef, # Open Cuica
80 => undef, # Mute Triangle
81 => undef, # Open Triangle
);
# The following tables give default values for hydrogen structures
# (which can't be obtained from the MIDI file)
# ================================================================
%DFLSONG=
(
version => "0.9.3",
bpm => 120,
volume => 0.5,
metronomeVolume => 0.5,
name => "noname",
author => "unknown",
notes => "imported from midi",
loopEnabled => "true",
mode => "pattern",
humanize_time => 0,
humanize_velocity => 0,
swing_factor => 0,
delayFXEnabled => "false",
delayFXWetLevel => 1,
delayFXFeedback => 0.4,
delayFXTime => 48,
);
%DFLINSTRUMENT=
(
volume => 1,
isMuted => 'false',
isLocked => 'false',
pan_L => 1,
pan_R => 1,
gain => 1,
FX1Level => 0,
FX2Level => 0,
FX3Level => 0,
FX4Level => 0,
Attack => 0,
Decay => 0,
Sustain => 1,
Release => 1000,
randomPitchFactor => 0,
);
%DFLLAYER=
(
min => 0,
max => 1,
gain => 1,
pitch => 0,
);
%DFLNOTE=
(
velocity => 0.8,
pan_L => 1,
pan_R => 1,
pitch => 0,
length => -1,
);
use vars qw($MIDI_DRUMCHAN $MIDI_CTRL_VOLUME $MIDI_CTRL_PAN %MIDI_CHANEVENTS);
# Reserved channel number for drum
# ================================
$MIDI_DRUMCHAN=9;
# MIDI controls
# =============
$MIDI_CTRL_VOLUME=7; # Channel Volume (formerly Main Volume)
$MIDI_CTRL_PAN=10; # Pan
# The following MIDI events are related to a MIDI channel:
# ========================================================
%MIDI_CHANEVENTS=
(
note_off => 1,
note_on => 1,
key_after_touch => 1,
control_change => 1,
patch_change => 1,
channel_after_touch => 1,
pitch_wheel_change => 1,
);
#########################################################################################
#
# Some global variables
#
#########################################################################################
package main;
my($SONG,%OPTS);
#########################################################################################
#
# Various utilities
#
#########################################################################################
sub StripSpaces
{
my($str)=shift;
$str =~ s/^\s+//;
$str =~ s/\s+$//;
return $str;
}
sub Warning
{
my($fmt)=shift;
printf STDERR "Warning: $fmt\n",@_;
}
sub Error
{
my($fmt)=shift;
printf STDERR "Error: $fmt\n",@_;
}
sub Trace
{
my($fmt)=shift;
if($OPTS{verbose})
{
printf "[trace] $fmt\n",@_;
}
}
#########################################################################################
#
# Some extension to the MIDI::Track package
#
#########################################################################################
package MIDI::Track;
sub searchEvent
{
my($self)=shift;
my($type)=shift;
my(@events)=$self->events();
my(@list);
foreach my $ev (@events)
{
if($ev->[0] eq $type)
{
push(@list,$ev);
last unless(wantarray);
}
}
if(wantarray)
{
return @list;
}
return $list[0];
}
sub label
{
my($self)=shift;
$self->{label}=shift if(@_>0);
return $self->{label};
}
sub index
{
my($self)=shift;
$self->{index}=shift if(@_>0);
return $self->{index};
}
#########################################################################################
#
# This class represents an hydrogen song
#
#########################################################################################
package HydrogenSong;
use XML::Simple;
use Data::Dumper;
sub _field_
{
my($name)=shift;
my($self)=shift;
$self->{$name}=shift if(@_>0);
return $self->{$name};
}
sub bpm { return _field_("bpm",@_) }
sub volume { return _field_("volume",@_) }
sub version { return _field_("version",@_) }
sub instruments { return _field_("instruments",@_) }
sub patterns { return _field_("patterns",@_) }
sub patternMap { return _field_("patternMap",@_) }
sub notes { return _field_("notes",@_) }
sub stats { return _field_("stats",@_) }
sub new
{
my($class)=shift;
my $self=bless({ %Cfg::DFLSONG },$class);
$self->loadKit($Cfg::KITNAME);
$self->notes([]);
$self->patterns([]);
$self->stats({});
return $self;
}
sub loadKit
{
my($self)=shift;
my($dkname)=shift;
# Load the hygrogen kit
# ---------------------
my($ref)=XMLin("$Cfg::KITPATH/$dkname/drumkit.xml");
die "can't load kit $dkname" unless($ref);
my($map)=$ref->{instrumentList}->{instrument};
my(@list);
while(my($key,$value)=each(%{$map}))
{
next if($value->{filename} eq "");
my($ins)={ %Cfg::DFLINSTRUMENT, %$value };
delete($ins->{exclude});
$ins->{name}=$key;
$ins->{drumkit}=$dkname;
$ins->{layer}=
{
%Cfg::DFLLAYER,
filename => $ins->{filename},
};
delete($ins->{filename});
push(@list,$ins);
}
@list=sort { $a->{id} <=> $b->{id} } @list;
$self->instruments([@list]);
}
my(%WARNEDINS);
sub addNote
{
my($self)=shift;
my($time)=shift;
my($insnum)=shift;
my($velocity)=shift;
my($h2ins);
my($stats)=$self->stats();
$stats->{midi}->[$insnum]++;
unless(defined($h2ins=$Cfg::INSTRUMENTMAP{$insnum}))
{
unless($WARNEDINS{$insnum})
{
$WARNEDINS{$insnum}=1;
main::Warning("MIDI instrument $insnum has no hydrogen equivalence - dropped");
}
return;
}
$stats->{h2}->[$h2ins]++;
my($note)=
{
%Cfg::DFLNOTE,
velocity => $velocity,
instrument => $h2ins,
position => int($time*48),
};
push(@{$self->{notes}},$note);
}
sub finalize
{
my($self)=shift;
my($notes)=$self->notes();
my($patsz)=32; # Max size of a pattern, in quarter-notes
my($maxpos)=$patsz*24; # 24 positions per quarter-note
if(@$notes==0)
{
die "the song does not contain any note";
}
# Slice the song in fixed size patterns
my($offset)=0;
my(@patterns,%seqs);
my(@list)=@{$notes};
while(@list>=0)
{
my($note)=$list[0];
if(defined($note))
{
# Position relative to pattern begining
my($relpos)=$note->{position}-$offset;
if($relpos<$maxpos)
{
# This fit in the current pattern
my($ins)=$note->{instrument};
unless($seqs{$ins})
{
$seqs{$ins}=[];
}
$note->{position}=$relpos;
push(@{$seqs{$ins}},$note);
shift(@list);
next;
}
}
my(@seqs)=map { $seqs{$_} } (0...31);
push(@patterns,
{
name => sprintf("Pattern %d",scalar(@patterns)),
index => scalar(@patterns),
sequences => [@seqs],
size => $patsz*24,
});
$offset+=$maxpos;
%seqs=();
last if(@list==0);
}
$self->patterns([@patterns]);
$self->patternMap([@patterns]);
}
sub asString
{
my($self)=shift;
my(@list);
push(@list,"<song>");
while(my($key,$value)=each(%{$self}))
{
unless(ref($value))
{
push(@list,"<$key>$value</$key>");
}
}
push(@list,"<instrumentList>");
foreach my $ins (@{$self->instruments()})
{
push(@list,"<instrument>");
while(my($key,$value)=each(%{$ins}))
{
unless(ref($value))
{
push(@list,"<$key>$value</$key>");
}
}
push(@list,"<exclude/>");
push(@list,"<layer>");
while(my($key,$value)=each(%{$ins->{layer}}))
{
unless(ref($value))
{
push(@list,"<$key>$value</$key>");
}
}
push(@list,"</layer>");
push(@list,"</instrument>");
}
push(@list,"</instrumentList>");
push(@list,"<patternList>");
foreach my $pat (@{$self->patterns()})
{
push(@list,"<pattern>");
push(@list,"<name>$pat->{name}</name>");
push(@list,"<size>$pat->{size}</size>");
push(@list,"<sequenceList>");
foreach my $seq (@{$pat->{sequences}})
{
push(@list,"<sequence>");
push(@list,"<noteList>");
foreach my $note (@{$seq})
{
push(@list,"<note>");
while(my($key,$value)=each(%{$note}))
{
push(@list,"<$key>$value</$key>");
}
push(@list,"</note>");
}
push(@list,"</noteList>");
push(@list,"</sequence>");
}
push(@list,"</sequenceList>");
push(@list,"</pattern>");
}
push(@list,"</patternList>");
push(@list,"<patternSequence>");
foreach my $pat (@{$self->patternMap()})
{
push(@list,"<group>");
push(@list,"<patternID>$pat->{name}</patternID>");
push(@list,"</group>");
}
push(@list,"</patternSequence>");
push(@list,"</song>");
my($tabs)=0;
my(@out);
foreach my $item (@list)
{
if($item =~ m|^</|)
{
# Closure
$tabs--;
}
push(@out,sprintf("%s%s",(" "x$tabs),$item));
if($item =~ m|^<[^/]|)
{
if(($item !~ m|</|) && ($item !~ m|/>|))
{
$tabs++;
}
}
}
push(@out,"");
return join("\n",@out);
}
sub saveAs
{
my($self)=shift;
my($fname)=shift;
my($fd);
unless(open($fd,">$fname"))
{
die "could not save file $fname - $!";
}
print $fd $self->asString();
close($fd);
return $self;
}
sub showInstrumentMapping
{
my($self)=shift;
my($listr)=$self->instruments();
my(@list)=@{$listr};
printf("Instruments mapping:\n");
printf("====================\n\n");
# Computes some statistics
# ------------------------
my($stats)=$self->stats();
# Display the instrument mapping
# ------------------------------
my(%htable);
foreach my $ins (@list)
{
$htable{$ins->{id}}=$ins;
}
my($line)=" +--------------------------------+--------------------------------+-------+\n";
print($line);
printf(" | General midi instruments | Hydrogen instrument | Notes |\n");
print($line);
my(%used);
foreach my $key (sort { $a <=> $b } keys(%MIDI::notenum2percussion))
{
my($value)=$MIDI::notenum2percussion{$key};
my($hid)=$Cfg::INSTRUMENTMAP{$key};
my($mapping)="-";
if(defined($hid))
{
my($ins)=$htable{$hid};
if($ins)
{
$used{$ins}=1;
$mapping=sprintf("(%02d) %s",$hid,$ins->{name});
}
}
my($count)=$stats->{midi}->[$key];
$count="-" unless($count>0);
printf(" | %-30s | %-30s | %5s |\n",
sprintf("(%0d) %s",$key,$value),
$mapping,$count);
}
foreach my $ins (@list)
{
next if($used{$ins});
my($mapping)=sprintf("(%02d) %s",$ins->{id},$ins->{name});
printf(" | %-30s | %-30s | %5s |\n","-",$mapping,"-");
}
print($line);
printf("\n\n");
}
#--------------------------------------------------------------------------------
#
# Show MIDI file information
# ==========================
#
# - midi file content (tracks, channels and patches)
#
#--------------------------------------------------------------------------------
package main;
sub ShowSongInfo
{
my($song)=shift;
my(@tracks)=$song->tracks();
my($i)=0;
my($ev);
my($ticks)=$song->ticks();
printf("General information:\n");
printf("====================\n\n");
printf(" Midi clock : %d ticks/quater-note\n",$ticks);
printf(" Tempo : %d\n",$SONG->bpm());
printf(" H2 kit : %s/%s\n","$Cfg::KITPATH/$Cfg::KITNAME");
printf("\n\n");
printf("MIDI Tracks:\n");
printf("============\n\n");
my($line)=" +----+-----+--------------------------------+------------------------------------+";
printf("%s\n",$line);
printf(" | T# | Ch# | Track Name | General Midi Patch |\n");
printf("%s\n",$line);
foreach my $t (@tracks)
{
my($name)="(noname)";
my($patch)="";
my($chan)="";
my($gmname)=""; # General midi name
if(defined($ev=$t->searchEvent("track_name")))
{
$name=StripSpaces($ev->[2]);
}
if(defined($ev=$t->searchEvent("patch_change")))
{
$chan=$ev->[2];
if($chan == $Cfg::MIDI_DRUMCHAN)
{
$patch="-";
$gmname="(drum channel)";
}
else
{
$patch=$ev->[3];
$gmname=$MIDI::number2patch{$patch};
}
}
printf(" | %2d | %3s | %-30s | %-3s %-30s |\n",$i,$chan,$name,$patch,$gmname);
$t->label($name);
$t->index($i);
$i++;
}
printf("%s\n",$line);
printf(" (T#=Track number, Ch#=MIDI channel number)\n");
printf("\n\n");
$SONG->showInstrumentMapping();
}
my(%WARNCTRL);
sub ProcessEvent_control_change
{
my($ctx)=shift;
my($ev)=shift;
my($param)=$ev->{param}->[0]; # MIDI control identifier
my($value)=$ev->{param}->[1]; # control value
if($param == $Cfg::MIDI_CTRL_VOLUME)
{
Trace("set volume $value - NOT IMPLEMENTED");
}
elsif($param == $Cfg::MIDI_CTRL_PAN)
{
Trace("set pan $value - NOT IMPLEMENTED");
}
else
{
unless($WARNCTRL{$param})
{
$WARNCTRL{$param}=1;
Trace("ignoring unknown MIDI control %d",$param);
}
}
}
sub ProcessEvent_note_on
{
my($ctx)=shift;
my($ev)=shift;
my($note)=$ev->{param}->[0]; # For drum, this corresponds to instrument selection
my($velocity)=$ev->{param}->[1]; # Usually corresponds to note volume
if($ev->{channel} != $Cfg::MIDI_DRUMCHAN)
{
# Ignore non-drum events
return;
}
$velocity=int($velocity/12.7)/10; # Convert velocity between 0 and 127
my($ticks)=$ctx->{song}->ticks();
my($tm)=$ev->{tickstamp}/$ticks;
$SONG->addNote($tm,$note,$velocity);
return 1;
}
sub ProcessEvent_set_tempo
{
my($ctx)=shift;
my($ev)=shift;
if($ctx->{convms})
{
Warning("tempo is defined more than once - secundary definitions ignored");
return;
}
if($ev->{tickstamp} != 0)
{
Warning("tempo is set after song begining - ignored");
return;
}
my($tempo)=$ev->{param}->[0]; # Number of micro-seconds per quarter-note
# Get the number of ticks per quarter-note
my($ticks)=$ctx->{song}->ticks();
# Compute the conversion ratio to obtain milliseconds from tick counts
$ctx->{convms}=$tempo/(1000*$ticks);
my($qnms)=$ticks*$ctx->{convms};
my($bpm)=int(60000/$qnms);
$ctx->{tempo}=$bpm;
Trace("set tempo ${bpm} bpm (one quarter-note lasts %dms)",$qnms,$bpm);
$SONG->bpm($bpm);
}
sub ProcessSong
{
my($song)=shift;
my(@tracks)=$song->tracks();
# ------------------------------------------------------------------------------------
# > Combine all tracks into a single grand event list
# > (we need to process the tempo changes and such, which might be on other tracks)
# ------------------------------------------------------------------------------------
my($songticks)=$song->ticks(); # Ticks per quarter-note
my(@events);
foreach my $track (@tracks)
{
my($index)=0;
my(@list)=$track->events();
my($tnum)=$track->index();
my($abstime)=0;
foreach my $ev (@list)
{
my(@ev)=@{$ev};
my($type)=shift(@ev);
my($reltime)=shift(@ev); # Relative timestamp
$abstime += $reltime;
my($notestamp)=$abstime/$songticks;
my($chan);
if($Cfg::MIDI_CHANEVENTS{$type})
{
$chan=shift(@ev);
}
push(@events,
{
type => $type, # Type of the MIDI event (ex: 'patch_change')
tickstamp => $abstime, # Absolute timestamp of the event, in number of ticks
notestamp => $notestamp, # Absolute timestamp, in quarter-notes
tracknum => $tnum,
index => $index++,
channel => $chan, # Channel number, when appropriate
param => [@ev],
});
}
}
# Sort all events in time order
@events=sort { $a->{tickstamp} <=> $b->{tickstamp} } @events;
# ------------------------------------------------------------------------------------
# > Process individual events
# ------------------------------------------------------------------------------------
my($ctx)={ song => $song };
foreach my $ev (@events)
{
my($type)=$ev->{type};
my($proc);
unless($proc=__PACKAGE__->can("ProcessEvent_$type"))
{
# We don't want this event type
next;
}
&$proc($ctx,$ev);
}
print "\n";
}
#--------------------------------------------------------------------------------
#
# Program entry point - command line parsing
#
#--------------------------------------------------------------------------------
my(@OPTIONS)=
(
"help|h",
"verbose|v",
);
Getopt::Long::Configure("bundling");
unless(GetOptions(\%OPTS,@OPTIONS))
{
print STDERR $USAGE;
exit(1);
}
if($OPTS{help})
{
print $USAGE;
exit(0);
}
my($midiFile,$h2File)=@ARGV;
if((@ARGV!=1) && (@ARGV!=2))
{
Error("Invalid number of parameters");
print STDERR $USAGE;
exit(1);
}
# 1) Read the MIDI file
# =====================
$SONG=HydrogenSong->new();
my($song)=eval { MIDI::Opus->new({ from_file => $midiFile }) };
unless($song)
{
Error("error while loading midi file $midiFile - $@");
exit(1);
}
ProcessSong($song);
if($h2File)
{
unless($h2File =~ /\.h2song$/)
{
$h2File="$h2File.h2song";
}
$SONG->finalize();
print "Saving h2song file: $h2File...\n";
$SONG->saveAs($h2File);
}
else
{
ShowSongInfo($song);
}

193
scripts/wavs-to-specimen.py Normal file
View File

@@ -0,0 +1,193 @@
#!/usr/bin/env python
# -*- coding: latin-1 -*-
# dir2beef - by Atte André Jensen
# source http://wiki.linuxaudio.org/wiki/scripts_wav2specimen
# found at lau-ml, 25. April 2008 15:35
# # how to use:
# put it in yout path and type the following in a terminal:
# python dir2beef.py --help
import os, optparse, sys, os.path, wave
parser = optparse.OptionParser()
if '-?' in sys.argv:
sys.argv.remove('-?')
sys.argv.append('-h')
parser.add_option('-d','--directory-containing-wavs', dest="wavDir", default='.')
parser.add_option('-l','--lowest-note-of-mapping', dest="startNote", default='36')
parser.add_option('-f','--force-overwrite', action="store_true", dest="forceOverwrite", default=False)
parser.add_option('-s','--stdout', action="store_true", dest="stdOut", default=False)
parser.add_option('-o','--outfile', dest="outFile")
(options, args) = parser.parse_args()
def getDir(dir):
result = []
import os.path
path = os.path.abspath(dir) + '/'
list = os.listdir(dir)
for file in list:
(root, ext) = os.path.splitext(file)
if os.path.isfile(path + file) and ext in ['.wav','.WAV']:
result.append(path + file)
return result
header = """<?xml version="1.0"?>
<beef>"""
patch = """ <patch>
<name>%s</name>
<file>%s</file>
<channel>0</channel>
<note>%s</note>
<volume>1,000000</volume>
<pan>0,000000</pan>
<play_mode>5</play_mode>
<cut>0</cut>
<cut_by>0</cut_by>
<range>0</range>
<lower_note>%s</lower_note>
<upper_note>%s</upper_note>
<sample_start>0</sample_start>
<sample_stop>%s</sample_stop>
<loop_start>0</loop_start>
<loop_stop>%s</loop_stop>
<cutoff>1,000000</cutoff>
<resonance>0,000000</resonance>
<pitch>0,000000</pitch>
<pitch_steps>2</pitch_steps>
<portamento>no</portamento>
<portamento_time>0,050000</portamento_time>
<monophonic>no</monophonic>
<legato>no</legato>
<volume_a>0,000000</volume_a>
<volume_env_on>no</volume_env_on>
<volume_amt>0,000000</volume_amt>
<volume_d>0,000000</volume_d>
<volume_s>1,000000</volume_s>
<volume_r>0,000000</volume_r>
<volume_delay>0,000000</volume_delay>
<volume_hold>0,000000</volume_hold>
<volume_lfo_amt>0,000000</volume_lfo_amt>
<volume_lfo_on>no</volume_lfo_on>
<volume_vel_amt>1,000000</volume_vel_amt>
<volume_lfo_a>0,000000</volume_lfo_a>
<volume_lfo_delay>0,000000</volume_lfo_delay>
<volume_lfo_beats>1,000000</volume_lfo_beats>
<volume_lfo_freq>1,000000</volume_lfo_freq>
<volume_lfo_global>no</volume_lfo_global>
<volume_lfo_sync>no</volume_lfo_sync>
<volume_lfo_positive>no</volume_lfo_positive>
<volume_lfo_shape>sine</volume_lfo_shape>
<panning_a>0,000000</panning_a>
<panning_env_on>no</panning_env_on>
<panning_amt>0,000000</panning_amt>
<panning_d>0,000000</panning_d>
<panning_s>1,000000</panning_s>
<panning_r>0,000000</panning_r>
<panning_delay>0,000000</panning_delay>
<panning_hold>0,000000</panning_hold>
<panning_lfo_amt>0,000000</panning_lfo_amt>
<panning_lfo_on>no</panning_lfo_on>
<panning_vel_amt>0,000000</panning_vel_amt>
<panning_lfo_a>0,000000</panning_lfo_a>
<panning_lfo_delay>0,000000</panning_lfo_delay>
<panning_lfo_beats>1,000000</panning_lfo_beats>
<panning_lfo_freq>1,000000</panning_lfo_freq>
<panning_lfo_global>no</panning_lfo_global>
<panning_lfo_sync>no</panning_lfo_sync>
<panning_lfo_positive>no</panning_lfo_positive>
<panning_lfo_shape>sine</panning_lfo_shape>
<cutoff_a>0,000000</cutoff_a>
<cutoff_env_on>no</cutoff_env_on>
<cutoff_amt>0,000000</cutoff_amt>
<cutoff_d>0,000000</cutoff_d>
<cutoff_s>1,000000</cutoff_s>
<cutoff_r>0,000000</cutoff_r>
<cutoff_delay>0,000000</cutoff_delay>
<cutoff_hold>0,000000</cutoff_hold>
<cutoff_lfo_amt>0,000000</cutoff_lfo_amt>
<cutoff_lfo_on>no</cutoff_lfo_on>
<cutoff_vel_amt>0,000000</cutoff_vel_amt>
<cutoff_lfo_a>0,000000</cutoff_lfo_a>
<cutoff_lfo_delay>0,000000</cutoff_lfo_delay>
<cutoff_lfo_beats>1,000000</cutoff_lfo_beats>
<cutoff_lfo_freq>1,000000</cutoff_lfo_freq>
<cutoff_lfo_global>no</cutoff_lfo_global>
<cutoff_lfo_sync>no</cutoff_lfo_sync>
<cutoff_lfo_positive>no</cutoff_lfo_positive>
<cutoff_lfo_shape>sine</cutoff_lfo_shape>
<resonance_a>0,000000</resonance_a>
<resonance_env_on>no</resonance_env_on>
<resonance_amt>0,000000</resonance_amt>
<resonance_d>0,000000</resonance_d>
<resonance_s>1,000000</resonance_s>
<resonance_r>0,000000</resonance_r>
<resonance_delay>0,000000</resonance_delay>
<resonance_hold>0,000000</resonance_hold>
<resonance_lfo_amt>0,000000</resonance_lfo_amt>
<resonance_lfo_on>no</resonance_lfo_on>
<resonance_vel_amt>0,000000</resonance_vel_amt>
<resonance_lfo_a>0,000000</resonance_lfo_a>
<resonance_lfo_delay>0,000000</resonance_lfo_delay>
<resonance_lfo_beats>1,000000</resonance_lfo_beats>
<resonance_lfo_freq>1,000000</resonance_lfo_freq>
<resonance_lfo_global>no</resonance_lfo_global>
<resonance_lfo_sync>no</resonance_lfo_sync>
<resonance_lfo_positive>no</resonance_lfo_positive>
<resonance_lfo_shape>sine</resonance_lfo_shape>
<pitch_a>0,000000</pitch_a>
<pitch_env_on>no</pitch_env_on>
<pitch_amt>0,000000</pitch_amt>
<pitch_d>0,000000</pitch_d>
<pitch_s>1,000000</pitch_s>
<pitch_r>0,000000</pitch_r>
<pitch_delay>0,000000</pitch_delay>
<pitch_hold>0,000000</pitch_hold>
<pitch_lfo_amt>0,000000</pitch_lfo_amt>
<pitch_lfo_on>no</pitch_lfo_on>
<pitch_vel_amt>0,000000</pitch_vel_amt>
<pitch_lfo_a>0,000000</pitch_lfo_a>
<pitch_lfo_delay>0,000000</pitch_lfo_delay>
<pitch_lfo_beats>1,000000</pitch_lfo_beats>
<pitch_lfo_freq>1,000000</pitch_lfo_freq>
<pitch_lfo_global>no</pitch_lfo_global>
<pitch_lfo_sync>no</pitch_lfo_sync>
<pitch_lfo_positive>no</pitch_lfo_positive>
<pitch_lfo_shape>sine</pitch_lfo_shape>
</patch>"""
footer = "</beef>"
# --- the action ----
output = []
output.append(header)
note = int(options.startNote)
for file in getDir(options.wavDir):
(head, tail) = os.path.split(file)
(basename, ext) = os.path.splitext(tail)
wav = wave.open(file)
end = wav.getnframes() -1
values = (basename,file,note, note, note, end, end)
output.append(patch % values)
note = note + 1
output.append(footer)
if options.stdOut == True:
print '\n'.join(output)
sys.exit()
if not options.outFile:
options.outFile = options.wavDir.strip('/').split('/')[-1:][0] + '.beef'
if not os.path.exists(options.outFile) or options.forceOverwrite:
FILE = open(options.outFile,"w")
for line in output:
FILE.write(line)
else:
print 'outfile "' + options.outFile + '" exists, use -f to force overwrite'

33
tutorials.md Normal file
View File

@@ -0,0 +1,33 @@
### Tutorials
Read [System Setup](system-setup.md) for system related topics.
* [LV2 plugins for mixing: My favorite basic plugins (by zthmusic) | Libre Music Production](http://libremusicproduction.com/articles/lv2-plugins-mixing-my-favorite-basic-plugins-zthmusic)
* [Loop-based Music Composition With Linux, Pt. 1](http://www.linuxjournal.com/node/1000304)
* [Dave Phillips' Articles and Tutorials - LinuxJournal](http://www.linuxjournal.com/users/dave-phillips)
* [▶ Rough Mix with Calf FX - YouTube](https://www.youtube.com/watch?v=JR6mRkFkoBQ)
* [▶ Hydrogen Drum Machine with CALF plugins - YouTube](https://www.youtube.com/watch?v=FJaSbPZgLnw)
* [Puredata - FLOSS Manuals](https://flossmanuals.net/PureData/)
* [Puredata tutorials](http://puredata.info/docs/tutorials)
* [new year with fluxus and mixxx](http://www.ponnuki.net/2011/01/year-event-fluxus-mixxx/)
* [Rosegarden - DebianEdu Tutorials](https://wiki.debian.org/DebianEdu/Documentation/Manuals/Rosegarden)
* [Making Music in the Rosegarden](http://www.penguinproducer.com/Blog/2011/11/making-music-in-the-rosegarden/)
* [abcmidi Tutorial](http://wiki.li(https://wiki.debian.org/DebianEdu/Documentation/Manuals/Rosegarden)nuxaudio.org/wiki/abcmiditutorial)
* [AlsaModularSynth - Making a vocoder](http://wiki.linuxaudio.org/wiki/amsvocodertutorial)
* [Musescore tutorials](https://musescore.org/en/tutorials)
* [Screencasting with FFmpeg, jack_capture and Xephyr [Linux-Sound]](http://wiki.linuxaudio.org/wiki/screencasttutorial)
* [seq24: toggle sequences with a MIDI controller [Linux-Sound]](http://wiki.linuxaudio.org/wiki/seq24togglemiditutorial)
* trackers: [Mod Tracking Tutorial -- Introduction](http://files.byondhome.com/Audiophiles/iainperegrine.modtracker_tutorial/modtracking_tutorial_intro.html)
* trackers: [Mod Tracking Tutorial -- Introduction](http://files.byondhome.com/Audiophiles/iainperegrine.modtracker_tutorial/modtracking_tutorial_part1.html)
* trackers: [Trackers and Linux. || kuro5hin.org](https://www.kuro5hin.org/story/2002/6/8/2524/90038)
* trackers: [A Tutorial on Cutting Up a Breakbeat Using a Tracker || kuro5hin.org](https://www.kuro5hin.org/story/2005/11/13/182235/45)
* trackers: [.:: Milkytracker Tutorial ::.](http://www.seele07.de/milkytutorial/data/start_here.html)
* [Cover - Prodigy - Breath - LMMS - ZynAddSubFX - Linux - YouTube](http://www.youtube.com/watch?v=gxTxhv8H9X0)
* [Getting Started With SooperLooper on Vimeo](http://vimeo.com/7315051)
* [Introduction_to_Ardour_3.0_MIDI : Dan MacDonald : Free Download &amp; Streaming : Internet Archive](http://www.archive.org/details/Introduction_to_Ardour_3.0_MIDI) * [01_PADsynth_strings.ogv - YouTube](http://www.youtube.com/watch?v=IA-7tpTfE-E)
* [Linux music tutorial: seq24, part 1 - YouTube](http://www.youtube.com/watch?v=J2WDHS1wYeM)
* [Seq24 Tutorial, Part 1 - setting loops - YouTube](http://www.youtube.com/watch?v=51W9PQfyMsg)
* [Amsynth - Linux Synthesizer - YouTube](http://www.youtube.com/watch?v=YHR9hQVrRIQ)
* [Ardour - Music Editing in Linux - Part #1 - YouTube](http://www.youtube.com/watch?v=43ES7p4ejX0)
* [You, Too Can Learn Renoise: Video Tutorial](http://createdigitalmusic.com/2009/10/you-too-can-learn-renoise-video-tutorial-from-dac-makes-you-a-tracker/)
* [Linux soft synth tutorial: part 6.1 - YouTube](http://www.youtube.com/watch?v=p6SoNX4bA1Y)