/* * OPL4 MIDI synthesizer functions * * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed and/or modified under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE.
*/
/* * Initializes all voices.
*/ void snd_opl4_synth_reset(struct snd_opl4 *opl4)
{ unsignedlong flags; int i;
spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++)
snd_opl4_write(opl4, OPL4_REG_MISC + i, OPL4_DAMP_BIT);
spin_unlock_irqrestore(&opl4->reg_lock, flags);
INIT_LIST_HEAD(&opl4->off_voices);
INIT_LIST_HEAD(&opl4->on_voices);
memset(opl4->voices, 0, sizeof(opl4->voices)); for (i = 0; i < OPL4_MAX_VOICES; i++) {
opl4->voices[i].number = i;
list_add_tail(&opl4->voices[i].list, &opl4->off_voices);
}
snd_midi_channel_set_clear(opl4->chset);
}
/* * Shuts down all voices.
*/ void snd_opl4_synth_shutdown(struct snd_opl4 *opl4)
{ unsignedlong flags; int i;
spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++)
snd_opl4_write(opl4, OPL4_REG_MISC + i,
opl4->voices[i].reg_misc & ~OPL4_KEY_ON_BIT);
spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/* * Executes the callback for all voices playing the specified note.
*/ staticvoid snd_opl4_do_for_note(struct snd_opl4 *opl4, int note, struct snd_midi_channel *chan, void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{ int i; unsignedlong flags; struct opl4_voice *voice;
spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i]; if (voice->chan == chan && voice->note == note) {
func(opl4, voice);
}
}
spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/* * Executes the callback for all voices of to the specified channel.
*/ staticvoid snd_opl4_do_for_channel(struct snd_opl4 *opl4, struct snd_midi_channel *chan, void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{ int i; unsignedlong flags; struct opl4_voice *voice;
spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i]; if (voice->chan == chan) {
func(opl4, voice);
}
}
spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/* * Executes the callback for all active voices.
*/ staticvoid snd_opl4_do_for_all(struct snd_opl4 *opl4, void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{ int i; unsignedlong flags; struct opl4_voice *voice;
spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i]; if (voice->chan)
func(opl4, voice);
}
spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
staticvoid snd_opl4_update_volume(struct snd_opl4 *opl4, struct opl4_voice *voice)
{ int att;
att = voice->sound->tone_attenuate;
att += snd_opl4_volume_table[opl4->chset->gs_master_volume & 0x7f];
att += snd_opl4_volume_table[voice->chan->gm_volume & 0x7f];
att += snd_opl4_volume_table[voice->chan->gm_expression & 0x7f];
att += snd_opl4_volume_table[voice->velocity];
att = 0x7f - (0x7f - att) * (voice->sound->volume_factor) / 0xfe - volume_boost; if (att < 0)
att = 0; elseif (att > 0x7e)
att = 0x7e;
snd_opl4_write(opl4, OPL4_REG_LEVEL + voice->number,
(att << 1) | voice->level_direct);
voice->level_direct = 0;
}
staticvoid snd_opl4_update_pan(struct snd_opl4 *opl4, struct opl4_voice *voice)
{ int pan = voice->sound->panpot;
if (!voice->chan->drum_channel)
pan += (voice->chan->control[MIDI_CTL_MSB_PAN] - 0x40) >> 3; if (pan < -7)
pan = -7; elseif (pan > 7)
pan = 7;
voice->reg_misc = (voice->reg_misc & ~OPL4_PAN_POT_MASK)
| (pan & OPL4_PAN_POT_MASK);
snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
}
staticvoid snd_opl4_update_vibrato_depth(struct snd_opl4 *opl4, struct opl4_voice *voice)
{ int depth;
/* determine the number of voices and voice parameters */
i = chan->drum_channel ? 0x80 : (chan->midi_program & 0x7f);
regions = &snd_yrw801_regions[i]; for (i = 0; i < regions->count; i++) { if (note >= regions->regions[i].key_min &&
note <= regions->regions[i].key_max) {
sound[voices] = ®ions->regions[i].sound; if (++voices >= 2) break;
}
}
/* allocate and initialize the needed voices */
spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < voices; i++) {
voice[i] = snd_opl4_get_voice(opl4);
list_move_tail(&voice[i]->list, &opl4->on_voices);
voice[i]->chan = chan;
voice[i]->note = note;
voice[i]->velocity = vel & 0x7f;
voice[i]->sound = sound[i];
}
/* set tone number (triggers header loading) */ for (i = 0; i < voices; i++) {
voice[i]->reg_f_number =
(sound[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8;
snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice[i]->number,
voice[i]->reg_f_number);
snd_opl4_write(opl4, OPL4_REG_TONE_NUMBER + voice[i]->number,
sound[i]->tone & 0xff);
}
/* set parameters which can be set while loading */ for (i = 0; i < voices; i++) {
voice[i]->reg_misc = OPL4_LFO_RESET_BIT;
snd_opl4_update_pan(opl4, voice[i]);
snd_opl4_update_pitch(opl4, voice[i]);
voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT;
snd_opl4_update_volume(opl4, voice[i]);
}
spin_unlock_irqrestore(&opl4->reg_lock, flags);
/* wait for completion of loading */
snd_opl4_wait_for_wave_headers(opl4);
/* set remaining parameters */
spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < voices; i++) {
snd_opl4_update_tone_parameters(opl4, voice[i]);
voice[i]->reg_lfo_vibrato = voice[i]->sound->reg_lfo_vibrato;
snd_opl4_update_vibrato_depth(opl4, voice[i]);
}
/* finally, switch on all voices */ for (i = 0; i < voices; i++) {
voice[i]->reg_misc =
(voice[i]->reg_misc & 0x1f) | OPL4_KEY_ON_BIT;
snd_opl4_write(opl4, OPL4_REG_MISC + voice[i]->number,
voice[i]->reg_misc);
}
spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
switch (type) { case MIDI_CTL_MSB_MODWHEEL:
chan->control[MIDI_CTL_VIBRATO_DEPTH] = chan->control[MIDI_CTL_MSB_MODWHEEL];
snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_vibrato_depth); break; case MIDI_CTL_MSB_MAIN_VOLUME:
snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_volume); break; case MIDI_CTL_MSB_PAN:
snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_pan); break; case MIDI_CTL_MSB_EXPRESSION:
snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_volume); break; case MIDI_CTL_VIBRATO_RATE: /* not yet supported */ break; case MIDI_CTL_VIBRATO_DEPTH:
snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_vibrato_depth); break; case MIDI_CTL_VIBRATO_DELAY: /* not yet supported */ break; case MIDI_CTL_E1_REVERB_DEPTH: /* * Each OPL4 voice has a bit called "Pseudo-Reverb", but * IMHO _not_ using it enhances the listening experience.
*/ break; case MIDI_CTL_PITCHBEND:
snd_opl4_do_for_channel(opl4, chan, snd_opl4_update_pitch); break;
}
}
void snd_opl4_sysex(void *private_data, unsignedchar *buf, int len, int parsed, struct snd_midi_channel_set *chset)
{ struct snd_opl4 *opl4 = private_data;
if (parsed == SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME)
snd_opl4_do_for_all(opl4, snd_opl4_update_volume);
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.