From cfb96e9ef563e4cf39350b5b8721f4b5b70585d2 Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Sat, 16 Dec 2017 20:36:41 +0300 Subject: Added experimental VLC plugin Plugin is made for VLC 2.2.2 Weatherwax (pre-installed in Linux Mint 18.3) Existing issues: - No ability to change codec properties yet, this thing is WIP - By unknown reason, resulted sound has different (RANDOM) sample rate which is different from 44100 Hz --- utils/vlc_codec/libadlmidi.c | 363 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 utils/vlc_codec/libadlmidi.c (limited to 'utils/vlc_codec/libadlmidi.c') diff --git a/utils/vlc_codec/libadlmidi.c b/utils/vlc_codec/libadlmidi.c new file mode 100644 index 0000000..4a86b64 --- /dev/null +++ b/utils/vlc_codec/libadlmidi.c @@ -0,0 +1,363 @@ +/***************************************************************************** + * libadlmidi.c: Software MIDI synthesizer using OPL3 Synth emulation + ***************************************************************************** + * Copyright © 2017 Vitaly Novichkov + * $Id$ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *****************************************************************************/ + +//#ifdef HAVE_CONFIG_H +//# include "config.h" +//#endif + +#include +#include +#include +#include + +#include + +#ifndef N_ +#define N_(x) (x) +#endif + +#ifndef _ +#define _(x) (x) +#endif + +#include + +#define SOUNDFONT_TEXT N_("Custom bank file") +#define SOUNDFONT_LONGTEXT N_( \ + "Custom bank file to use for software synthesis." ) + +//#define CHORUS_TEXT N_("Chorus") + +//#define GAIN_TEXT N_("Synthesis gain") +//#define GAIN_LONGTEXT N_("This gain is applied to synthesis output. " \ +// "High values may cause saturation when many notes are played at a time." ) + +//#define POLYPHONY_TEXT N_("Polyphony") +//#define POLYPHONY_LONGTEXT N_( \ +// "The polyphony defines how many voices can be played at a time. " \ +// "Larger values require more processing power.") + +//#define REVERB_TEXT N_("Reverb") + +#define SAMPLE_RATE_TEXT N_("Sample rate") + +static int Open (vlc_object_t *); +static void Close (vlc_object_t *); + +vlc_module_begin () + set_description (N_("ADLMIDI OPL3 Synth MIDI synthesizer")) + set_capability ("decoder", 150) + set_shortname (N_("ADLMIDI")) + set_category (CAT_INPUT) + set_subcategory (SUBCAT_INPUT_ACODEC) + set_callbacks (Open, Close) + add_loadfile ("custombank", "", + SOUNDFONT_TEXT, SOUNDFONT_LONGTEXT, false) + //add_bool ("synth-chorus", true, CHORUS_TEXT, CHORUS_TEXT, false) + //add_float ("synth-gain", .5, GAIN_TEXT, GAIN_LONGTEXT, false) + // change_float_range (0., 10.) + //add_integer ("synth-polyphony", 256, POLYPHONY_TEXT, POLYPHONY_LONGTEXT, false) + // change_integer_range (1, 65535) + //add_bool ("synth-reverb", true, REVERB_TEXT, REVERB_TEXT, true) + add_integer ("synth-sample-rate", 44100, SAMPLE_RATE_TEXT, SAMPLE_RATE_TEXT, true) + change_integer_range (22050, 96000) +vlc_module_end () + + +struct decoder_sys_t +{ + struct ADL_MIDIPlayer *synth; + int sample_rate; + int soundfont; + date_t end_date; +}; + + +//static int DecodeBlock (decoder_t *p_dec, block_t *p_block); //For different version +static block_t *DecodeBlock (decoder_t *p_dec, block_t **pp_block); +static void Flush (decoder_t *); + +static int Open (vlc_object_t *p_this) +{ + decoder_t *p_dec = (decoder_t *)p_this; + + if (p_dec->fmt_in.i_codec != VLC_CODEC_MIDI) + return VLC_EGENERIC; + + decoder_sys_t *p_sys = malloc (sizeof (*p_sys)); + if (unlikely(p_sys == NULL)) + return VLC_ENOMEM; + + p_sys->sample_rate = 44100;//var_InheritInteger (p_this, "synth-sample-rate"); + p_sys->synth = adl_init( p_sys->sample_rate ); + + char *font_path = var_InheritString (p_this, "custombank"); + if (font_path != NULL) + { + msg_Dbg (p_this, "loading custom bank file %s", font_path); + if (adl_openBankFile(p_sys->synth, font_path)) + msg_Err (p_this, "cannot load custom bank file %s: %s", font_path, adl_errorInfo(p_sys->synth)); + free (font_path); + } + else + { + adl_setBank(p_sys->synth, 58); + } + + p_dec->fmt_out.i_cat = AUDIO_ES; + + p_dec->fmt_out.audio.i_rate = p_sys->sample_rate; + p_dec->fmt_out.audio.i_channels = 2; + p_dec->fmt_out.audio.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; + + p_dec->fmt_out.i_codec = VLC_CODEC_S16L; + p_dec->fmt_out.audio.i_bitspersample = 16; + date_Init (&p_sys->end_date, p_dec->fmt_out.audio.i_rate, 1); + date_Set (&p_sys->end_date, 0); + + p_dec->p_sys = p_sys; + //==For different version== + //p_dec->pf_decode = DecodeBlock; + //p_dec->pf_flush = Flush; + p_dec->pf_decode_audio = DecodeBlock; + return VLC_SUCCESS;//VLCDEC_SUCCESS +} + + +static void Close (vlc_object_t *p_this) +{ + decoder_sys_t *p_sys = ((decoder_t *)p_this)->p_sys; + + adl_close(p_sys->synth); + free (p_sys); +} + +static void Flush (decoder_t *p_dec) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + + date_Set (&p_sys->end_date, VLC_TS_INVALID); + adl_panic(p_sys->synth); +} + + + +static block_t *DecodeBlock (decoder_t *p_dec, block_t **pp_block) +{ + block_t *p_block; + decoder_sys_t *p_sys = p_dec->p_sys; + block_t *p_out = NULL; + + if (pp_block == NULL) + return NULL; + p_block = *pp_block; + if (p_block == NULL) + return NULL; + *pp_block = NULL; + + if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) + { + date_Set (&p_sys->end_date, 0); + //fluid_synth_system_reset (p_sys->synth); + adl_panic(p_sys->synth); + adl_rt_resetState(p_sys->synth); + } + + if (p_block->i_pts > VLC_TS_INVALID && !date_Get (&p_sys->end_date)) + date_Set (&p_sys->end_date, p_block->i_pts); + else + if (p_block->i_pts < date_Get (&p_sys->end_date)) + { + msg_Warn (p_dec, "MIDI message in the past?"); + goto drop; + } + + if (p_block->i_buffer < 1) + goto drop; + + uint8_t event = p_block->p_buffer[0]; + uint8_t channel = p_block->p_buffer[0] & 0xf; + event &= 0xF0; + + if (event == 0xF0) + switch (channel) + { + case 0: + if (p_block->p_buffer[p_block->i_buffer - 1] != 0xF7) + { + case 7: + msg_Warn (p_dec, "fragmented SysEx not implemented"); + goto drop; + } + //fluid_synth_sysex (p_sys->synth, (char *)p_block->p_buffer + 1, + // p_block->i_buffer - 2, NULL, NULL, NULL, 0); + break; + case 0xF: + adl_rt_resetState(p_sys->synth); + break; + } + + uint8_t p1 = (p_block->i_buffer > 1) ? (p_block->p_buffer[1] & 0x7f) : 0; + uint8_t p2 = (p_block->i_buffer > 2) ? (p_block->p_buffer[2] & 0x7f) : 0; + + switch (event & 0xF0) + { + case 0x80: + adl_rt_noteOff(p_sys->synth, channel, p1); + break; + case 0x90: + adl_rt_noteOn(p_sys->synth, channel, p1, p2); + break; + /*case 0xA0: note aftertouch not implemented */ + case 0xB0: + adl_rt_controllerChange(p_sys->synth, channel, p1, p2); + break; + case 0xC0: + adl_rt_patchChange(p_sys->synth, channel, p1); + break; + case 0xD0: + adl_rt_channelAfterTouch(p_sys->synth, channel, p1); + break; + case 0xE0: + adl_rt_pitchBend(p_sys->synth, channel, (p2 << 7) | p1); + break; + } + + unsigned samples = + (p_block->i_pts - date_Get (&p_sys->end_date)) * 441 / 10000; + if (samples == 0) + goto drop; + + p_out = decoder_NewAudioBuffer (p_dec, samples); + if (p_out == NULL) + goto drop; + + p_out->i_pts = date_Get (&p_sys->end_date ); + samples = adl_generate(p_sys->synth, samples * 2, (short*)p_out->p_buffer); + samples /= 2; + p_out->i_length = date_Increment (&p_sys->end_date, samples) - p_out->i_pts; + +drop: + block_Release (p_block); + return p_out; +} + +#if 0 +static int DecodeBlock (decoder_t *p_dec, block_t *p_block) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + block_t *p_out = NULL; + + if (p_block == NULL) /* No Drain */ + return VLC_SUCCESS;//VLCDEC_SUCCESS + + if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) + { + Flush (p_dec); + if (p_block->i_flags & BLOCK_FLAG_CORRUPTED) + { + block_Release(p_block); + return VLC_SUCCESS;//VLCDEC_SUCCESS + } + } + + if (p_block->i_pts > VLC_TS_INVALID && !date_Get (&p_sys->end_date)) + date_Set (&p_sys->end_date, p_block->i_pts); + else + if (p_block->i_pts < date_Get (&p_sys->end_date)) + { + msg_Warn (p_dec, "MIDI message in the past?"); + goto drop; + } + + if (p_block->i_buffer < 1) + goto drop; + + uint8_t event = p_block->p_buffer[0]; + uint8_t channel = p_block->p_buffer[0] & 0xf; + event &= 0xF0; + + if (event == 0xF0) + switch (channel) + { + case 0: + if (p_block->p_buffer[p_block->i_buffer - 1] != 0xF7) + { + case 7: + msg_Warn (p_dec, "fragmented SysEx not implemented"); + goto drop; + } + //fluid_synth_sysex (p_sys->synth, (char *)p_block->p_buffer + 1, + // p_block->i_buffer - 2, NULL, NULL, NULL, 0); + break; + case 0xF: + adl_rt_resetState(p_sys->synth); + break; + } + + uint8_t p1 = (p_block->i_buffer > 1) ? (p_block->p_buffer[1] & 0x7f) : 0; + uint8_t p2 = (p_block->i_buffer > 2) ? (p_block->p_buffer[2] & 0x7f) : 0; + + switch (event & 0xF0) + { + case 0x80: + adl_rt_noteOff(p_sys->synth, channel, p1); + break; + case 0x90: + adl_rt_noteOn(p_sys->synth, channel, p1, p2); + break; + /*case 0xA0: note aftertouch not implemented */ + case 0xB0: + adl_rt_controllerChange(p_sys->synth, channel, p1, p2); + break; + case 0xC0: + adl_rt_patchChange(p_sys->synth, channel, p1); + break; + case 0xD0: + adl_rt_channelAfterTouch(p_sys->synth, channel, p1); + break; + case 0xE0: + adl_rt_pitchBendML(p_sys->synth, channel, p2, p1); + break; + } + + unsigned samples = + (p_block->i_pts - date_Get (&p_sys->end_date)) * 441 / 10000; + if (samples == 0) + goto drop; + + if (decoder_UpdateAudioFormat (p_dec)) + goto drop; + p_out = decoder_NewAudioBuffer (p_dec, samples); + if (p_out == NULL) + goto drop; + + p_out->i_pts = date_Get (&p_sys->end_date ); + p_out->i_length = date_Increment (&p_sys->end_date, samples) + - p_out->i_pts; + adl_generate(p_sys->synth, samples * 2, p_out->p_buffer); +drop: + block_Release (p_block); + if (p_out != NULL) + decoder_QueueAudio (p_dec, p_out); + return VLC_SUCCESS;//VLCDEC_SUCCESS +} + +#endif -- cgit v1.2.3