mirror of
https://github.com/nodiscc/awesome-linuxaudio.git
synced 2026-03-09 07:12:21 -05:00
split tutorials from main file, add scripts directory, move scripts from linuxaudio there
This commit is contained in:
41
README.md
41
README.md
@@ -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
|
||||
|
||||
@@ -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
3
scripts/README.md
Normal 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`
|
||||
53
scripts/linuxsampler-instrument-to-rosegarden.py
Normal file
53
scripts/linuxsampler-instrument-to-rosegarden.py
Normal 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
33
scripts/loopcrossfade.sh
Normal 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"
|
||||
982
scripts/midi-to-h2song-hydrogen.pl
Normal file
982
scripts/midi-to-h2song-hydrogen.pl
Normal 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
193
scripts/wavs-to-specimen.py
Normal 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
33
tutorials.md
Normal 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 & 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)
|
||||
Reference in New Issue
Block a user