Ultraschall Internals Documentation Reaper Internals Documentation Downloads Changelog of documentation Impressum and Contact
Ultraschall-logo Functions Engine
   Introduction/Concepts 
     Functions Reference 



Beta 2.7 - "Frank Zappa - The Return of the Son of Monster Magnet" - 15th of December 2018 - Build: 25377

Introduction and Concepts

 
Introduction
Introduction to the Ultraschall API How to use the Ultraschall API How to use Beta functions and hotfixes Introduction: Bugreporting and Feature Requests
Introduction: License
 
Datatypes
Datatypes: Introduction Datatypes: Trackstrings Datatypes: MediaItemArray Datatypes: MediaItemStateChunkArray
Datatypes: EnvelopePointObject Datatypes: EnvelopePointArray Datatypes: ColorTable Datatypes: Checking Datatypes
 
API-Variables
API-Variables
 
Rendering
Rendering: Introduction Rendering: About Renderstrings Rendering: About Rendering-functions Rendering: Change more render-settings
 
Arrangeview Snapshots
Arrangeview Snapshots: Introduction Arrangeview Snapshots: How to store, retrieve, delete Arrangeview Snapshots: How to restore
 
Navigation
Navigation: Introduction Navigation: Move Play and Editcursor Navigation: Go to markers, regionedges and itemedges Navigation: Center View
Navigation: Autoscroll and Followmode
 
Get/Set Project/Track/MediaItem-States
Get/Set States for Project, Tracks and Items
 
MediaItems
MediaItems: Introduction MediaItems: Getting MediaItems by Time and Tracks MediaItems: Splitting MediaItems by Time and Tracks MediaItems: Deleting MediaItems by Time and Tracks
MediaItems: SectionCut, RippleCut, RippleInsert MediaItems: Moving and Manipulating MediaItems: Inserting Items and Files MediaItems: Programming Spectral Edit
MediaItems: Miscellaneous
 
File Management
File Management: Introduction File Management: Read File Management: Write File Management: Analyse
File Management: Misc
 
Project Management
Project Management: Introduction Project Management: Check for changed projecttabs
 
Color Management
Color Management: Introduction Color Management: Native Color Conversion Color Management: Brightness, Contrast and Colorsaturation Color Management: Working with Colortables
Color Management: Creating Colortables Color Management: Applying Colortables
 
Background-Scripts
Background Scripts: Introduction
 
Cough and Mute-Buttons
Cough and Mute Buttons/Actions: Introduction Cough and Mute Buttons/Actions: Toggling Mute Cough and Mute Buttons/Actions: Toggling Mute Cough and Mute Buttons/Actions: Toggling Mute
 
Error Messaging System
Error Messaging System: Introduction Error Messaging System: Creating Error Messages Error Messaging System: Getting Error Messages Error Messaging System: Deleting Error Messages
Error Messaging System: Toggling showing errors in IDE instead
 
TrackStates
Trackstate Management: Introduction
 
Routing
Routing: Introduction Routing: Send and Receives Routing: Hardware Outs
 
ExtState Management
ExtState Management: Introduction ExtState Management: Ini-Files ExtState Management: Inifile-Functions ExtState Management: Ultraschall.ini
ExtState Management: Track and Item-Extstates ExtState Management: Track Extstates ExtState Management: Item Extstates
 
Marker Management
Markers and Regions: Introduction Markers and Regions: General How To Markers and Regions: Helpers and Manipulation
 
Helper Functions
Helper_Functions: Introduction Helper_Functions: Clipboard Management Helper_Functions: Data Manipulation Helper_Functions: Undo Management
Helper_Functions: Miscellaneous
 
Final Words
Final words

^ Introduction to the Ultraschall API

The Ultraschall-Extension is intended to be an extension for the DAW Reaper, that enhances it with podcast functionalities. Most DAWs are intended to be used by musicians, for music, but podcasters have their own needs to be fulfilled. In fact, in some places their needs differ from the needs of a musician heavily. Ultraschall is intended to optimise the Reaper's workflows, by reworking them with functionalities for the special needs of podcasters.

The Ultraschall-Framework itself is intended to include a set of Lua-functions, that help creating such functionalities. By giving programmers helper functions to get access to each and every corner of Reaper. That way, extending Ultraschall and Reaper is more comfortable to do.

This API was to be used within Ultraschall only, but quickly evolved into a huge 700 function-library, that many 3rd-party programmers and scripters may find use in, with many useful features, like:

Happy coding and let's see, what you can do with it :D

Meo Mespotine (mespotine.de)

For more information about Ultraschall itself, see ultraschall.fm and if you want to support us, see ultraschall.fm/danke for donating to us.

PS: In this documentation, I assume you have some basic knowledge in Lua and in using Reaper's own API-functions. Explaining both of these is beyond the scope of this doc.



^ How to use the Ultraschall API

Using the Ultraschall-API is quite easy.

First make sure, you use the right versions of Reaper and SWS: Reaper 5.95, SWS 2.9.7 and Julian Sader's plugin 0.951. You can download them at reaper.fm and sws-extension.org respectively. Julian Sader's plugin can be installed using ReaPack or gotten from his own github-site

To install the Ultraschall-API:
1. Just download the zip-file of the current version from api.ultraschall.fm.
2. Extract it to the UserPlugins-folder in the Resources-folder of Reaper. You can find the correct Resources-path in the Reaper-menu "Options -> Show REAPER resource path in explorer/finder...".
The folder UserPlugins should contain the folder ultraschallapi and the files ultraschallapireadme.txt and ultraschallapi.lua after this step.
3. Now create a new script, by opening the Actions-window (Menu: Actions -> Show action list) and clicking the New-button next to "ReaScript:", give it a name and hit save.

Include the following line in your script at the beginning:

        dofile(reaper.GetResourcePath().."/UserPlugins/ultraschall_api.lua")

Now you can program with the Ultraschall-API. Just add the functions AFTER the dofile-line.

  1. To test, if it's successfully installed, add

        ultraschall.ApiTest()

    after the dofile-line and hit ctrl+s or cmd+s(on mac).

The script should show a messagebox, that tells you, which of the Ultraschall-API-parts are activated. If it shows, everything works fine :)

If you are already familiar with programming Reaper's own API-functions, you know, that all of Reaper's functions are placed in a table called "reaper."
e.g. reaper.ShowConsoleMsg("msg")

The Ultraschall-API is quite the same, but is using the table "ultraschall." instead.
e.g. ultraschall.ApiTest()

Some functions need background-scripts be run first. Refer the Introduction to background-scripts for information about that.

See the functions-reference for all available functions or read on in this documentation for a collection of concepts introduced by the Ultraschall-API.

Oh, before I forget: The ultraschallapi-folder holds a folder "ScriptsExamples" in which you can find some demos and example-scripts using this API.



^ How to use Beta functions and hotfixes

Before a new version of the Ultraschall-API is finished, it is in beta-stage. But sometimes, bugs need to be fixed even before a new release is done or you may want to test new functions from the next release for bugs and quirks.
So I added two ways to deal with that.

  1. Hotfixes
    Hotfixes are available at api.ultraschall.fm. They provide fixes for functions and bugs that came across after a release, but no new functions.
    Just download the ultraschall_hotfixes.lua and put it into UserPlugins/ultraschall_api-folder.
    The chance is high, that there is already an ultraschall_hotfixes.lua-file in the folder. You can safely overwrite it.
    Voila, you have added the most recent bugfixes.

    To get the bugs back, just delete the ultraschall_hotfixes.lua.

    All fixes in ultraschall_hotfixes.lua will be part of the next full version of the Ultraschall-API.

    Check again from time to time to get the latest hotfixes.

  2. Beta-functions and features Beta-functions are functions intended for a future release of the Ultraschall-API. If you want to test them, just download the latest Beta-Pack from api.ultraschall.fm.
    Extract the files in the Beta-pack to UserPlugins of the Resources-folder of your Reaper-installation. Voila, the beta-functions are installed.

    To use them, you need to explicitly turn them on in your script, using the following lines:

         ultraschall.US_BetaFunctions="ON"          -- Only has an effect, if ultraschall-beta-functions exist in Scripts-folder  
                                                    -- Turn ON, if you want BETA-Api-Functions

    The documentation for beta-versions can be found at api.ultraschall.fm/Downloads.html

    Be aware, they are in beta-stage. They will probably change, disappear or be exchanged with better functions. Beta-functions are only for testing purposes, not for productive coding!



^ Introduction: Bugreporting and Feature Requests

If you find any bugs or itches and want to report them, I suggest you the following procedure:

  1. Make notes of: what operating-system you use(Mac, Win, Linux), which Reaper-version, which SWS-Version and which Ultraschall-Framework-Version.
  2. Write down, what you wanted to do, what you expected to happen and what has happened instead. Make it as detailed as possible(a code-fragment that triggers a bug, a screenvideo i.e. would be perfect), as more information helps to find out, where the problem lies. It's always better to write too much, than the other way around. Please keep in mind when sending code-fragments: they need to be able to trigger the bug without any of your other code. And please don't send me hundreds of lines of your code, as I can't debug it for you. Just concentrate on the line(s), that trigger the bug successfully.

  3. Send these notes either as:
    Issue at the GitHub-Repository of the Ultraschall-API(preferred): https://github.com/Ultraschall/Ultraschall-Api-for-Reaper.git
    eMail: lspmp3@yahoo.de(for framework-related stuff only!!)
    Sendegate: sendegate.de into the Ultraschall-section.

Bugreports that contain only a "it doesn't work" and "I expected it to work" will be ignored gracefully ;)

If you have feature-requests, we have open ears. Keep in mind, not everything you find a good idea actually is one. So we may or may not take on your idea, change and rework it into a way, that benefits all, not just your particular use-case. When in doubt, just try it! Keep also in mind: there are limitations. Some cool features we all would love to have, simply aren't implementable. Que sera, sera...

For your comments just send a mail at: lspmp3@yahoo.de(for framework-related stuff only!!) or go to sendegate.de into the Ultraschall-section.

PS: If you know how to implement impossible things or do things better than the current implementation, you are welcome to donate your improved codes. :)



^ Introduction: License

Copyright (c) 2014-2018 Ultraschall (http://ultraschall.fm)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights o use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Reaper and the Reaper-Logo are trademarks of Cockos inc and can be found at reaper.fm

The SWS-logo has been taken from the SWS-extension-project, which can be found at sws-extension.org

Ultraschall-API written by Meo Mespotine mespotine.de with contributions from Udo Sauer and Ralf Stockmann

If you want to donate to our project, head over to ultraschall.fm/danke.

Kudos to lokasenna, who suggested some cool things, that made some functions much faster and sparkled new ones. Cheers! MakeCopyOfTable-function adapted from Tyler Neylon's (twitter.com/tylerneylon) function, found at Stack Overflow Thanks to him for allowing me to use it :)



^ Datatypes: Introduction

Parameters and returnvalues are usually of specific types. Let's look at the following function:

        integer number_of_items, array MediaItemArray, array MediaItemStateChunkArray = ultraschall.GetMediaItemsAtPosition(number position, string trackstring)

The returnvalues number_of_items is of type integer, MediaItemArray is of type array/type, MediaItemStateChunkArray if of type array. The parameters position is of type number, trackstring is of type string. These parameters/returnvalues only accept/return data of these given types.

The Ultraschall-API uses a lot of the standard-datatypes provided by Lua and Reaper/SWS, which are:

If a datatype has "optional" before it, this parameter/returnvalue is optional. That means, such parameters don't need to be given, such return-values can be nil.

In addition to the already used datatypes, the Ultraschall-API introduces some more datatypes, like:

These Ultraschall-API-specific datatypes are described in more detail in the following chapters.



^ Datatypes: Trackstrings

Many functions allow you to process through multiple tracks. As good as this is, this provided some difficulty in telling a function, to which track it shall be applied to.
Hence the datatype: trackstring

A trackstring is just a simple string with all tracknumbers that you want a certain function to be applied to.
Examplecode:

        trackstring = "1,2,5,7,9"

These tracknumbers must be separated from each other using a comma. Whitespaces are not allowed.

With trackstrings, passing the wanted tracks to a function is really easy.



^ Datatypes: MediaItemArray

Many functions allow you to process through multiple mediaitems. To be able to pass multiple mediaitems at once to a function, I added the datatype MediaItemArray.

A MediaItemArray is an array with many MediaItems, indexed by an integer.
Examplecode:

        -- get the MediaItems
        MediaItem1=reaper.GetMediaItem(0,0)
        MediaItem2=reaper.GetMediaItem(0,1)
        MediaItem3=reaper.GetMediaItem(0,2)
        MediaItem4=reaper.GetMediaItem(0,3)
        
        -- create and fill the MediaItemArray
        MediaItemArray={}
        MediaItemArray[1]=MediaItem1
        MediaItemArray[2]=MediaItem2
        MediaItemArray[3]=MediaItem3
        MediaItemArray[4]=MediaItem4

MediaItemArrays will be read, until an index of the MediaItemArray is nil. In the above example, MediaItemArray[5] would be nil and therefore seen as the end of the array, even if there's a MediaItemArray[6]!

With MediaItemArray, passing the wanted MediaItems to a function is really easy.



^ Datatypes: MediaItemStateChunkArray

Many functions allow you to process through multiple mediaitem-statechunks. To be able to pass multiple mediaitem-statechunks at once to a function, I added the datatype MediaItemStateChunkArray.

A MediaItemStateChunkArray is an array with many MediaItemStateChunks, indexed by an integer.
Examplecode:

        -- get the MediaItems
        MediaItem1=reaper.GetMediaItem(0,0)
        MediaItem2=reaper.GetMediaItem(0,1)
        MediaItem3=reaper.GetMediaItem(0,2)
        MediaItem4=reaper.GetMediaItem(0,3)
        
        -- get the MediaItemStateChunks
        StateChunk1=reaper.GetItemStateChunk(MediaItem1, "", false)
        StateChunk2=reaper.GetItemStateChunk(MediaItem2, "", false)
        StateChunk3=reaper.GetItemStateChunk(MediaItem3, "", false)
        StateChunk4=reaper.GetItemStateChunk(MediaItem4, "", false)
        
        -- create and fill the MediaItemStateChunkArray
        MediaItemStateChunkArray={}
        MediaItemStateChunkArray[1]=StateChunk1
        MediaItemStateChunkArray[2]=StateChunk2
        MediaItemStateChunkArray[3]=StateChunk3
        MediaItemStateChunkArray[4]=StateChunk4

MediaItemStateChunkArrays will be read, until an index of the MediaItemStateChunkArray is nil. In the above example, MediaItemStateChunkArray[5] would be nil and therefore seen as the end of the array, even if there's a MediaItemStateChunkArray[6]!

With MediaItemStateChunkArrays, passing the wanted MediaItemStateChunks to a function is really easy.



^ Datatypes: EnvelopePointObject

When working with envelope-points, handling all the attributes such an envelope-point is quite unhandy. So I introduced the datatype EnvelopePointObject, containing all it's attributes.

An EnvelopePointObject is an array with all attributes an envelope-point has.
Examplecode:

        MediaTrack=reaper.GetTrack(0,0) -- get MediaTrack
        TrackEnvelope=reaper.GetTrackEnvelope(MediaTrack, 0) -- get TrackEnvelope
        retval, EnvelopePointObject = ultraschall.CreateEnvelopePointObject(TrackEnvelope, 1, 20, 10, 0, 0, false) -- create EnvelopePointObject

An EnvelopePointObject is an array with all the attributes of an envelope-point, containing the following values:

        EnvelopePointObject[1] - Trackenvelope; The TrackEnvelope-object, in which the point lies
        EnvelopePointObject[2] - integer; Envelope-idx
        EnvelopePointObject[3] - number; the time in seconds
        EnvelopePointObject[4] - number; the raw value of the envelope-point
        EnvelopePointObject[5] - integer; the shape of the envelope-point, with 
                                               0 - Linear
                                               1 - Square
                                               2 - Slow start/end
                                               3 - Fast start
                                               4 - Fast end
                                               5 - Bezier
        EnvelopePointObject[6] - number; tension of the envelope-point; -1 to 1; 0 for no tension
        EnvelopePointObject[7] - boolean; if the envelope-point is selected(true) or not(false)
        EnvelopePointObject[8] - number; dBValue converted from value

EnvelopePointObjects make handling of envelope-points and it's attributes much easier.



^ Datatypes: EnvelopePointArray

When working with multiple EnvelopePointObjects, I introduced the datatype EnvelopePointArray, containing multiple EnvelopePointObjects.

An EnvelopePointArray is an array with multiple EnvelopePointObjects, indexed by an integer.
Examplecode:

        -- create EnvelopePointObjects
        MediaTrack=reaper.GetTrack(0,0) -- get MediaTrack
        TrackEnvelope=reaper.GetTrackEnvelope(MediaTrack, 0) -- get TrackEnvelope
        retval, EnvelopePointObject1 = ultraschall.CreateEnvelopePointObject(TrackEnvelope, 1, 20, 10, 0, 0, false) -- create EnvelopePointObject1
        retval, EnvelopePointObject2 = ultraschall.CreateEnvelopePointObject(TrackEnvelope, 1, 20, 10, 0, 0, false) -- create EnvelopePointObject2
        retval, EnvelopePointObject3 = ultraschall.CreateEnvelopePointObject(TrackEnvelope, 1, 20, 10, 0, 0, false) -- create EnvelopePointObject3
        
        -- creating EnvelopePointArray
        EnvelopePointArray={}
        EnvelopePointArray[1]=EnvelopePointObject1
        EnvelopePointArray[2]=EnvelopePointObject2
        EnvelopePointArray[3]=EnvelopePointObject3

EnvelopePointArray makes passing multiple EnvelopePointObjects to functions much easier.



^ Datatypes: ColorTable

When having to work with many colors, like MediaTrack-colors or the colors set to individual MediaItems, one may wish to have a proper datastructure to store these colorvalues. For that, I introduce the ColorTable.

A ColorTable is an array containing the multiple colors, indexed by an integer.
Structure:

        ColorTable[index]["r"]=red color-value(0-255)
        ColorTable[index]["g"]=green color-value(0-255)
        ColorTable[index]["b"]=blue color-value(0-255)
        ColorTable[index]["gfxr"]=red color-value, useable by gfx-related-functions(0-1)
        ColorTable[index]["gfxg"]=green color-value, useable by gfx-related-functions(0-1)
        ColorTable[index]["gfxb"]=blue color-value, useable by gfx-related-functions(0-1)
        ColorTable[index]["nativecolor"]=the r-g-b-color-value converted to the native-color, used in your system
    

ColorTables can be used to store gradients, or temporary track-colors, or anything related to multiple colors.



^ Datatypes: Checking Datatypes

When you work with data of different sources but need to work with data of a specific type, it would be handy to have ways of checking, whether a certain variable has data of a specific type.
Lua provides ways of checking for valid datatypes(type() and math.type()) as well as Reaper(ValidatePtr() and ValidatePtr2()).

However, sometimes you want to have one function to check them all, and all these functions do not include Ultraschall-API-specific datatypes, so I added some stuff for that.

  1. type
    works like Lua's own type-function, but checks for Reaper's own datatypes as well, like:

           Lua: nil, number: integer, number: float, boolean, string, function, table, thread, userdata,  
           Reaper: ReaProject, MediaItem, MediaItem_Take, MediaTrack, TrackEnvelope, AudioAccessor, joystick_device, PCM_source  
           userdata: will be shown, if object isn't of any known type  

    to get of which type a variable is, just use

            datatype_of_variable = ultraschall.type(variable)

    where the return-value datatype_of_variable will hold a string describing the type of the variable.

    Due some API-restrictions, SWS-specific datatypes are not (yet) supported.

  2. Ultraschall-API specific or other Reaper-datatypes
    To check for Ultraschall-API specific or other Reaper-datatypes, you can use the following functions:

    Ultraschall-API-specific:

    Other Reaper-datatypes:

    They will be part of ultraschall.type() someday.

  3. other types of data
    These aren't datatypes, but you may want to check them for validity as well



^ API-Variables

When working with the Ultraschall-API or general programming in Reaper, some additional things may or may not be helpful to know.
So I added some API-variables, like:

  1. Script_Path - which contains the current path to Reaper's-scripts-folder
  2. Separator - which contains the correct separator for paths; on Windows it is \ on Mac and Linux it is /
  3. StartTime - contains the starting time of the Ultraschall-API as used in the current script. That means, the starting time of the now running script.
  4. Api_Path - the current path to the Ultraschall-API folder.
  5. Api_InstallPath - an API-variable that contains the path to the install-folder of the Ultraschall-API

These can be accessed using:

            scriptpath = ultraschall.Script_Path
    

which would put the current-scriptpath to the variable scriptpath.

You could change them as well, but that would be pointless.



^ Rendering: Introduction

The Ultraschall-API provides functions for rendering your projects, without having to use the Rendering-dialog of Reaper. This gives you a wide range of possibilities to customize your rendering-needs.

Basically the process is based on:

  1. creating a renderstring with all format-specific-settings, using the accompanying functions:
    CreateRenderCFG_AIFF, CreateRenderCFG_DDP, CreateRenderCFG_FLAC, CreateRenderCFG_MP3ABR, CreateRenderCFG_MP3CBR, CreateRenderCFG_MP3MaxQuality, CreateRenderCFG_MP3VBR, CreateRenderCFG_OGG, CreateRenderCFG_Opus,
    CreateRenderCFG_Opus2, CreateRenderCFG_WAV, CreateRenderCFG_WAVPACK, CreateRenderCFG_WebMVideo

  2. passing the render-string to one of the Render-functions
    RenderProject_RenderCFG, RenderProjectRegions_RenderCFG

So the following code should render the current project into an MP3 with a Constant Bitrate of 128kbps. Note: to render the currently opened project, it must be saved first!

       -- create Render-string
       render_cfg_string = ultraschall.CreateRenderCFG_MP3CBR(11, 2)
       
       -- Pass this Render-string to the rendering-function
       ultraschall.RenderProject_RenderCFG(nil, "c:\\exportfile.mp3", 0, -1, false, false, false, render_cfg_string)

One downside, though: when you want to render the currently opened project, it will be opened a second time for the rendering.
So if the current project has many effect-plugins and/or uses much memory, this means, the rendering of the current project using the UltraschallAPI-functions needs twice the ressources of your project! This is due Reaper-limitations, unfortunately.

Read the accompanying documentation-entries for CreateRenderCFG_MP3CBR and RenderProject_RenderCFG for more details and/or the following chapters.



^ Rendering: About Renderstrings

Render-strings(or render_cfg, as they are named in Reaper) are strings, that contain all settings for a specific Render-Output-format, as MP3, WAV, AIF, FLAC, etc.
They are usually stored into RPP-projectfiles and are quite cryptic(for those of you, who know what that means: BASE64-encoded).
These are essential for rendering a project using the provided rendering-functions, so the Ultraschall-API provides functions who create these render-strings.

All these functions start with CreateRenderCFG_ in their name, so if you are looking for a specific format, use CreateRenderCFG_audioformat (like CreateRenderCFG_FLAC, etc).
The parameters of these functions represent all format-options as you are used from the Render-dialog. They should be fairly complete, though some formats (Video, OGG) are limited with some of their options(fps, width, height, kbps, etc).
This is due my approach decoding them, though I tried to provide all settings that are useful in your everyday use(documented in the accompanying parameter-descriptions in the functions-reference).

Example for FLAC:

        render_string = ultraschall.CreateRenderCFG_FLAC(integer BitDepth, integer EncSpeed)

creates the render-string for the FLAC-fileformat. Just provide the BitDepth(e.g 0 for 24 Bit) and the encoding-speed(EncSpeed) (e.g 5 for the default encoding speed).
Have a look into the functions-reference to get the possible values for these parameters.

        render_string = ultraschall.CreateRenderCFG_FLAC(0, 5)

This will create a render-string for FLAC with 24bit-depth and the encoding-speed of 5(which is the default-setting in the render-dialog).

This renderstring can then be passed over to SetProject_RenderCFG to set it into a project-file, or to RenderProject_RenderCFG to render a projectfile using the format-settings in the Render-string.

The following render-string-functions are available in Ultraschall-API:
CreateRenderCFG_AIFF, CreateRenderCFG_DDP, CreateRenderCFG_FLAC, CreateRenderCFG_MP3ABR, CreateRenderCFG_MP3CBR, CreateRenderCFG_MP3MaxQuality, CreateRenderCFG_MP3VBR, CreateRenderCFG_OGG, CreateRenderCFG_Opus, CreateRenderCFG_Opus2, CreateRenderCFG_WAV, CreateRenderCFG_WAVPACK,
CreateRenderCFG_WebMVideo



^ Rendering: About Rendering-functions

The rendering-functions let you render a project, either a stored rpp-project-file or the currently opened one(only when it has been saved!).
They provide you with a wide range of functionality, so the rendering process should be quite close to Reaper's "official"-own-process using the render-dialog.
They also return the filenames of the rendered files, as well as MediaItemStateChunks of all rendered files, so you can easily import them into your project.

There are currently two different render-functions available:

  1. RenderProject_RenderCFG - with this one, you can render a whole project or just from startposition to endposition in seconds
  2. RenderProjectRegions_RenderCFG with this one, you can render specific regions of a project

Basically they are the same, with the only difference, that with the first one you can set a specific startposition and endposition, while the second one, you set it to a region-number.
That said, a lot of the parameters are the same.

         projectfilename_with_path - the projectfile with path, that you want to render. Set it to nil, if you want to render the 
                                     currently opened project(which must be saved before rendering!)
         renderfilename_with_path  - the filename with path of the output-file
         overwrite_without_asking  - if you want to overwrite already existing outputfiles, set this to true; else, set it to false

The following two are directly connected to some of Reaper's dialogs:

         renderclosewhendone - the render-progress-window, that is shown during the actual rendering can be closed automatically after  
                               rendering is finished(there's a checkbox in that window to set this). Set this to true to automatically 
                               close it; set it to false to keep it shown; set it to nil and it will use the setting the user set with 
                               the checkbox
         filenameincrease    - another of Reaper's dialogs, that will pop up, when an output-file already exists and overwrite_without_asking 
                               is set to false. It will ask you to automatically increase the filename with a number to prevent accidental 
                               overwriting. Set this to true to automatically increase filename; 
                               set it to false to show the dialog; set it to nil to use the settings the user chose

The last parameter is the place for your render-string:

         rendercfg           - the renderstring, as created using a CreateRenderCFG_XXX-function, as described in 
                               the chapter "Rendering: About Renderstrings"
                               if you omit it or set it to nil, it will use the format-settings already set in the projectfile

It also returns some interesting return-values:

        retval - 0, if rendering was successful; -1, in case of an error; -2, if the project hasn't been saved yet
        renderfilecount - the number of rendered files. Usually 1, but can be higher, when rendering stems as well
        MediaItemStateChunkArray - an array with MediaItemStateChunks of the rendered projects, ready to include into a project of your choice
                                   the first entry is for the Master-render-file
        Filearray - an array with filenames-with-path of all rendered files, with the first entry being the one of the master-track-rendered-file

With that, you should be able to successfully render your project and do some neat stuff afterwards.

For more enhanced customization of Rendering, see Rendering: Change more render-settings for more details.



^ Rendering: Change more render-settings

Reaper's own render-dialog provides you with much more possibilities, than the rendering-functions themselves provide you with.

In the likely case, that you want to influence more things for rendering, like samplerate, stereo or mono, etc, you should do the following things:

  1. make a copy of the rpp-file that you want to render using MakeCopyOfFile. The copy must have a different name and be in the same folder, as the original rpp-file!
  2. Now you can alter the copy of the projectfile using the following Ultraschall-Framework-functions, that represent certain elements from Reaper's Render-Dialog:

  3. after you've set the individual render-settings, you create a render-string, as described in Rendering: About Renderstrings.

  4. render using the altered copy(!) of the projectfile(not the original projectfile!), as described in Rendering: About Rendering-functions.

If you want to alter the currently opened project, you need to save it first. After that, use:

        retval, projectfilename_with_path = reaper.EnumProjects(-1,"")

to get the projectfilename_with_path of the current project as returnvalue. Use projectfilename_with_path and go through steps 1 through 4.

The functions in step 2) are just a small selection of the functions to alter project-files, as provided by the Ultraschall-API. Browse through the functions in the "Project-Files"-section of the index of the Ultraschall-API-Functions-Reference for many more of them.
However, changing some of the stuff might cause issues with the rendering-functions provided with this API.



^ Arrangeview Snapshots: Introduction

When working with big or complex projects:

Sometimes it's a good thing to have quick access to certain parts of the project, certain view-settings, zoom-factors. Arrangeview-snapshots are meant to help with that.

Arrangeview-Snapshots are snapshots that store the current position of the arrangeview as well as it's zoom-factor. You can decide, whether to store only the zoom-factor or the position. You can also give a short description to a ArrangeView-Snapshot, so you can store, what to expect from a certain snapshot.
They can be retrieved and the arrange-view can be set to these settings. That way, quick navigation through often accessed parts of the project is fast and easy.

Arrangeview-Snapshots are stored as ProjExtStates, which means, that the settings are stored in the project itself and can be retrieved the next time the project is loaded.

Due limitations with Reaper's own API, storing the vertical-scroll-position of the arrangeview isn't possible yet. This will change as soon as the limitation is raised from Reaper's own API.



^ Arrangeview Snapshots: How to store, retrieve, delete

If you want to store the current position and zoom-factor of the Arrangeview, you can use the function StoreArrangeviewSnapshot.

It accepts the following parameters:

        slot        - the slot for the snapshot, which must be an integer. The function will overwrite an already existing snapshot. To prevent that,
                      use ultraschall.IsValidArrangeviewSnapshot() to check, if it's already existing.
        description - a short description, what the snapshot contains so you know, what to expect from it
        position    - set to true to store the startposition and endposition of the arrangeview. Otherwise(false), it will only store the current horizontal zoom-factor
        vzoom       - set to true, if you want to store the vertical zoom-factor as well; set to false, if you don't want it to be stored.

If you want to retrieve the settings of a certain Arrange-View-Snapshot, you can use RetrieveArrangeviewSnapshot, which will return all settings from an Arrangeview-snapshot. The return-values of RetrieveArrangeviewSnapshot basically work the same as the parameters of StoreArrangeviewSnapshot.

If you want to check, whether a slot is already used, you can use IsValidArrangeviewSnapshot, which will return true in that case and false, if the slot is unused.

To delete a certain slot, just use DeleteArrangeviewSnapshot.



^ Arrangeview Snapshots: How to restore

When having stored an Arrangeview-Snapshot into a slot, you certainly want to restore it at one point. For that, use RestoreArrangeviewSnapshot.

This function let's you restore an earlier arrange-view completely, but also allows you to individually set, what you want to restore, using the parameters:

        slot        - is the Arrangeview-Snapshot you want to restore

the other parameters are optional, means, if you omit them or set them to nil, they will restore the setting from the snapshot or use a default setting

        position    - true, restore the start and endposition of the arrange-view; false, just restore the horizontal-zoom-factor
        vzoom       - set to true to restore the vertical zoom-factor or set to false to keep the current one
        hcentermode - this decides, what shall be in the center of the arrangeview, when position is set to false, with several options possible:
                       nil, keeps center of view in the center during zoom(default setting)
                        -1, default selection, as set in the reaper-prefs, 
                         0, edit-cursor or playcursor(if it's in the current zoomfactor of the view during playback/recording) in center,
                         1, keeps edit-cursor in center of zoom
                         2, keeps center of view in the center during zoom
                         3, keeps in center of zoom, what is beneath the mousecursor
         

This should give you full control in what to restore from an Arrangeview-Snapshot and what to ignore.



^ Navigation: Introduction

When editing and postproducing a project, navigating through it is essential. For that, I added some functions that are not part of Reaper's own API, to help navigation, with functions for:

  1. more control about moving the playcursor and the editcursor
  2. jumping to the next/previous closest marker/regionedge/itemedge
  3. centering the view to several possible center-positions selectable(mousecursor, editcursor, playcursor)
  4. Followmode, aka autoscrolling

This should give you more control about programming faster and quicker navigation-capabilities.



^ Navigation: Move Play and Editcursor

The Ultraschall-API provides you with many functions regarding changing the position of the playcursor and the editcursor.
For that we have numerous functions:



^ Navigation: Go to markers, regionedges and itemedges

Markers and items provide you with much additional helpful information regarding the project. They also provide you with an additional information: useful positions to navigate through.
To make use of that, I added some functions for that:

  1. GetClosestPreviousMarker, GetClosestNextMarker - Get the previous/next closest marker at a given position
  2. GetClosestPreviousRegionEdge, GetClosestNextRegionEdge - Get the previous/next closest regionedge at a given position
  3. GetPreviousClosestItemEdge, GetNextClosestItemEdge - Get the previous/next closest itemedge at a given position
  4. GetClosestGoToPoints - get previous/next markers/regionedges/itemedges/projectstart/projectend from position, for those who need the full marker/region/item-position-package

Let's go into more detail, by examining GetClosestPreviousRegionEdge.

        number markerindex, number position, string markername, string edge_type = 
                                                    ultraschall.GetClosestPreviousRegionEdge(integer cursor_type, optional number time_position)

This function allows you to get, which is the previous closest region-edge-position(either the start or the end of a region) as seen from a given position. To set that position, you need to set the parameter cursor_type:

        0 - Edit Cursor,   
        1 - Play Cursor,   
        2 - Mouse Cursor, or  
        3 - Timeposition  

If you set it to 3, you can use the optional parameter time_position, with which you can set any position, at which you want to know the previous closest region-edge.

If you run that function, it will return the markerindex, which is the index of all markers in your project, the position at which the regionedge is located, the markername and the type of the edge, which is either "beg" or "end".

The Marker-functions (from 1) ) work the same, the Item-edge-functions (from 3) ) however have an additional parameter trackstring, with which you can set, from which tracks you want to get the next/previous closest item-edge-position.

The function GetClosestGoToPoints is the combination of all of these functions, which let's you decide fully, which edges/positions you want to check for.
It will also check, if the next/previous closest edge is the beginning or the end of the project.



^ Navigation: Center View

Sometimes it's a good idea to center the arrangeview to a certain point, may it be different points of interests within your project or just to get back to the playcursor/editcursor out of the view.

For that, I added the function CenterViewToCursor.
Let's have a look at it:

        ultraschall.CenterViewToCursor(integer cursortype, optional number position) 

It has two parameters, of which cursortype allows you to give the type of the cursor to center around:

        1 - change arrangeview with edit-cursor centered
        2 - change arrangeview with play-cursor centered
        3 - change arrangeview with mouse-cursor-position centered
        4 - centers arrangeview around the position given with parameter position

The second parameter position is an optional one and only used, if cursortype is set to 4. It allows you to give a specific position in seconds, which the arrangeview shall be centered around.

This functions only centers the given position/cursor-position to the arrangeview. It keeps the zoom-factor intact.



^ Navigation: Autoscroll and Followmode

Reaper allows you to set autoscrolling during playback/recording. It allows you to set it to continuous scrolling or to "page-wise"-scrolling.
This is quite flexible but hidden somewhat within the actions of Reaper. So I added the function ToggleScrollingDuringPlayback.
It turns on autoscrolling for playback and recording AND continuous scrolling.
Let's have a look at it:

        ultraschall.ToggleScrollingDuringPlayback(integer scrolling_switch, boolean move_editcursor, boolean goto_playcursor)

The parameter scrolling_switch allows you to turn on/off autoscrolling completely, that means, it will turn on autoscroll for playback and recording AND it sets autoscrolling to continuous scrolling.
The parameter move_edit_cursor allows you to set, if the editcursor shall be moved to the current playposition. This has an effect only, if scrolling_switch is set to 1(off).
The last parameter goto_playcursor allows you to change the view to the current playcursor-position, if you turn on autoscrolling. This has an effect only, if scrolling_switch is set to 0(off).

When running the function, it changes, if neccessary, the toggle-states of the actions

        41817(View: Continuous scrolling during playback),  
        40036(View: Toggle auto-view-scroll during playback) and   
        40262(View: Toggle auto-view-scroll while recording), 

which means, it sets if a certain autoscrolling behavior is turned on or not.
If you have your own custom actions toggling these actions, you probably shouldn't use this function. Otherwise it probably messes up your workflows.



^ Get/Set States for Project, Tracks and Items

One of the long-term-goals of the Ultraschall-API is full access to all states within projects, tracks, items, envelopes, including all states only available in StateChunks.
As of the current version, all track-states and many project and item-states are get and settable.

The functions all work after the same principle, with the first parameter being the object to get/set the state from/to (Projectfile, MediaItem-object, MediaTrack-object and TrackEnvelope-Object).

The last parameter, which is an optional one, can be a StateChunk-representation of the object, like ProjectStateChunk, MediaItemStateChunk, TrackStateChunk, TrackEnvelopeStateChunk. This optional parameter will only be seen, when the first parameter(for the object) is set to nil.
That way, you can decide, whether to use the original-object or the StateChunk, whatever works better for you.

In Setting-State-functions, it is basically the same: the first parameter the object and the last parameter is the optional StateChunk(when the first parameter is set to nil). The parameters inbetween set the individual settings for that state.

Let's have a look at two example functions GetProject_CursorPos and SetProject_CursorPos:
Get Project-State:

        number cursorpos = ultraschall.GetProject_CursorPos(string projectfilename_with_path, optional string ProjectStateChunk) 

The first parameter is the filename with path to the RPP-Projectfile. When this is set to nil, you can pass a ProjectStateChunk(which is basically the content of the RPP-projectfile).

Set Project-State:

        integer retval = ultraschall.SetProject_CursorPos(string projectfilename_with_path, number cursorpos, optional string ProjectStateChunk) 

The first parameter is the filename with path to the RPP-Projectfile. When this is set to nil, you can pass a ProjectStateChunk(which is basically the content of the RPP-projectfile). The parameter in the middle, cursorpos, can be set by you. That way, the cursorposition of the Project/ProjectStateChunk can be set to the position you prefer.

The same principle is for MediaTrack-states and MediaItem-states:

        -- the first parameter either MediaItem or nil, the last parameter can be a MediaItemStateChunk when first parameter is nil
        number length = ultraschall.GetItemLength(MediaItem MediaItem, optional string MediaItemStateChunk)
        string MediaItemStateChunk = ultraschall.SetItemLength(MediaItem MediaItem, integer length, string MediaItemStateChunk)
        
        -- the first parameter either MediaTrack or nil, the last parameter can be a TrackStateChunk when first parameter is nil
        integer lockedstate = ultraschall.GetTrackLockState(integer tracknumber, optional string TrackStateChunk)
        boolean retval, string TrackStateChunk = ultraschall.SetTrackLockState(integer tracknumber, integer LockedState, optional string TrackStateChunk) 

StateChunks can be gotten using reaper.GetTrackStateChunk(), reaper.SetTrackStateChunk(), reaper.GetItemStateChunk(), reaper.SetItemStateChunk(), reaper.GetEnvelopeStateChunk(), reaper.SetEnvelopeStateChunk().
To get ProjectStateChunks, you need to read the rpp-file, as currently there's no Get/SetProjectStateChunk-function in Reaper's own API.



^ MediaItems: Introduction

When working with MediaItems, it often was frustrating for me to code, how to get their MediaItem-objects. Especially when "mass-working" with dozens and more MediaItems.
So I wrote a set of functions to work with MediaItems more comfortably.

These functions include getting MediaItem-objects by time AND track, getting MediaItem-states, editing, inserting, manipulating, spectral edit, previewing, RippleCut, RippleInsert, SectionCut, working with locked, selected items, applying Reaper-actions to MediaItems, etc.

When working with masses of MediaItems, I either use the datatypes MediaItemArrays or MediaItemStateChunkArrays.
When passing over the tracks wanted, I use the datatype trackstring.

All this stuff should help you getting and manipulating MediaItems much more easier.

Let's begin with getting items by time(range) and tracks.



^ MediaItems: Getting MediaItems by Time and Tracks

Let's face it, when editing items of a project in Reaper, you either click on the items or select them in a 2D-way, by drawing a boundary box around the items of your choice or using a time-selection.
What you do by that is selecting the items by time. What you also do is, selecting the items by track, as your boundary box may go over several tracks. Or you use a track-selection by clicking on the tracks you want.
In either way, you select them in a 2Dimensional way. Not with Reaper's own API. Sure, you can somehow choose the MediaItems by track or by project, but you can't select them by multiple tracks. And certainly not by a time-range.

This was annoying for me, so to address this, I wrote the two functions GetMediaItemsAtPosition and GetAllMediaItemsBetween (my favorite ones in this api, I have to admit ;) ).

Let's have a closer look at them.

GetMediaItemsAtPosition:

        integer number_of_items, array MediaItemArray, array MediaItemStateChunkArray 
                                                       = ultraschall.GetMediaItemsAtPosition(number position, string trackstring)

This function gives you all items at position passed with parameter position and within the tracks given by parameter trackstring.
It returns the number of items, an array with all MediaItems and an array with all StateChunks of the MediaItems returned.
With this function, you can easily get the items from a certain position, without having to deal with looking into the MediaItem-objects for the correct time-position, or even have to care, where to get the corresponding tracks from an item.
This function does this for you.

But what, if you want to get the MediaItems inbetween a startingposition and an endposition?
For this, I wrote the function

GetAllMediaItemsBetween:

         integer count, array MediaItemArray, array MediaItemStateChunkArray = 
                     ultraschall.GetAllMediaItemsBetween(number startposition, number endposition, string trackstring, boolean inside) 

which basically returns the same things, as GetMediaItemsAtPosition. The difference lies in the parameters.
You can pass to the function a startposition and an endposition(which must be bigger than or equal startposition), trackstrings, which is a string with all tracks, separated by commas as well as inside as parameters. When you set inside to true, it will return only items that are completely within startposition and endposition. When setting inside to false, it will also return items, that are partially within start- and endposition, like items beginning before startposition or ending after endposition.

With these two functions, getting items is much, much easier than before.

The returned MediaItems, MediaItemArrays and MediaItemStateChunkArrays can then be passed over to other functions, who accept them, for "mass manipulation" of the MediaItems.

In addition to them, I also added some more functions for getting MediaItems, namely:



^ MediaItems: Splitting MediaItems by Time and Tracks

Getting MediaItems by time and tracks is cool. Editing them by time and tracks is even better. For that, I also added some functions:
The easiest ones are SplitMediaItems_Position and SplitItemsAtPositionFromArray
Let's have a look at:

SplitMediaItems_Position:

            boolean retval, array MediaItemArray = ultraschall.SplitMediaItems_Position(number position, string trackstring, boolean crossfade)

This splits all items at position, that are in the tracks given by parameter trackstring. If you want to have the items So if you want to split all items in tracks 1,3,4 at position 22, you type:

            retval, MediaItemArray = ultraschall.SplitMediaItems_Position(22, "1,3,4", false)

There's another parameter crossfade. If you have it set to true or nil and have Automatic-Crossfade enabled (Preferences -> Media Item Defaults -> Overlap and crossfade items when splitting, length), crossfade will be done at the split.
If you want to avoid that, set it to false and a normal split with fadein/fadeout will appear(if set in the preferences: Preferences -> Media Item Defaults -> Create automatic fade-in/fade-out for new items, length).

This function returns, if splitting was successful and the newly "created"-right-hand-split-items as an MediaItemAray.

SplitItemsAtPositionFromArray:

The function SplitItemsAtPositionFromArray works quite similar, but with the difference, that you don't give tracks, but items to the function, that shall be split at position.

            boolean retval, array MediaItemArray = ultraschall.SplitItemsAtPositionFromArray(number position, array MediaItemArray, boolean crossfade)

position is the position, at which an item shall be split. MediaItemArray is an array with all MediaItems, that shall be split, if possible. crossfade sets if automatic crossfade shall be applied to or not, just as in SplitMediaItems_Position above.
This function will split only items, that have the position in them somewhere. That means, if you want to split at position 22 seconds, an item, that goes from 1 to 4 will not be split, an item from 18 to 25 will be split.

This function returns, if splitting was successful and the newly "created"-right-hand-split-items as an MediaItemAray. Only the right-hand-split-MediaItem of MediaItems, that could be split, will be returned. If a MediaItem could not be split(position outside MediaItemstart and MediaItemEnd), there will be no returned MediaItem for it then.



^ MediaItems: Deleting MediaItems by Time and Tracks

Deleting of MediaItems is often a useful thing. Unfortunately, this is inconvenient to do within the Reaper-API. The only such function is DeleteTrackMediaItem, but it requires you to give the function the track in which the MediaItem lies too. But often, you want to have a function that simply deletes a MediaItem-object or deletes items at position from numerous tracks, etc.
So I added some functions, that make life easier: DeleteMediaItem, DeleteMediaItemsFromArray, DeleteMediaItems_Position and DeleteMediaItemsBetween

In addition to deleting the MediaItems, all these functions return the statechunks of the deleted MediaItems. These statechunks contain an additional field

            "ULTRASCHALL_TRACKNUMBER"

which contains the track, in which the MediaItem was located before deleting it.
This may help doing cut and paste functions, as otherwise, you lose the information, in which track a certain MediaItem was located.

DeleteMediaItem:

            boolean retval, string MediaItemStateChunk = ultraschall.DeleteMediaItem(MediaItem MediaItem)

This function is simple. Just pass the MediaItem that you want to delete to it and it will delete it. If no such MediaItem exists, it will return false.

DeleteMediaItemsFromArray:

            boolean retval, array MediaItemArray = ultraschall.DeleteMediaItemsFromArray(array MediaItemArray)

This function might be more interesting if you want to delete a number of MediaItems at once. Just pass to it a MediaItemArray(like the one returned by functions like GetAllMediaItemsBetween)

DeleteMediaItems_Position:

            boolean retval, array MediaItemStateChunkArray = ultraschall.DeleteMediaItems_Position(number position, string trackstring)

This function deletes all items at position in the tracks, given by trackstring.
If you want to delete all items at position 22, within track 1,4,8 and 9, you just type:

            retval = ultraschall.DeleteMediaItems_Position(22, "1,4,8,9")

DeleteMediaItems_Position:

            boolean retval, array MediaItemStateChunkArray  = 
                        ultraschall.DeleteMediaItems_Between(number startposition, number endposition, string trackstring, boolean inside)

This function deletes MediaItems between start and endposition, within the track given by parameter trackstring. You can use the parameter inside to set, if you want to delete only items that are completely within start and endposition(true) or also include items, that are only partially within start and endposition.
This function works like GetAllMediaItemsBetween, with the additional benefit of deleting the MediaItems.
Let's assume, you want to delete all MediaItems between position 33 and 98, within the tracks 3, 5, 10 and 14 and only the items that are completely within the position 33 and 98, you type:

            retval, MediaItemStateChunkArray  = ultraschall.DeleteMediaItems_Between(33, 98, "3,5,10,14", true)

With all these functions, deleting MediaItems is now comfortable to do.



^ MediaItems: SectionCut, RippleCut, RippleInsert

One of Reaper's real great features is Ripple-Edit. On of the big shortcomings, it only allows Ripple Cut all tracks, Ripple Cut one track, no Ripple Cut.
But what if you want to RippleCut two or more tracks, but not all of them? What, if you want to RippleCut only selected tracks? What if you just want to cut a section without rippling?
Impossible you say!
With naked Reaper, yes. But possible with the Ultraschall-API.

For that I added the following functions: RippleCut, RippleCut_Reverse, RippleInsert, SectionCut, SectionCut_Inverse
All these functions return a MediaItemStateChunkArray, where every StateChunk includes an additional entry "ULTRASCHALL_TRACKNUMBER", which holds the tracknumber, in which the cut MediaItem/piece of a MediaItem was originally located.

RippleCut:

            integer number_items, array MediaItemArray_StateChunk 
                 = ultraschall.RippleCut(number startposition, number endposition, string trackstring, boolean moveenvelopepoints, boolean add_to_clipboard)

With this function, you can RippleCut between startposition, endposition within the tracks as given in trackstring. You can also decide, whether to move the envelope-points as well.
You can also decide, whether the cut items shall be put into the clipboard as well. This should give you total control in how RippleCut is behaving for your needs.
Let's take an example. If you want to cut between seconds 20 and 50 in track 1,4,5,7, not moving the markers but the envelope-points, you type in this Example:

            number_items, MediaItemArray_StateChunk = ultraschall.RippleCut(20, 50, "1,4,5,7", true, true)

This cuts out the section between seconds 20 and 50 and moves everything after that toward the beginning of the project, to fill the gap of the cut section. It also puts the cut items into the clipboard.

RippleCut_Reverse:

            integer number_items, array MediaItemArray_StateChunk = 
                 ultraschall.RippleCut_Reverse(number startposition, number endposition, string trackstring, boolean moveenvelopepoints, boolean add_to_clipboard)

With this function, you can RippleCut, but unlike RippleCut above, RippleCut_Reverse moves everything BEFORE the cut towards the end to fill the cut.
Everything else is just the same as RippleCut.

RippleInsert:

            integer number_of_items, array MediaItemArray, number endpos_inserted_items = 
                ultraschall.RippleInsert(number position, array MediaItemArray, string trackstring, boolean moveenvelopepoints, boolean movemarkers)

This function inserts the items in MediaItemArray at position within the tracks, given by trackstring. You can also decide, whether markers and envelope-points shall be moved.
This is quite the opposite of RippleCut: it will split the items at position, move the items after the split towards the end of the project and include the MediaItems in MediaItemArray.
The length of the movement is according the overall length of all MediaItems, beginning with the earliest and ending with the latest MediaItem-length in MediaItemArray.
One additional note: MediaItems will only be included into the tracks they were originally located in, means: an item from track 1 will be included into track 1. The parameter trackstring can only be used to exclude items from certain tracks.
So a trackstring "1,3,4" will only insert all items from tracks 1,3 and 4, leaving out all of the MediaItems from track 2.
Example:

            number_of_items, MediaItemArray, endpos_inserted_items = ultraschall.RippleInsert(20, MediaItemArray, "1,4,9,10" false, false)

This will insert all MediaItems from MediaItemArray, ordered by their relative position, at position 20 seconds. Only the MediaItems from tracks 1,4,9,10 will be included. All others will be ignored.
Markers and Envelopepoints will not move in this example.

SectionCut:

            integer number_items, array MediaItemArray_StateChunk = 
                              ultraschall.SectionCut(number startposition, number endposition, string trackstring, boolean add_to_clipboard)

This function just cuts out the section between start and endposition in the tracks, given in trackstring, leaving a "gap" in it. Useful, when you don't want to ripple stuff.
You can also decide, whether to put the cut items into the clipboard. If you want to cut between seconds 77 and 99 in tracks 1,2,4,6 do it like in this Example:

            number_items, MediaItemArray_StateChunk = ultraschall.SectionCut(77, 99, "1,2,4,6", false)

SectionCut_Inverse:

            integer number_items_beforestart, array MediaItemArray_StateChunk_beforestart, 
            integer number_items_afterend, array MediaItemArray_StateChunk_afterend 
                                   = ultraschall.SectionCut_Inverse(number startposition, number endposition, string trackstring, boolean add_to_clipboard)

This function cuts everything BEFORE AND AFTER start and endposition within the tracks given by trackstring. This is comparable to crop-functionality in graphic-applications like Photoshop, applied to MediaItems.
If you have a 10 minute project, but want to use only the audio from seconds 60 to 89 in tracks 1,2,7,8 you type in this Example:

            number_items_beforestart, MediaItemArray_StateChunk_beforestart, number_items_afterend, MediaItemArray_StateChunk_afterend = 
                                                                                            ultraschall.SectionCut_Inverse(60, 89, "1,2,7,8", false)

With that, everything before second 60 and everything after second 89 in tracks 1,2,7,8 will be deleted.
In addition to that, the function returns a MediaItemStateChunkArray for both, the items cut before startposition and one for the items cut after endposition.

This should give you many additional use-cases into your hands.



^ MediaItems: Moving and Manipulating

Getting, splitting, editing and deleting MediaItems isn't enough. In fact, you also want to manipulate them. And you also want to be able to manipulate many of them at once.
Hence, I added lots of functions to manipulate MediaItems, like:

Moving

Let's have a look at the moving-MediaItems-functions.

MoveMediaItemsAfter_By

            boolean retval = ultraschall.MoveMediaItemsAfter_By(number old_position, number change_position_by, string trackstring)

This function moves all MediaItems from old_position and later by a number of seconds, as given by the parameter change_position_by.
If change_position_by is negative, the MediaItems will be move towards the beginning of the project; a positive value will move the MediaItems toward the end.
The parameter trackstring tells the function, in which tracks the MediaItems shall be moved.

MoveMediaItemsBefore_By

            boolean retval = ultraschall.MoveMediaItemsBefore_By(number old_position, number change_position_by, string trackstring)

This basically works like the MoveMediaItemsAfter_By above, with the difference, that it moves the MediaItems BEFORE old_position.

MoveMediaItemsBetween_To

            boolean retval = ultraschall.MoveMediaItemsBetween_To(number startposition, number endposition, number newposition, string trackstring, boolean inside)

This also moves MediaItems, but the MediaItems between startposition and endposition. Unlike the functions above, you give the new position in seconds, at which the MediaItems shall start. The relative-positions of the MediaItems will stay intact.
The parameter inside allows you to tell the function, whether to include only MediaItems completely within start and endposition(true) or also MediaItems that are partially within start and endposition(false).

MoveMediaItems_FromArray

            integer retval, number earliest_itemtime, number latest_itemtime = ultraschall.MoveMediaItems_FromArray(array MediaItemArray, number newposition)

This moves the MediaItems in MediaItemArray to the newposition. It will retain the relative positions of the MediaItems as well.

Length

You can change the length of the MediaItems with functioncalls of Reaper's own API already. I added functions, that allow you to change the length of multiple MediaItems at once, using MediaItemArray. And not just the length to a given length, but also a deltalength. Let's have a look.

ChangeLengthOfMediaItems_FromArray

            boolean retval = ultraschall.ChangeLengthOfMediaItems_FromArray(array MediaItemArray, number newlength)

This changes the length of all MediaItems in the MediaItemArray to newlength in seconds.
Example:

            MediaItemArray={}
            MediaItemArray[1]=reaper.GetMediaItem(0,0)
            MediaItemArray[2]=reaper.GetMediaItem(0,1)
            MediaItemArray[3]=reaper.GetMediaItem(0,2)
        
            retval = ultraschall.ChangeLengthOfMediaItems_FromArray(MediaItemArray, 3)

This examplecode will change the length of all MediaItems in MediaItemArray to a length of 3 seconds.

ChangeDeltaLengthOfMediaItems_FromArray

            boolean retval = ultraschall.ChangeDeltaLengthOfMediaItems_FromArray(array MediaItemArray, number deltalength)

This changes the length of the MediaItems in MediaItemArray as well, BUT it will change the length BY deltalength in seconds. That means, if deltalength is 4, all MediaItems in the MediaItemArray will become longer by 4 seconds, if deltalength is -3, all MediaItems in MediaItemArray will become 3 seconds shorter(!)
Example:

            MediaItemArray={}
            MediaItemArray[1]=reaper.GetMediaItem(0,0) -- let's assume, this MediaItem is 10 seconds long
            MediaItemArray[2]=reaper.GetMediaItem(0,1) -- let's assume, this MediaItem is 30 seconds long
            MediaItemArray[3]=reaper.GetMediaItem(0,2) -- let's assume, this MediaItem is 5 seconds long
        
            retval = ultraschall.ChangeDeltaLengthOfMediaItems_FromArray(MediaItemArray, 4)

This examplecode will change the length of all MediaItems in MediaItemArray by 4 seconds, so the first item is now 14 seconds long, the second 34 and the third 9 seconds.

Offset

Just like the length of MediaItems, you can change the offset as well with functioncalls of Reaper's own API. I added functions, that allow you to change the length of multiple MediaItems at once, using MediaItemArray. And not just the length to a given length, but also a deltalength. Let's have a look.

ChangeOffsetOfMediaItems_FromArray

            boolean retval = ultraschall.ChangeOffsetOfMediaItems_FromArray(array MediaItemArray, number newoffset)

This changes the offset of all MediaItems in the MediaItemArray to newoffset in seconds.
Example:

            MediaItemArray={}
            MediaItemArray[1]=reaper.GetMediaItem(0,0)
            MediaItemArray[2]=reaper.GetMediaItem(0,1)
            MediaItemArray[3]=reaper.GetMediaItem(0,2)
        
            retval = ultraschall.ChangeOffsetOfMediaItems_FromArray(MediaItemArray, 3)

This examplecode will change the offset of all MediaItems in MediaItemArray to a the new offset of 3 seconds.

ChangeDeltaOffsetOfMediaItems_FromArray

            boolean retval = ultraschall.ChangeDeltaOffsetOfMediaItems_FromArray(array MediaItemArray, number deltaoffset)

This changes the offset of the MediaItems in MediaItemArray as well, BUT it will change the offset BY deltaoffset in seconds. That means, if deltaoffset is 4, all MediaItems in the MediaItemArray will start 4 seconds later, if deltaoffset is -3, all MediaItems in MediaItemArray will start 3 seconds earlier(!)
Example:

            MediaItemArray={}
            MediaItemArray[1]=reaper.GetMediaItem(0,0) -- let's assume, this MediaItem's offset starts at 10 seconds
            MediaItemArray[2]=reaper.GetMediaItem(0,1) -- let's assume, this MediaItem's offset starts at 30 seconds
            MediaItemArray[3]=reaper.GetMediaItem(0,2) -- let's assume, this MediaItem's offset starts at 0 seconds
            
            retval = ultraschall.ChangeDeltaOffsetOfMediaItems_FromArray(MediaItemArray, 4)

This examplecode will change the offset of all MediaItems in MediaItemArray by 4 seconds, so the first item's offset starts now at 14 seconds, the second 34 and the third 4 seconds.

Other functions for manipulating MediaItems:



^ MediaItems: Inserting Items and Files

Last, but not least, it would be nice to be able to insert MediaItems as well. And not just inserting them from a project, but also from files.
For that, I made: InsertMediaItem_MediaItem, InsertMediaItem_MediaItemStateChunk, InsertMediaItemArray, InsertMediaItemStateChunkArray, InsertMediaItemFromFile, InsertImageFile

Lets have a look at inserting MediaItems.

InsertMediaItem_MediaItem

            integer retval, MediaItem MediaItem, number startposition, number endposition, number length 
                        = ultraschall.InsertMediaItem_MediaItem(number position, MediaItem MediaItem, MediaTrack MediaTrack)

With this function, you can make a copy of an already existing MediaItem and insert at position into a certain track. It allows using MediaItems and MediaTracks of other projects than the current one, as well.
Just give the position, at which to insert the MediaItem, the MediaItem to be included and the MediaTrack into which to include the MediaItem.
It will return the newly created MediaItem, it's startposition, endposition and the length.

InsertMediaItem_MediaItemStateChunk

            integer retval, MediaItem MediaItem 
                        = ultraschall.InsertMediaItem_MediaItemStateChunk(number position, string MediaItemStateChunk, MediaTrack MediaTrack)

This is like InsertMediaItem_MediaItem, but uses a MediaItemStateChunk instead. The rest is just the same, including the possibility to insert the new MediaItem into a MediaTrack in another project than the current one.

InsertMediaItemArray

            integer number_of_items, array MediaItemArray = ultraschall.InsertMediaItemArray(number position, array MediaItemArray, string trackstring)
            

This allows you to insert multiple items at once, that are stored in a MediaItemArray at position. With trackstring you can set, into which tracks to insert the MediaItems.
There's a limitation, however: MediaItems will only be inserted into the tracks from where they originated from. That means, if you have MediaItems located in tracks 1-5 and you set trackstring to "1,2", only the MediaItems originating in tracks 1 and 2 will be inserted.
I'm still looking into a better way to provide the track, in which to insert the MediaItems into other MediaTracks as well.

InsertMediaItemStateChunkArray

            integer number_of_items, array MediaItemArray 
                            = ultraschall.InsertMediaItemStateChunkArray(number position, array MediaItemStateChunkArray, string trackstring)

This works like InsertMediaItemArray, but it's inserting MediaItemStateChunks from the MediaItemStateChunkArray instead.
In addition to that, it also lifts the track-limitation, when you insert the tracknumber into each MediaItemStateChunk using SetItemUSTRackNumber_StateChunk, which is automatically done by GetItem-functions from the Ultraschall-API.
The addition of the tracknumber is mandatory, otherwise the MediaItemStateChunk will not be inserted!

InsertMediaItemFromFile

            integer retval, MediaItem item, number endposition, integer numchannels, integer Samplerate, string Filetype
               = ultraschall.InsertMediaItemFromFile(string filename, integer track, number position, number endposition, integer editcursorpos, optional number offset)
               

This function allows you to insert a file as a new MediaItem into your project, including the tracknumber, position, endposition(if wanted), the editcursorposition and the offset, if wanted.
Example:

            retval, item, endposition, numchannels, Samplerate, Filetype = ultraschall.InsertMediaItemFromFile("C:\\file.wav", 1, 20, -1, 2, 0)

This example inserts the file file.wav into track 1, at position 20, with the length set to the length of the audio-file, the editcursorposition being put at the end of the new MediaItem and no offset-changes.

With that, you can easily insert files into your project.

InsertImageFile

            boolean retval, MediaItem item = ultraschall.InsertImageFile(string filename_with_path, integer track, number position, number length, boolean looped)

This is a special function, focusing on inserting image-files into the project. You can set the track, position, length of the image-MediaItem and, if it shall be looped.
If you don't loop it(looped=false), the MediaItem will have the length anyway, but the image will be shown only for 1 second in the VideoProcessor-window.



^ MediaItems: Programming Spectral Edit

With Reaper v5.50c, the devs introduced a new feature, called, spectral editing, which is a cool feature to influence frequencies in a spectral view of a MediaItem.
But they didn't include some functions to program this feature. So I added them myself.

The functions for using, manipulating, adding, deleting spectral-edits are: AddItemSpectralEdit, CountItemSpectralEdits, DeleteItemSpectralEdit, GetItemSpectralConfig, GetItemSpectralEdit, GetItemSpectralVisibilityState, SetItemSpectralConfig, SetItemSpectralEdit, SetItemSpectralVisibilityState

Let's go into this in more detail.

SetItemSpectralVisibilityState

To use the SpectralEdit-mode, you need to first enable visibility of it in a MediaItem. You can do this later too, but you will not see any of your changes, until you enable visibility first.

             string MediaItemStateChunk = ultraschall.SetItemSpectralVisibilityState(integer itemidx, integer state, optional string MediaItemStateChunk)
             

This function enables visibility of Spectral-Edit of item itemidx. The parameter state must be set to 1 to set to visible, or set to 0 to turn visibility off.
The function returns the altered MediaItemStateChunk in any way.
If you set itemidx to -1, you can use the optional parameter MediaItemStateChunk instead. This will add the corresponding entry for the visibility into the MediaItemStateChunk and returns the modified one.

Now that we have toggled the visibility of SpectralEdit, we might want to add SpectralEdit-instances to the MediaItem. For that we use:

AddItemSpectralEdit

             boolean retval, MediaItemStateChunk statechunk 
                         = ultraschall.AddItemSpectralEdit(integer itemidx, number start_pos, number end_pos, number gain, number fade, 
                                                           number freq_fade, number freq_range_bottom, number freq_range_top, integer h, 
                                                           integer byp_solo, number gate_thres, number gate_floor, number comp_thresh, 
                                                           number comp_exp_ratio, number n, number o, number fade2, number freq_fade2, 
                                                           string MediaItemStateChunk)
                                                           

This will add a SpectralEdit-instance into your MediaItem. You can add as many individual instances, as you want.

As you can see, you can influence a hell lot of parameters for such a SpectralEdit-instance, so I will not explain them in detail here. I suggest you to read the accompanying doc-entry for AddItemSpectralEdit, which explains the parameters in more detail.
Just some bits: Every Spectral-Edit-instance will be shown as a square/rectangle on top of the MediaItem. You can influence this rectangle's position and length, the frequency-ranges covered, the fades as well as all settings of all knobs appearing in it.
And as the cherry on the top: you can also bypass and solo it.
And, as the SetItemSpectralVisibilityState-function above, if you set itemidx to -1, you can add the Spectral-Edit-instance to a MediaItemStateChunk instead.

But what if you want to modify an already existing SpectralEdit-instance? Good question and I have a good answer to that:

SetItemSpectralEdit

             string MediaItemStateChunk 
                         = ultraschall.SetItemSpectralEdit(integer itemix, integer spectralidx, number start_pos, number end_pos, number gain, 
                                                           number fade, number freq_fade, number freq_range_bottom, number freq_range_top, 
                                                           integer h, integer byp_solo, number gate_thres, number gate_floor, number comp_thresh, 
                                                           number comp_exp_ratio, number n, number o, number fade2, number freq_fade2, 
                                                           string MediaItemStateChunk)
                                                           

This sets an already existing SpectralEdit-instance, and it probably reminds you very much of the AddItemSpectralEdit-function.
However, there is a small difference in it, the second parameter spectralidx, which tells the function, which spectral-edit-instance you want to change, with 1 for the first.
The rest is like AddItemSpectralEdit.

To delete such an instance, you can use the function:

DeleteItemSpectralEdit

             boolean retval, string MediaItemStateChunk = ultraschall.DeleteItemSpectralEdit(integer itemidx, integer spectralidx, string MediaItemStateChunk)
             

With that, you can easily delete a SpectralEdit-instance, by giving the item's idx(itemidx) and the number of the SpectralEdit-instance(spectralidx).
And, as most of the functions before: when setting itemidx to -1, you can use the optional parameter MediaItemStateChunk

To successfully set an already existing instance, you probably want to know, what current settings are in a SpectralEdit-instance.
For that, there is:

GetItemSpectralEdit

             number start_pos, number end_pos, number gain, number fade, number freq_fade, 
             number freq_range_bottom, number freq_range_top, integer h, integer byp_solo, 
             number gate_thres, number gate_floor, number comp_thresh, number comp_exp_ratio, 
             number n, number o, number fade2, number freq_fade2 
                         = ultraschall.GetItemSpectralEdit(integer itemidx, integer spectralidx, string MediaItemStateChunk)
                         

This returns all settings you can set with AddItemSpectralEdit and SetItemSpectralEdit, by giving the item's idx within the project(itemidx) and the SpectralEdit-instance(spectralidx).
And you know the drill: when setting itemidx to -1, you can pass a MediaItemStateChunk to the function.

Some other functions for SpectralEdit-management are:



^ MediaItems: Miscellaneous

Still not enough? Well, I've added numerous other functions, and I want to introduce you to some of the gems included.
Feel free to browse through the Functions-Reference to find more.

What about, previewing MediaItems and files? Use this:

PreviewMediaItem

            boolean retval = ultraschall.PreviewMediaItem(MediaItem MediaItem, integer Previewtype)
            

This previews an existing MediaItem, which means: Reaper will play it, regardless of what you hear in your project currently.
You can also set, where you want to have it previewed, through the MediaExplorer, the MediaItem itself, through the volume-settings of the track, in which it lies and through the track, in which it lies(including FX and such).
You can just play one MediaItem at a time, unless you play one through the MediaExplorer and one through another previewing-type.

If you want to preview a file not in the current project, you can use:

PreviewMediaFile

            boolean retval = ultraschall.PreviewMediaFile(string filename_with_path)
            

which will simply play the file you gave using filename_with_path.

To stop any preview, just use

StopAnyPreview

            ultraschall.StopAnyPreview()
            

which stops any previewing, be it from a MediaItem or an external mediafile.

What about applying Actions to MediaItems? Use this:

ApplyActionToMediaItem

            boolean retval = ultraschall.ApplyActionToMediaItem(MediaItem MediaItem, string actioncommandid, 
                                                                integer repeat_action, boolean midi, optional HWND MIDI_hwnd)
                                                                

which allows applying main and midi-editor-actions to MediaItem. Just pass the command_id/action_command_id to the parameter.
With parameter repeat_action, you can set, how often the action shall be applied to the MediaItem.
To apply MIDI-Editor, actions, set midi=true and pass over a HWND of the used MIDI-Editor, using Reaper's own API function MIDIEditor_GetActive.

To apply action to multiple MediaItems, use:

ApplyActionToMediaItemArray

            boolean retval = ultraschall.ApplyActionToMediaItemArray(MediaItemArray MediaItemArray, string actioncommandid, 
                                                                     integer repeat_action, boolean midi, optional HWND MIDI_hwnd)
            

which works just the same as ApplyActionToMediaItem, but uses a MediaItemArray that includes the MediaItems to be affected.

In addition to actions, you can also apply functions to MediaItems:

ApplyFunctionToMediaItemArray

            table returnvalues 
                    = ultraschall.ApplyFunctionToMediaItemArray(MediaItemArray MediaItemArray, function functionname, 
                                                                        functionparameters1, ..., functionparametersn)

You just pass to it the MediaItemArray, the functionname, the parameters for the function functionname.
Keep in mind: if a parameter of functionname shall hold the MediaItem, you need to set the accompanying parameter to nil, ApplyFunctionToMediaItemArray will automatically insert the appropriate MediaItem at this nil parameter.

What else? What about Normalizing MediaItems? Use this:

NormalizeItems

            integer retval = ultraschall.NormalizeItems(array MediaItemArray)
            

Just pass to it a MediaItemArray, that holds all MediaItems to be normalized.

And last, but not least: What about applying MediaItemStateChunks to MediaItems? Use this:

ApplyStateChunkToItems

            boolean retval, integer skippeditemscount, array skipped_MediaItemStateChunkArray 
                    = ultraschall.ApplyStateChunkToItems(array MediaItemStateChunkArray, boolean undostate)
                    

This applies the MediaItemStateChunks in MediaItemStateChunkArray to the appropriate MediaItems. That means, if a StateChunk is of a certain, existing MediaItem, the function will apply the StateChunk to the MediaItem.
This function is especially helpful, when mass manipulating StateChunks and wanting to mass-apply the changed ones back.
Easy to do now.



^ File Management: Introduction

Even if file-management isn't that hard to program in Lua, it is quite inconvenient. Especially for "normal" use-cases, it is often a drag to always go through the four steps, checking if file exists, open file, read/write, close file.
Wouldn't it be cool, to have functions to do it for you?

Well now, there are, as the Ultraschall-API includes 28 functions to do it for you.
These functions include functions for reading and writing, copying them.
You can also check for valid filetypes, for valid directories, can count files and directories in paths, get paths and files in a path, and get length or number of lines in a file.

With that, you can do a lot of file-operation quite easy, without having to dig through the little details of the Lua-Reference-Manual.

Let's start with reading files.



^ File Management: Read

To read files, I included some nice functions, like: ReadFullFile, ReadBinaryFile, ReadBinaryFileFromPattern, ReadBinaryFileUntilPattern,
ReadFileAsLines_Array, ReadLinerangeFromFile, ReadValueFromFile, ReadBinaryFile_Offset

Let's start with the function probably used the most:

ReadFullFile

            string contents, integer length_of_file, integer number_of_lines = ultraschall.ReadFullFile(string filename_with_path, boolean binary)

This reads a file fully and returns it's contents to the return-variable contents. It will also return the length of the file.
If you set the parameter binary to true, it will read the files as binary files; if set to false or nil, it will read the file either until the end or until a eof-character comes up.
If you're dealing with textfiles, set it to false or nil, otherwise to true.

If you want to return all lines from a textfile, that have a certain character-pattern in them, use this function:

ReadValueFromFile

            string contents, string linenumbers, integer numberoflines, integer number_of_foundlines = 
                                                ultraschall.ReadValueFromFile(string filename_with_path, string value)
            

This reads a file as textfile and returns it. When you give parameter value a string, it will return all lines from the file, that contain this string.
The returned values are contents, the linenumbers returned as a comma-separated-csv, the total number of lines in the file and the number of lines found and returned
This should help you to read only the lines useful for you, however, it is much slower than ReadFullFile() due the massive pattern-matching used in it.
So, even if you can read the full file with that, better use ReadFullFile when you want the full file returned.

If you want to return everything from a pattern to the end of a file, use:

ReadBinaryFileFromPattern

            integer length, string content = ultraschall.ReadBinaryFileFromPattern(string input_filename_with_path, string pattern)

with this function, you can read a file from a pattern onwards. That means, the function searches for the first(!) instance of pattern and returns the file from that pattern until the end of the file.

Similar to this is:

ReadBinaryFileUntilPattern

            integer length, string content = ultraschall.ReadBinaryFileUntilPattern(string input_filename_with_path, string pattern)

which reads a file until(!) a certain pattern is found in it and returns this. That means, it returns the file from it's start until the pattern.

But what if you want to read a file from a start-offset to an endoffset? Use this:

ReadBinaryFile_Offset

            integer length, string content = 
                    ultraschall.ReadBinaryFile_Offset(string input_filename_with_path, integer startoffset, integer numberofbytes)

This function returns the contents of the file from startoffset(in Bytes) until startoffset+numberofbytes.
If you set number of bytes to -1, the function will return the file from startoffset to it's end.
Positive value in startoffset will be related to the beginning of the file. If you want to return the file from a startoffset related to the end of the file,
use negative value for startoffset.
A startoffset of -1 is for the end of the file, -2 for the last byte in the file, -3 for the second to last byte in the file, etc.
Even with negative startoffset, parameter numberofbytes will count from the startoffset to startoffset+numberofbytes.
If the startoffset+numberofbytes reach or cross the end of the file, it will return a shorter string.
To check, whether it returned the requested length, check the returnvalue length.
Returnvalue content holds the requested part of the file

When you have a textfile and you want to work with it on an individual line-basis, use:

ReadFileAsLines_Array

            array contents, boolean correctnumberoflines, integer number_of_lines = 
                    ultraschall.ReadFileAsLines_Array(string filename_with_path, integer firstlinenumber, integer lastlinenumber)

This returns a textfile, split into it's individual lines put into an array. You can also set the linenumbers, that you want to have returned.
If the linenumbers returned are fewer than you requested, correctnumberoflines will be false, otherwise it will be true.

If you want to have the lines returned in one string, use the following instead:

ReadLinerangeFromFile

            string contents, boolean correctnumberoflines, integer number_of_lines = 
                     ultraschall.ReadLinerangeFromFile(string filename_with_path, integer firstlinenumber, integer lastlinenumber)

This works basically the same as ReadFileAsLines_Array, but returns the found-lines as a newline-separated string.

These functions should fulfill most of your daily file-read-usecases.



^ File Management: Write

Reading files is cool, writing files is cool as well, so I added some for exactly that: WriteValueToFile, WriteValueToFile_Insert, WriteValueToFile_Replace, WriteValueToFile_InsertBinary, WriteValueToFile_ReplaceBinary

Let's start with the function, that you'll probably use the most for writing:

WriteValueToFile

            integer retval = ultraschall.WriteValueToFile(string filename_with_path, string value, optional boolean binarymode, optional boolean append)
            

This writes a value to filename_with_path. Optionally, you can control, if the file shall be written as binary-file and if value shall be appended to the current contents of the file.
Default is, file will be stored as binary and value replaces the current contents of the file.

To insert values into a textfile, use:

WriteValueToFile_Insert

            integer retval = ultraschall.WriteValueToFile_Insert(string filename_with_path, integer linenumber, string value)
            

This inserts value after the line, given by parameter linenumber. This works only for textfiles, not for binary-files.

To replace parts of a textfile, use:

WriteValueToFile_Replace

            integer retval = ultraschall.WriteValueToFile_Replace(string filename_with_path, integer startlinenumber, integer endlinenumber, string value)

This replaces the lines between (including)-startnumber and (including)-endlinenumber with the parameter value. This works only for textfiles, not for binary-files.

For binary-files, we have dedicated functions for that as well:

WriteValueToFile_InsertBinary

            integer retval = ultraschall.WriteValueToFile_InsertBinary(string filename_with_path, integer byteposition, string value)
            

This inserts parameter value at the fileoffset given by parameter byteposition. This works for binary-files and for textfiles(though textfiles may cause issues at some points).

To replace contents of a binaryfile, you can use:

WriteValueToFile_ReplaceBinary

            integer retval = ultraschall.WriteValueToFile_ReplaceBinary(string filename_with_path, integer startbyteposition, integer endbyteposition, string value)
            

This inserts parameter value between (including) startbyteposition and (including)endbyteposition. This works for binary-files and for textfiles(though textfiles may cause issues at some points).

This should make saving files easier than having to code it completely yourself by hand.



^ File Management: Analyse

Sometimes, you want to know more about the contents of a file. For that, I included some functions as well: CountLinesInFile, GetLengthOfFile, CheckForValidFileFormats, OnlyFilesOfCertainType

For reading files, you probably want to know the length of the files or number of lines in it. For that you can use:

GetLengthOfFile

            integer lengthoffile = ultraschall.GetLengthOfFile(string filename_with_path)

This returns the length of the file in bytes.

CountLinesInFile

            integer linesinfile = ultraschall.CountLinesInFile(string filename_with_path)

This returns the number of lines in a textfile.

If you want to know, what type a certain file is, you can use:

CheckForValidFileFormats

            string fileformat, boolean supported_by_reaper, string mediatype = ultraschall.CheckForValidFileFormats(string filename_with_path)

This returns the type of a file, which is either

            JPG, PNG, GIF, LCF, ICO, WAV, AIFF, ASF/WMA/WMV, MP3, MP3 -ID3TAG, FLAC, MKV/MKA/MKS/MK3D/WEBM, AVI, RPP_PROJECT, unknown 

and it returns, if the filetype is supported by Reaper and what kind of mediatype the file is, which is either

            Image, Audio, Audio/Video, Video, Reaper
            

But what, if you want to know all files of a certain types in a filelist(like the file in a directory)? Use this:

OnlyFilesOfCertainType

            integer foundfilecount, array foundfilearray = ultraschall.OnlyFilesOfCertainType(array filearray, string filetype)
  

This returns all files from parameter filearray, that are of a certain filetype. Create an array with all filenames you want to check for and use this for parameter filearray. In the parameter filetype, you can use one of:

            JPG, PNG, GIF, LCF, ICO, WAV, AIFF, ASF/WMA/WMV, MP3, MP3 -ID3TAG, FLAC, MKV/MKA/MKS/MK3D/WEBM, AVI, RPP_PROJECT, unknown 

After that, the function returns an array with all files, that are of the filetype you gave to parameter filetype.



^ File Management: Misc

Last, but not least, I added many other useful functions regarding file-management, like:



^ Project Management: Introduction

Beside of many functions to read and set project-states in RPP-files and ProjectStateChunks, I also added some other things to work with projects.



^ Project Management: Check for changed projecttabs

Sometimes, you want to know, if projecttabs have been reordered, closed, opened, created. For this, I created the function

CheckForChangedProjectTabs

            boolean retval, integer countReorderedProj, array reorderedProj, integer countNewProj, 
                                         array newProj, integer countClosedProj, array closedProj 
                                                      = ultraschall.CheckForChangedProjectTabs(boolean update)

This checks, if there are changed projecttabs and the number of these changes.
Let's go into details:
When you run a script, that includes the Ultraschall-API, the API creates a list of the currently existing projecttabs.
When you run this function, it will check this internal list and compare, if projects have been added, reordered or closed since them.

This function returns if there's a change, into the return-value retval. The other return-values return

  • the number of reordered projects(countReorderedProj),
  • an array with all reorderd Projects(reorderedProj),
  • the number of new projects(countNewProj),
  • an array with all newly created/opened projects(newProj),
  • the number of closed projects(countClosedProj) and
  • an array with all closed projects(closedProj)

When you want to update this internal list, you should set parameter update to true, otherwise set it to false.

That way, you can for instance work with newly created projects, to automatically add things to projects, that can't be added using TemplateProjects.



^ Color Management: Introduction

Reaper has a lot of cool themeing abilities, which allows you to customize most of the design of Reaper yourself.
One of these things is: customized colors.

Unfortunately, Reaper has it's own way to deal with color. In fact there are two main ways of dealing with color:

The r,g,b-color is, as you are used to it: you have a red-color-value, a green-color-value and a blue-color-value, each going from 0 to 255.
The higher the value of one of these colors, the brighter that part of the color becomes.

The native-color is a system-dependent color-value used by Reaper. It is more difficult to understand, but basically it is

    red+(green\*green)+(blue\*blue\*blue)|0x1000000

Or it isn't, because on MacOS, you need to reverse red and blue, so it becomes

    blue+(green\*green)+(red\*red\*red)|0x1000000  

Why that is, is a mystery to me, but there's a function by Reaper, that does the correct conversion for you automatically: reaper.ColorToNative(r,g,b)|0x1000000
But you always need to add that |0x1000000 after the function, and I usually forget, how to write that properly.

There's also some other nice stuff in many gfx-applications, that is completely missing from Reaper's own color-functions: adjusting brightness, saturation and contrast of colors, which would be helpful for adjusting track-colors and item-colors and such.

And what about manipulating multiple colors at once?

So I thought, why not adding functions, that make that stuff easier?



^ Color Management: Native Color Conversion

For color-conversion into a system-dependent native-color, there are the functions:

ConvertColor, ConvertColorReverse, ConvertColorToMac, ConvertColorFromMac, ConvertColorToWin, ConvertColorFromWin, ConvertGFXToColor, ConvertColorToGFX

Let's start with standard-color-conversion, from/to system-dependent native-colors:

ConvertColor

                integer colorvalue, boolean retval = ultraschall.ConvertColor(integer r, integer g, integer b)
            

This function converts red, green and blue-values into the native-color of your system. Unlike Reaper's own function, you don't need to add |0x1000000, which makes it easier to use.
If for one reason or another the conversion fails, it will return 0 and false as returnvalues.
If you want to convert it into a Mac-native-color-value while on Windows or a Windows one while on Mac, just swap the r and the b values.

ConvertColorReverse

                integer r, integer g, integer b, boolean retval = ultraschall.ConvertColorReverse(integer colorvalue)

This one converts a native-color-value into it's original red, green and blue-colorvalues. If color-conversion failed, the returnvalue retval is false, else it is true.

But what if you want to convert a color from/to a native-Mac-color, even if you're using Windows or Linux? And what if you're using Mac and want to convert from/to the native-color for Windows/Linux?
For that, I added four functions:

ConvertColorToMac

                integer mac_colorvalue, boolean retval = ultraschall.ConvertColorToMac(integer red, integer green, integer blue)

This converts a red, green, blue-value to a Mac-native-colorvalue. This works on Windows, Mac and Linux the same way.
Will set returnvalue retval to false, if conversion failed; if conversion succeeded, it will be set to true.

ConvertColorFromMac

                integer red, integer green, integer blue, boolean retval = ultraschall.ConvertColorFromMac(integer mac_colorvalue)

This converts a Mac-native colorvalue into it's red, green and blue-values. If retval is true then conversion was successful; if false, conversion failed.

ConvertColorToWin

                integer win_linux_colorvalue, boolean retval = ultraschall.ConvertColorToWin(integer red, integer green, integer blue)

This converts a red, green, blue-value to a Windows/Linus-native-colorvalue. This works on Windows, Mac and Linux the same way.
Will set returnvalue retval to false, if conversion failed; if conversion succeeded, it will be set to true.

ConvertColorFromWin

                integer red, integer green, integer blue, boolean retval = ultraschall.ConvertColorFromWin(integer win_colorvalue)
                

This converts a Windows/Linux-native colorvalue into it's red, green and blue-values. If retval is true then conversion was successful; if false, conversion failed.

If you want to convert the red, green, blue, alpha-colorvalues with a range from 0 to 255, into colorvalues useable by the gfx.functions for gfx.init-windows (range 0 ... 1), you can use the following functions:

ConvertColorToGFX

                number r, number g, number b, number a = ultraschall.ConvertColorToGFX(integer r, integer g, integer b, integer a)

This converts red, green, blue, alpha-values(0-255) into gfx-useable functions(0-1). Simply pass the values between 0 to 255 to the function. These returned values can be used by gfx.set.

ConvertGFXToColor

                integer r, integer g, integer b, integer a = ultraschall.ConvertGFXToColor(number r, number g, number b, number a)

This converts red, green, blue, alpha-values(0-255) into gfx-useable functions(0-1). Simply pass the values between 0 to 255 to the function.

With that, you can convert color-values into all color-values you ever want to have.



^ Color Management: Brightness, Contrast and Colorsaturation

Sometimes, you want to alter brightness, contrast or saturation of a color but have no idea, how to do that. For that, I added three functions that do that for you: ChangeColorBrightness, ChangeColorContrast, ChangeColorSaturation

Let's start with adjusting the brightness:

ChangeColorBrightness

                integer red, integer green, integer blue, boolean retval = 
                        ultraschall.ChangeColorBrightness(integer r, integer g, integer b, 
                                                          integer bright_r, optional integer bright_g, optional integer bright_b)
                                                          

This function alters the brightness of a color. Just pass to it the old r,g,b-values and the by how much the color shall be brightened/darkened.
To do this, set bright_r, bright_g and bright_b to the new deltavalue. If these delta-values are negative, the color will become darker, if positive, it will become brighter.
If you pass only bright_r and omit bright_g and bright_b, the deltavalue set by bright_r will be applied to red, green and blue at the same time.
To prevent that, set bright_g and bright_b to 0.
It returns the changed colorvalues and the returnvalue retval, which will tell you, if changing saturation was successful(true) or not(false).

Sometimes adjusting brightness is not enough, so let's see, how we can alter contrast of a color:

ChangeColorContrast

                integer red, integer green, integer blue, boolean retval = 
                        ultraschall.ChangeColorContrast(integer r, integer g, integer b, 
                                                        integer Minimum_r, optional integer Maximum_r, 
                                                        optional integer Minimum_g, optional integer Maximum_g, 
                                                        optional integer Minimum_b, optional integer Maximum_b)

This function alters the contrast of a color. It does it by assuming, that the color-value you pass to it, will be seen as the center of the brightness-range, while Minimum_color and Maximum_color are the minimum and maximum of the color-range available.
For example: If you pass as parameter r the value 100, the function will assume, that the current minimum is at 0 and the current maximum is at 255. When you pass now Minimum_r as 0 and Maximum_r as 200, it will calculate the red-colorvalue in relation to the new minimum and maximum. That means, it will divide the new range of 200 by 255 and multiply this value by the old red-value.

               new_redcolor = ((Maximum_r-Minimum_r)/255)*r
               

The more apart Minimum and Maximum become, the stronger the contrast, the closer they become to each other, the weaker the contrast.
You can also influence the brightness by making Maximum and Minimum higher(making it brighter) or lower(making it darker).

If you use only the Minimum_r and Maximum_r-parameters, these will be applied to red, green and blue at the same time.
To prevent that, set Minimum_g to 0 and Minimum_b to 0, Maximum_g to 255 and Maximum_b to 255.

To intensify or desaturate color, you can use the following function:

ChangeColorSaturation

                integer red, integer green, integer blue, number median, boolean retval = 
                        ultraschall.ChangeColorSaturation(integer r, integer g, integer b, integer delta)

This saturates/desaturates a color-value.
Using it is easy, just pass red, green and blue to it, as well as a delta-value that affects the saturation.
To desaturate, set the value for delta negative, to saturate, make delta a positive value.
It will return the new color-values(r,g,b), as well as the median. It also returns retval, which will tell you, if changing saturation was successful(true) or not(false).

The function calculates the new saturation-value by first calculating a median-brightness-value from the red, green and blue-value.
For desaturation: after that, it will add delta to values below median and subtract delta from values above delta.
For saturation: after that, it will subtract delta from values below median and add delta to values above delta.

By that, it will desaturate or saturate.



^ Color Management: Working with Colortables

When working with multiple colors at once, you can use a ColorTable, which can hold multiple colors, as 0-255 integer- and 0-1 float-representation, as well as the current native-color. Such colortables can be used to apply colors to track-colors or item-colors. This is still work in progress but will become more elaborated over time.

There are currently multiple color-table-functions available, like: CreateColorTable, CreateSonicRainboomColorTable, IsValidColorTable, ApplyColorTableToTrackColors

Over time, I intend to add functions to these colortables as well, so adding, removing, altering and changing colors in the colortable(like brightness, saturation and such) is possible more easily.



^ Color Management: Creating Colortables

Let's create such a new ColorTable:

CreateColorTable

                array ColorTable = ultraschall.CreateColorTable(integer startr, integer startg, integer startb, 
                                                                integer endr, integer endg, integer endb, integer number_of_steps)

This creates a new ColorTable with colors from a given color-range. You set startr, startg, startb to the first color, endr, endg and endb to the last color and the number_of_steps from the first to the last color.
After that, you'll have a ColorTable with number_of_steps-colors from startcolor to endcolor. So the following code returns a colortable with 50 shades of gray from black to white:

                ColorTable = ultraschall.CreateColorTable(0, 0, 0, 255, 255, 255, 50)

If you've created multiple ColorTables and want to have them combined into one, just combine them using the function ConcatIntegerIndexedTables.

CreateSonicRainboomColorTable

                array ColorTable = ultraschall.CreateSonicRainboomColorTable()
                

This is a simple function, that creates a ColorTable in Ultraschall's "Sonic Rainboom"-style.

If you want to check, whether a certain table is a valid ColorTable, you can easily check this, using the function IsValidColorTable:

IsValidColorTable

                boolean retval = ultraschall.IsValidColorTable(array ColorTable)
                

Simply pass a table to check for and it will return, whether it is a valid ColorTable(true) or nor(false)



^ Color Management: Applying Colortables

Now, that we've created a new ColorTable, we might want to apply it somewhere, like tracks or items. For that, we have: ApplyColorTableToTrackColors and ApplyColorTableToItemColors

Let's bring some colors into our lives:

ApplyColorTableToTrackColors

                boolean retval = ultraschall.ApplyColorTableToTrackColors(array ColorTable, integer Spread, integer StartTrack, integer EndTrack)

This applies a ColorTable to tracks to colorize them. To do that, create a Colortable and pass it to this function to parameter ColorTable.
The parameter Spread decides, whether to apply a ColorTable once(0), or cyclic/repeating(1 or nil) or whether to spread the colors over all tracks equally(2).
The final parameters decide, to which tracks to apply this, starting from track StartTrack to EndTrack. Use nil as StartTrack to automatically use the first, nil as EndTrack to automatically use the last track in the project.

ApplyColorTableToItemColors

                boolean retval = ultraschall.ApplyColorTableToItemColors(array ColorTable, integer Spread, MediaItemArray MediaItemArray)
                

This applies a ColorTable to MediaItems and works basically as the ApplyColorTableToTrackColors-function above, with the exception, that you pass MediaItems as MediaItemArray.
The rest is the same, pass to the function a ColorTable and in the parameter Spread, how to spread/cycle the colors over the MediaItems.



^ Background Scripts: Introduction

Some things in Reaper can't be solved easily without having something monitoring in the background. So to include some new features otherwise impossible, I added some background-scripts, that can be easily run using:

RunBackgroundHelperFeatures

                ultraschall.RunBackgroundHelperFeatures()
                

This starts the background-scripts, that provide additional features.

Without having the background-scripts started, some functions will always produce error-codes.
Features that use these background-scripts are:

GetLastCursorPosition - gets the last editcursor-position before the current one. Helpful for left-click-triggered scripts, who change the editcursorposition due the mouseclick



^ Cough and Mute Buttons/Actions: Introduction

When recording over a long period of time, especially when recording interviews for podcasts, radio and such, you probably run into the issue that someone is coughing or drinking something. Though both of these things are important, a recording of it is usually not wished.
For that, Reaper provides a Mute-Envelope for each track, which allows to mute a track for a period of time and unmute it again. This muting can be recorded, so it will remain even after the recording is finished.
Problem is: You need to "arm" the mute-envelope first, otherwise any mute-information as send by e.g. MIDI-devices will not be added to the mute-envelope and therefor is lost after the recording.
In some cases, this behavior is a good thing and gives you flexibility when to record the mutes and when not, but for casual users, who do not want to have this kind of freedom, this can be really hard to use.

So I added some functions, that add mute-points into a mute-envelope regardless of it's arming-state. This should make using the mute-envelope for your benefits much easier.

Let's go into more detail.



^ Cough and Mute Buttons/Actions: Toggling Mute

The first and foremost thing you probably want is actually muting a mute-envelope of a track, for that there are the two functions: ToggleMute, ToggleMute_TrackObject

ToggleMute

                    integer retval = ultraschall.ToggleMute(integer track, number position, integer state)
                    

This mutes a given track using the mute-envelope of the track(not the mute-buttons in the TCP/MCP).
Just pass over the tracknumber(with 1 for the first track, 2 for the second, etc!), the position, at which to insert the mute-point and the state of muting.
The state of muting can be either muted(0) or unmuted(1)

To mute a certain MediaTrack-object, you can use the function:

ToggleMute_TrackObject

                    integer retval = ultraschall.ToggleMute_TrackObject(MediaTrack trackobject, number position, integer state)
                    

This works the same as ToggleMute, but accepts a MediaTrack instead of a tracknumber. The rest is the same, position for the position of the new mutepoint and state for mute(0) or unmute(1).



^ Cough and Mute Buttons/Actions: Toggling Mute

Toggling is cool, but you probably want to know, if toggling is even necessary of where the next/previous mute-object currently is.
For that, I added the functions: IsMuteAtPosition, IsMuteAtPosition_TrackObject, GetNextMuteState, GetNextMuteState_TrackObject, GetPreviousMuteState, GetPreviousMuteState_TrackObject

If you want to know, how many mute-points exist in a track, you can use:

CountMuteEnvelopePoints

                    integer retval = ultraschall.CountMuteEnvelopePoints(integer track)
                    

This counts the envelope-points of a mute-envelope in a given track.

To check, if there's already a mute-point at a given position in a track, you can use the IsMuteAtPosition-function.

IsMuteAtPosition

                    boolean retval, optional integer envIDX, optional number envVal  = ultraschall.IsMuteAtPosition(integer tracknumber, number position)
                    

This checks, whether there is a mute-point in track tracknumber at position. Parameter tracknumber is 1-based, with 1 for the first track, 2 for the second, etc.
If there is a mutepoint at the position, it will return the index of the mute-point and it's current value.
It will return false in case of an error

With this, you can check, whether adding/toggling at the position is actually necessary.

If you want to know, which is the next or previous mute-point, you can use:

GetNextMuteState

                    integer envIDX, number envVal, number envPosition = ultraschall.GetNextMuteState(integer track, number position)
                    

Returns the attributes of the next mute-point from position. Just give the tracknumber and the position and it returns the corresponding indexnumber, value and position.
It will return -1, if no such mute-point exists

GetPreviousMuteState

                    integer envIDX, number envVal, number envPosition = ultraschall.GetPreviousMuteState(integer track, number position)
                    

Returns the attributes of the previous mute-point from position. Just give the tracknumber and the position and it returns the corresponding indexnumber, value and position.
It will return -1, if no such mute-point exists

If you want to use a trackobject rather than the tracknumber, you can use the functions:

With that, you should get a good overview over the mute-points in your project.



^ Cough and Mute Buttons/Actions: Toggling Mute

Being able to set and toggle and find mute-points is quite good, but sometimes, you want to get rid of them as well.
For that, I added the functions: DeleteMuteState, DeleteMuteState_TrackObject

DeleteMuteState

                    boolean retval = ultraschall.DeleteMuteState(integer tracknumber, number position)
                    

This deletes a mute-point in track with tracknumber at position. It returns false, if there is no mute-point to delete. Parameter tracknumber is 1-based, with 1 for track 1, 2 for track 2, etc.

To delete a mute-point using a MediaTrack-object, you can use instead:

DeleteMuteState_TrackObject

                    boolean retval = ultraschall.DeleteMuteState_TrackObject(MediaTrack MediaTrack, number position)
                    

Works exactly like DeleteMuteState, but expects a MediaTrack-object instead of a tracknumber.



^ Error Messaging System: Introduction

When coding, one of the most boring things, beside documentation, is debugging.
This is especially true, when the development-system used itself creates error- and bugmessages from hell, that just tell you that something went wrong, but not what.

And as I'm known to be lazy, I thought "When creating many API-functions, they should be able to tell me, where I went wrong, what went wrong and when I'm holding it wrong."

Thus, I created an error-messaging-system that tells you exactly that: Which parameter caused which issue and a hint in how to solve this, if needed.
And furthermore, I worked this error-messaging system out in a way, that it does not stop script-execution if you do not want to. In fact, if you want to reuse error-messages created by an Ultraschall-API-function you used, you can do that.
So using error checks from other Ultraschall-API-functions for your benefits is quite easy.

Let's go into more details on that.



^ Error Messaging System: Creating Error Messages

Before you can use error-messages, they need to be created first. For that, you can use the function: AddErrorMessage.

Let's create an error-message.

AddErrorMessage

                    boolean retval, integer errorcount = 
                        ultraschall.AddErrorMessage(string functionname, string parametername, string errormessage, integer errorcode)
                        

This creates a new error-message, that will be fed into the error-messaging-system.
The parameters are the functionname, the name of the parameter(set it to "" if you don't want to set this parameter), and error-message and an error-code.

The functionname is for the programmer who uses your function, so they know, which function caused this error.
The parametername is simply the name of the parameter, so if a validity-check for a parameter didn't go well, set this to the name of the problematic parameter.
The errormessage should tell the programmer, what went wrong. It should be easy to understand. Tell, in a few words, what went wrong and what was expected instead.
So if checking for an integer-parameter went wrong because the user passed a string instead, the errormessage should be "Must be an integer." The errorcode must be unique, means, every error-message in your function must get it's own errorcode, even if two error-messages are quite similar. This is to help the user to determine, which error happened, without having to check the error-message itself.
A unique errorcode has also other benefits for you as function-coder: if you want to change the error-message to be more descriptive, a check for the errorcode would still work. Another thing: a unique errorcode will help to determine possible bugs in your function more easily, as you immediately know, which errormessage caused it.
I usually use negative numbers for Ultraschall-API-errorcodes, but you are free to choose them anyway you like. But once you decided on one, you must keep it forever until it becomes obsolete!

The function returns two parameters, retval and errorcount. Returnvalue retval tells you, if adding the error-message worked well. If not(usually when passing invalid parameters) it returns false, otherwise true.
Returnvalue errorcount tells you, which number the newly created errormessage has withing the error-messaging-system. This can be used to get the errormessage directly(more on that later).

Example: Let's assume, you have a function with one parameter which shall be a string and you want to check for it to be valid:

                    function myfunction(this_is_a_string)
                        if type(this_is_a_string)~="string" then
                            retval, errorcount = ultraschall.AddErrorMessage("myfunction", "this_is_a_string", "must be a string", -1)
                            return false, errorcount
                        end
                    end
                    

This checks, if the parameter thisisa_string is actually a string. If not, it adds an error-message to the error-messaging-system.
After that, the function returns false and the index of the error-message, so the user can use this number to retrieve the error-message directly from the Ultraschall-API.

Now, we've created a new error-message, which is now kept by the Ultraschall-API-instance locally within your script.



^ Error Messaging System: Getting Error Messages

Now that we've created a new error-message, we probably want to retrieve it somehow.
For that, I added the following functions: CountErrorMessages, GetLastErrorMessage, GetLastErrorMessage2, ReadErrorMessage, ShowLastErrorMessage

ShowLastErrorMessage

                    ultraschall.ShowLastErrorMessage()
                    

This is probably the most useful for debugging purposes. It simply shows a message-box with the last added error-message in the error-messaging-system, including the functionname, the parametername, the errormessage and the errorcode. Add this at the end of your script or within a function, after the code where problems arise to see, which function caused the trouble.

GetLastErrorMessage

                    boolean retval, integer errcode, string functionname, string parmname, string errormessage, 
                    string lastreadtime, string err_creation_date, string err_creation_timestamp, integer errorcounter 
                                                                                            = ultraschall.GetLastErrorMessage()

This returns you the last error-message without opening a messagebox. It also returns attributes associated with the errormessage. Returnvalue retval returns, if an errormessage exists(true) or not(false). The other returnvalues are

GetLastErrorMessage2

                    boolean retval, array ErrorMessages = ultraschall.GetLastErrorMessage2(integer count, boolean setread)
                    

Very much like GetLastErrorMessage, but returns the values as an array. You can also get the last x error-messages(as set by parameter count) and if the read-status of an errormessage shall be set to true.
When setread is set to true, ShowLastErrorMessage will not show this errormessage, even if it's the last one.

For more sophisticated use-cases, where you need to retrieve one specific errormessage, you can use the following:

ReadErrorMessage

                    boolean retval, integer errcode, string functionname, string parmname, string errormessage, 
                    string lastreadtime, string err_creation_date, string err_creation_timestamp = 
                                                                        ultraschall.ReadErrorMessage(integer errornumber)
                                                                        

Just like GetLastErrorMessage, but returns a specific errormessage, as specified by the parameter errornumber.

In addition to that, there's also:



^ Error Messaging System: Deleting Error Messages

If you want to get rid of old errormessages, you can delete them using: DeleteLastErrorMessage, DeleteAllErrorMessages, DeleteErrorMessage
These functions work in the same vein, as the read-functions.

DeleteLastErrorMessage

                    boolean retval = ultraschall.DeleteLastErrorMessage()
                    

This simply deletes the last error-message from the error-messaging system. It returns true, if it succeeded.

DeleteAllErrorMessages

                    boolean retval = ultraschall.DeleteAllErrorMessages()
                    

Just like DeleteLastErrorMessage, but deletes all errormessages from the error-messaging system. It also returns true, if it succeeded.

DeleteErrorMessage

                    boolean retval = ultraschall.DeleteErrorMessage(integer errornumber)
                    

This deletes a specific errormessage, as specified by errornumber. Remember, to get the current number of error-messages available, use CountErrorMessages!

And that's all you need to know about deleting errormessages.



^ Error Messaging System: Toggling showing errors in IDE instead

As cool, as the error-messaging-system is for debugging, you may prefer getting your errors like any other Lua/Reaper-API-error at the bottom of the IDE or in Reaper's error-window.
So I included toggling exactly that, using: ToggleIDE_Errormessages

ToggleIDE_Errormessages

                    boolean retval = ultraschall.ToggleIDE_Errormessages(optional boolean togglevalue)
                    

This toggles, whether to display error-messages at the bottom of the ReaScript-IDE-window/Reaper's error-window or if you prefer using Ultraschall-API's own functions for error-handling.
Optionally, you can set it by setting togglevalue to either true(show errormessages at the bottom of the IDE) or false(use the Ultraschall-API error-management-behavior).



^ Trackstate Management: Introduction

Many things in regards of customizations have also to do with influencing states of MediaTracks. So I added tons of stuff for that too.
I also thought, when I'm at it, I change some of the confusing behavior of Reaper's own API to something more viable.
For instance, when passing a tracknumber to a function, the first track will always be 1, the second always be 2, etc. The master-track, if applicable, will always be 0. That way, you can always be sure, which track in a function is which track.

Another thing is working with trackstrings, which allow you to easily pass over a number of tracks at once.
They are basically a string with the tracknumbers wished, separated by commas. Example:

                    trackstring_of_track1_to_3 = "1,2,3"
                    

Many Ultraschall-API-functions support trackstrings and save you from the pain of having to loop through all tracks you want to pass and influence.
There are also many functions, who create you trackstrings, like for locked tracks, selected tracks, all tracks, etc.

I talked about Trackstates earlier. For that I added full access to all(!) TrackStates available, even those, who are only available through TrackStateChunks. Access means, getting and setting them. Read more on this subject in the chapter Get/Set States for Project, Tracks and Items.

Some other things you can do now easily are:

and many more related to working with TrackStateChunks.



^ Routing: Introduction

In the past, I had numerous situations, where I wanted to influence my routing-settings programmatically. But every time I started, I stumbled and shuddered. Not because it is impossible with Reaper's own API, but because it's so complicated, irritating, confusing and so far away from the way the routing is managed with the user-interface of Reaper, where you can see easily all individual sends/receives.
This problem is applies to the whole management of HardwareOuts as well, which is also painful to program.
So I desired some functions that keep programming the routing simple and down to it's basic components while retaining the flexibility of the full routing.

All these functions, HWOut as well as Routing(Send/Receive) are based on a simple concept, as seen in the user-interface of routing-settings:

Let's start with the routing.



^ Routing: Send and Receives

Before we start, some basics, if you're not familiar with routing.

Routing is a way to direct the output of one channel/track/fx to another channel/track/fx.
In our case of track-routing, that means, one track sends a signal and another track receives it. If you look into the routing-settings or routing-matrix of Reaper, you can exactly see that: one track sends a signal which can be received by one or many tracks.
The most common of such routings is: Track on sends a signal, and the master-channel receives it to output it.
This allows you to build complex audio-settings and configurations, where one output-signal can be send to a reverb-effect, while at the same time being send to a flanger; in parallel, so the reverbed signal isn't flanged, vice versa.

One more thing: To make send/receive work, you need to understand, that you have to set it in the track, that receives the signal(means, to the track who listens to the sending track)! That means, you apply the following functions to the MediaTrack that receives the send-signal.

To do the Send/Receive-stuff, I added the following functions: AddTrackAUXSendReceives, GetTrackAUXSendReceives, SetTrackAUXSendReceives, DeleteTrackAUXSendReceives, CountTrackAUXSendReceives
Let's create a new send/receive-setting.

AddTrackAUXSendReceives

                   boolean retval = ultraschall.AddTrackAUXSendReceives(integer tracknumber, integer recv_tracknumber, integer post_pre_fader, 
                                                                        number volume, number pan, integer mute, integer mono_stereo, integer phase, 
                                                                        integer chan_src, integer snd_chan, number unknown, integer midichanflag, 
                                                                        integer automation, boolean undo)

This adds a receive to a specific track. To tell the function, from which track the received signal comes from, you need to set the parameter recv_tracknumber to the tracknumber of the sending track.
The other parameters are simply all other settings for this send/receive, like

Setting a receive-setting works basically the same:

SetTrackAUXSendReceives

                    boolean retval = ultraschall.SetTrackAUXSendReceives(integer tracknumber, integer idx, integer recv_tracknumber, 
                                                                         integer post_pre_fader, number volume, number pan, integer mute, 
                                                                         integer mono_stereo, integer phase, integer chan_src, integer snd_chan, 
                                                                         number unknown, integer midichanflag, integer automation, boolean undo)

This works the same as AddTrackAUXSendReceives, but changes an already existing setting.

Setting is one thing, but what if you want to know the settings of a receive?

GetTrackAUXSendReceives

                    integer recv_tracknumber, integer post_pre_fader, number volume, 
                    number pan, integer mute, integer mono_stereo, integer phase, integer chan_src, 
                    integer snd_chan, number unknown, integer midichanflag, integer automation 
                                                        = ultraschall.GetTrackAUXSendReceives(integer tracknumber, integer idx)

This receives all the settings of a receive of a track. The same rules for the former functions (AddTrackAUXSendReceives and SetTrackAUXSendReceives) apply also here.

And when you want to delete a receive, you can use the following function:

DeleteTrackAUXSendReceives

                    boolean retval = ultraschall.DeleteTrackAUXSendReceives(integer tracknumber, integer idx, boolean undo)
                    

This deletes a receive-setting from a track. Just pass to it the tracknumber, which receives and the idx of the receive-setting. To know, which setting to delete, you can use GetTrackAUXSendReceives.

If you want to know, how many receives a track has, you can use:

CountTrackAUXSendReceives

                    integer count_SendReceives = ultraschall.CountTrackAUXSendReceives(integer tracknumber)
                    

This returns the number of received tracks a the track tracknumber has.

Another thing: Routing-settings of the master-track aren't possible that way. You need to use GetTrackMainSendState and SetTrackMainSendState to route the signal of a track to the master-channel. Unlike the former functions, MainSend must be set in the sending track!



^ Routing: Hardware Outs

Send and Receive is one thing, but if you want to hear anything, you need to send the audiosignal to actual audiohardware. To do that, you can set HardwareOuts(HWOuts) to tracks, as well as the MasterTrack.
For that, I included the functions: AddTrackHWOut, SetTrackHWOut, GetTrackHWOut, DeleteTrackHWOut, CountTrackHWOuts

AddTrackHWOut

                    boolean retval = ultraschall.AddTrackHWOut(integer tracknumber, integer outputchannel, integer post_pre_fader, 
                                                               number volume, number pan, integer mute, integer phase, integer source, 
                                                               number unknown, integer automationmode, boolean undo)
                                                               

This adds a new HWOut to a track. The tracknumber is either 0 for the master track or 1 and higher for track 1 or higher.
The other options are to some extend familiar to the AUXSendReceive-functions:

Setting an already existing HWOut can be done using the function:

SetTrackHWOut

                    boolean retval = ultraschall.SetTrackHWOut(integer tracknumber, integer idx, integer outputchannel, 
                                                               integer post_pre_fader, number volume, number pan, integer mute, 
                                                               integer phase, integer source, number unknown, integer automationmode, 
                                                               boolean undo)

This sets the settings of an already existing HWOut. The rest is the same, as the parameters for AddTrackHWOut.

To set HWOuts, you probably want to know, what the old values are of a HWOut. For that, use the function:

GetTrackHWOut

                    integer outputchannel, integer post_pre_fader, number volume, number pan, 
                    integer mute, integer phase, integer source, number unknown, integer automationmode 
                                                                = ultraschall.GetTrackHWOut(integer tracknumber, integer idx)
                                                                

This returns the settings of a HWOut. The tracknumbers are the same, with 0 for the master-track; 1 and higher for track 1 and higher. The idx is the index of the HWOut-setting of the track.

To delete it, use:

DeleteTrackHWOut

                    boolean retval = ultraschall.DeleteTrackHWOut(integer tracknumber, integer idx, boolean undo)

This deletes a HWOut-setting from a track.

To get the number of HWOuts of a certain track, use:

CountTrackHWOuts

                    integer count_HWOuts = ultraschall.CountTrackHWOuts(integer tracknumber)


^ ExtState Management: Introduction

One of the concepts of Reaper to exchange data between scripts, functions, instances are so called extstates.
Such extstates are usually key-value-stores, in which you can store strings. They can be stored either as ini-files or in Reaper's memory itself.

In the Ultraschall-API, I added numerous functions to get/set/enumerate values in ini-files as well as analyzing them for certain patterns and information.
You can find all functions related to storing/analyzing ini-files in the index of the functions-reference Configuration-Files Management -> Ini-Files.

Another thing I added is extstate-management for tracks and items. That means, you can easily store additional information about MediaItems or MediaTracks in your project, just like ItemNotes or ProjectNotes, but more flexible.
That way, you can do things like storing additional metadata for items, without having to store them into the item-notes field.
You can find all these functions for item/track-extstates in the functions-reference-index under Metadata Management -> Extension States(Guid) and Extension States

Let's go into more detail on these concepts.



^ ExtState Management: Ini-Files

Ini-files are an easy way to store information. Many of Reaper's own configuration-files are ini-files.
They all work under the same structural concept:

    [section]
    key=value
    anotherkey=anothervalue
    
    [anothersection]
    morekeyadditions=with a value
    thelaskey=and the last value
    

As you can see, you have one or more sections in the file, usually written [sectionname], with one or more key=values associated with them.

Sections are used to create a basic semantical structure in the ini-file.
So if you want to create an ini-file for yourself, a sectionname should reflect a little, what kind of nature the key-values are, that you store in such a section.
The same goes for the keys, but their names should be orientated on the nature of the value stored with this specific key.

Names of sections should be unique within a file. Names of keys should be unique within a section(!), values can be anything.
You should avoid an = in a section name and brackets [ ] in a keyname, as this could confuse reading of an ini-file.

You can create such ini-files by hand and edit them by hand.

These key-value-stores can be read and set using Ultraschall-API-functions.

Note: Reaper's own configuration-files can be changed only, when Reaper isn't running. To be more precise: you can change them, but Reaper will ignore these changes until restart, sometimes even overwrite them.
Changing Reaper's config-files needs a restart of Reaper for the changes to take effect.
Some of the configuration-settings can be set also at runtime, but need another approach, which you can find on the Reaper-Internals-page for Configuration Variables



^ ExtState Management: Inifile-Functions

Let's start with setting and getting key-values in ini-files.

GetIniFileExternalState

                   string value = ultraschall.GetIniFileExternalState(string section, string key, string ini_filename_with_path)
                   

This gets a value stored in a key within a specific section. Just pass to the function the name of the section, the name of the key and the ini-file, from where to read the value.

Setting a key-value is also quite simple. Just use:

SetIniFileExternalState

                   boolean retval = ultraschall.SetIniFileExternalState(string section, string key, string value, string ini_filename_with_path)
                   

This sets a value into a specific key within a specific section. Just pass over sectionname, keyname, the value to be stored(as string!) and the filename with path of the ini-file.

This is easy, but sometimes you have to deal with unknown ini-files or want to search through it for sections, keys and values stored inside of ini-files, without haveing to guess.
For that, I added numerous functions for:

Count Ini-File-ExtStates

Let's count extstates first. There are two kinds of counting-functions, two for counting sections in the ini-file and two for counting the keys in a specific section.
They also split into two functions, with one for counting all of them, with the other only counting the ones, that contain a specific string in them, so e.g. you can count the sections with the string ultra in them.
Let's have a look at some of them:

CountIniFileExternalState_sec

                   integer sectioncount = ultraschall.CountIniFileExternalState_sec(string ini_filename_with_path)
                   

This counts all sections contained within the ini-file.

CountSectionsByPattern

                   integer number_of_sections, string sectionnames 
                                       = ultraschall.CountSectionsByPattern(string pattern, string ini_filename_with_path)
                                       

This counts all sections in the ini-file, that have a specific string in them, specified by the parameter pattern.
It also returns a list of all sections with the pattern in them found.

In addition to that, I also added a function, that allows you to count the values within an ini-file including a certain pattern

CountValuesByPattern

                   integer number_of_keys, string sections_and_keys 
                                           = ultraschall.CountValuesByPattern(string pattern, string ini_filename_with_path)
                                           

Pass to it the pattern to look for during counting and the ini-filename with path.
It also returns a string with the sections, keys and values found, that fit the pattern.
It is a comma-separated string of the format: [section1],key1=,value,key4=,value,[section4],key2=,value

The other functions work quite the same, but focused on a key within a specific section.

Enumerate Ini-Files by number

These functions are meant to enumerate within sections and keys.

EnumerateIniFileExternalState_sec

                   string sectionname = ultraschall.EnumerateIniFileExternalState_sec(integer number_of_section, string ini_filename_with_path)

This enumerates the sections within an ini-file. You just pass the index-number to the function into parameter number_of_section and the ini-filename with path and it returns the appropriate sectionname.

EnumerateIniFileExternalState_key

                   string keyname = ultraschall.EnumerateIniFileExternalState_key(string section, integer number, string ini_filename_with_path)
                   

This enumerates the keys within a given section. Just pass over to it the sectionname, the index-number of the key and the ini-filename and it returns the correct name.

Enumerate Ini-Files by pattern

These functions are meant to to enumerate sections, keys and values in a file, that follow a certain name-pattern.

EnumerateSectionsByPattern

                   string sectionname = ultraschall.EnumerateSectionsByPattern(string pattern, integer id, string ini_filename_with_path)
                   

This enumerates the name of a section, that follows a certain pattern. Just pass over to it the string-pattern the name shall contain, the index of the section you want to have returned and the ini-filename. Example:

                   string sectionname = ultraschall.EnumerateSectionsByPattern("Hawaii", 3, "c:\\test.ini")
                   

This returns from the file c:\test.ini the third section, that contains the string Hawaii in it.

EnumerateKeysByPattern

                   string keyname = ultraschall.EnumerateKeysByPattern(string pattern, string section, integer id, string ini_filename_with_path)
                   

This enumerates the name of a key within a certain section, that follows a certain pattern. Pass to it the string-pattern, the sectionname, the index of the key you want to have returned and the ini-filename. Like with EnumerateSectionsByPattern, you can use the parameter id to determine, that only the id'th key shall be returned, that follows the string-pattern.

EnumerateValuesByPattern

                   string value, string keyname = ultraschall.EnumerateValuesByPattern(string pattern, string section, string id, string ini_filename_with_path)

Just like EnumerateKeysByPattern, but this enumerates through the values within a given section. It will return the value and it's corresponding keyname found.



^ ExtState Management: Ultraschall.ini

The Ultraschall-Podcasting-Extension itself makes use of it's own configuration-file, called ultraschall.ini, which is located in the resourcesfolder of Reaper.
This file can be used by you as well, even if it's mainly intended for an installed Ultraschall-installation. The Ultraschall-API has some functions to deal with the ultraschall.ini.

You need to know, however: sectionnames must contain a leading signature, that is used purely by you, like MaxMiller_sectionname.
All signature variants of Ultraschall_ or US_ or ULT_ (no matter if upper/lower/camelcase) as well as sections with no leading signature are reserved for us.
That way, you don't risk naming conflicts with Ultraschall's own config-settings.
This is only for the ultraschall.ini-file, all other ini-files can be used by you the way you want.

The functions themselves basically work, as the functions described in the chapter ExtState Management: Inifile-Functions.



^ ExtState Management: Track and Item-Extstates

Another concept, I introduce with the Ultraschall-API are Track and Item-Extstates.

To store them, I make use of the fact, that tracks and items have their own guids. I use these guids as basis for the section-name, while the key and value can be freely chosen by you.
A valid guid is a string that follows the following pattern {........-....-....-....-............} where . is a hexadecimal value(0-F) These extstates are stored as ProjectExtStates, that means: when you save the project, these extstates will be saved with it.

To make this possible in general, I included two functions: GetGuidExtState, SetGuidExtState
Let's go into more detail:

SetGuidExtState

                  integer retval = ultraschall.SetGuidExtState(string guid, string key, string value, integer savelocation, boolean overwrite, boolean persists)

This sets an extstate using a guid. So if you have an object, that provides a guid, use that one. You can add additional characters before or after the guid, but it must contain a valid guid.
After that, set the key and the value.
With this function, you can also choose, whether to store the extstate as a global extstate that can be used from everywhere in Reaper or only within the current project.
You can choose, whether to overwrite it, if it already exists and if the state shall persist after existing Reaper(only when storing it as a global extstate).

To get such a stored state, just use:

GetGuidExtState

                  integer retval, string value = ultraschall.GetGuidExtState(string guid, string key, integer savelocation)
                  

With this one, you can simply get this extstate again. Just pass to it the guid, the key and the savelocation(either global or in the project) and it returns a success-indicator-return value as well as the stored value itself.

With these two functions as a base, I made variants for tracks and items.

One important thing to mention: these extstates are bound to the project(if they aren't stored globally via parameter savelocation), so when copying an item with extstates into another project, the extstates will not be part of the new project.
I will look into that problem at some point.



^ ExtState Management: Track Extstates

Let's store some extstates for tracks. For that, I added the functions: GetTrackExtState, SetTrackExtState
TrackExtStates are stored as ProjectExtState within the projectfile.

SetTrackExtState

                  boolean retval = ultraschall.SetTrackExtState(MediaTrack track, string key, string value, boolean overwrite)
                  

This sets an extstate to a certain MediaTrack. Just pass to it a MediaTrack-object, a key and the value to store. You can also decide, whether you want to overwrite an already existing trackextstate.

GetTrackExtState

                  boolean retval, string value = ultraschall.GetTrackExtState(MediaTrack track, string key)

This returns a stored trackextstate. Just pass to it a MediaTrack-object and the key, whose value you want. It will return if getting the value was successful or not with return-value retval, as well as the value with value.



^ ExtState Management: Item Extstates

Let's store some extstates for items. For that, I added the functions: GetItemExtState, SetItemExtState
ItemExtStates are stored as ProjectExtState within the projectfile. These functions work basically like the ones for TrackExtStates

SetItemExtState

                  boolean retval = ultraschall.SetItemExtState(MediaItem item, string key, string value, boolean overwrite)
                  

This sets an extstate to a certain MediaItem. Just pass to it a MediaItem-object, a key and the value to store. You can also decide, whether you want to overwrite an already existing itemextstate.

GetItemExtState

                  boolean retval, string value = ultraschall.GetItemExtState(MediaItem item, string key)

This returns a stored itemextstate. Just pass to it a MediaItem-object and the key, whose value you want. It will return if getting the value was successful or not with return-value retval, as well as the value with value.



^ Markers and Regions: Introduction

In the Ultraschall-extension, we make heavy use of markers and regions for all kinds of things. So, to simplify programming for markers, I added a number of functions to deal with them.

The markers used by Ultraschall and represented in the Ultraschall-API, are: normal markers, chapter markers, planned chapter markers, edit-markers, edit-regions, time-markers and PodRangeRegions.

These kind of markers get the most attention, though regular marker/region/tempo-time signature marker-management is part of this API as well.



^ Markers and Regions: General How To

All functions for all kinds of markers settable in the Ultraschall-Api share the same basic subset of workflows.
All markers can be added, set, deleted, enumerated, counted, imported and exported from/to a file, checked for being a certain markertype and converted into a certain markertype(if possible).
Let's take edit-markers as an example, who have the functions:

AddEditMarker, SetEditMarker, DeleteEditMarker, EnumerateEditMarkers, CountEditMarkers, ImportEditFromFile, ExportEditMarkersToFile, IsMarkerEdit, MarkerToEditMarker, EditToMarker

AddEditMarker

                  integer marker_number = ultraschall.AddEditMarker(number position, integer shown_number, string edittitle)
                  

This adds a new edit-marker. Give it a position, the shown number and an edittitle, which should hint at the reason of having to edit later.
The title will be shown in the marker as "edit: Title". You can change it manually in Reaper, but should retain "edit: ", as this signals the
Ultraschall-API, if this is an edit-marker. The editmarker also is red (255 0 0).
It will return the marker-number, which is the index of all markers in the project, means: markers and regions.

SetEditMarker

                  boolean retval = ultraschall.SetEditMarker(integer edit_index, number position, integer shown_number, string edittitle)
                  

This sets an already existing edit-marker. Pass to it the index-number of the edit-marker, which is the index for edit-markers only(!).
The rest is like AddEditMarker.
It will return the true, if setting it was successful.

DeleteEditMarker

                  boolean retval = ultraschall.boolean retval = ultraschall.DeleteEditMarker(integer edit_index)
                  

This deletes an edit-marker. Pass to it the index-number of the edit-marker, which is the index for edit-markers only(!). That means, if you have
five markers with the last one being an edit-marker and you want to delete that, the number is 1, not 5!
It will return the true, if deleting it was successful.

EnumerateEditMarkers

                  integer retnumber, integer shown_number, number position, string edittitle = ultraschall.EnumerateEditMarkers(integer edit_index)
                  

This enumerates an edit-marker and all of it's attributes. Just pass over to it the edit-index-number and it will return the overall marker-index-number(markers and regions)
the shown-number as well as it's position and the title of the edit(without the _edit: ).

CountEditMarkers

                  integer number_of_edit_markers = ultraschall.CountEditMarkers()
                  

This returns the number of edit-markers in your project. With that information, you know, how many of these editmarkers can be enumerated.

ExportEditMarkersToFile

                  integer retval = ultraschall.ExportEditMarkersToFile(string filename_with_path, number PodRangeStart, number PodRangeEnd)
                  

This exports the edit-markers into an exportfile. You can also pass to it from which startposition to which endposition the markers shall be exported.
The file created contains the attributes for one edit-marker each line. The format for each line in the file is: hh:mm:ss.mss Title
This file is generic, so it can be reimported as other types or markers, like normal markers, as well.
Speaking of importing:

ImportEditFromFile

                  array editmarkers = ultraschall.ImportEditFromFile(string filename_with_path, PodRangestart)
                  

This imports a markerexportfile, as created by functions like ExportEditMarkersToFile into the project. Podrangestart is for adding an offset to the edit-marker-positions.

IsMarkerEdit

                  boolean retval = ultraschall.IsMarkerEdit(integer markerid)
                  

Checks, if a certain marker is an edit-marker or not. The markerid is the index for all markers and regions, not only edit-markers.
Returns true, if it's an edit-marker, false if not.

MarkerToEditMarker

                  integer idx, integer shown_number, number position, string markertitle = ultraschall.MarkerToEditMarker(integer markerindex)
                  

This converts a marker into an edit-marker, which usually means, it colors it red (255, 0, 0) and adds "_edit: " at the beginning of the title.
The markerindex is the one for all markers. It returns the overall-marker-indexnumber, the shown number as well as it's position and the new markertitle.

EditToMarker

                  integer idx, integer shown_number, number position, string markertitle = ultraschall.EditToMarker(integer edit_index)
                  

This also converts, but this time the other way around: From edit-marker to a normal marker, which means: coloring it with the standard-color and removing the _edit: from the beginning of the title.

The other marker-types follow the same lead, so if you understood it for this marker, you understood it for all others as well.
One limitation though, you can't (yet) export/import regions into/from an exportfile.



^ Markers and Regions: Helpers and Manipulation

Adding and getting markers is quite good, but sometimes, you want to do more with them. For that, I added numerous functions as well, like:

So, with this, most of your wishes in regards of marker-management should be fulfilled.



^ Helper_Functions: Introduction

Beside all the concepts you just read about, I also added tons of functions and things to the API, that are helpful in one way or another in your everyday coding.
Manipulation of Lua's datastructures, clipboard-functions, stuff for undo-management, checking for the current system being either Mac, Windows or Other, Reaper's Configuration-Settings, User Interface-stuff, applying actions to MediaTracks and MediaItems and many other things.

In this chapter, I want you to introduce you to some of these things.



^ Helper_Functions: Clipboard Management

Getting and setting things to the clipboard is really helpful in many ways. With SWS 2.9.7 we got clipboard-functions, Reaper itself has clipboard-functions for items and such.
But: it's in parts not really convenient to use.
reaper.CF_SetClipboard() needs an obscure datastructure called Faststrings to circumvent a Lua-restriction of strings with a length of only 1023 bytes. Inconvenient to use, as you always need to create and destroy these FastStrings or they'll eat up memory for no good.
Putting MediaItems into the clipboard or finding out, which MediaItems are located in the clipboard is also possible but really inconvenient.
So I added functions to deal with that.

GetStringFromClipboard_SWS

                    string clipboard_string = ultraschall.GetStringFromClipboard_SWS()
                    

This is like reaper.CF_SetClipboard(), but is doing all the FastString-management for you in the background. In other words: it simply returns the string-contents from the clipboard.

When dealing with MediaItems, I also created functions for that. Let's put something into the clipboard.

PutMediaItemsToClipboard_MediaItemArray

                    boolean retval = ultraschall.PutMediaItemsToClipboard_MediaItemArray(MediaItemArray MediaItemArray)
                    

This function adds all MediaItems in MediaItem into the clipboard. It returns true, in case of success.

Putting MediaItems into the clipboard is cool, but sometimes you simply want to know, what already is in the clipboard.

GetMediaItemsFromClipboard

                    integer count, array MediaItemStateChunkArray = ultraschall.GetMediaItemsFromClipboard()
                    

This function returns the number of MediaItems in the clipboard as well as an array with all StateChunks of the MediaItems in the clipboard.
Why not the MediaItem-objects? Because, they only work within the current project, but MediaItems in the clipboard can also be from another project, not having any references to the current project. Working with StateChunks instead makes them independent from a certain project.
But what if you want to insert them into a project? Use InsertMediaItem_MediaItemStateChunk to insert a MediaItem into a project using it's StateChunk.
How does GetMediaItemsFromClipboard work? It inserts the Items from the clipboard at the end of the project, gets them, using GetAllMediaItemsBetween and deletes them again.



^ Helper_Functions: Data Manipulation

One of the bread and butter things you have to do in your daily programming is to deal with data-structures of many kinds, be it tables, strings, integers, bits, etc.
Dealing with them can sometimes be a pain in the butt, when using pure Lua. And as I'm lazy(did I already mention that?), I wrote me some functions to make things easier to deal with, especially with datastructures existing and fundamental in Reaper.

Dealing with Numbers and Bits

Dealing with Strings

Dealing with Tables



^ Helper_Functions: Undo Management

Undo-Management can be difficult to program in Reaper at times, so I added some things to deal with that to some extend.

PreventCreatingUndoPoint

                    ultraschall.PreventCreatingUndoPoint()
                    

This function prevents creating an undo-point in non-defer-scripts, which is sometimes desireable.

MakeFunctionUndoable

                    boolean retval, string current_UndoMessage, retvals_1, ..., retvals_2 
                                            = ultraschall.MakeFunctionUndoable(function Func, string UndoMessage, integer Flag, 
                                                                                            Func_parameters_1, ... Func_parameters_n)
                                                                                            

With this function, you can create an undopoint for a specific function.
You need to pass the function itself, an undo-message that is shown in the undo-history and an undo-flag.
The other parameters are all the parameters the function Func needs to run. Just write them in the order you would do, if you would run the function Func directly.
It will return a boolean(true for success), the current undo-message and alle the return-values returned by function Func.



^ Helper_Functions: Miscellaneous

In this final chapter, I show you around some nice things that didn't fit anywhere else in the docs. They may or may not be of use for you.

Checking for the current operating system

Reaper-related stuff

Ultraschall-Api-Variables

Configuration Settings

User Interface stuff

Apply actions to MediaTracks and MediaItems

miscellaneous

and more and more and more....



^ Final words

I can't believe it, but after over one and a half years, I can finally write these last words for this mammoth-project.
But what can I say, what I haven't said in this docs?

Feel free to use it. Feel free to code with it. Feel free to learn from the functions I wrote. Dig into the functions-reference, which holds much much more.

If you like it, you can donate to our project at: ultraschall.fm/danke.

Let me take some more time to thank to the many Reaper-Forum-users, who provided me with ideas, hints, bits and pieces and tons of information, which itself informed this API.
I don't want to pick up anyone specific, so if you think, I want to thank you for your ideas and contributions, help or whatever, I do exactly that:
Thank you :)

Let me also thank the Reaper-guys Justin and Schwa, who created this piece of great software. Even though I constantly throw more and more Feature-requests into the forum, I'm deeply impressed with this piece of software and the more I dive into it, the more I can find.
Reaper is like a fractalised fractal: the closer you get, the more details are revealed.

More thanks also to the whole podcast community in Germany, be it at PodStock, ChaosCommunicationCongress, Sendegate, which gave me more than just countless hours of good entertainment and information. You probably saved my life countless times and I don't know how many podcasts I listened through in the endless hours of coding this monster.
Without you, I wouldn't have survived coding this.

Also thanks to the wonderful Ultraschall-team, whose focus on improving Reaper for podcasters shows, what is possible in Reaper and how powerful this software is. And who answered ma tons of questions.
And who survived my constantly being annyoing with this API ;)
I want to thank in particular Ralf Stockmann, whose rant in the Sendegate brought me into the project and back into programming.
Though, after not having seen the lights of the days for months now, I'm not sure, whether this was a good thing ;)

Thanks also to all the other people who supported me over the years. I dedicate this thing to you. And for someone special even a whole function, yay :D

So, that's all for now. Feel free to work with it and have fun.

I go to sleep now. I think, I deserved it :)

Meo Mespotine, Leipzig(Germany) 30th of November 2018




Automatically generated by Ultraschall-API 4.00 Beta 2.7 - 79 chapters available